mirror of
https://github.com/minetest/irrlicht.git
synced 2024-09-07 05:10:19 +02:00
This breaks compiling. To have old values replace false with EZW_OFF and true with EWZ_AUTO. There's a bit history to this change. ZWriteFineControl got introduced after 1.8 so it was never in a released version. Basically it was needed after some changes had been made to allow shaders to have zwrite enabled independent of the material-type (which worked badly for shaders). This had caused other problems as it was then enabled too often instead. So to quickly fix those bugs and avoid breaking compatibility I had introduced a new enum ZWriteFineControl in SMaterial. This worked and didn't break compiling - but I noticed by now that introducing a second flag for this made maintainance for an already very hard to understand problem (figuring out the implementation of transparency and zwriting) even more complicated. So to keep maintance somewhat sane I decided to break compiling now and merge those two flags. The behavior should not be affected by this commit - except for users which set this flag already in their code and have to switch to the enum now. Serialization is switched on loading old files (so SMaterial has enum already and writes that out). git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6026 dfc29bdd-3216-0410-991c-e03cc46cb475
2083 lines
50 KiB
C++
2083 lines
50 KiB
C++
// 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;i<NumTextures;++i)
|
|
{
|
|
Textures[i].flags = os::Byteswap::byteswap(Textures[i].flags);
|
|
Textures[i].contents = os::Byteswap::byteswap(Textures[i].contents);
|
|
//os::Printer::log("Loaded texture", Textures[i].strName, ELL_INFORMATION);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CQ3LevelMesh::loadLightmaps(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
NumLightMaps = l->length / 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;i<NumVertices;i++)
|
|
{
|
|
Vertices[i].vPosition[0] = os::Byteswap::byteswap(Vertices[i].vPosition[0]);
|
|
Vertices[i].vPosition[1] = os::Byteswap::byteswap(Vertices[i].vPosition[1]);
|
|
Vertices[i].vPosition[2] = os::Byteswap::byteswap(Vertices[i].vPosition[2]);
|
|
Vertices[i].vTextureCoord[0] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[0]);
|
|
Vertices[i].vTextureCoord[1] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[1]);
|
|
Vertices[i].vLightmapCoord[0] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[0]);
|
|
Vertices[i].vLightmapCoord[1] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[1]);
|
|
Vertices[i].vNormal[0] = os::Byteswap::byteswap(Vertices[i].vNormal[0]);
|
|
Vertices[i].vNormal[1] = os::Byteswap::byteswap(Vertices[i].vNormal[1]);
|
|
Vertices[i].vNormal[2] = os::Byteswap::byteswap(Vertices[i].vNormal[2]);
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadFaces(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
NumFaces = l->length / 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<NumFaces;i++)
|
|
{
|
|
Faces[i].textureID = os::Byteswap::byteswap(Faces[i].textureID);
|
|
Faces[i].fogNum = os::Byteswap::byteswap(Faces[i].fogNum);
|
|
Faces[i].type = os::Byteswap::byteswap(Faces[i].type);
|
|
Faces[i].vertexIndex = os::Byteswap::byteswap(Faces[i].vertexIndex);
|
|
Faces[i].numOfVerts = os::Byteswap::byteswap(Faces[i].numOfVerts);
|
|
Faces[i].meshVertIndex = os::Byteswap::byteswap(Faces[i].meshVertIndex);
|
|
Faces[i].numMeshVerts = os::Byteswap::byteswap(Faces[i].numMeshVerts);
|
|
Faces[i].lightmapID = os::Byteswap::byteswap(Faces[i].lightmapID);
|
|
Faces[i].lMapCorner[0] = os::Byteswap::byteswap(Faces[i].lMapCorner[0]);
|
|
Faces[i].lMapCorner[1] = os::Byteswap::byteswap(Faces[i].lMapCorner[1]);
|
|
Faces[i].lMapSize[0] = os::Byteswap::byteswap(Faces[i].lMapSize[0]);
|
|
Faces[i].lMapSize[1] = os::Byteswap::byteswap(Faces[i].lMapSize[1]);
|
|
Faces[i].lMapPos[0] = os::Byteswap::byteswap(Faces[i].lMapPos[0]);
|
|
Faces[i].lMapPos[1] = os::Byteswap::byteswap(Faces[i].lMapPos[1]);
|
|
Faces[i].lMapPos[2] = os::Byteswap::byteswap(Faces[i].lMapPos[2]);
|
|
Faces[i].lMapBitsets[0][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][0]);
|
|
Faces[i].lMapBitsets[0][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][1]);
|
|
Faces[i].lMapBitsets[0][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][2]);
|
|
Faces[i].lMapBitsets[1][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][0]);
|
|
Faces[i].lMapBitsets[1][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][1]);
|
|
Faces[i].lMapBitsets[1][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][2]);
|
|
Faces[i].vNormal[0] = os::Byteswap::byteswap(Faces[i].vNormal[0]);
|
|
Faces[i].vNormal[1] = os::Byteswap::byteswap(Faces[i].vNormal[1]);
|
|
Faces[i].vNormal[2] = os::Byteswap::byteswap(Faces[i].vNormal[2]);
|
|
Faces[i].size[0] = os::Byteswap::byteswap(Faces[i].size[0]);
|
|
Faces[i].size[1] = os::Byteswap::byteswap(Faces[i].size[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadPlanes(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadNodes(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadLeafs(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadLeafFaces(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadVisData(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadEntities(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
core::array<u8> 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<NumMeshVerts;i++)
|
|
MeshVerts[i] = os::Byteswap::byteswap(MeshVerts[i]);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadBrushes(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadBrushSides(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
void CQ3LevelMesh::loadLeafBrushes(tBSPLump* l, io::IReadFile* file)
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
inline bool isQ3WhiteSpace( const u8 symbol )
|
|
{
|
|
return symbol == ' ' || symbol == '\t' || symbol == '\r';
|
|
}
|
|
|
|
/*!
|
|
*/
|
|
inline bool isQ3ValidName( const u8 symbol )
|
|
{
|
|
return (symbol >= '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<const c8*>(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 = video::EZW_OFF;
|
|
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 = video::EZW_AUTO;
|
|
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 = video::EZW_ON;
|
|
}
|
|
|
|
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_irr( 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_irr( buf, sizeof ( buf ),
|
|
"quake3::constructMesh needed %04u 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<S3DVertex2TCoords_64> 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_irr( buf, sizeof ( buf ),
|
|
"quake3::createCurvedSurface_bezier needed %04u ms to create bezier patch.(%ux%u)",
|
|
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<u8> 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<u8> 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_irr( buf, sizeof ( buf ),
|
|
"quake3::cleanMeshes start for %u 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_irr( 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_irr( buf, sizeof ( buf ),
|
|
"quake3::cleanMeshes needed %04u ms to clean %u of %u 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_irr( 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_irr( buf, sizeof ( buf ),
|
|
"quake3::calcBoundingBoxes needed %04u 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_irr( 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<u32> 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_irr( buf, sizeof ( buf ),
|
|
"quake3::loadTextures needed %04u 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<f32>& 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_
|