From f9158592e1478b2013afc7041d9ed041cf2d2f4a Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Mon, 13 Jan 2014 19:47:58 +1000 Subject: Update Irrlicht to 1.8.1. Include actual change markers this time. lol --- .../source/Irrlicht/CQ3LevelMesh.cpp | 2082 ++++++++++++++++++++ 1 file changed, 2082 insertions(+) create mode 100644 libraries/irrlicht-1.8.1/source/Irrlicht/CQ3LevelMesh.cpp (limited to 'libraries/irrlicht-1.8.1/source/Irrlicht/CQ3LevelMesh.cpp') diff --git a/libraries/irrlicht-1.8.1/source/Irrlicht/CQ3LevelMesh.cpp b/libraries/irrlicht-1.8.1/source/Irrlicht/CQ3LevelMesh.cpp new file mode 100644 index 0000000..0e0c05a --- /dev/null +++ b/libraries/irrlicht-1.8.1/source/Irrlicht/CQ3LevelMesh.cpp @@ -0,0 +1,2082 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_BSP_LOADER_ + +#include "CQ3LevelMesh.h" +#include "ISceneManager.h" +#include "os.h" +#include "SMeshBufferLightMap.h" +#include "irrString.h" +#include "ILightSceneNode.h" +#include "IQ3Shader.h" +#include "IFileList.h" + +//#define TJUNCTION_SOLVER_ROUND +//#define TJUNCTION_SOLVER_0125 + +namespace irr +{ +namespace scene +{ + + using namespace quake3; + +//! constructor +CQ3LevelMesh::CQ3LevelMesh(io::IFileSystem* fs, scene::ISceneManager* smgr, + const Q3LevelLoadParameter &loadParam) + : LoadParam(loadParam), Textures(0), NumTextures(0), LightMaps(0), NumLightMaps(0), + Vertices(0), NumVertices(0), Faces(0), NumFaces(0), Models(0), NumModels(0), + Planes(0), NumPlanes(0), Nodes(0), NumNodes(0), Leafs(0), NumLeafs(0), + LeafFaces(0), NumLeafFaces(0), MeshVerts(0), NumMeshVerts(0), + Brushes(0), NumBrushes(0), BrushEntities(0), FileSystem(fs), + SceneManager(smgr), FramesPerSecond(25.f) +{ + #ifdef _DEBUG + IReferenceCounted::setDebugName("CQ3LevelMesh"); + #endif + + for ( s32 i = 0; i!= E_Q3_MESH_SIZE; ++i ) + { + Mesh[i] = 0; + } + + Driver = smgr ? smgr->getVideoDriver() : 0; + if (Driver) + Driver->grab(); + + if (FileSystem) + FileSystem->grab(); + + // load default shaders + InitShader(); +} + + +//! destructor +CQ3LevelMesh::~CQ3LevelMesh() +{ + cleanLoader (); + + if (Driver) + Driver->drop(); + + if (FileSystem) + FileSystem->drop(); + + s32 i; + + for ( i = 0; i!= E_Q3_MESH_SIZE; ++i ) + { + if ( Mesh[i] ) + { + Mesh[i]->drop(); + Mesh[i] = 0; + } + } + + for ( i = 1; i < NumModels; i++ ) + { + BrushEntities[i]->drop(); + } + delete [] BrushEntities; BrushEntities = 0; + + ReleaseShader(); + ReleaseEntity(); +} + + +//! loads a level from a .bsp-File. Also tries to load all needed textures. Returns true if successful. +bool CQ3LevelMesh::loadFile(io::IReadFile* file) +{ + if (!file) + return false; + + LevelName = file->getFileName(); + + file->read(&header, sizeof(tBSPHeader)); + + #ifdef __BIG_ENDIAN__ + header.strID = os::Byteswap::byteswap(header.strID); + header.version = os::Byteswap::byteswap(header.version); + #endif + + if ( (header.strID != 0x50534249 || // IBSP + ( header.version != 0x2e // quake3 + && header.version != 0x2f // rtcw + ) + ) + && + ( header.strID != 0x50534252 || header.version != 1 ) // RBSP, starwars jedi, sof + ) + { + os::Printer::log("Could not load .bsp file, unknown header.", file->getFileName(), ELL_ERROR); + return false; + } + +#if 0 + if ( header.strID == 0x50534252 ) // RBSP Raven + { + LoadParam.swapHeader = 1; + } +#endif + + // now read lumps + file->read(&Lumps[0], sizeof(tBSPLump)*kMaxLumps); + + s32 i; + if ( LoadParam.swapHeader ) + { + for ( i=0; i< kMaxLumps;++i) + { + Lumps[i].offset = os::Byteswap::byteswap(Lumps[i].offset); + Lumps[i].length = os::Byteswap::byteswap(Lumps[i].length); + } + } + + ReleaseEntity(); + + // load everything + loadEntities(&Lumps[kEntities], file); // load the entities + loadTextures(&Lumps[kShaders], file); // Load the textures + loadLightmaps(&Lumps[kLightmaps], file); // Load the lightmaps + loadVerts(&Lumps[kVertices], file); // Load the vertices + loadFaces(&Lumps[kFaces], file); // Load the faces + loadPlanes(&Lumps[kPlanes], file); // Load the Planes of the BSP + loadNodes(&Lumps[kNodes], file); // load the Nodes of the BSP + loadLeafs(&Lumps[kLeafs], file); // load the Leafs of the BSP + loadLeafFaces(&Lumps[kLeafFaces], file); // load the Faces of the Leafs of the BSP + loadVisData(&Lumps[kVisData], file); // load the visibility data of the clusters + loadModels(&Lumps[kModels], file); // load the models + loadMeshVerts(&Lumps[kMeshVerts], file); // load the mesh vertices + loadBrushes(&Lumps[kBrushes], file); // load the brushes of the BSP + loadBrushSides(&Lumps[kBrushSides], file); // load the brushsides of the BSP + loadLeafBrushes(&Lumps[kLeafBrushes], file); // load the brushes of the leaf + loadFogs(&Lumps[kFogs], file ); // load the fogs + + loadTextures(); + constructMesh(); + solveTJunction(); + + cleanMeshes(); + calcBoundingBoxes(); + cleanLoader(); + + return true; +} + +/*! +*/ +void CQ3LevelMesh::cleanLoader () +{ + delete [] Textures; Textures = 0; + delete [] LightMaps; LightMaps = 0; + delete [] Vertices; Vertices = 0; + delete [] Faces; Faces = 0; + delete [] Models; Models = 0; + delete [] Planes; Planes = 0; + delete [] Nodes; Nodes = 0; + delete [] Leafs; Leafs = 0; + delete [] LeafFaces; LeafFaces = 0; + delete [] MeshVerts; MeshVerts = 0; + delete [] Brushes; Brushes = 0; + + Lightmap.clear(); + Tex.clear(); +} + +//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh. +u32 CQ3LevelMesh::getFrameCount() const +{ + return 1; +} + + +//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level. +IMesh* CQ3LevelMesh::getMesh(s32 frameInMs, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop) +{ + return Mesh[frameInMs]; +} + + +void CQ3LevelMesh::loadTextures(tBSPLump* l, io::IReadFile* file) +{ + NumTextures = l->length / sizeof(tBSPTexture); + if ( !NumTextures ) + return; + Textures = new tBSPTexture[NumTextures]; + + file->seek(l->offset); + file->read(Textures, l->length); + + if ( LoadParam.swapHeader ) + { + for (s32 i=0;ilength / sizeof(tBSPLightmap); + if ( !NumLightMaps ) + return; + LightMaps = new tBSPLightmap[NumLightMaps]; + + file->seek(l->offset); + file->read(LightMaps, l->length); +} + +/*! +*/ +void CQ3LevelMesh::loadVerts(tBSPLump* l, io::IReadFile* file) +{ + NumVertices = l->length / sizeof(tBSPVertex); + if ( !NumVertices ) + return; + Vertices = new tBSPVertex[NumVertices]; + + file->seek(l->offset); + file->read(Vertices, l->length); + + if ( LoadParam.swapHeader ) + for (s32 i=0;ilength / sizeof(tBSPFace); + if (!NumFaces) + return; + Faces = new tBSPFace[NumFaces]; + + file->seek(l->offset); + file->read(Faces, l->length); + + if ( LoadParam.swapHeader ) + { + for ( s32 i=0;i entity; + entity.set_used( l->length + 2 ); + entity[l->length + 1 ] = 0; + + file->seek(l->offset); + file->read( entity.pointer(), l->length); + + parser_parse( entity.pointer(), l->length, &CQ3LevelMesh::scriptcallback_entity ); +} + + +/*! + load fog brushes +*/ +void CQ3LevelMesh::loadFogs(tBSPLump* l, io::IReadFile* file) +{ + u32 files = l->length / sizeof(tBSPFog); + + file->seek( l->offset ); + tBSPFog fog; + const IShader *shader; + STexShader t; + for ( u32 i = 0; i!= files; ++i ) + { + file->read( &fog, sizeof( fog ) ); + + shader = getShader( fog.shader ); + t.Texture = 0; + t.ShaderID = shader ? shader->ID : -1; + + FogMap.push_back ( t ); + } +} + + +/*! + load models named in bsp +*/ +void CQ3LevelMesh::loadModels(tBSPLump* l, io::IReadFile* file) +{ + NumModels = l->length / sizeof(tBSPModel); + Models = new tBSPModel[NumModels]; + + file->seek( l->offset ); + file->read(Models, l->length); + + if ( LoadParam.swapHeader ) + { + for ( s32 i = 0; i < NumModels; i++) + { + Models[i].min[0] = os::Byteswap::byteswap(Models[i].min[0]); + Models[i].min[1] = os::Byteswap::byteswap(Models[i].min[1]); + Models[i].min[2] = os::Byteswap::byteswap(Models[i].min[2]); + Models[i].max[0] = os::Byteswap::byteswap(Models[i].max[0]); + Models[i].max[1] = os::Byteswap::byteswap(Models[i].max[1]); + Models[i].max[2] = os::Byteswap::byteswap(Models[i].max[2]); + + Models[i].faceIndex = os::Byteswap::byteswap(Models[i].faceIndex); + Models[i].numOfFaces = os::Byteswap::byteswap(Models[i].numOfFaces); + Models[i].brushIndex = os::Byteswap::byteswap(Models[i].brushIndex); + Models[i].numOfBrushes = os::Byteswap::byteswap(Models[i].numOfBrushes); + } + } + + BrushEntities = new SMesh*[NumModels]; +} + +/*! +*/ +void CQ3LevelMesh::loadMeshVerts(tBSPLump* l, io::IReadFile* file) +{ + NumMeshVerts = l->length / sizeof(s32); + if (!NumMeshVerts) + return; + MeshVerts = new s32[NumMeshVerts]; + + file->seek(l->offset); + file->read(MeshVerts, l->length); + + if ( LoadParam.swapHeader ) + { + for (int i=0;i= 'a' && symbol <= 'z' ) || + (symbol >= 'A' && symbol <= 'Z' ) || + (symbol >= '0' && symbol <= '9' ) || + (symbol == '/' || symbol == '_' || symbol == '.' ); +} + +/*! +*/ +void CQ3LevelMesh::parser_nextToken() +{ + u8 symbol; + + Parser.token = ""; + Parser.tokenresult = Q3_TOKEN_UNRESOLVED; + + // skip white space + do + { + if ( Parser.index >= Parser.sourcesize ) + { + Parser.tokenresult = Q3_TOKEN_EOF; + return; + } + + symbol = Parser.source [ Parser.index ]; + Parser.index += 1; + } while ( isQ3WhiteSpace( symbol ) ); + + // first symbol, one symbol + switch ( symbol ) + { + case 0: + Parser.tokenresult = Q3_TOKEN_EOF; + return; + + case '/': + // comment or divide + if ( Parser.index >= Parser.sourcesize ) + { + Parser.tokenresult = Q3_TOKEN_EOF; + return; + } + symbol = Parser.source [ Parser.index ]; + Parser.index += 1; + if ( isQ3WhiteSpace( symbol ) ) + { + Parser.tokenresult = Q3_TOKEN_MATH_DIVIDE; + return; + } + else + if ( symbol == '*' ) + { + // C-style comment in quake? + } + else + if ( symbol == '/' ) + { + // skip to eol + do + { + if ( Parser.index >= Parser.sourcesize ) + { + Parser.tokenresult = Q3_TOKEN_EOF; + return; + } + symbol = Parser.source [ Parser.index ]; + Parser.index += 1; + } while ( symbol != '\n' ); + Parser.tokenresult = Q3_TOKEN_COMMENT; + return; + } + // take /[name] as valid token..?!?!?. mhmm, maybe + break; + + case '\n': + Parser.tokenresult = Q3_TOKEN_EOL; + return; + case '{': + Parser.tokenresult = Q3_TOKEN_START_LIST; + return; + case '}': + Parser.tokenresult = Q3_TOKEN_END_LIST; + return; + + case '"': + // string literal + do + { + if ( Parser.index >= Parser.sourcesize ) + { + Parser.tokenresult = Q3_TOKEN_EOF; + return; + } + symbol = Parser.source [ Parser.index ]; + Parser.index += 1; + if ( symbol != '"' ) + Parser.token.append( symbol ); + } while ( symbol != '"' ); + Parser.tokenresult = Q3_TOKEN_ENTITY; + return; + } + + // user identity + Parser.token.append( symbol ); + + // continue till whitespace + bool validName = true; + do + { + if ( Parser.index >= Parser.sourcesize ) + { + Parser.tokenresult = Q3_TOKEN_EOF; + return; + } + symbol = Parser.source [ Parser.index ]; + + validName = isQ3ValidName( symbol ); + if ( validName ) + { + Parser.token.append( symbol ); + Parser.index += 1; + } + } while ( validName ); + + Parser.tokenresult = Q3_TOKEN_TOKEN; + return; +} + + +/* + parse entity & shader + calls callback on content in {} +*/ +void CQ3LevelMesh::parser_parse( const void * data, const u32 size, CQ3LevelMesh::tParserCallback callback ) +{ + Parser.source = static_cast(data); + Parser.sourcesize = size; + Parser.index = 0; + + SVarGroupList *groupList; + + s32 active; + s32 last; + + SVariable entity ( "" ); + + groupList = new SVarGroupList(); + + groupList->VariableGroup.push_back( SVarGroup() ); + active = last = 0; + + do + { + parser_nextToken(); + + switch ( Parser.tokenresult ) + { + case Q3_TOKEN_START_LIST: + { + //stack = core::min_( stack + 1, 7 ); + + groupList->VariableGroup.push_back( SVarGroup() ); + last = active; + active = groupList->VariableGroup.size() - 1; + entity.clear(); + } break; + + // a unregisterd variable is finished + case Q3_TOKEN_EOL: + { + if ( entity.isValid() ) + { + groupList->VariableGroup[active].Variable.push_back( entity ); + entity.clear(); + } + } break; + + case Q3_TOKEN_TOKEN: + case Q3_TOKEN_ENTITY: + { + Parser.token.make_lower(); + + // store content based on line-delemiter + if ( 0 == entity.isValid() ) + { + entity.name = Parser.token; + entity.content = ""; + + } + else + { + if ( entity.content.size() ) + { + entity.content += " "; + } + entity.content += Parser.token; + } + } break; + + case Q3_TOKEN_END_LIST: + { + //stack = core::max_( stack - 1, 0 ); + + // close tag for first + if ( active == 1 ) + { + (this->*callback)( groupList, Q3_TOKEN_END_LIST ); + + // new group + groupList->drop(); + groupList = new SVarGroupList(); + groupList->VariableGroup.push_back( SVarGroup() ); + last = 0; + } + + active = last; + entity.clear(); + + } break; + + default: + break; + } + + } while ( Parser.tokenresult != Q3_TOKEN_EOF ); + + (this->*callback)( groupList, Q3_TOKEN_EOF ); + + groupList->drop(); +} + + +/* + this loader applies only textures for stage 1 & 2 +*/ +s32 CQ3LevelMesh::setShaderFogMaterial( video::SMaterial &material, const tBSPFace * face ) const +{ + material.MaterialType = video::EMT_SOLID; + material.Wireframe = false; + material.Lighting = false; + material.BackfaceCulling = false; + material.setTexture(0, 0); + material.setTexture(1, 0); + material.setTexture(2, 0); + material.setTexture(3, 0); + material.ZBuffer = video::ECFN_LESSEQUAL; + material.ZWriteEnable = false; + material.MaterialTypeParam = 0.f; + + s32 shaderState = -1; + + if ( (u32) face->fogNum < FogMap.size() ) + { + material.setTexture(0, FogMap [ face->fogNum ].Texture); + shaderState = FogMap [ face->fogNum ].ShaderID; + } + + return shaderState; + +} +/* + this loader applies only textures for stage 1 & 2 +*/ +s32 CQ3LevelMesh::setShaderMaterial( video::SMaterial &material, const tBSPFace * face ) const +{ + material.MaterialType = video::EMT_SOLID; + material.Wireframe = false; + material.Lighting = false; + material.BackfaceCulling = true; + material.setTexture(0, 0); + material.setTexture(1, 0); + material.setTexture(2, 0); + material.setTexture(3, 0); + material.ZBuffer = video::ECFN_LESSEQUAL; + material.ZWriteEnable = true; + material.MaterialTypeParam = 0.f; + + s32 shaderState = -1; + + if ( face->textureID >= 0 && face->textureID < (s32)Tex.size() ) + { + material.setTexture(0, Tex [ face->textureID ].Texture); + shaderState = Tex [ face->textureID ].ShaderID; + } + + if ( face->lightmapID >= 0 && face->lightmapID < (s32)Lightmap.size() ) + { + material.setTexture(1, Lightmap [ face->lightmapID ]); + material.MaterialType = LoadParam.defaultLightMapMaterial; + } + + // store shader ID + material.MaterialTypeParam2 = (f32) shaderState; + + const IShader *shader = getShader(shaderState); + if ( 0 == shader ) + return shaderState; + + return shaderState; + +#if 0 + const SVarGroup *group; + + + // generic + group = shader->getGroup( 1 ); + if ( group ) + { + material.BackfaceCulling = getCullingFunction( group->get( "cull" ) ); + + if ( group->isDefined( "surfaceparm", "nolightmap" ) ) + { + material.MaterialType = video::EMT_SOLID; + material.setTexture(1, 0); + } + + } + + // try to get the best of the 8 texture stages.. + + // texture 1, texture 2 + u32 startPos; + for ( s32 g = 2; g <= 3; ++g ) + { + group = shader->getGroup( g ); + if ( 0 == group ) + continue; + + startPos = 0; + + if ( group->isDefined( "depthwrite" ) ) + { + material.ZWriteEnable = true; + } + + SBlendFunc blendfunc ( LoadParam.defaultModulate ); + getBlendFunc( group->get( "blendfunc" ), blendfunc ); + getBlendFunc( group->get( "alphafunc" ), blendfunc ); + + if ( 0 == LoadParam.alpharef && + ( blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL || + blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF + ) + ) + { + blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + blendfunc.param0 = 0.f; + } + + material.MaterialType = blendfunc.type; + material.MaterialTypeParam = blendfunc.param0; + + // try if we can match better + shaderState |= (material.MaterialType == video::EMT_SOLID ) ? 0x00020000 : 0; + } + + //material.BackfaceCulling = false; + + if ( shader->VarGroup->VariableGroup.size() <= 4 ) + { + shaderState |= 0x00010000; + } + + material.MaterialTypeParam2 = (f32) shaderState; + return shaderState; +#endif +} + +/*! + Internal function to build a mesh. +*/ +scene::SMesh** CQ3LevelMesh::buildMesh(s32 num) +{ + scene::SMesh** newmesh = new SMesh *[quake3::E_Q3_MESH_SIZE]; + + s32 i, j, k,s; + + for (i = 0; i < E_Q3_MESH_SIZE; i++) + { + newmesh[i] = new SMesh(); + } + + s32 *index; + + video::S3DVertex2TCoords temp[3]; + video::SMaterial material; + video::SMaterial material2; + + SToBuffer item [ E_Q3_MESH_SIZE ]; + u32 itemSize; + + for (i = Models[num].faceIndex; i < Models[num].numOfFaces + Models[num].faceIndex; ++i) + { + const tBSPFace * face = Faces + i; + + s32 shaderState = setShaderMaterial( material, face ); + itemSize = 0; + + const IShader *shader = getShader(shaderState); + + if ( face->fogNum >= 0 ) + { + setShaderFogMaterial ( material2, face ); + item[itemSize].index = E_Q3_MESH_FOG; + item[itemSize].takeVertexColor = 1; + itemSize += 1; + } + + switch( face->type ) + { + case 1: // normal polygons + case 2: // patches + case 3: // meshes + if ( 0 == shader ) + { + if ( LoadParam.cleanUnResolvedMeshes || material.getTexture(0) ) + { + item[itemSize].takeVertexColor = 1; + item[itemSize].index = E_Q3_MESH_GEOMETRY; + itemSize += 1; + } + else + { + item[itemSize].takeVertexColor = 1; + item[itemSize].index = E_Q3_MESH_UNRESOLVED; + itemSize += 1; + } + } + else + { + item[itemSize].takeVertexColor = 1; + item[itemSize].index = E_Q3_MESH_ITEMS; + itemSize += 1; + } + break; + + case 4: // billboards + //item[itemSize].takeVertexColor = 1; + //item[itemSize].index = E_Q3_MESH_ITEMS; + //itemSize += 1; + break; + + } + + for ( u32 g = 0; g != itemSize; ++g ) + { + scene::SMeshBufferLightMap* buffer = 0; + + if ( item[g].index == E_Q3_MESH_GEOMETRY ) + { + if ( 0 == item[g].takeVertexColor ) + { + item[g].takeVertexColor = material.getTexture(0) == 0 || material.getTexture(1) == 0; + } + + if (Faces[i].lightmapID < -1 || Faces[i].lightmapID > NumLightMaps-1) + { + Faces[i].lightmapID = -1; + } + +#if 0 + // there are lightmapsids and textureid with -1 + const s32 tmp_index = ((Faces[i].lightmapID+1) * (NumTextures+1)) + (Faces[i].textureID+1); + buffer = (SMeshBufferLightMap*) newmesh[E_Q3_MESH_GEOMETRY]->getMeshBuffer(tmp_index); + buffer->setHardwareMappingHint ( EHM_STATIC ); + buffer->getMaterial() = material; +#endif + } + + // Construct a unique mesh for each shader or combine meshbuffers for same shader + if ( 0 == buffer ) + { + + if ( LoadParam.mergeShaderBuffer == 1 ) + { + // combine + buffer = (SMeshBufferLightMap*) newmesh[ item[g].index ]->getMeshBuffer( + item[g].index != E_Q3_MESH_FOG ? material : material2 ); + } + + // create a seperate mesh buffer + if ( 0 == buffer ) + { + buffer = new scene::SMeshBufferLightMap(); + newmesh[ item[g].index ]->addMeshBuffer( buffer ); + buffer->drop(); + buffer->getMaterial() = item[g].index != E_Q3_MESH_FOG ? material : material2; + if ( item[g].index == E_Q3_MESH_GEOMETRY ) + buffer->setHardwareMappingHint ( EHM_STATIC ); + } + } + + + switch(Faces[i].type) + { + case 4: // billboards + break; + case 2: // patches + createCurvedSurface_bezier( buffer, i, + LoadParam.patchTesselation, + item[g].takeVertexColor + ); + break; + + case 1: // normal polygons + case 3: // mesh vertices + index = MeshVerts + face->meshVertIndex; + k = buffer->getVertexCount(); + + // reallocate better if many small meshes are used + s = buffer->getIndexCount()+face->numMeshVerts; + if ( buffer->Indices.allocated_size () < (u32) s ) + { + if ( buffer->Indices.allocated_size () > 0 && + face->numMeshVerts < 20 && NumFaces > 1000 + ) + { + s = buffer->getIndexCount() + (NumFaces >> 3 * face->numMeshVerts ); + } + buffer->Indices.reallocate( s); + } + + for ( j = 0; j < face->numMeshVerts; ++j ) + { + buffer->Indices.push_back( k + index [j] ); + } + + s = k+face->numOfVerts; + if ( buffer->Vertices.allocated_size () < (u32) s ) + { + if ( buffer->Indices.allocated_size () > 0 && + face->numOfVerts < 20 && NumFaces > 1000 + ) + { + s = buffer->getIndexCount() + (NumFaces >> 3 * face->numOfVerts ); + } + buffer->Vertices.reallocate( s); + } + for ( j = 0; j != face->numOfVerts; ++j ) + { + copy( &temp[0], &Vertices[ j + face->vertexIndex ], item[g].takeVertexColor ); + buffer->Vertices.push_back( temp[0] ); + } + break; + + } // end switch + } + } + + return newmesh; +} + +/*! +*/ +void CQ3LevelMesh::solveTJunction() +{ +} + +/*! + constructs a mesh from the quake 3 level file. +*/ +void CQ3LevelMesh::constructMesh() +{ + if ( LoadParam.verbose > 0 ) + { + LoadParam.startTime = os::Timer::getRealTime(); + + if ( LoadParam.verbose > 1 ) + { + snprintf( buf, sizeof ( buf ), + "quake3::constructMesh start to create %d faces, %d vertices,%d mesh vertices", + NumFaces, + NumVertices, + NumMeshVerts + ); + os::Printer::log(buf, ELL_INFORMATION); + } + + } + + s32 i, j; + + // First the main level + SMesh **tmp = buildMesh(0); + + for (i = 0; i < E_Q3_MESH_SIZE; i++) + { + Mesh[i] = tmp[i]; + } + delete [] tmp; + + // Then the brush entities + + for (i = 1; i < NumModels; i++) + { + tmp = buildMesh(i); + BrushEntities[i] = tmp[0]; + + // We only care about the main geometry here + for (j = 1; j < E_Q3_MESH_SIZE; j++) + { + tmp[j]->drop(); + } + delete [] tmp; + } + + if ( LoadParam.verbose > 0 ) + { + LoadParam.endTime = os::Timer::getRealTime(); + + snprintf( buf, sizeof ( buf ), + "quake3::constructMesh needed %04d ms to create %d faces, %d vertices,%d mesh vertices", + LoadParam.endTime - LoadParam.startTime, + NumFaces, + NumVertices, + NumMeshVerts + ); + os::Printer::log(buf, ELL_INFORMATION); + } + +} + + +void CQ3LevelMesh::S3DVertex2TCoords_64::copy( video::S3DVertex2TCoords &dest ) const +{ +#if defined (TJUNCTION_SOLVER_ROUND) + dest.Pos.X = core::round_( (f32) Pos.X ); + dest.Pos.Y = core::round_( (f32) Pos.Y ); + dest.Pos.Z = core::round_( (f32) Pos.Z ); +#elif defined (TJUNCTION_SOLVER_0125) + dest.Pos.X = (f32) ( floor ( Pos.X * 8.f + 0.5 ) * 0.125 ); + dest.Pos.Y = (f32) ( floor ( Pos.Y * 8.f + 0.5 ) * 0.125 ); + dest.Pos.Z = (f32) ( floor ( Pos.Z * 8.f + 0.5 ) * 0.125 ); +#else + dest.Pos.X = (f32) Pos.X; + dest.Pos.Y = (f32) Pos.Y; + dest.Pos.Z = (f32) Pos.Z; +#endif + + dest.Normal.X = (f32) Normal.X; + dest.Normal.Y = (f32) Normal.Y; + dest.Normal.Z = (f32) Normal.Z; + dest.Normal.normalize(); + + dest.Color = Color.toSColor(); + + dest.TCoords.X = (f32) TCoords.X; + dest.TCoords.Y = (f32) TCoords.Y; + + dest.TCoords2.X = (f32) TCoords2.X; + dest.TCoords2.Y = (f32) TCoords2.Y; +} + + +void CQ3LevelMesh::copy( S3DVertex2TCoords_64 * dest, const tBSPVertex * source, s32 vertexcolor ) const +{ +#if defined (TJUNCTION_SOLVER_ROUND) + dest->Pos.X = core::round_( source->vPosition[0] ); + dest->Pos.Y = core::round_( source->vPosition[2] ); + dest->Pos.Z = core::round_( source->vPosition[1] ); +#elif defined (TJUNCTION_SOLVER_0125) + dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 ); + dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 ); + dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 ); +#else + dest->Pos.X = source->vPosition[0]; + dest->Pos.Y = source->vPosition[2]; + dest->Pos.Z = source->vPosition[1]; +#endif + + dest->Normal.X = source->vNormal[0]; + dest->Normal.Y = source->vNormal[2]; + dest->Normal.Z = source->vNormal[1]; + dest->Normal.normalize(); + + dest->TCoords.X = source->vTextureCoord[0]; + dest->TCoords.Y = source->vTextureCoord[1]; + dest->TCoords2.X = source->vLightmapCoord[0]; + dest->TCoords2.Y = source->vLightmapCoord[1]; + + if ( vertexcolor ) + { + //u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 ); + u32 a = source->color[3]; + u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 ); + u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 ); + u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 ); + + dest->Color.set(a * 1.f/255.f, r * 1.f/255.f, + g * 1.f/255.f, b * 1.f/255.f); + } + else + { + dest->Color.set( 1.f, 1.f, 1.f, 1.f ); + } +} + + +inline void CQ3LevelMesh::copy( video::S3DVertex2TCoords * dest, const tBSPVertex * source, s32 vertexcolor ) const +{ +#if defined (TJUNCTION_SOLVER_ROUND) + dest->Pos.X = core::round_( source->vPosition[0] ); + dest->Pos.Y = core::round_( source->vPosition[2] ); + dest->Pos.Z = core::round_( source->vPosition[1] ); +#elif defined (TJUNCTION_SOLVER_0125) + dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 ); + dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 ); + dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 ); +#else + dest->Pos.X = source->vPosition[0]; + dest->Pos.Y = source->vPosition[2]; + dest->Pos.Z = source->vPosition[1]; +#endif + + dest->Normal.X = source->vNormal[0]; + dest->Normal.Y = source->vNormal[2]; + dest->Normal.Z = source->vNormal[1]; + dest->Normal.normalize(); + + dest->TCoords.X = source->vTextureCoord[0]; + dest->TCoords.Y = source->vTextureCoord[1]; + dest->TCoords2.X = source->vLightmapCoord[0]; + dest->TCoords2.Y = source->vLightmapCoord[1]; + + if ( vertexcolor ) + { + //u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 ); + u32 a = source->color[3]; + u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 ); + u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 ); + u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 ); + + dest->Color.set(a << 24 | r << 16 | g << 8 | b); + } + else + { + dest->Color.set(0xFFFFFFFF); + } +} + + +void CQ3LevelMesh::SBezier::tesselate( s32 level ) +{ + //Calculate how many vertices across/down there are + s32 j, k; + + column[0].set_used( level + 1 ); + column[1].set_used( level + 1 ); + column[2].set_used( level + 1 ); + + const f64 w = 0.0 + (1.0 / (f64) level ); + + //Tesselate along the columns + for( j = 0; j <= level; ++j) + { + const f64 f = w * (f64) j; + + column[0][j] = control[0].getInterpolated_quadratic(control[3], control[6], f ); + column[1][j] = control[1].getInterpolated_quadratic(control[4], control[7], f ); + column[2][j] = control[2].getInterpolated_quadratic(control[5], control[8], f ); + } + + const u32 idx = Patch->Vertices.size(); + Patch->Vertices.reallocate(idx+level*level); + //Tesselate across the rows to get final vertices + video::S3DVertex2TCoords v; + S3DVertex2TCoords_64 f; + for( j = 0; j <= level; ++j) + { + for( k = 0; k <= level; ++k) + { + f = column[0][j].getInterpolated_quadratic(column[1][j], column[2][j], w * (f64) k); + f.copy( v ); + Patch->Vertices.push_back( v ); + } + } + + Patch->Indices.reallocate(Patch->Indices.size()+6*level*level); + // connect + for( j = 0; j < level; ++j) + { + for( k = 0; k < level; ++k) + { + const s32 inx = idx + ( k * ( level + 1 ) ) + j; + + Patch->Indices.push_back( inx + 0 ); + Patch->Indices.push_back( inx + (level + 1 ) + 0 ); + Patch->Indices.push_back( inx + (level + 1 ) + 1 ); + + Patch->Indices.push_back( inx + 0 ); + Patch->Indices.push_back( inx + (level + 1 ) + 1 ); + Patch->Indices.push_back( inx + 1 ); + } + } +} + + +/*! + no subdivision +*/ +void CQ3LevelMesh::createCurvedSurface_nosubdivision(SMeshBufferLightMap* meshBuffer, + s32 faceIndex, + s32 patchTesselation, + s32 storevertexcolor) +{ + tBSPFace * face = &Faces[faceIndex]; + u32 j,k,m; + + // number of control points across & up + const u32 controlWidth = face->size[0]; + const u32 controlHeight = face->size[1]; + if ( 0 == controlWidth || 0 == controlHeight ) + return; + + video::S3DVertex2TCoords v; + + m = meshBuffer->Vertices.size(); + meshBuffer->Vertices.reallocate(m+controlHeight * controlWidth); + for ( j = 0; j!= controlHeight * controlWidth; ++j ) + { + copy( &v, &Vertices [ face->vertexIndex + j ], storevertexcolor ); + meshBuffer->Vertices.push_back( v ); + } + + meshBuffer->Indices.reallocate(meshBuffer->Indices.size()+6*(controlHeight-1) * (controlWidth-1)); + for ( j = 0; j!= controlHeight - 1; ++j ) + { + for ( k = 0; k!= controlWidth - 1; ++k ) + { + meshBuffer->Indices.push_back( m + k + 0 ); + meshBuffer->Indices.push_back( m + k + controlWidth + 0 ); + meshBuffer->Indices.push_back( m + k + controlWidth + 1 ); + + meshBuffer->Indices.push_back( m + k + 0 ); + meshBuffer->Indices.push_back( m + k + controlWidth + 1 ); + meshBuffer->Indices.push_back( m + k + 1 ); + } + m += controlWidth; + } +} + + +/*! +*/ +void CQ3LevelMesh::createCurvedSurface_bezier(SMeshBufferLightMap* meshBuffer, + s32 faceIndex, + s32 patchTesselation, + s32 storevertexcolor) +{ + + tBSPFace * face = &Faces[faceIndex]; + u32 j,k; + + // number of control points across & up + const u32 controlWidth = face->size[0]; + const u32 controlHeight = face->size[1]; + + if ( 0 == controlWidth || 0 == controlHeight ) + return; + + // number of biquadratic patches + const u32 biquadWidth = (controlWidth - 1)/2; + const u32 biquadHeight = (controlHeight -1)/2; + + if ( LoadParam.verbose > 1 ) + { + LoadParam.startTime = os::Timer::getRealTime(); + } + + // Create space for a temporary array of the patch's control points + core::array controlPoint; + controlPoint.set_used( controlWidth * controlHeight ); + + for( j = 0; j < controlPoint.size(); ++j) + { + copy( &controlPoint[j], &Vertices [ face->vertexIndex + j ], storevertexcolor ); + } + + // create a temporary patch + Bezier.Patch = new scene::SMeshBufferLightMap(); + + //Loop through the biquadratic patches + for( j = 0; j < biquadHeight; ++j) + { + for( k = 0; k < biquadWidth; ++k) + { + // set up this patch + const s32 inx = j*controlWidth*2 + k*2; + + // setup bezier control points for this patch + Bezier.control[0] = controlPoint[ inx + 0]; + Bezier.control[1] = controlPoint[ inx + 1]; + Bezier.control[2] = controlPoint[ inx + 2]; + Bezier.control[3] = controlPoint[ inx + controlWidth + 0 ]; + Bezier.control[4] = controlPoint[ inx + controlWidth + 1 ]; + Bezier.control[5] = controlPoint[ inx + controlWidth + 2 ]; + Bezier.control[6] = controlPoint[ inx + controlWidth * 2 + 0]; + Bezier.control[7] = controlPoint[ inx + controlWidth * 2 + 1]; + Bezier.control[8] = controlPoint[ inx + controlWidth * 2 + 2]; + + Bezier.tesselate( patchTesselation ); + } + } + + // stitch together with existing geometry + // TODO: only border needs to be checked + const u32 bsize = Bezier.Patch->getVertexCount(); + const u32 msize = meshBuffer->getVertexCount(); +/* + for ( j = 0; j!= bsize; ++j ) + { + const core::vector3df &v = Bezier.Patch->Vertices[j].Pos; + + for ( k = 0; k!= msize; ++k ) + { + const core::vector3df &m = meshBuffer->Vertices[k].Pos; + + if ( !v.equals( m, tolerance ) ) + continue; + + meshBuffer->Vertices[k].Pos = v; + //Bezier.Patch->Vertices[j].Pos = m; + } + } +*/ + + // add Patch to meshbuffer + meshBuffer->Vertices.reallocate(msize+bsize); + for ( j = 0; j!= bsize; ++j ) + { + meshBuffer->Vertices.push_back( Bezier.Patch->Vertices[j] ); + } + + // add indices to meshbuffer + meshBuffer->Indices.reallocate(meshBuffer->getIndexCount()+Bezier.Patch->getIndexCount()); + for ( j = 0; j!= Bezier.Patch->getIndexCount(); ++j ) + { + meshBuffer->Indices.push_back( msize + Bezier.Patch->Indices[j] ); + } + + delete Bezier.Patch; + + if ( LoadParam.verbose > 1 ) + { + LoadParam.endTime = os::Timer::getRealTime(); + + snprintf( buf, sizeof ( buf ), + "quake3::createCurvedSurface_bezier needed %04d ms to create bezier patch.(%dx%d)", + LoadParam.endTime - LoadParam.startTime, + biquadWidth, + biquadHeight + ); + os::Printer::log(buf, ELL_INFORMATION); + } + +} + + + +/*! + Loads entities from file +*/ +void CQ3LevelMesh::getConfiguration( io::IReadFile* file ) +{ + tBSPLump l; + l.offset = file->getPos(); + l.length = file->getSize (); + + core::array entity; + entity.set_used( l.length + 2 ); + entity[l.length + 1 ] = 0; + + file->seek(l.offset); + file->read( entity.pointer(), l.length); + + parser_parse( entity.pointer(), l.length, &CQ3LevelMesh::scriptcallback_config ); + + if ( Entity.size () ) + Entity.getLast().name = file->getFileName(); +} + + +//! get's an interface to the entities +tQ3EntityList & CQ3LevelMesh::getEntityList() +{ +// Entity.sort(); + return Entity; +} + +//! returns the requested brush entity +IMesh* CQ3LevelMesh::getBrushEntityMesh(s32 num) const +{ + if (num < 1 || num >= NumModels) + return 0; + + return BrushEntities[num]; +} + +//! returns the requested brush entity +IMesh* CQ3LevelMesh::getBrushEntityMesh(quake3::IEntity &ent) const +{ + // This is a helper function to parse the entity, + // so you don't have to. + + s32 num; + + const quake3::SVarGroup* group = ent.getGroup(1); + const core::stringc& modnum = group->get("model"); + + if (!group->isDefined("model")) + return 0; + + const char *temp = modnum.c_str() + 1; // We skip the first character. + num = core::strtol10(temp); + + return getBrushEntityMesh(num); +} + + +/*! +*/ +const IShader * CQ3LevelMesh::getShader(u32 index) const +{ + index &= 0xFFFF; + + if ( index < Shader.size() ) + { + return &Shader[index]; + } + + return 0; +} + + +/*! + loads the shader definition +*/ +const IShader* CQ3LevelMesh::getShader( const c8 * filename, bool fileNameIsValid ) +{ + core::stringc searchName ( filename ); + + IShader search; + search.name = searchName; + search.name.replace( '\\', '/' ); + search.name.make_lower(); + + + core::stringc message; + s32 index; + + //! is Shader already in cache? + index = Shader.linear_search( search ); + if ( index >= 0 ) + { + if ( LoadParam.verbose > 1 ) + { + message = searchName + " found " + Shader[index].name; + os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION); + } + + return &Shader[index]; + } + + io::path loadFile; + + if ( !fileNameIsValid ) + { + // extract the shader name from the last path component in filename + // "scripts/[name].shader" + core::stringc cut( search.name ); + + s32 end = cut.findLast( '/' ); + s32 start = cut.findLast( '/', end - 1 ); + + loadFile = LoadParam.scriptDir; + loadFile.append( cut.subString( start, end - start ) ); + loadFile.append( ".shader" ); + } + else + { + loadFile = search.name; + } + + // already loaded the file ? + index = ShaderFile.binary_search( loadFile ); + if ( index >= 0 ) + return 0; + + // add file to loaded files + ShaderFile.push_back( loadFile ); + + if ( !FileSystem->existFile( loadFile.c_str() ) ) + { + if ( LoadParam.verbose > 1 ) + { + message = loadFile + " for " + searchName + " failed "; + os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION); + } + return 0; + } + + if ( LoadParam.verbose ) + { + message = loadFile + " for " + searchName; + os::Printer::log("quake3:getShader Load shader", message.c_str(), ELL_INFORMATION); + } + + + io::IReadFile *file = FileSystem->createAndOpenFile( loadFile.c_str() ); + if ( file ) + { + getShader ( file ); + file->drop (); + } + + + // search again + index = Shader.linear_search( search ); + return index >= 0 ? &Shader[index] : 0; +} + +/*! + loads the shader definition +*/ +void CQ3LevelMesh::getShader( io::IReadFile* file ) +{ + if ( 0 == file ) + return; + + // load script + core::array script; + const long len = file->getSize(); + + script.set_used( len + 2 ); + + file->seek( 0 ); + file->read( script.pointer(), len ); + script[ len + 1 ] = 0; + + // start a parser instance + parser_parse( script.pointer(), len, &CQ3LevelMesh::scriptcallback_shader ); +} + + +//! adding default shaders +void CQ3LevelMesh::InitShader() +{ + ReleaseShader(); + + IShader element; + + SVarGroup group; + SVariable variable ( "noshader" ); + + group.Variable.push_back( variable ); + + element.VarGroup = new SVarGroupList(); + element.VarGroup->VariableGroup.push_back( group ); + element.VarGroup->VariableGroup.push_back( SVarGroup() ); + element.name = element.VarGroup->VariableGroup[0].Variable[0].name; + element.ID = Shader.size(); + Shader.push_back( element ); + + if ( LoadParam.loadAllShaders ) + { + io::EFileSystemType current = FileSystem->setFileListSystem ( io::FILESYSTEM_VIRTUAL ); + io::path save = FileSystem->getWorkingDirectory(); + + io::path newDir; + newDir = "/"; + newDir += LoadParam.scriptDir; + newDir += "/"; + FileSystem->changeWorkingDirectoryTo ( newDir.c_str() ); + + core::stringc s; + io::IFileList *fileList = FileSystem->createFileList (); + for (u32 i=0; i< fileList->getFileCount(); ++i) + { + s = fileList->getFullFileName(i); + if ( s.find ( ".shader" ) >= 0 ) + { + if ( 0 == LoadParam.loadSkyShader && s.find ( "sky.shader" ) >= 0 ) + { + } + else + { + getShader ( s.c_str () ); + } + } + } + fileList->drop (); + + FileSystem->changeWorkingDirectoryTo ( save ); + FileSystem->setFileListSystem ( current ); + } +} + + +//! script callback for shaders +//! i'm having troubles with the reference counting, during callback.. resorting.. +void CQ3LevelMesh::ReleaseShader() +{ + for ( u32 i = 0; i!= Shader.size(); ++i ) + { + Shader[i].VarGroup->drop(); + } + Shader.clear(); + ShaderFile.clear(); +} + + +/*! +*/ +void CQ3LevelMesh::ReleaseEntity() +{ + for ( u32 i = 0; i!= Entity.size(); ++i ) + { + Entity[i].VarGroup->drop(); + } + Entity.clear(); +} + + +// config in simple (quake3) and advanced style +void CQ3LevelMesh::scriptcallback_config( SVarGroupList *& grouplist, eToken token ) +{ + IShader element; + + if ( token == Q3_TOKEN_END_LIST ) + { + if ( 0 == grouplist->VariableGroup[0].Variable.size() ) + return; + + element.name = grouplist->VariableGroup[0].Variable[0].name; + } + else + { + if ( grouplist->VariableGroup.size() != 2 ) + return; + + element.name = "configuration"; + } + + grouplist->grab(); + element.VarGroup = grouplist; + element.ID = Entity.size(); + Entity.push_back( element ); +} + + +// entity only has only one valid level.. and no assoziative name.. +void CQ3LevelMesh::scriptcallback_entity( SVarGroupList *& grouplist, eToken token ) +{ + if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup.size() != 2 ) + return; + + grouplist->grab(); + + IEntity element; + element.VarGroup = grouplist; + element.ID = Entity.size(); + element.name = grouplist->VariableGroup[1].get( "classname" ); + + + Entity.push_back( element ); +} + + +//!. script callback for shaders +void CQ3LevelMesh::scriptcallback_shader( SVarGroupList *& grouplist,eToken token ) +{ + if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup[0].Variable.size()==0) + return; + + + IShader element; + + grouplist->grab(); + element.VarGroup = grouplist; + element.name = element.VarGroup->VariableGroup[0].Variable[0].name; + element.ID = Shader.size(); +/* + core::stringc s; + dumpShader ( s, &element ); + printf ( s.c_str () ); +*/ + Shader.push_back( element ); +} + + +/*! + delete all buffers without geometry in it. +*/ +void CQ3LevelMesh::cleanMeshes() +{ + if ( 0 == LoadParam.cleanUnResolvedMeshes ) + return; + + s32 i; + + // First the main level + for (i = 0; i < E_Q3_MESH_SIZE; i++) + { + bool texture0important = ( i == 0 ); + + cleanMesh(Mesh[i], texture0important); + } + + // Then the brush entities + for (i = 1; i < NumModels; i++) + { + cleanMesh(BrushEntities[i], true); + } +} + +void CQ3LevelMesh::cleanMesh(SMesh *m, const bool texture0important) +{ + // delete all buffers without geometry in it. + u32 run = 0; + u32 remove = 0; + + IMeshBuffer *b; + + run = 0; + remove = 0; + + if ( LoadParam.verbose > 0 ) + { + LoadParam.startTime = os::Timer::getRealTime(); + if ( LoadParam.verbose > 1 ) + { + snprintf( buf, sizeof ( buf ), + "quake3::cleanMeshes start for %d meshes", + m->MeshBuffers.size() + ); + os::Printer::log(buf, ELL_INFORMATION); + } + } + + u32 i = 0; + s32 blockstart = -1; + s32 blockcount = 0; + + while( i < m->MeshBuffers.size()) + { + run += 1; + + b = m->MeshBuffers[i]; + + if ( b->getVertexCount() == 0 || b->getIndexCount() == 0 || + ( texture0important && b->getMaterial().getTexture(0) == 0 ) + ) + { + if ( blockstart < 0 ) + { + blockstart = i; + blockcount = 0; + } + blockcount += 1; + i += 1; + + // delete Meshbuffer + i -= 1; + remove += 1; + b->drop(); + m->MeshBuffers.erase(i); + } + else + { + // clean blockwise + if ( blockstart >= 0 ) + { + if ( LoadParam.verbose > 1 ) + { + snprintf( buf, sizeof ( buf ), + "quake3::cleanMeshes cleaning mesh %d %d size", + blockstart, + blockcount + ); + os::Printer::log(buf, ELL_INFORMATION); + } + blockstart = -1; + } + i += 1; + } + } + + if ( LoadParam.verbose > 0 ) + { + LoadParam.endTime = os::Timer::getRealTime(); + snprintf( buf, sizeof ( buf ), + "quake3::cleanMeshes needed %04d ms to clean %d of %d meshes", + LoadParam.endTime - LoadParam.startTime, + remove, + run + ); + os::Printer::log(buf, ELL_INFORMATION); + } +} + + +// recalculate bounding boxes +void CQ3LevelMesh::calcBoundingBoxes() +{ + if ( LoadParam.verbose > 0 ) + { + LoadParam.startTime = os::Timer::getRealTime(); + + if ( LoadParam.verbose > 1 ) + { + snprintf( buf, sizeof ( buf ), + "quake3::calcBoundingBoxes start create %d textures and %d lightmaps", + NumTextures, + NumLightMaps + ); + os::Printer::log(buf, ELL_INFORMATION); + } + } + + s32 g; + + // create bounding box + for ( g = 0; g != E_Q3_MESH_SIZE; ++g ) + { + for ( u32 j=0; j < Mesh[g]->MeshBuffers.size(); ++j) + { + ((SMeshBufferLightMap*)Mesh[g]->MeshBuffers[j])->recalculateBoundingBox(); + } + + Mesh[g]->recalculateBoundingBox(); + // Mesh[0] is the main bbox + if (g!=0) + Mesh[0]->BoundingBox.addInternalBox(Mesh[g]->getBoundingBox()); + } + + for (g = 1; g < NumModels; g++) + { + for ( u32 j=0; j < BrushEntities[g]->MeshBuffers.size(); ++j) + { + ((SMeshBufferLightMap*)BrushEntities[g]->MeshBuffers[j])-> + recalculateBoundingBox(); + } + + BrushEntities[g]->recalculateBoundingBox(); + } + + if ( LoadParam.verbose > 0 ) + { + LoadParam.endTime = os::Timer::getRealTime(); + + snprintf( buf, sizeof ( buf ), + "quake3::calcBoundingBoxes needed %04d ms to create %d textures and %d lightmaps", + LoadParam.endTime - LoadParam.startTime, + NumTextures, + NumLightMaps + ); + os::Printer::log( buf, ELL_INFORMATION); + } +} + + +//! loads the textures +void CQ3LevelMesh::loadTextures() +{ + if (!Driver) + return; + + if ( LoadParam.verbose > 0 ) + { + LoadParam.startTime = os::Timer::getRealTime(); + + if ( LoadParam.verbose > 1 ) + { + snprintf( buf, sizeof ( buf ), + "quake3::loadTextures start create %d textures and %d lightmaps", + NumTextures, + NumLightMaps + ); + os::Printer::log( buf, ELL_INFORMATION); + } + } + + c8 lightmapname[255]; + s32 t; + + // load lightmaps. + Lightmap.set_used(NumLightMaps); + +/* + bool oldMipMapState = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); + Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false); +*/ + core::dimension2d lmapsize(128,128); + + video::IImage* lmapImg; + for ( t = 0; t < NumLightMaps ; ++t) + { + sprintf(lightmapname, "%s.lightmap.%d", LevelName.c_str(), t); + + // lightmap is a CTexture::R8G8B8 format + lmapImg = Driver->createImageFromData( + video::ECF_R8G8B8, lmapsize, + LightMaps[t].imageBits, false, true ); + + Lightmap[t] = Driver->addTexture( lightmapname, lmapImg ); + lmapImg->drop(); + } + +// Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState); + + // load textures + Tex.set_used( NumTextures ); + + const IShader* shader; + + core::stringc list; + io::path check; + tTexArray textureArray; + + // pre-load shaders + for ( t=0; t< NumTextures; ++t) + { + shader = getShader(Textures[t].strName, false); + } + + for ( t=0; t< NumTextures; ++t) + { + Tex[t].ShaderID = -1; + Tex[t].Texture = 0; + + list = ""; + + // get a shader ( if one exists ) + shader = getShader( Textures[t].strName, false); + if ( shader ) + { + Tex[t].ShaderID = shader->ID; + + // if texture name == stage1 Texture map + const SVarGroup * group; + + group = shader->getGroup( 2 ); + if ( group ) + { + if ( core::cutFilenameExtension( check, group->get( "map" ) ) == Textures[t].strName ) + { + list += check; + } + else + if ( check == "$lightmap" ) + { + // we check if lightmap is in stage 1 and texture in stage 2 + group = shader->getGroup( 3 ); + if ( group ) + list += group->get( "map" ); + } + } + } + else + { + // no shader, take it + list += Textures[t].strName; + } + + u32 pos = 0; + getTextures( textureArray, list, pos, FileSystem, Driver ); + + Tex[t].Texture = textureArray[0]; + } + + if ( LoadParam.verbose > 0 ) + { + LoadParam.endTime = os::Timer::getRealTime(); + + snprintf( buf, sizeof ( buf ), + "quake3::loadTextures needed %04d ms to create %d textures and %d lightmaps", + LoadParam.endTime - LoadParam.startTime, + NumTextures, + NumLightMaps + ); + os::Printer::log( buf, ELL_INFORMATION); + } +} + + +//! Returns an axis aligned bounding box of the mesh. +const core::aabbox3d& CQ3LevelMesh::getBoundingBox() const +{ + return Mesh[0]->getBoundingBox(); +} + + +void CQ3LevelMesh::setBoundingBox(const core::aabbox3df& box) +{ + Mesh[0]->setBoundingBox(box); +} + + +//! Returns the type of the animated mesh. +E_ANIMATED_MESH_TYPE CQ3LevelMesh::getMeshType() const +{ + return scene::EAMT_BSP; +} + +} // end namespace scene +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_BSP_LOADER_ -- cgit v1.1