/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 | | } |