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