From 959831f4ef5a3e797f576c3de08cd65032c997ad Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Sun, 13 Jan 2013 18:54:10 +1000 Subject: Remove damned ancient DOS line endings from Irrlicht. Hopefully I did not go overboard. --- .../irrlicht-1.8/source/Irrlicht/CQ3LevelMesh.cpp | 4164 ++++++++++---------- 1 file changed, 2082 insertions(+), 2082 deletions(-) (limited to 'libraries/irrlicht-1.8/source/Irrlicht/CQ3LevelMesh.cpp') diff --git a/libraries/irrlicht-1.8/source/Irrlicht/CQ3LevelMesh.cpp b/libraries/irrlicht-1.8/source/Irrlicht/CQ3LevelMesh.cpp index 0e0c05a..00b0fa1 100644 --- a/libraries/irrlicht-1.8/source/Irrlicht/CQ3LevelMesh.cpp +++ b/libraries/irrlicht-1.8/source/Irrlicht/CQ3LevelMesh.cpp @@ -1,2082 +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_ +// 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