Coverage Report

Created: 2026-01-09 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/PlugIns/BSPSceneManager/src/OgreBspLevel.cpp
Line
Count
Source
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
    (Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
#include "OgreBspLevel.h"
29
#include "OgreException.h"
30
#include "OgreMaterial.h"
31
#include "OgreMaterialManager.h"
32
#include "OgreMovableObject.h"
33
#include "OgreSceneManager.h"
34
#include "OgrePatchSurface.h"
35
#include "OgreQuake3ShaderManager.h"
36
#include "OgreQuake3Shader.h"
37
#include "OgreMath.h"
38
#include "OgreStringVector.h"
39
#include "OgreStringConverter.h"
40
#include "OgreLogManager.h"
41
#include "OgreTechnique.h"
42
#include "OgrePass.h"
43
#include "OgreTextureUnitState.h"
44
#include "OgreResourceGroupManager.h"
45
#include "OgreHardwarePixelBuffer.h"
46
#include "OgreBspSceneManager.h"
47
48
namespace Ogre {
49
50
0
    #define NUM_FACES_PER_PROGRESS_REPORT 100
51
0
    #define NUM_NODES_PER_PROGRESS_REPORT 50
52
0
    #define NUM_LEAVES_PER_PROGRESS_REPORT 50
53
0
    #define NUM_BRUSHES_PER_PROGRESS_REPORT 50
54
55
    //-----------------------------------------------------------------------
56
    BspLevel::BspLevel(ResourceManager* creator, const String& name, 
57
        ResourceHandle handle, const String& group, bool isManual, 
58
        ManualResourceLoader* loader)
59
0
      : Resource(creator, name, handle, group, isManual, loader), 
60
0
        mRootNode(0),
61
0
        mLeafFaceGroups(0),
62
0
        mFaceGroups(0), 
63
0
        mBrushes(0),
64
0
        mSkyEnabled(false)
65
0
    {
66
0
        mVisData.tableData = 0;
67
68
0
        if (createParamDictionary("BspLevel"))
69
0
        {
70
            // nothing
71
0
        }
72
0
    }
73
74
    //-----------------------------------------------------------------------
75
    BspLevel::~BspLevel()
76
0
    {
77
        // have to call this here reather than in Resource destructor
78
        // since calling virtual methods in base destructors causes crash
79
0
        unload(); 
80
81
0
    }
82
83
    //-----------------------------------------------------------------------
84
    void BspLevel::loadImpl()
85
0
    {
86
0
        mSkyEnabled = false;
87
88
        // Use Quake3 file loader
89
0
        Quake3Level q3;
90
0
        DataStreamPtr stream = 
91
0
            ResourceGroupManager::getSingleton().openResource(mName, 
92
0
                ResourceGroupManager::getSingleton().getWorldResourceGroupName());
93
94
0
        q3.loadFromStream(stream);
95
96
0
        loadQuake3Level(q3);
97
98
0
    }
99
    //-----------------------------------------------------------------------
100
    bool BspLevel::isSkyEnabled(void) const
101
0
    {
102
0
        return mSkyEnabled;
103
0
    }
104
    //-----------------------------------------------------------------------
105
    const String& BspLevel::getSkyMaterialName(void) const
106
0
    {
107
0
        return mSkyMaterial;
108
0
    }
109
    //-----------------------------------------------------------------------
110
    Real BspLevel::getSkyCurvature(void) const
111
0
    {
112
0
        return mSkyCurvature;
113
0
    }
114
    //-----------------------------------------------------------------------
115
    void BspLevel::load(const DataStreamPtr& stream)
116
0
    {
117
        // Use Quake3 file loader
118
0
        Quake3Level q3;
119
0
        q3.loadFromStream(stream);
120
121
0
        loadQuake3Level(q3);
122
123
0
    }
124
    //-----------------------------------------------------------------------
125
    void BspLevel::unloadImpl()
126
0
    {
127
0
        if (mRenderOp.vertexData)
128
0
            OGRE_DELETE mRenderOp.vertexData;
129
0
        mIndexes.reset();
130
0
        if (mFaceGroups)
131
0
            OGRE_DELETE_ARRAY_T(mFaceGroups, StaticFaceGroup, (size_t)mNumFaceGroups, MEMCATEGORY_GEOMETRY);
132
0
        if (mLeafFaceGroups)
133
0
            OGRE_FREE(mLeafFaceGroups, MEMCATEGORY_GEOMETRY);
134
0
        if (mRootNode)
135
0
            OGRE_DELETE [] mRootNode;
136
0
        if (mVisData.tableData)
137
0
            OGRE_FREE(mVisData.tableData, MEMCATEGORY_GEOMETRY);
138
0
        if (mBrushes)
139
0
            OGRE_DELETE_ARRAY_T(mBrushes, Brush, (size_t)mNumBrushes, MEMCATEGORY_GEOMETRY);
140
141
        // no need to delete index buffer, will be handled by shared pointer
142
0
        OGRE_DELETE mRenderOp.indexData;
143
0
        mRenderOp.indexData = 0;
144
0
        mRenderOp.vertexData = 0;
145
0
        mRootNode = 0;
146
0
        mFaceGroups = 0;
147
0
        mLeafFaceGroups = 0;
148
0
        mBrushes = 0;
149
0
        mVisData.tableData = 0;
150
0
        for (PatchMap::iterator pi = mPatches.begin(); pi != mPatches.end(); ++pi)
151
0
        {
152
0
            OGRE_DELETE pi->second;
153
0
        }
154
0
        mPatches.clear();
155
0
    }
156
    //-----------------------------------------------------------------------
157
    size_t BspLevel::calculateLoadingStages(const String& levelName)
158
0
    {
159
0
        DataStreamPtr stream = 
160
0
            ResourceGroupManager::getSingleton().openResource(levelName, 
161
0
            ResourceGroupManager::getSingleton().getWorldResourceGroupName());
162
0
        return calculateLoadingStages(stream);
163
0
    }
164
    //-----------------------------------------------------------------------
165
    size_t BspLevel::calculateLoadingStages(DataStreamPtr& stream)
166
0
    {
167
0
        Quake3Level q3;
168
169
        // Load header only
170
0
        q3.loadHeaderFromStream(stream);
171
172
        // Ok, count up the things that we will report
173
0
        size_t stages = 0;
174
175
        // loadEntities (1 stage)
176
0
        ++stages;
177
        // extractLightmaps (external, 1 stage)
178
0
        ++stages;
179
        // initQuake3Patches
180
0
        ++stages;
181
        // vertex setup
182
0
        ++stages;
183
        // face setup
184
0
        ++stages;
185
        // patch building
186
0
        ++stages;
187
        // material setup
188
        // this is not strictly based on load, since we only know the number
189
        // of faces, not the number of materials
190
        // raise one event for every 50 faces, plus one at the end
191
0
        stages += (q3.mNumFaces / NUM_FACES_PER_PROGRESS_REPORT) + 1;
192
        // node setup
193
0
        stages += (q3.mNumNodes / NUM_NODES_PER_PROGRESS_REPORT) + 1;
194
        // brush setup
195
0
        stages += (q3.mNumBrushes / NUM_BRUSHES_PER_PROGRESS_REPORT) + 1;
196
        // leaf setup
197
0
        stages += (q3.mNumLeaves / NUM_LEAVES_PER_PROGRESS_REPORT) + 1;
198
        // vis
199
0
        ++stages;
200
201
0
        return stages;
202
203
0
    }
204
    //-----------------------------------------------------------------------
205
    void BspLevel::loadQuake3Level(const Quake3Level& q3lvl)
206
0
    {
207
0
        MaterialManager& mm = MaterialManager::getSingleton();
208
0
        ResourceGroupManager& rgm = ResourceGroupManager::getSingleton();
209
210
0
        rgm._notifyCustomStageStarted("Parsing entities");
211
0
        loadEntities(q3lvl);
212
0
        rgm._notifyCustomStageEnded();
213
214
        // Extract lightmaps into textures
215
0
        rgm._notifyCustomStageStarted("Extracting lightmaps");
216
0
        q3lvl.extractLightmaps();
217
0
        rgm._notifyCustomStageEnded();
218
219
        //-----------------------------------------------------------------------
220
        // Vertices
221
        //-----------------------------------------------------------------------
222
        // Allocate memory for vertices & copy
223
0
        mRenderOp.vertexData = OGRE_NEW VertexData();
224
225
        /// Create vertex declaration
226
0
        VertexDeclaration* decl = mRenderOp.vertexData->vertexDeclaration;
227
0
        size_t offset = 0;
228
0
        offset += decl->addElement(0, offset, VET_FLOAT3, VES_POSITION).getSize();
229
0
        offset += decl->addElement(0, offset, VET_FLOAT3, VES_NORMAL).getSize();
230
0
        offset += decl->addElement(0, offset, VET_COLOUR, VES_DIFFUSE).getSize();
231
0
        offset += decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0).getSize();
232
0
        decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 1);
233
234
        // Build initial patches - we need to know how big the vertex buffer needs to be
235
        // to accommodate the subdivision
236
0
        rgm._notifyCustomStageStarted("Initialising patches");
237
0
        initQuake3Patches(q3lvl, decl);
238
0
        rgm._notifyCustomStageEnded();
239
240
        /// Create the vertex buffer, allow space for patches
241
0
        rgm._notifyCustomStageStarted("Setting up vertex data");
242
0
        HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager::getSingleton()
243
0
            .createVertexBuffer(
244
0
                sizeof(BspVertex), 
245
0
                q3lvl.mNumVertices + mPatchVertexCount, 
246
0
                HardwareBuffer::HBU_STATIC_WRITE_ONLY);
247
        //COPY static vertex data - Note that we can't just block-copy the vertex data because we have to reorder
248
        //    our vertex elements; this is to ensure compatibility with older cards when using
249
        //    hardware vertex buffers - Direct3D requires that the buffer format maps onto a
250
        //    FVF in those older drivers. 
251
        // Lock just the non-patch area for now
252
0
        BspVertex* pVert = static_cast<BspVertex*>(
253
0
            vbuf->lock(0, q3lvl.mNumVertices * sizeof(BspVertex), HardwareBuffer::HBL_DISCARD) );
254
        // Keep another base pointer for use later in patch building
255
0
        for (int v = 0; v < q3lvl.mNumVertices; ++v)
256
0
        {
257
0
            quakeVertexToBspVertex(&q3lvl.mVertices[v], pVert++);
258
0
        }
259
0
        vbuf->unlock();
260
        // Setup binding
261
0
        mRenderOp.vertexData->vertexBufferBinding->setBinding(0, vbuf);
262
        // Set other data
263
0
        mRenderOp.vertexData->vertexStart = 0;
264
0
        mRenderOp.vertexData->vertexCount = q3lvl.mNumVertices + mPatchVertexCount;
265
0
        rgm._notifyCustomStageEnded();
266
267
        //-----------------------------------------------------------------------
268
        // Faces
269
        // --------
270
0
        rgm._notifyCustomStageStarted("Setting up face data");
271
0
        mNumLeafFaceGroups = q3lvl.mNumLeafFaces;
272
0
        mLeafFaceGroups = OGRE_ALLOC_T(int, mNumLeafFaceGroups, MEMCATEGORY_GEOMETRY);
273
0
        memcpy(mLeafFaceGroups, q3lvl.mLeafFaces, sizeof(int)*mNumLeafFaceGroups);
274
0
        mNumFaceGroups = q3lvl.mNumFaces;
275
0
        mFaceGroups = OGRE_NEW_ARRAY_T(StaticFaceGroup, mNumFaceGroups, MEMCATEGORY_GEOMETRY);
276
        // Set up index buffer
277
        // NB Quake3 indexes are 32-bit
278
        // Copy the indexes into a software area for staging
279
0
        size_t numIndexes = q3lvl.mNumElements + mPatchIndexCount;
280
        // Create an index buffer manually in system memory, allow space for patches
281
0
        mIndexes = std::make_shared<HardwareIndexBuffer>(nullptr, HardwareIndexBuffer::IT_32BIT, numIndexes,
282
0
                                                         new DefaultHardwareBuffer(numIndexes * sizeof(int)));
283
        // Write main indexes
284
0
        mIndexes->writeData(0, sizeof(unsigned int) * q3lvl.mNumElements, q3lvl.mElements, true);
285
        // create actual hardware index buffer
286
        // Create enough index space to render whole level, index data is per-frame
287
0
        mRenderOp.indexData = OGRE_NEW IndexData();
288
0
        mRenderOp.indexData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(
289
0
            HardwareIndexBuffer::IT_32BIT, numIndexes, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
290
0
        rgm._notifyCustomStageEnded();
291
292
        // now build patch information
293
0
        rgm._notifyCustomStageStarted("Building patches");
294
0
        buildQuake3Patches(q3lvl.mNumVertices, q3lvl.mNumElements);
295
0
        rgm._notifyCustomStageEnded();
296
297
        //-----------------------------------------------------------------------
298
        // Create materials for shaders
299
        //-----------------------------------------------------------------------
300
        // NB this only works for the 'default' shaders for now
301
        //  i.e. those that don't have a .shader script and thus default
302
        //  to just texture + lightmap
303
        // TODO: pre-parse all .shader files and create lookup for next stage (use ROGL shader_file_t)
304
305
        // Material names are shadername#lightmapnumber
306
        // This is because I like to define materials up front completely
307
        //  rather than combine lightmap and shader dynamically (it's
308
        //  more generic). It results in more materials, but they're small
309
        //  beer anyway. Texture duplication is prevented by infrastructure.
310
        // To do this I actually need to parse the faces since they have the
311
        //  shader/lightmap combo (lightmap number is not in the shader since
312
        //  it can be used with multiple lightmaps)
313
0
        String shaderName;
314
0
        int face;
315
0
        face = q3lvl.mNumFaces;
316
0
        ResourceHandle matHandle;
317
0
        String meshName;
318
319
0
        String resourceGroup = ResourceGroupManager::getSingleton().getWorldResourceGroupName();
320
0
        size_t progressCountdown = NUM_FACES_PER_PROGRESS_REPORT;
321
0
        size_t progressCount = 0;
322
323
0
        while(face--)
324
0
        {
325
            // Progress reporting
326
0
            if (progressCountdown == NUM_FACES_PER_PROGRESS_REPORT)
327
0
            {
328
0
                ++progressCount;
329
0
                StringStream str;
330
0
                str << "Loading materials (phase " << progressCount << ")"; 
331
0
                rgm._notifyCustomStageStarted(str.str());
332
0
            }
333
0
            else if (progressCountdown == 0)
334
0
            {
335
                // stage report
336
0
                rgm._notifyCustomStageEnded();
337
0
                progressCountdown = NUM_FACES_PER_PROGRESS_REPORT + 1; 
338
339
0
            }
340
341
            // Check to see if existing material
342
            // Format shader#lightmap
343
0
            int shadIdx = q3lvl.mFaces[face].shader;
344
0
            StringStream tmp;
345
0
            tmp << q3lvl.mShaders[shadIdx].name << "#" << q3lvl.mFaces[face].lm_texture;
346
0
            shaderName = tmp.str();
347
348
0
            MaterialPtr shadMat = MaterialManager::getSingleton().getByName(shaderName);
349
0
            if (!shadMat)
350
0
            {
351
                // Build new material
352
353
                // Colour layer
354
                // NB no extension in Q3A(doh), have to try shader, .jpg, .tga
355
0
                String tryName = q3lvl.mShaders[shadIdx].name;
356
                // Try shader first
357
0
                Quake3Shader* pShad = Quake3ShaderManager::getSingleton().getByName(tryName);
358
0
                if (pShad)
359
0
                {
360
0
                    shadMat = pShad->createAsMaterial(q3lvl.mFaces[face].lm_texture);
361
                    // Do skydome (use this material)
362
0
                    if (pShad->skyDome)
363
0
                    {
364
0
                        mSkyEnabled = true;
365
0
                        mSkyMaterial = shadMat->getName();
366
0
                        mSkyCurvature = 20 - (pShad->cloudHeight / 256 * 18);
367
0
                    }
368
0
                }
369
0
                else
370
0
                {
371
                    // No shader script, try default type texture
372
0
                    shadMat = mm.create(shaderName, resourceGroup);
373
0
                    Pass *shadPass = shadMat->getTechnique(0)->getPass(0);
374
                    // Try jpg
375
0
                    TextureUnitState* tex = 0;
376
0
                    if (ResourceGroupManager::getSingleton().resourceExists(resourceGroup, tryName + ".jpg"))
377
0
                    {
378
0
                        tex = shadPass->createTextureUnitState(tryName + ".jpg");
379
0
                    }
380
0
                    else if (ResourceGroupManager::getSingleton().resourceExists(resourceGroup, tryName + ".tga"))
381
0
                    {
382
0
                        tex = shadPass->createTextureUnitState(tryName + ".tga");
383
0
                    }
384
385
0
                    if (tex)
386
0
                    {
387
                        // Set replace on all first layer textures for now
388
0
                        tex->setColourOperation(LBO_REPLACE);
389
0
                        tex->setTextureAddressingMode(TextureUnitState::TAM_WRAP);
390
0
                    }
391
392
0
                    if (q3lvl.mFaces[face].lm_texture >= 0)
393
0
                    {
394
                        // Add lightmap, additive blending
395
0
                        StringStream lightmapName;
396
0
                        lightmapName << "@lightmap" << q3lvl.mFaces[face].lm_texture;
397
0
                        tex = shadPass->createTextureUnitState(lightmapName.str());
398
                        // Blend
399
0
                        tex->setColourOperation(LBO_MODULATE);
400
                        // Use 2nd texture co-ordinate set
401
0
                        tex->setTextureCoordSet(1);
402
                        // Clamp
403
0
                        tex->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
404
405
0
                    }
406
                    // Set culling mode to none
407
0
                    shadMat->setCullingMode(CULL_NONE);
408
                    // No dynamic lighting
409
0
                    shadMat->setLightingEnabled(false);
410
411
0
                }
412
0
            }
413
0
            matHandle = shadMat->getHandle();
414
0
            shadMat->load();
415
416
            // Copy face data
417
0
            StaticFaceGroup* dest = &mFaceGroups[face];
418
0
            bsp_face_t* src = &q3lvl.mFaces[face];
419
420
0
            if (q3lvl.mShaders[src->shader].surface_flags & SURF_SKY)
421
0
            {
422
0
                dest->isSky = true;
423
0
            }
424
0
            else
425
0
            {
426
0
                dest->isSky = false;
427
0
            }
428
429
430
0
            dest->materialHandle = matHandle;
431
0
            dest->elementStart = src->elem_start;
432
0
            dest->numElements = src->elem_count;
433
0
            dest->numVertices = src->vert_count;
434
0
            dest->vertexStart = src->vert_start;
435
0
            if (src->type == BSP_FACETYPE_NORMAL)
436
0
            {
437
0
                dest->fType = FGT_FACE_LIST;
438
                // Assign plane
439
0
                dest->plane.normal = Vector3(src->normal[0], src->normal[1], src->normal[2]);
440
0
                dest->plane.d = -dest->plane.normal.dotProduct(
441
0
                    Vector3(src->org[0], src->org[1], src->org[2]));
442
443
                // Don't rebase indexes here - Quake3 re-uses some indexes for multiple vertex
444
                // groups eg repeating small details have the same relative vertex data but
445
                // use the same index data.
446
447
0
            }
448
0
            else if (src->type == BSP_FACETYPE_PATCH)
449
0
            {
450
                // Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0?
451
0
                if (dest->numVertices == 0 || src->mesh_cp[0] == 0)
452
0
                {
453
0
                    dest->fType = FGT_UNKNOWN;
454
0
                }
455
0
                else
456
0
                {
457
458
                    // Set up patch surface
459
0
                    dest->fType = FGT_PATCH;
460
                    
461
                    // Locate the patch we already built
462
0
                    PatchMap::iterator p = mPatches.find(face);
463
0
                    if (p == mPatches.end())
464
0
                    {
465
0
                        OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Patch not found from previous built state",
466
0
                        "BspLevel::loadQuake3Level");
467
0
                    }
468
469
0
                    dest->patchSurf = p->second;
470
                    
471
0
                }
472
473
474
0
            }
475
0
            else if (src->type == BSP_FACETYPE_MESH)
476
0
            {
477
0
                dest->fType = FGT_FACE_LIST;
478
                // Assign plane
479
0
                dest->plane.normal = Vector3(src->normal[0], src->normal[1], src->normal[2]);
480
0
                dest->plane.d = -dest->plane.normal.dotProduct(
481
0
                    Vector3(src->org[0], src->org[1], src->org[2]));                
482
0
            }
483
0
            else
484
0
            {
485
0
                LogManager::getSingleton().logError("Unknown Face Type");
486
0
            }
487
488
            // progress reporting
489
0
            --progressCountdown;
490
491
0
        }
492
        // final stage report
493
0
        rgm._notifyCustomStageEnded();
494
495
        //-----------------------------------------------------------------------
496
        // Nodes
497
        //-----------------------------------------------------------------------
498
        // Allocate memory for all nodes (leaves and splitters)
499
0
        mNumNodes = q3lvl.mNumNodes + q3lvl.mNumLeaves;
500
0
        mNumLeaves = q3lvl.mNumLeaves;
501
0
        mLeafStart = q3lvl.mNumNodes;
502
0
        mRootNode = OGRE_NEW BspNode[mNumNodes];
503
0
        int i;
504
        // Convert nodes
505
        // In our array, first q3lvl.mNumNodes are non-leaf, others are leaves
506
0
        progressCountdown = NUM_NODES_PER_PROGRESS_REPORT;
507
0
        progressCount = 0;
508
509
0
        for (i = 0; i < q3lvl.mNumNodes; ++i)
510
0
        {
511
            // Progress reporting
512
0
            if (progressCountdown == NUM_NODES_PER_PROGRESS_REPORT)
513
0
            {
514
0
                ++progressCount;
515
0
                StringStream str;
516
0
                str << "Loading nodes (phase " << progressCount << ")"; 
517
0
                    rgm._notifyCustomStageStarted(str.str());
518
0
            }
519
0
            else if (progressCountdown == 0)
520
0
            {
521
                // stage report
522
0
                rgm._notifyCustomStageEnded();
523
0
                progressCountdown = NUM_NODES_PER_PROGRESS_REPORT + 1; 
524
525
0
            }
526
527
0
            BspNode* node = &mRootNode[i];
528
0
            bsp_node_t* q3node = &q3lvl.mNodes[i];
529
530
            // Set non-leaf
531
0
            node->mIsLeaf = false;
532
            // Set owner
533
0
            node->mOwner = this;
534
            // Set plane
535
0
            node->mSplitPlane.normal.x = q3lvl.mPlanes[q3node->plane].normal[0];
536
0
            node->mSplitPlane.normal.y = q3lvl.mPlanes[q3node->plane].normal[1];
537
0
            node->mSplitPlane.normal.z = q3lvl.mPlanes[q3node->plane].normal[2];
538
0
            node->mSplitPlane.d = -q3lvl.mPlanes[q3node->plane].dist;
539
            // Set bounding box
540
0
            node->mBounds.setMinimum(Vector3(&q3node->bbox[0]));
541
0
            node->mBounds.setMaximum(Vector3(&q3node->bbox[3]));
542
            // Set back pointer
543
            // Negative indexes in Quake3 mean leaves
544
0
            if (q3node->back < 0)
545
0
            {
546
                // Points to leaf, offset to leaf start and negate index
547
0
                node->mBack = &mRootNode[mLeafStart + (~(q3node->back))];
548
549
0
            }
550
0
            else
551
0
            {
552
                // Points to node
553
0
                node->mBack = &mRootNode[q3node->back];
554
0
            }
555
            // Set front pointer
556
            // Negative indexes in Quake3 mean leaves
557
0
            if (q3node->front < 0)
558
0
            {
559
                // Points to leaf, offset to leaf start and negate index
560
0
                node->mFront = &mRootNode[mLeafStart + (~(q3node->front))];
561
562
0
            }
563
0
            else
564
0
            {
565
                // Points to node
566
0
                node->mFront = &mRootNode[q3node->front];
567
0
            }
568
569
0
            --progressCountdown;
570
571
572
0
        }
573
        // final stage report
574
0
        rgm._notifyCustomStageEnded();
575
576
        //-----------------------------------------------------------------------
577
        // Brushes
578
        //-----------------------------------------------------------------------
579
        // Reserve enough memory for all brushes, solid or not (need to maintain indexes)
580
0
        mNumBrushes = q3lvl.mNumBrushes;
581
0
        mBrushes = OGRE_NEW_ARRAY_T(BspNode::Brush, mNumBrushes, MEMCATEGORY_GEOMETRY);
582
0
        progressCountdown = NUM_BRUSHES_PER_PROGRESS_REPORT;
583
0
        progressCount = 0;
584
585
0
        for (i = 0; i < q3lvl.mNumBrushes; ++i)
586
0
        {
587
            // Progress reporting
588
0
            if (progressCountdown == NUM_BRUSHES_PER_PROGRESS_REPORT)
589
0
            {
590
0
                ++progressCount;
591
0
                StringStream str;
592
0
                str << "Loading brushes (phase " << progressCount << ")"; 
593
0
                    rgm._notifyCustomStageStarted(str.str());
594
0
            }
595
0
            else if (progressCountdown == 0)
596
0
            {
597
                // stage report
598
0
                rgm._notifyCustomStageEnded();
599
0
                progressCountdown = NUM_BRUSHES_PER_PROGRESS_REPORT + 1; 
600
601
0
            }
602
0
            bsp_brush_t* q3brush = &q3lvl.mBrushes[i];
603
604
            // Create a new OGRE brush
605
0
            BspNode::Brush *pBrush = &(mBrushes[i]);
606
0
            int brushSideIdx, numBrushSides;
607
0
            numBrushSides = q3brush->numsides;
608
0
            brushSideIdx = q3brush->firstside;
609
            // Iterate over the sides and create plane for each
610
0
            while (numBrushSides--)
611
0
            {
612
0
                bsp_brushside_t* q3brushside = &q3lvl.mBrushSides[brushSideIdx];
613
0
                bsp_plane_t* q3brushplane = &q3lvl.mPlanes[q3brushside->planenum];
614
                // Notice how we normally invert Q3A plane distances, but here we do not
615
                // Because we want plane normals pointing out of solid brushes, not in
616
0
                Plane brushSide(
617
0
                    Vector3(
618
0
                        q3brushplane->normal[0], 
619
0
                        q3brushplane->normal[1], 
620
0
                        q3brushplane->normal[2]), 
621
0
                    q3brushplane->dist);
622
0
                pBrush->planes.push_back(brushSide);
623
0
                ++brushSideIdx;
624
0
            }
625
            // Build world fragment
626
0
            pBrush->fragment.fragmentType = WFT_PLANE_BOUNDED_REGION;
627
0
            pBrush->fragment.planes = &(pBrush->planes);
628
629
0
            --progressCountdown;
630
631
0
        }
632
        // final stage report
633
0
        rgm._notifyCustomStageEnded();
634
635
636
637
        //-----------------------------------------------------------------------
638
        // Leaves
639
        //-----------------------------------------------------------------------
640
0
        progressCountdown = NUM_LEAVES_PER_PROGRESS_REPORT;
641
0
        progressCount = 0;
642
0
        for (i = 0; i < q3lvl.mNumLeaves; ++i)
643
0
        {
644
            // Progress reporting
645
0
            if (progressCountdown == NUM_LEAVES_PER_PROGRESS_REPORT)
646
0
            {
647
0
                ++progressCount;
648
0
                StringStream str;
649
0
                str << "Loading leaves (phase " << progressCount << ")"; 
650
0
                    rgm._notifyCustomStageStarted(str.str());
651
0
            }
652
0
            else if (progressCountdown == 0)
653
0
            {
654
                // stage report
655
0
                rgm._notifyCustomStageEnded();
656
0
                progressCountdown = NUM_LEAVES_PER_PROGRESS_REPORT + 1; 
657
658
0
            }
659
0
            BspNode* node = &mRootNode[i + mLeafStart];
660
0
            bsp_leaf_t* q3leaf = &q3lvl.mLeaves[i];
661
662
            // Set leaf
663
0
            node->mIsLeaf = true;
664
            // Set owner
665
0
            node->mOwner = this;
666
            // Set bounding box
667
0
            node->mBounds.setMinimum(Vector3(&q3leaf->bbox[0]));
668
0
            node->mBounds.setMaximum(Vector3(&q3leaf->bbox[3]));
669
            // Set faces
670
0
            node->mFaceGroupStart = q3leaf->face_start;
671
0
            node->mNumFaceGroups = q3leaf->face_count;
672
673
0
            node->mVisCluster = q3leaf->cluster;
674
675
            // Load Brushes for this leaf
676
0
            int brushIdx, brushCount, realBrushIdx;
677
0
            brushCount = q3leaf->brush_count;
678
0
            brushIdx = q3leaf->brush_start;
679
680
0
            while(brushCount--)
681
0
            {
682
0
                realBrushIdx = q3lvl.mLeafBrushes[brushIdx];
683
0
                bsp_brush_t* q3brush = &q3lvl.mBrushes[realBrushIdx];
684
                // Only load solid ones, we don't care about any other types
685
                // Shader determines this
686
0
                bsp_shader_t* brushShader = &q3lvl.mShaders[q3brush->shaderIndex];
687
0
                if (brushShader->content_flags & CONTENTS_SOLID)
688
0
                {
689
                    // Get brush 
690
0
                    BspNode::Brush *pBrush = &(mBrushes[realBrushIdx]);
691
0
                    assert(pBrush->fragment.fragmentType == WFT_PLANE_BOUNDED_REGION);
692
                    // Assign node pointer
693
0
                    node->mSolidBrushes.push_back(pBrush);
694
0
                }
695
0
                ++brushIdx;
696
0
            }
697
698
0
            --progressCountdown;
699
700
0
        }
701
        // final stage report
702
0
        rgm._notifyCustomStageEnded();
703
704
705
706
        // Vis - just copy
707
        // final stage report
708
0
        rgm._notifyCustomStageStarted("Copying Vis data");
709
0
        mVisData.numClusters = q3lvl.mVis->cluster_count;
710
0
        mVisData.rowLength = q3lvl.mVis->row_size;
711
0
        mVisData.tableData = OGRE_ALLOC_T(unsigned char, q3lvl.mVis->row_size * q3lvl.mVis->cluster_count, MEMCATEGORY_GEOMETRY);
712
0
        memcpy(mVisData.tableData, q3lvl.mVis->data, q3lvl.mVis->row_size * q3lvl.mVis->cluster_count);
713
0
        rgm._notifyCustomStageEnded();
714
715
716
717
0
    }
718
    //-----------------------------------------------------------------------
719
    unsigned int BspLevel::cacheGeometry(uint32* pIndexes, const StaticFaceGroup* faceGroup)
720
0
    {
721
        // Skip sky always
722
0
        if (faceGroup->isSky)
723
0
            return 0;
724
725
0
        size_t idxStart, numIdx, vertexStart;
726
727
0
        if (faceGroup->fType == FGT_FACE_LIST)
728
0
        {
729
0
            idxStart = faceGroup->elementStart;
730
0
            numIdx = faceGroup->numElements;
731
0
            vertexStart = faceGroup->vertexStart;
732
0
        }
733
0
        else if (faceGroup->fType == FGT_PATCH)
734
0
        {
735
736
0
            idxStart = faceGroup->patchSurf->getIndexOffset();
737
0
            numIdx = faceGroup->patchSurf->getCurrentIndexCount();
738
0
            vertexStart = faceGroup->patchSurf->getVertexOffset();
739
0
        }
740
0
        else
741
0
        {
742
            // Unsupported face type
743
0
            return 0;
744
0
        }
745
746
        // Copy index data
747
0
        unsigned int* pSrc = static_cast<unsigned int*>(mIndexes->lock(
748
0
            idxStart * sizeof(unsigned int), numIdx * sizeof(unsigned int), HardwareBuffer::HBL_READ_ONLY));
749
        // Offset the indexes here
750
        // we have to do this now rather than up-front because the
751
        // indexes are sometimes reused to address different vertex chunks
752
0
        for (size_t elem = 0; elem < numIdx; ++elem)
753
0
        {
754
0
            *pIndexes++ = *pSrc++ + static_cast<unsigned int>(vertexStart);
755
0
        }
756
0
        mIndexes->unlock();
757
758
        // return number of elements
759
0
        return static_cast<unsigned int>(numIdx);
760
0
    }
761
    bool BspLevel::cacheGeometry(const std::vector<StaticFaceGroup*>& materialFaceGroups)
762
0
    {
763
        // Empty existing cache
764
0
        mRenderOp.indexData->indexCount = 0;
765
        // lock index buffer ready to receive data
766
0
        uint32* pIdx =
767
0
            static_cast<uint32*>(mRenderOp.indexData->indexBuffer->lock(HardwareBuffer::HBL_DISCARD));
768
0
        for (auto faceGroup : materialFaceGroups)
769
0
        {
770
            // Cache each
771
0
            unsigned int numelems = cacheGeometry(pIdx, faceGroup);
772
0
            mRenderOp.indexData->indexCount += numelems;
773
0
            pIdx += numelems;
774
0
        }
775
        // Unlock the buffer
776
0
        mRenderOp.indexData->indexBuffer->unlock();
777
778
        // Skip if no faces to process (we're not doing flare types yet)
779
0
        return mRenderOp.indexData->indexCount != 0;
780
0
    }
781
782
    //-----------------------------------------------------------------------
783
    void BspLevel::initQuake3Patches(const Quake3Level & q3lvl, VertexDeclaration* decl)
784
0
    {
785
0
        int face;
786
787
0
        mPatchVertexCount = 0;
788
0
        mPatchIndexCount = 0;
789
790
        // We're just building the patch here to get a hold on the size of the mesh
791
        // although we'll reuse this information later
792
0
        face = q3lvl.mNumFaces;
793
0
        while (face--)
794
0
        {
795
796
0
            bsp_face_t* src = &q3lvl.mFaces[face];
797
798
0
            if (src->type == BSP_FACETYPE_PATCH)
799
0
            {
800
                // Seems to be some crap in the Q3 level where vertex count = 0 or num control points = 0?
801
0
                if (src->vert_count == 0 || src->mesh_cp[0] == 0)
802
0
                {
803
0
                    continue;
804
0
                }
805
0
                PatchSurface* ps = OGRE_NEW PatchSurface();
806
                // Set up control points & format
807
                // Reuse the vertex declaration 
808
                // Copy control points into a buffer so we can convert their format
809
0
                BspVertex* pControlPoints = OGRE_ALLOC_T(BspVertex, src->vert_count, MEMCATEGORY_GEOMETRY);
810
0
                bsp_vertex_t* pSrc = q3lvl.mVertices + src->vert_start;
811
0
                for (int v = 0; v < src->vert_count; ++v)
812
0
                {
813
0
                    quakeVertexToBspVertex(pSrc++, &pControlPoints[v]);
814
0
                }
815
                // Define the surface, but don't build it yet (no vertex / index buffer)
816
0
                ps->defineSurface(
817
0
                    pControlPoints,
818
0
                    decl, 
819
0
                    src->mesh_cp[0],
820
0
                    src->mesh_cp[1],
821
0
                    PatchSurface::PST_BEZIER);
822
                // Get stats
823
0
                mPatchVertexCount += ps->getRequiredVertexCount();
824
0
                mPatchIndexCount += ps->getRequiredIndexCount();
825
826
                // Save the surface for later
827
0
                mPatches[face] = ps;
828
0
            }
829
830
831
0
        }
832
833
0
    }
834
    //-----------------------------------------------------------------------
835
    void BspLevel::buildQuake3Patches(size_t vertOffset, size_t indexOffset)
836
0
    {
837
        // Loop through the patches
838
0
        PatchMap::iterator i, iend;
839
0
        iend = mPatches.end();
840
841
0
        size_t currVertOffset = vertOffset;
842
0
        size_t currIndexOffset = indexOffset;
843
844
0
        HardwareVertexBufferSharedPtr vbuf = mRenderOp.vertexData->vertexBufferBinding->getBuffer(0);
845
846
0
        for (i = mPatches.begin(); i != iend; ++i)
847
0
        {
848
0
            PatchSurface* ps = i->second;
849
            
850
0
            ps->build(vbuf, currVertOffset, mIndexes, currIndexOffset);
851
852
            // No need for control points anymore
853
0
            BspVertex* pCP = static_cast<BspVertex*>(ps->getControlPointBuffer());
854
0
            OGRE_FREE(pCP, MEMCATEGORY_GEOMETRY);
855
0
            ps->notifyControlPointBufferDeallocated();
856
857
0
            currVertOffset += ps->getRequiredVertexCount();
858
0
            currIndexOffset += ps->getRequiredIndexCount();
859
        
860
0
        }
861
0
    }
862
    //-----------------------------------------------------------------------
863
    bool BspLevel::isLeafVisible(const BspNode* from, const BspNode* to) const
864
0
    {
865
0
        if (to->mVisCluster == -1)
866
0
            return false;
867
0
        if (from->mVisCluster == -1)
868
            // Camera outside world?
869
0
            return true;
870
871
872
0
        if (!from->isLeaf() || !to->isLeaf())
873
0
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
874
0
                "Both nodes must be leaf nodes for visibility testing.",
875
0
                "BspLevel::isLeafVisible");
876
877
        // Use PVS to determine visibility
878
879
        /*
880
        // In wordier terms, the fairly cryptic (but fast) version is doing this:
881
        //   Could make it a macro for even more speed?
882
883
        // Row offset = from cluster number * row size
884
        int offset = from->mVisCluster*mVisData.rowLength;
885
886
        // Column offset (in bytes) = to cluster number divided by 8 (since 8 bits per bytes)
887
        offset += to->mVisCluster >> 3;
888
889
        // Get the right bit within the byte, i.e. bitwise 'and' with bit at remainder position
890
        int result = *(mVisData.tableData + offset) & (1 << (to->mVisCluster & 7));
891
892
        return (result != 0);
893
        */
894
895
        //return ((*(mVisData.tableData + from->mVisCluster * mVisData.rowLength +
896
        //           ((to->mVisCluster)>>3)) & (1 << ((to->mVisCluster) & 7))) != 0);
897
898
0
        return (*(mVisData.tableData + from->mVisCluster*mVisData.rowLength +
899
0
                   ((to->mVisCluster)>>3)) & (1 << ((to->mVisCluster) & 7))) != 0;
900
901
0
    }
902
    //-----------------------------------------------------------------------
903
    const BspNode* BspLevel::getRootNode(void)
904
0
    {
905
0
        return mRootNode;
906
0
    }
907
    //-----------------------------------------------------------------------
908
    BspNode* BspLevel::findLeaf(const Vector3& point) const
909
0
    {
910
0
        BspNode* node = mRootNode;
911
912
0
        while (!node->isLeaf())
913
0
        {
914
0
            node = node->getNextNode(point);
915
0
        }
916
917
0
        return node;
918
919
0
    }
920
    //-----------------------------------------------------------------------
921
    void BspLevel::loadEntities(const Quake3Level& q3lvl)
922
0
    {
923
0
        char* strEnt;
924
0
        String line;
925
0
        StringVector vecparams;
926
0
        Vector3 origin = Vector3::ZERO;
927
0
        Radian angle ( 0 );
928
0
        size_t pos;
929
0
        char* lineend;
930
0
        bool isPlayerStart;
931
932
0
        isPlayerStart = false;
933
0
        strEnt = (char*)q3lvl.mEntities;
934
935
0
        lineend = strchr(strEnt, '\n');
936
0
        while (lineend != 0)
937
0
        {
938
0
            *lineend = '\0';
939
0
            line = strEnt;
940
0
            strEnt = lineend+1;
941
0
            StringUtil::trim(line);
942
0
            if (line.length() > 0)
943
0
            {
944
0
                StringUtil::toLowerCase(line);
945
                // Remove quotes
946
0
                while( ( pos = line.find('\"',0) ) != String::npos )
947
0
                {
948
0
                    line = line.substr(0,pos) + line.substr(pos+1,line.length()-(pos+1));
949
0
                }
950
0
                vecparams = StringUtil::split(line);
951
0
                StringVector::iterator params = vecparams.begin();
952
953
0
                if (params[0] == "origin")
954
0
                {
955
0
                    origin.x = static_cast<Real>(atof(params[1].c_str()));
956
0
                    origin.y = static_cast<Real>(atof(params[2].c_str()));
957
0
                    origin.z = static_cast<Real>(atof(params[3].c_str()));
958
0
                }
959
0
                if (params[0] == "angle")
960
0
                {
961
0
                    angle = Degree(static_cast<Real>(atof(params[1].c_str())));
962
0
                }
963
0
                if (params[0] == "classname" && params[1] == "info_player_deathmatch")
964
0
                {
965
0
                    isPlayerStart = true;
966
0
                }
967
0
                if (params[0] == "}")
968
0
                {
969
0
                    if (isPlayerStart)
970
0
                    {
971
                        // Save player start
972
0
                        ViewPoint vp;
973
0
                        vp.position = origin;
974
0
                        vp.orientation.FromAngleAxis(angle, Vector3::UNIT_Z);
975
0
                        mPlayerStarts.push_back(vp);
976
0
                    }
977
0
                    isPlayerStart = false;
978
0
                }
979
0
            }
980
981
0
            lineend = strchr(strEnt, '\n');
982
0
        }
983
984
985
0
    }
986
    //-----------------------------------------------------------------------
987
    void BspLevel::_notifyObjectMoved(const MovableObject* mov, 
988
            const Vector3& pos)
989
0
    {
990
991
        // Locate any current nodes the object is supposed to be attached to
992
0
        MovableToNodeMap::iterator i = mMovableToNodeMap.find(mov);
993
0
        if (i != mMovableToNodeMap.end())
994
0
        {
995
0
            std::list<BspNode*>::iterator nodeit, nodeitend;
996
0
            nodeitend = i->second.end();
997
0
            for (nodeit = i->second.begin(); nodeit != nodeitend; ++nodeit)
998
0
            {
999
                // Tell each node
1000
0
                (*nodeit)->_removeMovable(mov);
1001
0
            }
1002
            // Clear the existing list of nodes because we'll reevaluate it
1003
0
            i->second.clear();
1004
0
        }
1005
1006
0
        tagNodesWithMovable(mRootNode, mov, pos);
1007
0
    }
1008
    //-----------------------------------------------------------------------
1009
    void BspLevel::tagNodesWithMovable(BspNode* node, const MovableObject* mov,
1010
        const Vector3& pos)
1011
0
    {
1012
0
        if (node->isLeaf())
1013
0
        {
1014
            // Add to movable->node map
1015
            // Insert all the time, will get current if already there
1016
0
            std::pair<MovableToNodeMap::iterator, bool> p = mMovableToNodeMap.emplace(mov, std::list<BspNode*>());
1017
1018
0
            p.first->second.push_back(node);
1019
1020
            // Add movable to node
1021
0
            node->_addMovable(mov);
1022
1023
0
        }
1024
0
        else
1025
0
        {
1026
            // Find distance to dividing plane
1027
0
            Real dist = node->getDistance(pos);
1028
0
            if (Math::Abs(dist) < mov->getBoundingRadius())
1029
0
            {
1030
                // Bounding sphere crosses the plane, do both
1031
0
                tagNodesWithMovable(node->getBack(), mov, pos);
1032
0
                tagNodesWithMovable(node->getFront(), mov, pos);
1033
0
            }
1034
0
            else if (dist < 0)
1035
0
            {    //-----------------------------------------------------------------------
1036
1037
                // Do back
1038
0
                tagNodesWithMovable(node->getBack(), mov, pos);
1039
0
            }
1040
0
            else
1041
0
            {
1042
                // Do front
1043
0
                tagNodesWithMovable(node->getFront(), mov, pos);
1044
0
            }
1045
0
        }
1046
0
    }
1047
    //-----------------------------------------------------------------------
1048
    void BspLevel::_notifyObjectDetached(const MovableObject* mov)  
1049
0
    {
1050
        // Locate any current nodes the object is supposed to be attached to
1051
0
        MovableToNodeMap::iterator i = mMovableToNodeMap.find(mov);
1052
0
        if (i != mMovableToNodeMap.end())
1053
0
        {
1054
0
            std::list<BspNode*>::iterator nodeit, nodeitend;
1055
0
            nodeitend = i->second.end();
1056
0
            for (nodeit = i->second.begin(); nodeit != nodeitend; ++nodeit)
1057
0
            {
1058
                // Tell each node
1059
0
                (*nodeit)->_removeMovable(mov);
1060
0
            }
1061
            // delete the entry for this MovableObject
1062
0
            mMovableToNodeMap.erase(i);
1063
0
        }
1064
0
    }
1065
    //-----------------------------------------------------------------------
1066
    void BspLevel::quakeVertexToBspVertex(const bsp_vertex_t* src, BspVertex* dest)
1067
0
    {
1068
0
        memcpy(dest->position, src->point, sizeof(float) * 3);
1069
0
        memcpy(dest->normal, src->normal,  sizeof(float) * 3);
1070
0
        dest->colour = src->color;
1071
0
        dest->texcoords[0]  = src->texture[0];
1072
0
        dest->texcoords[1]  = src->texture[1];
1073
0
        dest->lightmap[0]  = src->lightmap[0];
1074
0
        dest->lightmap[1]  = src->lightmap[1];
1075
0
    }
1076
    //-----------------------------------------------------------------------
1077
    size_t BspLevel::calculateSize(void) const
1078
0
    {
1079
0
        return 0; // TODO
1080
0
    }
1081
}