mirror of
https://github.com/TTimo/doom3.gpl.git
synced 2026-03-20 00:49:30 +01:00
hello world
This commit is contained in:
1031
neo/tools/compilers/aas/AASBuild.cpp
Normal file
1031
neo/tools/compilers/aas/AASBuild.cpp
Normal file
File diff suppressed because it is too large
Load Diff
499
neo/tools/compilers/aas/AASBuild_file.cpp
Normal file
499
neo/tools/compilers/aas/AASBuild_file.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASBuild_local.h"
|
||||
|
||||
#define VERTEX_HASH_BOXSIZE (1<<6) // must be power of 2
|
||||
#define VERTEX_HASH_SIZE (VERTEX_HASH_BOXSIZE*VERTEX_HASH_BOXSIZE)
|
||||
#define EDGE_HASH_SIZE (1<<14)
|
||||
|
||||
#define INTEGRAL_EPSILON 0.01f
|
||||
#define VERTEX_EPSILON 0.1f
|
||||
|
||||
#define AAS_PLANE_NORMAL_EPSILON 0.00001f
|
||||
#define AAS_PLANE_DIST_EPSILON 0.01f
|
||||
|
||||
|
||||
idHashIndex *aas_vertexHash;
|
||||
idHashIndex *aas_edgeHash;
|
||||
idBounds aas_vertexBounds;
|
||||
int aas_vertexShift;
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::SetupHash
|
||||
================
|
||||
*/
|
||||
void idAASBuild::SetupHash( void ) {
|
||||
aas_vertexHash = new idHashIndex( VERTEX_HASH_SIZE, 1024 );
|
||||
aas_edgeHash = new idHashIndex( EDGE_HASH_SIZE, 1024 );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::ShutdownHash
|
||||
================
|
||||
*/
|
||||
void idAASBuild::ShutdownHash( void ) {
|
||||
delete aas_vertexHash;
|
||||
delete aas_edgeHash;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::ClearHash
|
||||
================
|
||||
*/
|
||||
void idAASBuild::ClearHash( const idBounds &bounds ) {
|
||||
int i;
|
||||
float f, max;
|
||||
|
||||
aas_vertexHash->Clear();
|
||||
aas_edgeHash->Clear();
|
||||
aas_vertexBounds = bounds;
|
||||
|
||||
max = bounds[1].x - bounds[0].x;
|
||||
f = bounds[1].y - bounds[0].y;
|
||||
if ( f > max ) {
|
||||
max = f;
|
||||
}
|
||||
aas_vertexShift = (float) max / VERTEX_HASH_BOXSIZE;
|
||||
for ( i = 0; (1<<i) < aas_vertexShift; i++ ) {
|
||||
}
|
||||
if ( i == 0 ) {
|
||||
aas_vertexShift = 1;
|
||||
}
|
||||
else {
|
||||
aas_vertexShift = i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::HashVec
|
||||
================
|
||||
*/
|
||||
ID_INLINE int idAASBuild::HashVec( const idVec3 &vec ) {
|
||||
int x, y;
|
||||
|
||||
x = (((int) (vec[0] - aas_vertexBounds[0].x + 0.5)) + 2) >> 2;
|
||||
y = (((int) (vec[1] - aas_vertexBounds[0].y + 0.5)) + 2) >> 2;
|
||||
return (x + y * VERTEX_HASH_BOXSIZE) & (VERTEX_HASH_SIZE-1);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::GetVertex
|
||||
================
|
||||
*/
|
||||
bool idAASBuild::GetVertex( const idVec3 &v, int *vertexNum ) {
|
||||
int i, hashKey, vn;
|
||||
aasVertex_t vert, *p;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if ( idMath::Fabs(v[i] - idMath::Rint(v[i])) < INTEGRAL_EPSILON ) {
|
||||
vert[i] = idMath::Rint(v[i]);
|
||||
}
|
||||
else {
|
||||
vert[i] = v[i];
|
||||
}
|
||||
}
|
||||
|
||||
hashKey = idAASBuild::HashVec( vert );
|
||||
|
||||
for ( vn = aas_vertexHash->First( hashKey ); vn >= 0; vn = aas_vertexHash->Next( vn ) ) {
|
||||
p = &file->vertices[vn];
|
||||
// first compare z-axis because hash is based on x-y plane
|
||||
if (idMath::Fabs( vert.z - p->z ) < VERTEX_EPSILON &&
|
||||
idMath::Fabs( vert.x - p->x ) < VERTEX_EPSILON &&
|
||||
idMath::Fabs( vert.y - p->y ) < VERTEX_EPSILON )
|
||||
{
|
||||
*vertexNum = vn;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*vertexNum = file->vertices.Num();
|
||||
aas_vertexHash->Add( hashKey, file->vertices.Num() );
|
||||
file->vertices.Append( vert );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::GetEdge
|
||||
================
|
||||
*/
|
||||
bool idAASBuild::GetEdge( const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ) {
|
||||
int v2num, hashKey, e;
|
||||
int *vertexNum;
|
||||
aasEdge_t edge;
|
||||
bool found;
|
||||
|
||||
if ( v1num != -1 ) {
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
found = GetVertex( v1, &v1num );
|
||||
}
|
||||
found &= GetVertex( v2, &v2num );
|
||||
// if both vertexes are the same or snapped onto each other
|
||||
if ( v1num == v2num ) {
|
||||
*edgeNum = 0;
|
||||
return true;
|
||||
}
|
||||
hashKey = aas_edgeHash->GenerateKey( v1num, v2num );
|
||||
// if both vertexes where already stored
|
||||
if ( found ) {
|
||||
for ( e = aas_edgeHash->First( hashKey ); e >= 0; e = aas_edgeHash->Next( e ) ) {
|
||||
|
||||
vertexNum = file->edges[e].vertexNum;
|
||||
if ( vertexNum[0] == v2num ) {
|
||||
if ( vertexNum[1] == v1num ) {
|
||||
// negative for a reversed edge
|
||||
*edgeNum = -e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( vertexNum[0] == v1num ) {
|
||||
if ( vertexNum[1] == v2num ) {
|
||||
*edgeNum = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if edge found in hash
|
||||
if ( e >= 0 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*edgeNum = file->edges.Num();
|
||||
aas_edgeHash->Add( hashKey, file->edges.Num() );
|
||||
|
||||
edge.vertexNum[0] = v1num;
|
||||
edge.vertexNum[1] = v2num;
|
||||
|
||||
file->edges.Append( edge );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::GetFaceForPortal
|
||||
================
|
||||
*/
|
||||
bool idAASBuild::GetFaceForPortal( idBrushBSPPortal *portal, int side, int *faceNum ) {
|
||||
int i, j, v1num;
|
||||
int numFaceEdges, faceEdges[MAX_POINTS_ON_WINDING];
|
||||
idWinding *w;
|
||||
aasFace_t face;
|
||||
|
||||
if ( portal->GetFaceNum() > 0 ) {
|
||||
if ( side ) {
|
||||
*faceNum = -portal->GetFaceNum();
|
||||
}
|
||||
else {
|
||||
*faceNum = portal->GetFaceNum();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
w = portal->GetWinding();
|
||||
// turn the winding into a sequence of edges
|
||||
numFaceEdges = 0;
|
||||
v1num = -1; // first vertex unknown
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ ) {
|
||||
|
||||
GetEdge( (*w)[i].ToVec3(), (*w)[(i+1)%w->GetNumPoints()].ToVec3(), &faceEdges[numFaceEdges], v1num );
|
||||
|
||||
if ( faceEdges[numFaceEdges] ) {
|
||||
// last vertex of this edge is the first vertex of the next edge
|
||||
v1num = file->edges[ abs(faceEdges[numFaceEdges]) ].vertexNum[ INTSIGNBITNOTSET(faceEdges[numFaceEdges]) ];
|
||||
|
||||
// this edge is valid so keep it
|
||||
numFaceEdges++;
|
||||
}
|
||||
}
|
||||
|
||||
// should have at least 3 edges
|
||||
if ( numFaceEdges < 3 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the polygon is invalid if some edge is found twice
|
||||
for ( i = 0; i < numFaceEdges; i++ ) {
|
||||
for ( j = i+1; j < numFaceEdges; j++ ) {
|
||||
if ( faceEdges[i] == faceEdges[j] || faceEdges[i] == -faceEdges[j] ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
portal->SetFaceNum( file->faces.Num() );
|
||||
|
||||
face.planeNum = file->planeList.FindPlane( portal->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
|
||||
face.flags = portal->GetFlags();
|
||||
face.areas[0] = face.areas[1] = 0;
|
||||
face.firstEdge = file->edgeIndex.Num();
|
||||
face.numEdges = numFaceEdges;
|
||||
for ( i = 0; i < numFaceEdges; i++ ) {
|
||||
file->edgeIndex.Append( faceEdges[i] );
|
||||
}
|
||||
if ( side ) {
|
||||
*faceNum = -file->faces.Num();
|
||||
}
|
||||
else {
|
||||
*faceNum = file->faces.Num();
|
||||
}
|
||||
file->faces.Append( face );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::GetAreaForLeafNode
|
||||
================
|
||||
*/
|
||||
bool idAASBuild::GetAreaForLeafNode( idBrushBSPNode *node, int *areaNum ) {
|
||||
int s, faceNum;
|
||||
idBrushBSPPortal *p;
|
||||
aasArea_t area;
|
||||
|
||||
if ( node->GetAreaNum() ) {
|
||||
*areaNum = -node->GetAreaNum();
|
||||
return true;
|
||||
}
|
||||
|
||||
area.flags = node->GetFlags();
|
||||
area.cluster = area.clusterAreaNum = 0;
|
||||
area.contents = node->GetContents();
|
||||
area.firstFace = file->faceIndex.Num();
|
||||
area.numFaces = 0;
|
||||
area.reach = NULL;
|
||||
area.rev_reach = NULL;
|
||||
|
||||
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
|
||||
s = (p->GetNode(1) == node);
|
||||
|
||||
if ( !GetFaceForPortal( p, s, &faceNum ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
file->faceIndex.Append( faceNum );
|
||||
area.numFaces++;
|
||||
|
||||
if ( faceNum > 0 ) {
|
||||
file->faces[abs(faceNum)].areas[0] = file->areas.Num();
|
||||
}
|
||||
else {
|
||||
file->faces[abs(faceNum)].areas[1] = file->areas.Num();
|
||||
}
|
||||
}
|
||||
|
||||
if ( !area.numFaces ) {
|
||||
*areaNum = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
*areaNum = -file->areas.Num();
|
||||
node->SetAreaNum( file->areas.Num() );
|
||||
file->areas.Append( area );
|
||||
|
||||
DisplayRealTimeString( "\r%6d", file->areas.Num() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::StoreTree_r
|
||||
================
|
||||
*/
|
||||
int idAASBuild::StoreTree_r( idBrushBSPNode *node ) {
|
||||
int areaNum, nodeNum, child0, child1;
|
||||
aasNode_t aasNode;
|
||||
|
||||
if ( !node ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( node->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( !node->GetChild(0) && !node->GetChild(1) ) {
|
||||
if ( GetAreaForLeafNode( node, &areaNum ) ) {
|
||||
return areaNum;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
aasNode.planeNum = file->planeList.FindPlane( node->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
|
||||
aasNode.children[0] = aasNode.children[1] = 0;
|
||||
nodeNum = file->nodes.Num();
|
||||
file->nodes.Append( aasNode );
|
||||
|
||||
// !@#$%^ cause of some bug we cannot set the children directly with the StoreTree_r return value
|
||||
child0 = StoreTree_r( node->GetChild(0) );
|
||||
file->nodes[nodeNum].children[0] = child0;
|
||||
child1 = StoreTree_r( node->GetChild(1) );
|
||||
file->nodes[nodeNum].children[1] = child1;
|
||||
|
||||
if ( !child0 && !child1 ) {
|
||||
file->nodes.SetNum( file->nodes.Num()-1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nodeNum;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::GetSizeEstimate_r
|
||||
================
|
||||
*/
|
||||
typedef struct sizeEstimate_s {
|
||||
int numEdgeIndexes;
|
||||
int numFaceIndexes;
|
||||
int numAreas;
|
||||
int numNodes;
|
||||
} sizeEstimate_t;
|
||||
|
||||
void idAASBuild::GetSizeEstimate_r( idBrushBSPNode *parent, idBrushBSPNode *node, struct sizeEstimate_s &size ) {
|
||||
idBrushBSPPortal *p;
|
||||
int s;
|
||||
|
||||
if ( !node ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !node->GetChild(0) && !node->GetChild(1) ) {
|
||||
// multiple branches of the bsp tree might point to the same leaf node
|
||||
if ( node->GetParent() == parent ) {
|
||||
size.numAreas++;
|
||||
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
|
||||
s = (p->GetNode(1) == node);
|
||||
size.numFaceIndexes++;
|
||||
size.numEdgeIndexes += p->GetWinding()->GetNumPoints();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
size.numNodes++;
|
||||
}
|
||||
|
||||
GetSizeEstimate_r( node, node->GetChild(0), size );
|
||||
GetSizeEstimate_r( node, node->GetChild(1), size );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::SetSizeEstimate
|
||||
================
|
||||
*/
|
||||
void idAASBuild::SetSizeEstimate( const idBrushBSP &bsp, idAASFileLocal *file ) {
|
||||
sizeEstimate_t size;
|
||||
|
||||
size.numEdgeIndexes = 1;
|
||||
size.numFaceIndexes = 1;
|
||||
size.numAreas = 1;
|
||||
size.numNodes = 1;
|
||||
|
||||
GetSizeEstimate_r( NULL, bsp.GetRootNode(), size );
|
||||
|
||||
file->planeList.Resize( size.numNodes / 2, 1024 );
|
||||
file->vertices.Resize( size.numEdgeIndexes / 3, 1024 );
|
||||
file->edges.Resize( size.numEdgeIndexes / 2, 1024 );
|
||||
file->edgeIndex.Resize( size.numEdgeIndexes, 4096 );
|
||||
file->faces.Resize( size.numFaceIndexes, 1024 );
|
||||
file->faceIndex.Resize( size.numFaceIndexes, 4096 );
|
||||
file->areas.Resize( size.numAreas, 1024 );
|
||||
file->nodes.Resize( size.numNodes, 1024 );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASBuild::StoreFile
|
||||
================
|
||||
*/
|
||||
bool idAASBuild::StoreFile( const idBrushBSP &bsp ) {
|
||||
aasEdge_t edge;
|
||||
aasFace_t face;
|
||||
aasArea_t area;
|
||||
aasNode_t node;
|
||||
|
||||
common->Printf( "[Store AAS]\n" );
|
||||
|
||||
SetupHash();
|
||||
ClearHash( bsp.GetTreeBounds() );
|
||||
|
||||
file = new idAASFileLocal();
|
||||
|
||||
file->Clear();
|
||||
|
||||
SetSizeEstimate( bsp, file );
|
||||
|
||||
// the first edge is a dummy
|
||||
memset( &edge, 0, sizeof( edge ) );
|
||||
file->edges.Append( edge );
|
||||
|
||||
// the first face is a dummy
|
||||
memset( &face, 0, sizeof( face ) );
|
||||
file->faces.Append( face );
|
||||
|
||||
// the first area is a dummy
|
||||
memset( &area, 0, sizeof( area ) );
|
||||
file->areas.Append( area );
|
||||
|
||||
// the first node is a dummy
|
||||
memset( &node, 0, sizeof( node ) );
|
||||
file->nodes.Append( node );
|
||||
|
||||
// store the tree
|
||||
StoreTree_r( bsp.GetRootNode() );
|
||||
|
||||
// calculate area bounds and a reachable point in the area
|
||||
file->FinishAreas();
|
||||
|
||||
ShutdownHash();
|
||||
|
||||
common->Printf( "\r%6d areas\n", file->areas.Num() );
|
||||
|
||||
return true;
|
||||
}
|
||||
359
neo/tools/compilers/aas/AASBuild_gravity.cpp
Normal file
359
neo/tools/compilers/aas/AASBuild_gravity.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASBuild_local.h"
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::SetPortalFlags_r
|
||||
============
|
||||
*/
|
||||
void idAASBuild::SetPortalFlags_r( idBrushBSPNode *node ) {
|
||||
int s;
|
||||
idBrushBSPPortal *p;
|
||||
idVec3 normal;
|
||||
|
||||
if ( !node ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !node->GetChild(0) && !node->GetChild(1) ) {
|
||||
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
|
||||
s = (p->GetNode(1) == node);
|
||||
|
||||
// if solid at the other side of the portal
|
||||
if ( p->GetNode(!s)->GetContents() & AREACONTENTS_SOLID ) {
|
||||
if ( s ) {
|
||||
normal = -p->GetPlane().Normal();
|
||||
}
|
||||
else {
|
||||
normal = p->GetPlane().Normal();
|
||||
}
|
||||
if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) {
|
||||
p->SetFlag( FACE_FLOOR );
|
||||
}
|
||||
else {
|
||||
p->SetFlag( FACE_SOLID );
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SetPortalFlags_r( node->GetChild(0) );
|
||||
SetPortalFlags_r( node->GetChild(1) );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::PortalIsGap
|
||||
============
|
||||
*/
|
||||
bool idAASBuild::PortalIsGap( idBrushBSPPortal *portal, int side ) {
|
||||
idVec3 normal;
|
||||
|
||||
// if solid at the other side of the portal
|
||||
if ( portal->GetNode(!side)->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( side ) {
|
||||
normal = -(portal->GetPlane().Normal());
|
||||
}
|
||||
else {
|
||||
normal = portal->GetPlane().Normal();
|
||||
}
|
||||
if ( normal * aasSettings->invGravityDir > aasSettings->minFloorCos ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::GravSubdivLeafNode
|
||||
============
|
||||
*/
|
||||
#define FACE_CHECKED BIT(31)
|
||||
#define GRAVSUBDIV_EPSILON 0.1f
|
||||
|
||||
void idAASBuild::GravSubdivLeafNode( idBrushBSPNode *node ) {
|
||||
int s1, s2, i, j, k, side1;
|
||||
int numSplits, numSplitters;
|
||||
idBrushBSPPortal *p1, *p2;
|
||||
idWinding *w1, *w2;
|
||||
idVec3 normal;
|
||||
idPlane plane;
|
||||
idPlaneSet planeList;
|
||||
float d, min, max;
|
||||
int *splitterOrder;
|
||||
int *bestNumSplits;
|
||||
int floor, gap, numFloorChecked;
|
||||
|
||||
// if this leaf node is already classified it cannot have a combination of floor and gap portals
|
||||
if ( node->GetFlags() & (AREA_FLOOR|AREA_GAP) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
floor = gap = 0;
|
||||
|
||||
// check if the area has a floor
|
||||
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
|
||||
s1 = (p1->GetNode(1) == node);
|
||||
|
||||
if ( p1->GetFlags() & FACE_FLOOR ) {
|
||||
floor++;
|
||||
}
|
||||
}
|
||||
|
||||
// find seperating planes between gap and floor portals
|
||||
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
|
||||
s1 = (p1->GetNode(1) == node);
|
||||
|
||||
// if the portal is a gap seen from this side
|
||||
if ( PortalIsGap( p1, s1 ) ) {
|
||||
gap++;
|
||||
// if the area doesn't have a floor
|
||||
if ( !floor ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
|
||||
numFloorChecked = 0;
|
||||
|
||||
w1 = p1->GetWinding();
|
||||
|
||||
// test all edges of the gap
|
||||
for ( i = 0; i < w1->GetNumPoints(); i++ ) {
|
||||
|
||||
// create a plane through the edge of the gap parallel to the direction of gravity
|
||||
normal = (*w1)[(i+1)%w1->GetNumPoints()].ToVec3() - (*w1)[i].ToVec3();
|
||||
normal = normal.Cross( aasSettings->invGravityDir );
|
||||
if ( normal.Normalize() < 0.2f ) {
|
||||
continue;
|
||||
}
|
||||
plane.SetNormal( normal );
|
||||
plane.FitThroughPoint( (*w1)[i].ToVec3() );
|
||||
|
||||
// get the side of the plane the gap is on
|
||||
side1 = w1->PlaneSide( plane, GRAVSUBDIV_EPSILON );
|
||||
if ( side1 == SIDE_ON ) {
|
||||
break;
|
||||
}
|
||||
|
||||
// test if the plane through the edge of the gap seperates the gap from a floor portal
|
||||
for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
|
||||
s2 = (p2->GetNode(1) == node);
|
||||
|
||||
if ( !( p2->GetFlags() & FACE_FLOOR ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( p2->GetFlags() & FACE_CHECKED ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
w2 = p2->GetWinding();
|
||||
|
||||
min = 2.0f * GRAVSUBDIV_EPSILON;
|
||||
max = GRAVSUBDIV_EPSILON;
|
||||
if ( side1 == SIDE_FRONT ) {
|
||||
for ( j = 0; j < w2->GetNumPoints(); j++ ) {
|
||||
d = plane.Distance( (*w2)[j].ToVec3() );
|
||||
if ( d >= GRAVSUBDIV_EPSILON ) {
|
||||
break; // point at the same side of the plane as the gap
|
||||
}
|
||||
d = idMath::Fabs( d );
|
||||
if ( d < min ) {
|
||||
min = d;
|
||||
}
|
||||
if ( d > max ) {
|
||||
max = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( j = 0; j < w2->GetNumPoints(); j++ ) {
|
||||
d = plane.Distance( (*w2)[j].ToVec3() );
|
||||
if ( d <= -GRAVSUBDIV_EPSILON ) {
|
||||
break; // point at the same side of the plane as the gap
|
||||
}
|
||||
d = idMath::Fabs( d );
|
||||
if ( d < min ) {
|
||||
min = d;
|
||||
}
|
||||
if ( d > max ) {
|
||||
max = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a point of the floor portal was found to be at the same side of the plane as the gap
|
||||
if ( j < w2->GetNumPoints() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the floor portal touches the plane
|
||||
if ( min < GRAVSUBDIV_EPSILON && max > GRAVSUBDIV_EPSILON ) {
|
||||
planeList.FindPlane( plane, 0.00001f, 0.1f );
|
||||
}
|
||||
|
||||
p2->SetFlag( FACE_CHECKED );
|
||||
numFloorChecked++;
|
||||
|
||||
}
|
||||
if ( numFloorChecked == floor ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ( p2 = node->GetPortals(); p2; p2 = p2->Next(s2) ) {
|
||||
s2 = (p2->GetNode(1) == node);
|
||||
p2->RemoveFlag( FACE_CHECKED );
|
||||
}
|
||||
}
|
||||
|
||||
// if the leaf node does not have both floor and gap portals
|
||||
if ( !( gap && floor) ) {
|
||||
if ( floor ) {
|
||||
node->SetFlag( AREA_FLOOR );
|
||||
}
|
||||
else if ( gap ) {
|
||||
node->SetFlag( AREA_GAP );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if no valid seperators found
|
||||
if ( planeList.Num() == 0 ) {
|
||||
// NOTE: this should never happend, if it does the leaf node has degenerate portals
|
||||
return;
|
||||
}
|
||||
|
||||
splitterOrder = (int *) _alloca( planeList.Num() * sizeof( int ) );
|
||||
bestNumSplits = (int *) _alloca( planeList.Num() * sizeof( int ) );
|
||||
numSplitters = 0;
|
||||
|
||||
// test all possible seperators and sort them from best to worst
|
||||
for ( i = 0; i < planeList.Num(); i += 2 ) {
|
||||
numSplits = 0;
|
||||
|
||||
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
|
||||
s1 = (p1->GetNode(1) == node);
|
||||
if ( p1->GetWinding()->PlaneSide( planeList[i], 0.1f ) == SIDE_CROSS ) {
|
||||
numSplits++;
|
||||
}
|
||||
}
|
||||
|
||||
for ( j = 0; j < numSplitters; j++ ) {
|
||||
if ( numSplits < bestNumSplits[j] ) {
|
||||
for ( k = numSplitters; k > j; k-- ) {
|
||||
bestNumSplits[k] = bestNumSplits[k-1];
|
||||
splitterOrder[k] = splitterOrder[k-1];
|
||||
}
|
||||
bestNumSplits[j] = numSplits;
|
||||
splitterOrder[j] = i;
|
||||
numSplitters++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j >= numSplitters ) {
|
||||
bestNumSplits[j] = numSplits;
|
||||
splitterOrder[j] = i;
|
||||
numSplitters++;
|
||||
}
|
||||
}
|
||||
|
||||
// try all seperators in order from best to worst
|
||||
for ( i = 0; i < numSplitters; i++ ) {
|
||||
if ( node->Split( planeList[splitterOrder[i]], -1 ) ) {
|
||||
// we found a seperator that works
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i >= numSplitters) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayRealTimeString( "\r%6d", ++numGravitationalSubdivisions );
|
||||
|
||||
// test children for further splits
|
||||
GravSubdivLeafNode( node->GetChild(0) );
|
||||
GravSubdivLeafNode( node->GetChild(1) );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::GravSubdiv_r
|
||||
============
|
||||
*/
|
||||
void idAASBuild::GravSubdiv_r( idBrushBSPNode *node ) {
|
||||
|
||||
if ( !node ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !node->GetChild(0) && !node->GetChild(1) ) {
|
||||
GravSubdivLeafNode( node );
|
||||
return;
|
||||
}
|
||||
|
||||
GravSubdiv_r( node->GetChild(0) );
|
||||
GravSubdiv_r( node->GetChild(1) );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::GravitationalSubdivision
|
||||
============
|
||||
*/
|
||||
void idAASBuild::GravitationalSubdivision( idBrushBSP &bsp ) {
|
||||
numGravitationalSubdivisions = 0;
|
||||
|
||||
common->Printf( "[Gravitational Subdivision]\n" );
|
||||
|
||||
SetPortalFlags_r( bsp.GetRootNode() );
|
||||
GravSubdiv_r( bsp.GetRootNode() );
|
||||
|
||||
common->Printf( "\r%6d subdivisions\n", numGravitationalSubdivisions );
|
||||
}
|
||||
575
neo/tools/compilers/aas/AASBuild_ledge.cpp
Normal file
575
neo/tools/compilers/aas/AASBuild_ledge.cpp
Normal file
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASBuild_local.h"
|
||||
|
||||
#define LEDGE_EPSILON 0.1f
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idLedge
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
/*
|
||||
============
|
||||
idLedge::idLedge
|
||||
============
|
||||
*/
|
||||
idLedge::idLedge( void ) {
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idLedge::idLedge
|
||||
============
|
||||
*/
|
||||
idLedge::idLedge( const idVec3 &v1, const idVec3 &v2, const idVec3 &gravityDir, idBrushBSPNode *n ) {
|
||||
start = v1;
|
||||
end = v2;
|
||||
node = n;
|
||||
numPlanes = 4;
|
||||
planes[0].SetNormal( (v1 - v2).Cross( gravityDir ) );
|
||||
planes[0].Normalize();
|
||||
planes[0].FitThroughPoint( v1 );
|
||||
planes[1].SetNormal( (v1 - v2).Cross( planes[0].Normal() ) );
|
||||
planes[1].Normalize();
|
||||
planes[1].FitThroughPoint( v1 );
|
||||
planes[2].SetNormal( v1 - v2 );
|
||||
planes[2].Normalize();
|
||||
planes[2].FitThroughPoint( v1 );
|
||||
planes[3].SetNormal( v2 - v1 );
|
||||
planes[3].Normalize();
|
||||
planes[3].FitThroughPoint( v2 );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idLedge::AddPoint
|
||||
============
|
||||
*/
|
||||
void idLedge::AddPoint( const idVec3 &v ) {
|
||||
if ( planes[2].Distance( v ) > 0.0f ) {
|
||||
start = v;
|
||||
planes[2].FitThroughPoint( start );
|
||||
}
|
||||
if ( planes[3].Distance( v ) > 0.0f ) {
|
||||
end = v;
|
||||
planes[3].FitThroughPoint( end );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idLedge::CreateBevels
|
||||
|
||||
NOTE: this assumes the gravity is vertical
|
||||
============
|
||||
*/
|
||||
void idLedge::CreateBevels( const idVec3 &gravityDir ) {
|
||||
int i, j;
|
||||
idBounds bounds;
|
||||
idVec3 size, normal;
|
||||
|
||||
bounds.Clear();
|
||||
bounds.AddPoint( start );
|
||||
bounds.AddPoint( end );
|
||||
size = bounds[1] - bounds[0];
|
||||
|
||||
// plane through ledge
|
||||
planes[0].SetNormal( (start - end).Cross( gravityDir ) );
|
||||
planes[0].Normalize();
|
||||
planes[0].FitThroughPoint( start );
|
||||
// axial bevels at start and end point
|
||||
i = size[1] > size[0];
|
||||
normal = vec3_origin;
|
||||
normal[i] = 1.0f;
|
||||
j = end[i] > start[i];
|
||||
planes[1+j].SetNormal( normal );
|
||||
planes[1+!j].SetNormal( -normal );
|
||||
planes[1].FitThroughPoint( start );
|
||||
planes[2].FitThroughPoint( end );
|
||||
numExpandedPlanes = 3;
|
||||
// if additional bevels are required
|
||||
if ( idMath::Fabs( size[!i] ) > 0.01f ) {
|
||||
normal = vec3_origin;
|
||||
normal[!i] = 1.0f;
|
||||
j = end[!i] > start[!i];
|
||||
planes[3+j].SetNormal( normal );
|
||||
planes[3+!j].SetNormal( -normal );
|
||||
planes[3].FitThroughPoint( start );
|
||||
planes[4].FitThroughPoint( end );
|
||||
numExpandedPlanes = 5;
|
||||
}
|
||||
// opposite of first
|
||||
planes[numExpandedPlanes+0] = -planes[0];
|
||||
// number of planes used for splitting
|
||||
numSplitPlanes = numExpandedPlanes + 1;
|
||||
// top plane
|
||||
planes[numSplitPlanes+0].SetNormal( (start - end).Cross( planes[0].Normal() ) );
|
||||
planes[numSplitPlanes+0].Normalize();
|
||||
planes[numSplitPlanes+0].FitThroughPoint( start );
|
||||
// bottom plane
|
||||
planes[numSplitPlanes+1] = -planes[numSplitPlanes+0];
|
||||
// total number of planes
|
||||
numPlanes = numSplitPlanes + 2;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idLedge::Expand
|
||||
============
|
||||
*/
|
||||
void idLedge::Expand( const idBounds &bounds, float maxStepHeight ) {
|
||||
int i, j;
|
||||
idVec3 v;
|
||||
|
||||
for ( i = 0; i < numExpandedPlanes; i++ ) {
|
||||
|
||||
for ( j = 0; j < 3; j++ ) {
|
||||
if ( planes[i].Normal()[j] > 0.0f ) {
|
||||
v[j] = bounds[0][j];
|
||||
}
|
||||
else {
|
||||
v[j] = bounds[1][j];
|
||||
}
|
||||
}
|
||||
|
||||
planes[i].SetDist( planes[i].Dist() + v * -planes[i].Normal() );
|
||||
}
|
||||
|
||||
planes[numSplitPlanes+0].SetDist( planes[numSplitPlanes+0].Dist() + maxStepHeight );
|
||||
planes[numSplitPlanes+1].SetDist( planes[numSplitPlanes+1].Dist() + 1.0f );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idLedge::ChopWinding
|
||||
============
|
||||
*/
|
||||
idWinding *idLedge::ChopWinding( const idWinding *winding ) const {
|
||||
int i;
|
||||
idWinding *w;
|
||||
|
||||
w = winding->Copy();
|
||||
for ( i = 0; i < numPlanes && w; i++ ) {
|
||||
w = w->Clip( -planes[i], ON_EPSILON, true );
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idLedge::PointBetweenBounds
|
||||
============
|
||||
*/
|
||||
bool idLedge::PointBetweenBounds( const idVec3 &v ) const {
|
||||
return ( planes[2].Distance( v ) < LEDGE_EPSILON ) && ( planes[3].Distance( v ) < LEDGE_EPSILON );
|
||||
}
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idAASBuild
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::LedgeSubdivFlood_r
|
||||
============
|
||||
*/
|
||||
void idAASBuild::LedgeSubdivFlood_r( idBrushBSPNode *node, const idLedge *ledge ) {
|
||||
int s1, i;
|
||||
idBrushBSPPortal *p1;
|
||||
idWinding *w;
|
||||
idList<idBrushBSPNode *> nodeList;
|
||||
|
||||
if ( node->GetFlags() & NODE_VISITED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if this is not already a ledge area
|
||||
if ( !( node->GetFlags() & AREA_LEDGE ) ) {
|
||||
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
|
||||
s1 = (p1->GetNode(1) == node);
|
||||
|
||||
if ( !(p1->GetFlags() & FACE_FLOOR) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// split the area if some part of the floor portal is inside the expanded ledge
|
||||
w = ledge->ChopWinding( p1->GetWinding() );
|
||||
if ( !w ) {
|
||||
continue;
|
||||
}
|
||||
delete w;
|
||||
|
||||
for ( i = 0; i < ledge->numSplitPlanes; i++ ) {
|
||||
if ( node->PlaneSide( ledge->planes[i], 0.1f ) != SIDE_CROSS ) {
|
||||
continue;
|
||||
}
|
||||
if ( !node->Split( ledge->planes[i], -1 ) ) {
|
||||
continue;
|
||||
}
|
||||
numLedgeSubdivisions++;
|
||||
DisplayRealTimeString( "\r%6d", numLedgeSubdivisions );
|
||||
node->GetChild(0)->SetFlag( NODE_VISITED );
|
||||
LedgeSubdivFlood_r( node->GetChild(1), ledge );
|
||||
return;
|
||||
}
|
||||
|
||||
node->SetFlag( AREA_LEDGE );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
node->SetFlag( NODE_VISITED );
|
||||
|
||||
// get all nodes we might need to flood into
|
||||
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
|
||||
s1 = (p1->GetNode(1) == node);
|
||||
|
||||
if ( p1->GetNode( !s1 )->GetContents() & AREACONTENTS_SOLID ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// flood through this portal if the portal is partly inside the expanded ledge
|
||||
w = ledge->ChopWinding( p1->GetWinding() );
|
||||
if ( !w ) {
|
||||
continue;
|
||||
}
|
||||
delete w;
|
||||
// add to list, cannot flood directly cause portals might be split on the way
|
||||
nodeList.Append( p1->GetNode( !s1 ) );
|
||||
}
|
||||
|
||||
// flood into other nodes
|
||||
for ( i = 0; i < nodeList.Num(); i++ ) {
|
||||
LedgeSubdivLeafNodes_r( nodeList[i], ledge );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::LedgeSubdivLeafNodes_r
|
||||
|
||||
The node the ledge was originally part of might be split by other ledges.
|
||||
Here we recurse down the tree from the original node to find all the new leaf nodes the ledge might be part of.
|
||||
============
|
||||
*/
|
||||
void idAASBuild::LedgeSubdivLeafNodes_r( idBrushBSPNode *node, const idLedge *ledge ) {
|
||||
if ( !node ) {
|
||||
return;
|
||||
}
|
||||
if ( !node->GetChild(0) && !node->GetChild(1) ) {
|
||||
LedgeSubdivFlood_r( node, ledge );
|
||||
return;
|
||||
}
|
||||
LedgeSubdivLeafNodes_r( node->GetChild(0), ledge );
|
||||
LedgeSubdivLeafNodes_r( node->GetChild(1), ledge );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::LedgeSubdiv
|
||||
============
|
||||
*/
|
||||
void idAASBuild::LedgeSubdiv( idBrushBSPNode *root ) {
|
||||
int i, j;
|
||||
idBrush *brush;
|
||||
idList<idBrushSide *> sideList;
|
||||
|
||||
// create ledge bevels and expand ledges
|
||||
for ( i = 0; i < ledgeList.Num(); i++ ) {
|
||||
|
||||
ledgeList[i].CreateBevels( aasSettings->gravityDir );
|
||||
ledgeList[i].Expand( aasSettings->boundingBoxes[0], aasSettings->maxStepHeight );
|
||||
|
||||
// if we should write out a ledge map
|
||||
if ( ledgeMap ) {
|
||||
sideList.SetNum( 0 );
|
||||
for ( j = 0; j < ledgeList[i].numPlanes; j++ ) {
|
||||
sideList.Append( new idBrushSide( ledgeList[i].planes[j], -1 ) );
|
||||
}
|
||||
|
||||
brush = new idBrush();
|
||||
brush->FromSides( sideList );
|
||||
|
||||
ledgeMap->WriteBrush( brush );
|
||||
|
||||
delete brush;
|
||||
}
|
||||
|
||||
// flood tree from the ledge node and subdivide areas with the ledge
|
||||
LedgeSubdivLeafNodes_r( ledgeList[i].node, &ledgeList[i] );
|
||||
|
||||
// remove the node visited flags
|
||||
ledgeList[i].node->RemoveFlagRecurseFlood( NODE_VISITED );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::IsLedgeSide_r
|
||||
============
|
||||
*/
|
||||
bool idAASBuild::IsLedgeSide_r( idBrushBSPNode *node, idFixedWinding *w, const idPlane &plane, const idVec3 &normal, const idVec3 &origin, const float radius ) {
|
||||
int res, i;
|
||||
idFixedWinding back;
|
||||
float dist;
|
||||
|
||||
if ( !node ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ( node->GetChild(0) && node->GetChild(1) ) {
|
||||
dist = node->GetPlane().Distance( origin );
|
||||
if ( dist > radius ) {
|
||||
res = SIDE_FRONT;
|
||||
}
|
||||
else if ( dist < -radius ) {
|
||||
res = SIDE_BACK;
|
||||
}
|
||||
else {
|
||||
res = w->Split( &back, node->GetPlane(), LEDGE_EPSILON );
|
||||
}
|
||||
if ( res == SIDE_FRONT ) {
|
||||
node = node->GetChild(0);
|
||||
}
|
||||
else if ( res == SIDE_BACK ) {
|
||||
node = node->GetChild(1);
|
||||
}
|
||||
else if ( res == SIDE_ON ) {
|
||||
// continue with the side the winding faces
|
||||
if ( node->GetPlane().Normal() * normal > 0.0f ) {
|
||||
node = node->GetChild(0);
|
||||
}
|
||||
else {
|
||||
node = node->GetChild(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( IsLedgeSide_r( node->GetChild(1), &back, plane, normal, origin, radius ) ) {
|
||||
return true;
|
||||
}
|
||||
node = node->GetChild(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ( node->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ ) {
|
||||
if ( plane.Distance( (*w)[i].ToVec3() ) > 0.0f ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::AddLedge
|
||||
============
|
||||
*/
|
||||
void idAASBuild::AddLedge( const idVec3 &v1, const idVec3 &v2, idBrushBSPNode *node ) {
|
||||
int i, j, merged;
|
||||
|
||||
// first try to merge the ledge with existing ledges
|
||||
merged = -1;
|
||||
for ( i = 0; i < ledgeList.Num(); i++ ) {
|
||||
|
||||
for ( j = 0; j < 2; j++ ) {
|
||||
if ( idMath::Fabs( ledgeList[i].planes[j].Distance( v1 ) ) > LEDGE_EPSILON ) {
|
||||
break;
|
||||
}
|
||||
if ( idMath::Fabs( ledgeList[i].planes[j].Distance( v2 ) ) > LEDGE_EPSILON ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j < 2 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !ledgeList[i].PointBetweenBounds( v1 ) &&
|
||||
!ledgeList[i].PointBetweenBounds( v2 ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( merged == -1 ) {
|
||||
ledgeList[i].AddPoint( v1 );
|
||||
ledgeList[i].AddPoint( v2 );
|
||||
merged = i;
|
||||
}
|
||||
else {
|
||||
ledgeList[merged].AddPoint( ledgeList[i].start );
|
||||
ledgeList[merged].AddPoint( ledgeList[i].end );
|
||||
ledgeList.RemoveIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if the ledge could not be merged
|
||||
if ( merged == -1 ) {
|
||||
ledgeList.Append( idLedge( v1, v2, aasSettings->gravityDir, node ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::FindLeafNodeLedges
|
||||
============
|
||||
*/
|
||||
void idAASBuild::FindLeafNodeLedges( idBrushBSPNode *root, idBrushBSPNode *node ) {
|
||||
int s1, i;
|
||||
idBrushBSPPortal *p1;
|
||||
idWinding *w;
|
||||
idVec3 v1, v2, normal, origin;
|
||||
idFixedWinding winding;
|
||||
idBounds bounds;
|
||||
idPlane plane;
|
||||
float radius;
|
||||
|
||||
for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
|
||||
s1 = (p1->GetNode(1) == node);
|
||||
|
||||
if ( !(p1->GetFlags() & FACE_FLOOR) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( s1 ) {
|
||||
plane = p1->GetPlane();
|
||||
w = p1->GetWinding()->Reverse();
|
||||
}
|
||||
else {
|
||||
plane = -p1->GetPlane();
|
||||
w = p1->GetWinding();
|
||||
}
|
||||
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ ) {
|
||||
|
||||
v1 = (*w)[i].ToVec3();
|
||||
v2 = (*w)[(i+1)%w->GetNumPoints()].ToVec3();
|
||||
normal = (v2 - v1).Cross( aasSettings->gravityDir );
|
||||
if ( normal.Normalize() < 0.5f ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
winding.Clear();
|
||||
winding += v1 + normal * LEDGE_EPSILON * 0.5f;
|
||||
winding += v2 + normal * LEDGE_EPSILON * 0.5f;
|
||||
winding += winding[1].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir;
|
||||
winding += winding[0].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir;
|
||||
|
||||
winding.GetBounds( bounds );
|
||||
origin = (bounds[1] - bounds[0]) * 0.5f;
|
||||
radius = origin.Length() + LEDGE_EPSILON;
|
||||
origin = bounds[0] + origin;
|
||||
|
||||
plane.FitThroughPoint( v1 + aasSettings->maxStepHeight * aasSettings->gravityDir );
|
||||
|
||||
if ( !IsLedgeSide_r( root, &winding, plane, normal, origin, radius ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddLedge( v1, v2, node );
|
||||
}
|
||||
|
||||
if ( w != p1->GetWinding() ) {
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::FindLedges_r
|
||||
============
|
||||
*/
|
||||
void idAASBuild::FindLedges_r( idBrushBSPNode *root, idBrushBSPNode *node ) {
|
||||
if ( !node ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !node->GetChild(0) && !node->GetChild(1) ) {
|
||||
if ( node->GetFlags() & NODE_VISITED ) {
|
||||
return;
|
||||
}
|
||||
FindLeafNodeLedges( root, node );
|
||||
node->SetFlag( NODE_VISITED );
|
||||
return;
|
||||
}
|
||||
|
||||
FindLedges_r( root, node->GetChild(0) );
|
||||
FindLedges_r( root, node->GetChild(1) );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::WriteLedgeMap
|
||||
============
|
||||
*/
|
||||
void idAASBuild::WriteLedgeMap( const idStr &fileName, const idStr &ext ) {
|
||||
ledgeMap = new idBrushMap( fileName, ext );
|
||||
ledgeMap->SetTexture( "textures/base_trim/bluetex4q_ed" );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::LedgeSubdivision
|
||||
|
||||
NOTE: this assumes the bounding box is higher than the maximum step height
|
||||
only ledges with vertical sides are considered
|
||||
============
|
||||
*/
|
||||
void idAASBuild::LedgeSubdivision( idBrushBSP &bsp ) {
|
||||
numLedgeSubdivisions = 0;
|
||||
ledgeList.Clear();
|
||||
|
||||
common->Printf( "[Ledge Subdivision]\n" );
|
||||
|
||||
bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED );
|
||||
FindLedges_r( bsp.GetRootNode(), bsp.GetRootNode() );
|
||||
bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED );
|
||||
|
||||
common->Printf( "\r%6d ledges\n", ledgeList.Num() );
|
||||
|
||||
LedgeSubdiv( bsp.GetRootNode() );
|
||||
|
||||
common->Printf( "\r%6d subdivisions\n", numLedgeSubdivisions );
|
||||
}
|
||||
149
neo/tools/compilers/aas/AASBuild_local.h
Normal file
149
neo/tools/compilers/aas/AASBuild_local.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __AASBUILD_LOCAL_H__
|
||||
#define __AASBUILD_LOCAL_H__
|
||||
|
||||
#include "AASFile.h"
|
||||
#include "AASFile_local.h"
|
||||
|
||||
#include "Brush.h"
|
||||
#include "BrushBSP.h"
|
||||
#include "AASReach.h"
|
||||
#include "AASCluster.h"
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idAASBuild
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
typedef struct aasProcNode_s {
|
||||
idPlane plane;
|
||||
int children[2]; // negative numbers are (-1 - areaNumber), 0 = solid
|
||||
} aasProcNode_t;
|
||||
|
||||
|
||||
class idLedge {
|
||||
|
||||
public:
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
idBrushBSPNode * node;
|
||||
int numExpandedPlanes;
|
||||
int numSplitPlanes;
|
||||
int numPlanes;
|
||||
idPlane planes[8];
|
||||
|
||||
public:
|
||||
idLedge( void );
|
||||
idLedge( const idVec3 &v1, const idVec3 &v2, const idVec3 &gravityDir, idBrushBSPNode *n );
|
||||
void AddPoint( const idVec3 &v );
|
||||
void CreateBevels( const idVec3 &gravityDir );
|
||||
void Expand( const idBounds &bounds, float maxStepHeight );
|
||||
idWinding * ChopWinding( const idWinding *winding ) const;
|
||||
bool PointBetweenBounds( const idVec3 &v ) const;
|
||||
};
|
||||
|
||||
|
||||
class idAASBuild {
|
||||
|
||||
public:
|
||||
idAASBuild( void );
|
||||
~idAASBuild( void );
|
||||
bool Build( const idStr &fileName, const idAASSettings *settings );
|
||||
bool BuildReachability( const idStr &fileName, const idAASSettings *settings );
|
||||
void Shutdown( void );
|
||||
|
||||
private:
|
||||
const idAASSettings * aasSettings;
|
||||
idAASFileLocal * file;
|
||||
aasProcNode_t * procNodes;
|
||||
int numProcNodes;
|
||||
int numGravitationalSubdivisions;
|
||||
int numMergedLeafNodes;
|
||||
int numLedgeSubdivisions;
|
||||
idList<idLedge> ledgeList;
|
||||
idBrushMap * ledgeMap;
|
||||
|
||||
private: // map loading
|
||||
void ParseProcNodes( idLexer *src );
|
||||
bool LoadProcBSP( const char *name, ID_TIME_T minFileTime );
|
||||
void DeleteProcBSP( void );
|
||||
bool ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius );
|
||||
void ClipBrushSidesWithProcBSP( idBrushList &brushList );
|
||||
int ContentsForAAS( int contents );
|
||||
idBrushList AddBrushesForMapBrush( const idMapBrush *mapBrush, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList );
|
||||
idBrushList AddBrushesForMapPatch( const idMapPatch *mapPatch, const idVec3 &origin, const idMat3 &axis, int entityNum, int primitiveNum, idBrushList brushList );
|
||||
idBrushList AddBrushesForMapEntity( const idMapEntity *mapEnt, int entityNum, idBrushList brushList );
|
||||
idBrushList AddBrushesForMapFile( const idMapFile * mapFile, idBrushList brushList );
|
||||
bool CheckForEntities( const idMapFile *mapFile, idStrList &entityClassNames ) const;
|
||||
void ChangeMultipleBoundingBoxContents_r( idBrushBSPNode *node, int mask );
|
||||
|
||||
private: // gravitational subdivision
|
||||
void SetPortalFlags_r( idBrushBSPNode *node );
|
||||
bool PortalIsGap( idBrushBSPPortal *portal, int side );
|
||||
void GravSubdivLeafNode( idBrushBSPNode *node );
|
||||
void GravSubdiv_r( idBrushBSPNode *node );
|
||||
void GravitationalSubdivision( idBrushBSP &bsp );
|
||||
|
||||
private: // ledge subdivision
|
||||
void LedgeSubdivFlood_r( idBrushBSPNode *node, const idLedge *ledge );
|
||||
void LedgeSubdivLeafNodes_r( idBrushBSPNode *node, const idLedge *ledge );
|
||||
void LedgeSubdiv( idBrushBSPNode *root );
|
||||
bool IsLedgeSide_r( idBrushBSPNode *node, idFixedWinding *w, const idPlane &plane, const idVec3 &normal, const idVec3 &origin, const float radius );
|
||||
void AddLedge( const idVec3 &v1, const idVec3 &v2, idBrushBSPNode *node );
|
||||
void FindLeafNodeLedges( idBrushBSPNode *root, idBrushBSPNode *node );
|
||||
void FindLedges_r( idBrushBSPNode *root, idBrushBSPNode *node );
|
||||
void LedgeSubdivision( idBrushBSP &bsp );
|
||||
void WriteLedgeMap( const idStr &fileName, const idStr &ext );
|
||||
|
||||
private: // merging
|
||||
bool AllGapsLeadToOtherNode( idBrushBSPNode *nodeWithGaps, idBrushBSPNode *otherNode );
|
||||
bool MergeWithAdjacentLeafNodes( idBrushBSP &bsp, idBrushBSPNode *node );
|
||||
void MergeLeafNodes_r( idBrushBSP &bsp, idBrushBSPNode *node );
|
||||
void MergeLeafNodes( idBrushBSP &bsp );
|
||||
|
||||
private: // storing file
|
||||
void SetupHash( void );
|
||||
void ShutdownHash( void );
|
||||
void ClearHash( const idBounds &bounds );
|
||||
int HashVec( const idVec3 &vec );
|
||||
bool GetVertex( const idVec3 &v, int *vertexNum );
|
||||
bool GetEdge( const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num );
|
||||
bool GetFaceForPortal( idBrushBSPPortal *portal, int side, int *faceNum );
|
||||
bool GetAreaForLeafNode( idBrushBSPNode *node, int *areaNum );
|
||||
int StoreTree_r( idBrushBSPNode *node );
|
||||
void GetSizeEstimate_r( idBrushBSPNode *parent, idBrushBSPNode *node, struct sizeEstimate_s &size );
|
||||
void SetSizeEstimate( const idBrushBSP &bsp, idAASFileLocal *file );
|
||||
bool StoreFile( const idBrushBSP &bsp );
|
||||
|
||||
};
|
||||
|
||||
#endif /* !__AASBUILD_LOCAL_H__ */
|
||||
163
neo/tools/compilers/aas/AASBuild_merge.cpp
Normal file
163
neo/tools/compilers/aas/AASBuild_merge.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASBuild_local.h"
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::AllGapsLeadToOtherNode
|
||||
============
|
||||
*/
|
||||
bool idAASBuild::AllGapsLeadToOtherNode( idBrushBSPNode *nodeWithGaps, idBrushBSPNode *otherNode ) {
|
||||
int s;
|
||||
idBrushBSPPortal *p;
|
||||
|
||||
for ( p = nodeWithGaps->GetPortals(); p; p = p->Next(s) ) {
|
||||
s = (p->GetNode(1) == nodeWithGaps);
|
||||
|
||||
if ( !PortalIsGap( p, s ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( p->GetNode(!s) != otherNode ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::MergeWithAdjacentLeafNodes
|
||||
============
|
||||
*/
|
||||
bool idAASBuild::MergeWithAdjacentLeafNodes( idBrushBSP &bsp, idBrushBSPNode *node ) {
|
||||
int s, numMerges = 0, otherNodeFlags;
|
||||
idBrushBSPPortal *p;
|
||||
|
||||
do {
|
||||
for ( p = node->GetPortals(); p; p = p->Next(s) ) {
|
||||
s = (p->GetNode(1) == node);
|
||||
|
||||
// both leaf nodes must have the same contents
|
||||
if ( node->GetContents() != p->GetNode(!s)->GetContents() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// cannot merge leaf nodes if one is near a ledge and the other is not
|
||||
if ( (node->GetFlags() & AREA_LEDGE) != (p->GetNode(!s)->GetFlags() & AREA_LEDGE) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// cannot merge leaf nodes if one has a floor portal and the other a gap portal
|
||||
if ( node->GetFlags() & AREA_FLOOR ) {
|
||||
if ( p->GetNode(!s)->GetFlags() & AREA_GAP ) {
|
||||
if ( !AllGapsLeadToOtherNode( p->GetNode(!s), node ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( node->GetFlags() & AREA_GAP ) {
|
||||
if ( p->GetNode(!s)->GetFlags() & AREA_FLOOR ) {
|
||||
if ( !AllGapsLeadToOtherNode( node, p->GetNode(!s) ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
otherNodeFlags = p->GetNode(!s)->GetFlags();
|
||||
|
||||
// try to merge the leaf nodes
|
||||
if ( bsp.TryMergeLeafNodes( p, s ) ) {
|
||||
node->SetFlag( otherNodeFlags );
|
||||
if ( node->GetFlags() & AREA_FLOOR ) {
|
||||
node->RemoveFlag( AREA_GAP );
|
||||
}
|
||||
numMerges++;
|
||||
DisplayRealTimeString( "\r%6d", ++numMergedLeafNodes );
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while( p );
|
||||
|
||||
if ( numMerges ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::MergeLeafNodes_r
|
||||
============
|
||||
*/
|
||||
void idAASBuild::MergeLeafNodes_r( idBrushBSP &bsp, idBrushBSPNode *node ) {
|
||||
|
||||
if ( !node ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->GetContents() & AREACONTENTS_SOLID ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->GetFlags() & NODE_DONE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !node->GetChild(0) && !node->GetChild(1) ) {
|
||||
MergeWithAdjacentLeafNodes( bsp, node );
|
||||
node->SetFlag( NODE_DONE );
|
||||
return;
|
||||
}
|
||||
|
||||
MergeLeafNodes_r( bsp, node->GetChild(0) );
|
||||
MergeLeafNodes_r( bsp, node->GetChild(1) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASBuild::MergeLeafNodes
|
||||
============
|
||||
*/
|
||||
void idAASBuild::MergeLeafNodes( idBrushBSP &bsp ) {
|
||||
numMergedLeafNodes = 0;
|
||||
|
||||
common->Printf( "[Merge Leaf Nodes]\n" );
|
||||
|
||||
MergeLeafNodes_r( bsp, bsp.GetRootNode() );
|
||||
bsp.GetRootNode()->RemoveFlagRecurse( NODE_DONE );
|
||||
bsp.PruneMergedTree_r( bsp.GetRootNode() );
|
||||
|
||||
common->Printf( "\r%6d leaf nodes merged\n", numMergedLeafNodes );
|
||||
}
|
||||
557
neo/tools/compilers/aas/AASCluster.cpp
Normal file
557
neo/tools/compilers/aas/AASCluster.cpp
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASFile.h"
|
||||
#include "AASFile_local.h"
|
||||
#include "AASCluster.h"
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::UpdatePortal
|
||||
================
|
||||
*/
|
||||
bool idAASCluster::UpdatePortal( int areaNum, int clusterNum ) {
|
||||
int portalNum;
|
||||
aasPortal_t *portal;
|
||||
|
||||
// find the portal for this area
|
||||
for ( portalNum = 1; portalNum < file->portals.Num(); portalNum++ ) {
|
||||
if ( file->portals[portalNum].areaNum == areaNum ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( portalNum >= file->portals.Num() ) {
|
||||
common->Error( "no portal for area %d", areaNum );
|
||||
return true;
|
||||
}
|
||||
|
||||
portal = &file->portals[portalNum];
|
||||
|
||||
// if the portal is already fully updated
|
||||
if ( portal->clusters[0] == clusterNum ) {
|
||||
return true;
|
||||
}
|
||||
if ( portal->clusters[1] == clusterNum ) {
|
||||
return true;
|
||||
}
|
||||
// if the portal has no front cluster yet
|
||||
if ( !portal->clusters[0] ) {
|
||||
portal->clusters[0] = clusterNum;
|
||||
}
|
||||
// if the portal has no back cluster yet
|
||||
else if ( !portal->clusters[1] )
|
||||
{
|
||||
portal->clusters[1] = clusterNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove the cluster portal flag contents
|
||||
file->areas[areaNum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the area cluster number to the negative portal number
|
||||
file->areas[areaNum].cluster = -portalNum;
|
||||
|
||||
// add the portal to the cluster using the portal index
|
||||
file->portalIndex.Append( portalNum );
|
||||
file->clusters[clusterNum].numPortals++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::FloodClusterAreas_r
|
||||
================
|
||||
*/
|
||||
bool idAASCluster::FloodClusterAreas_r( int areaNum, int clusterNum ) {
|
||||
aasArea_t *area;
|
||||
aasFace_t *face;
|
||||
int faceNum, i;
|
||||
idReachability *reach;
|
||||
|
||||
area = &file->areas[areaNum];
|
||||
|
||||
// if the area is already part of a cluster
|
||||
if ( area->cluster > 0 ) {
|
||||
if ( area->cluster == clusterNum ) {
|
||||
return true;
|
||||
}
|
||||
// there's a reachability going from one cluster to another only in one direction
|
||||
common->Error( "cluster %d touched cluster %d at area %d\r\n", clusterNum, file->areas[areaNum].cluster, areaNum );
|
||||
return false;
|
||||
}
|
||||
|
||||
// if this area is a cluster portal
|
||||
if ( area->contents & AREACONTENTS_CLUSTERPORTAL ) {
|
||||
return UpdatePortal( areaNum, clusterNum );
|
||||
}
|
||||
|
||||
// set the area cluster number
|
||||
area->cluster = clusterNum;
|
||||
|
||||
if ( !noFaceFlood ) {
|
||||
// use area faces to flood into adjacent areas
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = abs(file->faceIndex[area->firstFace + i]);
|
||||
face = &file->faces[faceNum];
|
||||
if ( face->areas[0] == areaNum ) {
|
||||
if ( face->areas[1] ) {
|
||||
if ( !FloodClusterAreas_r( face->areas[1], clusterNum ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( face->areas[0] ) {
|
||||
if ( !FloodClusterAreas_r( face->areas[0], clusterNum ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use the reachabilities to flood into other areas
|
||||
for ( reach = file->areas[areaNum].reach; reach; reach = reach->next ) {
|
||||
if ( !FloodClusterAreas_r( reach->toAreaNum, clusterNum) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// use the reversed reachabilities to flood into other areas
|
||||
for ( reach = file->areas[areaNum].rev_reach; reach; reach = reach->rev_next ) {
|
||||
if ( !FloodClusterAreas_r( reach->fromAreaNum, clusterNum) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::RemoveAreaClusterNumbers
|
||||
================
|
||||
*/
|
||||
void idAASCluster::RemoveAreaClusterNumbers( void ) {
|
||||
int i;
|
||||
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
file->areas[i].cluster = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::NumberClusterAreas
|
||||
================
|
||||
*/
|
||||
void idAASCluster::NumberClusterAreas( int clusterNum ) {
|
||||
int i, portalNum;
|
||||
aasCluster_t *cluster;
|
||||
aasPortal_t *portal;
|
||||
|
||||
cluster = &file->clusters[clusterNum];
|
||||
cluster->numAreas = 0;
|
||||
cluster->numReachableAreas = 0;
|
||||
|
||||
// number all areas in this cluster WITH reachabilities
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
|
||||
if ( file->areas[i].cluster != clusterNum ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
file->areas[i].clusterAreaNum = cluster->numAreas++;
|
||||
cluster->numReachableAreas++;
|
||||
}
|
||||
|
||||
// number all portals in this cluster WITH reachabilities
|
||||
for ( i = 0; i < cluster->numPortals; i++ ) {
|
||||
portalNum = file->portalIndex[cluster->firstPortal + i];
|
||||
portal = &file->portals[portalNum];
|
||||
|
||||
if ( !(file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( portal->clusters[0] == clusterNum ) {
|
||||
portal->clusterAreaNum[0] = cluster->numAreas++;
|
||||
}
|
||||
else {
|
||||
portal->clusterAreaNum[1] = cluster->numAreas++;
|
||||
}
|
||||
cluster->numReachableAreas++;
|
||||
}
|
||||
|
||||
// number all areas in this cluster WITHOUT reachabilities
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
|
||||
if ( file->areas[i].cluster != clusterNum ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
file->areas[i].clusterAreaNum = cluster->numAreas++;
|
||||
}
|
||||
|
||||
// number all portals in this cluster WITHOUT reachabilities
|
||||
for ( i = 0; i < cluster->numPortals; i++ ) {
|
||||
portalNum = file->portalIndex[cluster->firstPortal + i];
|
||||
portal = &file->portals[portalNum];
|
||||
|
||||
if ( file->areas[portal->areaNum].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( portal->clusters[0] == clusterNum ) {
|
||||
portal->clusterAreaNum[0] = cluster->numAreas++;
|
||||
}
|
||||
else {
|
||||
portal->clusterAreaNum[1] = cluster->numAreas++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::FindClusters
|
||||
================
|
||||
*/
|
||||
bool idAASCluster::FindClusters( void ) {
|
||||
int i, clusterNum;
|
||||
aasCluster_t cluster;
|
||||
|
||||
RemoveAreaClusterNumbers();
|
||||
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
// if the area is already part of a cluster
|
||||
if ( file->areas[i].cluster ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if not flooding through faces only use areas that have reachabilities
|
||||
if ( noFaceFlood ) {
|
||||
if ( !(file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// if the area is a cluster portal
|
||||
if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cluster.numAreas = 0;
|
||||
cluster.numReachableAreas = 0;
|
||||
cluster.firstPortal = file->portalIndex.Num();
|
||||
cluster.numPortals = 0;
|
||||
clusterNum = file->clusters.Num();
|
||||
file->clusters.Append( cluster );
|
||||
|
||||
// flood the areas in this cluster
|
||||
if ( !FloodClusterAreas_r( i, clusterNum ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// number the cluster areas
|
||||
NumberClusterAreas( clusterNum );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::CreatePortals
|
||||
================
|
||||
*/
|
||||
void idAASCluster::CreatePortals( void ) {
|
||||
int i;
|
||||
aasPortal_t portal;
|
||||
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
// if the area is a cluster portal
|
||||
if ( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) {
|
||||
portal.areaNum = i;
|
||||
portal.clusters[0] = portal.clusters[1] = 0;
|
||||
portal.maxAreaTravelTime = 0;
|
||||
file->portals.Append( portal );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::TestPortals
|
||||
================
|
||||
*/
|
||||
bool idAASCluster::TestPortals( void ) {
|
||||
int i;
|
||||
aasPortal_t *portal, *portal2;
|
||||
aasArea_t *area, *area2;
|
||||
idReachability *reach;
|
||||
bool ok;
|
||||
|
||||
ok = true;
|
||||
for ( i = 1; i < file->portals.Num(); i++ ) {
|
||||
portal = &file->portals[i];
|
||||
area = &file->areas[portal->areaNum];
|
||||
|
||||
// if this portal was already removed
|
||||
if ( !( area->contents & AREACONTENTS_CLUSTERPORTAL) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// may not removed this portal if it has a reachability to a removed portal
|
||||
for ( reach = area->reach; reach; reach = reach->next ) {
|
||||
area2 = &file->areas[ reach->toAreaNum ];
|
||||
if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) {
|
||||
continue;
|
||||
}
|
||||
if ( area2->cluster < 0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( reach ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// may not removed this portal if it has a reversed reachability to a removed portal
|
||||
for ( reach = area->rev_reach; reach; reach = reach->rev_next ) {
|
||||
area2 = &file->areas[ reach->toAreaNum ];
|
||||
if ( area2->contents & AREACONTENTS_CLUSTERPORTAL ) {
|
||||
continue;
|
||||
}
|
||||
if ( area2->cluster < 0 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( reach ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// portal should have two clusters set
|
||||
if ( !portal->clusters[0] ) {
|
||||
area->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
if ( !portal->clusters[1] ) {
|
||||
area->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// this portal may not have reachabilities to a portal that doesn't seperate the same clusters
|
||||
for ( reach = area->reach; reach; reach = reach->next ) {
|
||||
area2 = &file->areas[ reach->toAreaNum ];
|
||||
|
||||
if ( !(area2->contents & AREACONTENTS_CLUSTERPORTAL) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( area2->cluster > 0 ) {
|
||||
area2->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
portal2 = &file->portals[ -file->areas[ reach->toAreaNum ].cluster ];
|
||||
|
||||
if ( ( portal2->clusters[0] != portal->clusters[0] && portal2->clusters[0] != portal->clusters[1] ) ||
|
||||
( portal2->clusters[1] != portal->clusters[0] && portal2->clusters[1] != portal->clusters[1] ) ) {
|
||||
area2->contents &= ~AREACONTENTS_CLUSTERPORTAL;
|
||||
ok = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::RemoveInvalidPortals
|
||||
================
|
||||
*/
|
||||
void idAASCluster::RemoveInvalidPortals( void ) {
|
||||
int i, j, k, face1Num, face2Num, otherAreaNum, numOpenAreas, numInvalidPortals;
|
||||
aasFace_t *face1, *face2;
|
||||
|
||||
numInvalidPortals = 0;
|
||||
for ( i = 0; i < file->areas.Num(); i++ ) {
|
||||
if ( !( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
numOpenAreas = 0;
|
||||
for ( j = 0; j < file->areas[i].numFaces; j++ ) {
|
||||
face1Num = file->faceIndex[ file->areas[i].firstFace + j ];
|
||||
face1 = &file->faces[ abs(face1Num) ];
|
||||
otherAreaNum = face1->areas[ face1Num < 0 ];
|
||||
|
||||
if ( !otherAreaNum ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( k = 0; k < j; k++ ) {
|
||||
face2Num = file->faceIndex[ file->areas[i].firstFace + k ];
|
||||
face2 = &file->faces[ abs(face2Num) ];
|
||||
if ( otherAreaNum == face2->areas[ face2Num < 0 ] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( k < j ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !( file->areas[otherAreaNum].contents & AREACONTENTS_CLUSTERPORTAL ) ) {
|
||||
numOpenAreas++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( numOpenAreas <= 1 ) {
|
||||
file->areas[i].contents &= AREACONTENTS_CLUSTERPORTAL;
|
||||
numInvalidPortals++;
|
||||
}
|
||||
}
|
||||
|
||||
common->Printf( "\r%6d invalid portals removed\n", numInvalidPortals );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::Build
|
||||
================
|
||||
*/
|
||||
bool idAASCluster::Build( idAASFileLocal *file ) {
|
||||
|
||||
common->Printf( "[Clustering]\n" );
|
||||
|
||||
this->file = file;
|
||||
this->noFaceFlood = true;
|
||||
|
||||
RemoveInvalidPortals();
|
||||
|
||||
while( 1 ) {
|
||||
|
||||
// delete all existing clusters
|
||||
file->DeleteClusters();
|
||||
|
||||
// create the portals from the portal areas
|
||||
CreatePortals();
|
||||
|
||||
common->Printf( "\r%6d", file->portals.Num() );
|
||||
|
||||
// find the clusters
|
||||
if ( !FindClusters() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// test the portals
|
||||
if ( !TestPortals() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
common->Printf( "\r%6d portals\n", file->portals.Num() );
|
||||
common->Printf( "%6d clusters\n", file->clusters.Num() );
|
||||
|
||||
for ( int i = 0; i < file->clusters.Num(); i++ ) {
|
||||
common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
|
||||
}
|
||||
|
||||
file->ReportRoutingEfficiency();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASCluster::BuildSingleCluster
|
||||
================
|
||||
*/
|
||||
bool idAASCluster::BuildSingleCluster( idAASFileLocal *file ) {
|
||||
int i, numAreas;
|
||||
aasCluster_t cluster;
|
||||
|
||||
common->Printf( "[Clustering]\n" );
|
||||
|
||||
this->file = file;
|
||||
|
||||
// delete all existing clusters
|
||||
file->DeleteClusters();
|
||||
|
||||
cluster.firstPortal = 0;
|
||||
cluster.numPortals = 0;
|
||||
cluster.numAreas = file->areas.Num();
|
||||
cluster.numReachableAreas = 0;
|
||||
// give all reachable areas in the cluster a number
|
||||
for ( i = 0; i < file->areas.Num(); i++ ) {
|
||||
file->areas[i].cluster = file->clusters.Num();
|
||||
if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
||||
file->areas[i].clusterAreaNum = cluster.numReachableAreas++;
|
||||
}
|
||||
}
|
||||
// give the remaining areas a number within the cluster
|
||||
numAreas = cluster.numReachableAreas;
|
||||
for ( i = 0; i < file->areas.Num(); i++ ) {
|
||||
if ( file->areas[i].flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY) ) {
|
||||
continue;
|
||||
}
|
||||
file->areas[i].clusterAreaNum = numAreas++;
|
||||
}
|
||||
file->clusters.Append( cluster );
|
||||
|
||||
common->Printf( "%6d portals\n", file->portals.Num() );
|
||||
common->Printf( "%6d clusters\n", file->clusters.Num() );
|
||||
|
||||
for ( i = 0; i < file->clusters.Num(); i++ ) {
|
||||
common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
|
||||
}
|
||||
|
||||
file->ReportRoutingEfficiency();
|
||||
|
||||
return true;
|
||||
}
|
||||
62
neo/tools/compilers/aas/AASCluster.h
Normal file
62
neo/tools/compilers/aas/AASCluster.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __AASCLUSTER_H__
|
||||
#define __AASCLUSTER_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Area Clustering
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idAASCluster {
|
||||
|
||||
public:
|
||||
bool Build( idAASFileLocal *file );
|
||||
bool BuildSingleCluster( idAASFileLocal *file );
|
||||
|
||||
private:
|
||||
idAASFileLocal * file;
|
||||
bool noFaceFlood;
|
||||
|
||||
private:
|
||||
bool UpdatePortal( int areaNum, int clusterNum );
|
||||
bool FloodClusterAreas_r( int areaNum, int clusterNum );
|
||||
void RemoveAreaClusterNumbers( void );
|
||||
void NumberClusterAreas( int clusterNum );
|
||||
bool FindClusters( void );
|
||||
void CreatePortals( void );
|
||||
bool TestPortals( void );
|
||||
void ReportEfficiency( void );
|
||||
void RemoveInvalidPortals( void );
|
||||
};
|
||||
|
||||
#endif /* !__AASCLUSTER_H__ */
|
||||
1314
neo/tools/compilers/aas/AASFile.cpp
Normal file
1314
neo/tools/compilers/aas/AASFile.cpp
Normal file
File diff suppressed because it is too large
Load Diff
351
neo/tools/compilers/aas/AASFile.h
Normal file
351
neo/tools/compilers/aas/AASFile.h
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __AASFILE_H__
|
||||
#define __AASFILE_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
AAS File
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
#define AAS_FILEID "DewmAAS"
|
||||
#define AAS_FILEVERSION "1.07"
|
||||
|
||||
// travel flags
|
||||
#define TFL_INVALID BIT(0) // not valid
|
||||
#define TFL_WALK BIT(1) // walking
|
||||
#define TFL_CROUCH BIT(2) // crouching
|
||||
#define TFL_WALKOFFLEDGE BIT(3) // walking of a ledge
|
||||
#define TFL_BARRIERJUMP BIT(4) // jumping onto a barrier
|
||||
#define TFL_JUMP BIT(5) // jumping
|
||||
#define TFL_LADDER BIT(6) // climbing a ladder
|
||||
#define TFL_SWIM BIT(7) // swimming
|
||||
#define TFL_WATERJUMP BIT(8) // jump out of the water
|
||||
#define TFL_TELEPORT BIT(9) // teleportation
|
||||
#define TFL_ELEVATOR BIT(10) // travel by elevator
|
||||
#define TFL_FLY BIT(11) // fly
|
||||
#define TFL_SPECIAL BIT(12) // special
|
||||
#define TFL_WATER BIT(21) // travel through water
|
||||
#define TFL_AIR BIT(22) // travel through air
|
||||
|
||||
// face flags
|
||||
#define FACE_SOLID BIT(0) // solid at the other side
|
||||
#define FACE_LADDER BIT(1) // ladder surface
|
||||
#define FACE_FLOOR BIT(2) // standing on floor when on this face
|
||||
#define FACE_LIQUID BIT(3) // face seperating two areas with liquid
|
||||
#define FACE_LIQUIDSURFACE BIT(4) // face seperating liquid and air
|
||||
|
||||
// area flags
|
||||
#define AREA_FLOOR BIT(0) // AI can stand on the floor in this area
|
||||
#define AREA_GAP BIT(1) // area has a gap
|
||||
#define AREA_LEDGE BIT(2) // if entered the AI bbox partly floats above a ledge
|
||||
#define AREA_LADDER BIT(3) // area contains one or more ladder faces
|
||||
#define AREA_LIQUID BIT(4) // area contains a liquid
|
||||
#define AREA_CROUCH BIT(5) // AI cannot walk but can only crouch in this area
|
||||
#define AREA_REACHABLE_WALK BIT(6) // area is reachable by walking or swimming
|
||||
#define AREA_REACHABLE_FLY BIT(7) // area is reachable by flying
|
||||
|
||||
// area contents flags
|
||||
#define AREACONTENTS_SOLID BIT(0) // solid, not a valid area
|
||||
#define AREACONTENTS_WATER BIT(1) // area contains water
|
||||
#define AREACONTENTS_CLUSTERPORTAL BIT(2) // area is a cluster portal
|
||||
#define AREACONTENTS_OBSTACLE BIT(3) // area contains (part of) a dynamic obstacle
|
||||
#define AREACONTENTS_TELEPORTER BIT(4) // area contains (part of) a teleporter trigger
|
||||
|
||||
// bits for different bboxes
|
||||
#define AREACONTENTS_BBOX_BIT 24
|
||||
|
||||
#define MAX_REACH_PER_AREA 256
|
||||
#define MAX_AAS_TREE_DEPTH 128
|
||||
|
||||
#define MAX_AAS_BOUNDING_BOXES 4
|
||||
|
||||
// reachability to another area
|
||||
class idReachability {
|
||||
public:
|
||||
int travelType; // type of travel required to get to the area
|
||||
short toAreaNum; // number of the reachable area
|
||||
short fromAreaNum; // number of area the reachability starts
|
||||
idVec3 start; // start point of inter area movement
|
||||
idVec3 end; // end point of inter area movement
|
||||
int edgeNum; // edge crossed by this reachability
|
||||
unsigned short travelTime; // travel time of the inter area movement
|
||||
byte number; // reachability number within the fromAreaNum (must be < 256)
|
||||
byte disableCount; // number of times this reachability has been disabled
|
||||
idReachability * next; // next reachability in list
|
||||
idReachability * rev_next; // next reachability in reversed list
|
||||
unsigned short * areaTravelTimes; // travel times within the fromAreaNum from reachabilities that lead towards this area
|
||||
public:
|
||||
void CopyBase( idReachability &reach );
|
||||
};
|
||||
|
||||
class idReachability_Walk : public idReachability {
|
||||
};
|
||||
|
||||
class idReachability_BarrierJump : public idReachability {
|
||||
};
|
||||
|
||||
class idReachability_WaterJump : public idReachability {
|
||||
};
|
||||
|
||||
class idReachability_WalkOffLedge : public idReachability {
|
||||
};
|
||||
|
||||
class idReachability_Swim : public idReachability {
|
||||
};
|
||||
|
||||
class idReachability_Fly : public idReachability {
|
||||
};
|
||||
|
||||
class idReachability_Special : public idReachability {
|
||||
public:
|
||||
idDict dict;
|
||||
};
|
||||
|
||||
// index
|
||||
typedef int aasIndex_t;
|
||||
|
||||
// vertex
|
||||
typedef idVec3 aasVertex_t;
|
||||
|
||||
// edge
|
||||
typedef struct aasEdge_s {
|
||||
int vertexNum[2]; // numbers of the vertexes of this edge
|
||||
} aasEdge_t;
|
||||
|
||||
// area boundary face
|
||||
typedef struct aasFace_s {
|
||||
unsigned short planeNum; // number of the plane this face is on
|
||||
unsigned short flags; // face flags
|
||||
int numEdges; // number of edges in the boundary of the face
|
||||
int firstEdge; // first edge in the edge index
|
||||
short areas[2]; // area at the front and back of this face
|
||||
} aasFace_t;
|
||||
|
||||
// area with a boundary of faces
|
||||
typedef struct aasArea_s {
|
||||
int numFaces; // number of faces used for the boundary of the area
|
||||
int firstFace; // first face in the face index used for the boundary of the area
|
||||
idBounds bounds; // bounds of the area
|
||||
idVec3 center; // center of the area an AI can move towards
|
||||
unsigned short flags; // several area flags
|
||||
unsigned short contents; // contents of the area
|
||||
short cluster; // cluster the area belongs to, if negative it's a portal
|
||||
short clusterAreaNum; // number of the area in the cluster
|
||||
int travelFlags; // travel flags for traveling through this area
|
||||
idReachability * reach; // reachabilities that start from this area
|
||||
idReachability * rev_reach; // reachabilities that lead to this area
|
||||
} aasArea_t;
|
||||
|
||||
// nodes of the bsp tree
|
||||
typedef struct aasNode_s {
|
||||
unsigned short planeNum; // number of the plane that splits the subspace at this node
|
||||
int children[2]; // child nodes, zero is solid, negative is -(area number)
|
||||
} aasNode_t;
|
||||
|
||||
// cluster portal
|
||||
typedef struct aasPortal_s {
|
||||
short areaNum; // number of the area that is the actual portal
|
||||
short clusters[2]; // number of cluster at the front and back of the portal
|
||||
short clusterAreaNum[2]; // number of this portal area in the front and back cluster
|
||||
unsigned short maxAreaTravelTime; // maximum travel time through the portal area
|
||||
} aasPortal_t;
|
||||
|
||||
// cluster
|
||||
typedef struct aasCluster_s {
|
||||
int numAreas; // number of areas in the cluster
|
||||
int numReachableAreas; // number of areas with reachabilities
|
||||
int numPortals; // number of cluster portals
|
||||
int firstPortal; // first cluster portal in the index
|
||||
} aasCluster_t;
|
||||
|
||||
// trace through the world
|
||||
typedef struct aasTrace_s {
|
||||
// parameters
|
||||
int flags; // areas with these flags block the trace
|
||||
int travelFlags; // areas with these travel flags block the trace
|
||||
int maxAreas; // size of the 'areas' array
|
||||
int getOutOfSolid; // trace out of solid if the trace starts in solid
|
||||
// output
|
||||
float fraction; // fraction of trace completed
|
||||
idVec3 endpos; // end position of trace
|
||||
int planeNum; // plane hit
|
||||
int lastAreaNum; // number of last area the trace went through
|
||||
int blockingAreaNum; // area that could not be entered
|
||||
int numAreas; // number of areas the trace went through
|
||||
int * areas; // array to store areas the trace went through
|
||||
idVec3 * points; // points where the trace entered each new area
|
||||
aasTrace_s( void ) { areas = NULL; points = NULL; getOutOfSolid = false; flags = travelFlags = maxAreas = 0; }
|
||||
} aasTrace_t;
|
||||
|
||||
// settings
|
||||
class idAASSettings {
|
||||
public:
|
||||
// collision settings
|
||||
int numBoundingBoxes;
|
||||
idBounds boundingBoxes[MAX_AAS_BOUNDING_BOXES];
|
||||
bool usePatches;
|
||||
bool writeBrushMap;
|
||||
bool playerFlood;
|
||||
bool noOptimize;
|
||||
bool allowSwimReachabilities;
|
||||
bool allowFlyReachabilities;
|
||||
idStr fileExtension;
|
||||
// physics settings
|
||||
idVec3 gravity;
|
||||
idVec3 gravityDir;
|
||||
idVec3 invGravityDir;
|
||||
float gravityValue;
|
||||
float maxStepHeight;
|
||||
float maxBarrierHeight;
|
||||
float maxWaterJumpHeight;
|
||||
float maxFallHeight;
|
||||
float minFloorCos;
|
||||
// fixed travel times
|
||||
int tt_barrierJump;
|
||||
int tt_startCrouching;
|
||||
int tt_waterJump;
|
||||
int tt_startWalkOffLedge;
|
||||
|
||||
public:
|
||||
idAASSettings( void );
|
||||
|
||||
bool FromFile( const idStr &fileName );
|
||||
bool FromParser( idLexer &src );
|
||||
bool FromDict( const char *name, const idDict *dict );
|
||||
bool WriteToFile( idFile *fp ) const;
|
||||
bool ValidForBounds( const idBounds &bounds ) const;
|
||||
bool ValidEntity( const char *classname ) const;
|
||||
|
||||
private:
|
||||
bool ParseBool( idLexer &src, bool &b );
|
||||
bool ParseInt( idLexer &src, int &i );
|
||||
bool ParseFloat( idLexer &src, float &f );
|
||||
bool ParseVector( idLexer &src, idVec3 &vec );
|
||||
bool ParseBBoxes( idLexer &src );
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
||||
- when a node child is a solid leaf the node child number is zero
|
||||
- two adjacent areas (sharing a plane at opposite sides) share a face
|
||||
this face is a portal between the areas
|
||||
- when an area uses a face from the faceindex with a positive index
|
||||
then the face plane normal points into the area
|
||||
- the face edges are stored counter clockwise using the edgeindex
|
||||
- two adjacent convex areas (sharing a face) only share One face
|
||||
this is a simple result of the areas being convex
|
||||
- the areas can't have a mixture of ground and gap faces
|
||||
other mixtures of faces in one area are allowed
|
||||
- areas with the AREACONTENTS_CLUSTERPORTAL in the settings have
|
||||
the cluster number set to the negative portal number
|
||||
- edge zero is a dummy
|
||||
- face zero is a dummy
|
||||
- area zero is a dummy
|
||||
- node zero is a dummy
|
||||
- portal zero is a dummy
|
||||
- cluster zero is a dummy
|
||||
|
||||
*/
|
||||
|
||||
|
||||
class idAASFile {
|
||||
public:
|
||||
virtual ~idAASFile( void ) {}
|
||||
|
||||
const char * GetName( void ) const { return name.c_str(); }
|
||||
unsigned int GetCRC( void ) const { return crc; }
|
||||
|
||||
int GetNumPlanes( void ) const { return planeList.Num(); }
|
||||
const idPlane & GetPlane( int index ) const { return planeList[index]; }
|
||||
int GetNumVertices( void ) const { return vertices.Num(); }
|
||||
const aasVertex_t & GetVertex( int index ) const { return vertices[index]; }
|
||||
int GetNumEdges( void ) const { return edges.Num(); }
|
||||
const aasEdge_t & GetEdge( int index ) const { return edges[index]; }
|
||||
int GetNumEdgeIndexes( void ) const { return edgeIndex.Num(); }
|
||||
const aasIndex_t & GetEdgeIndex( int index ) const { return edgeIndex[index]; }
|
||||
int GetNumFaces( void ) const { return faces.Num(); }
|
||||
const aasFace_t & GetFace( int index ) const { return faces[index]; }
|
||||
int GetNumFaceIndexes( void ) const { return faceIndex.Num(); }
|
||||
const aasIndex_t & GetFaceIndex( int index ) const { return faceIndex[index]; }
|
||||
int GetNumAreas( void ) const { return areas.Num(); }
|
||||
const aasArea_t & GetArea( int index ) { return areas[index]; }
|
||||
int GetNumNodes( void ) const { return nodes.Num(); }
|
||||
const aasNode_t & GetNode( int index ) const { return nodes[index]; }
|
||||
int GetNumPortals( void ) const { return portals.Num(); }
|
||||
const aasPortal_t & GetPortal( int index ) { return portals[index]; }
|
||||
int GetNumPortalIndexes( void ) const { return portalIndex.Num(); }
|
||||
const aasIndex_t & GetPortalIndex( int index ) const { return portalIndex[index]; }
|
||||
int GetNumClusters( void ) const { return clusters.Num(); }
|
||||
const aasCluster_t & GetCluster( int index ) const { return clusters[index]; }
|
||||
|
||||
const idAASSettings & GetSettings( void ) const { return settings; }
|
||||
|
||||
void SetPortalMaxTravelTime( int index, int time ) { portals[index].maxAreaTravelTime = time; }
|
||||
void SetAreaTravelFlag( int index, int flag ) { areas[index].travelFlags |= flag; }
|
||||
void RemoveAreaTravelFlag( int index, int flag ) { areas[index].travelFlags &= ~flag; }
|
||||
|
||||
virtual idVec3 EdgeCenter( int edgeNum ) const = 0;
|
||||
virtual idVec3 FaceCenter( int faceNum ) const = 0;
|
||||
virtual idVec3 AreaCenter( int areaNum ) const = 0;
|
||||
|
||||
virtual idBounds EdgeBounds( int edgeNum ) const = 0;
|
||||
virtual idBounds FaceBounds( int faceNum ) const = 0;
|
||||
virtual idBounds AreaBounds( int areaNum ) const = 0;
|
||||
|
||||
virtual int PointAreaNum( const idVec3 &origin ) const = 0;
|
||||
virtual int PointReachableAreaNum( const idVec3 &origin, const idBounds &searchBounds, const int areaFlags, const int excludeTravelFlags ) const = 0;
|
||||
virtual int BoundsReachableAreaNum( const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const = 0;
|
||||
virtual void PushPointIntoAreaNum( int areaNum, idVec3 &point ) const = 0;
|
||||
virtual bool Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const = 0;
|
||||
virtual void PrintInfo( void ) const = 0;
|
||||
|
||||
protected:
|
||||
idStr name;
|
||||
unsigned int crc;
|
||||
|
||||
idPlaneSet planeList;
|
||||
idList<aasVertex_t> vertices;
|
||||
idList<aasEdge_t> edges;
|
||||
idList<aasIndex_t> edgeIndex;
|
||||
idList<aasFace_t> faces;
|
||||
idList<aasIndex_t> faceIndex;
|
||||
idList<aasArea_t> areas;
|
||||
idList<aasNode_t> nodes;
|
||||
idList<aasPortal_t> portals;
|
||||
idList<aasIndex_t> portalIndex;
|
||||
idList<aasCluster_t> clusters;
|
||||
idAASSettings settings;
|
||||
};
|
||||
|
||||
#endif /* !__AASFILE_H__ */
|
||||
76
neo/tools/compilers/aas/AASFileManager.cpp
Normal file
76
neo/tools/compilers/aas/AASFileManager.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASFile.h"
|
||||
#include "AASFile_local.h"
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
AAS File Manager
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idAASFileManagerLocal : public idAASFileManager {
|
||||
public:
|
||||
virtual ~idAASFileManagerLocal( void ) {}
|
||||
|
||||
virtual idAASFile * LoadAAS( const char *fileName, unsigned int mapFileCRC );
|
||||
virtual void FreeAAS( idAASFile *file );
|
||||
};
|
||||
|
||||
idAASFileManagerLocal AASFileManagerLocal;
|
||||
idAASFileManager * AASFileManager = &AASFileManagerLocal;
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileManagerLocal::LoadAAS
|
||||
================
|
||||
*/
|
||||
idAASFile *idAASFileManagerLocal::LoadAAS( const char *fileName, unsigned int mapFileCRC ) {
|
||||
idAASFileLocal *file = new idAASFileLocal();
|
||||
if ( !file->Load( fileName, mapFileCRC ) ) {
|
||||
delete file;
|
||||
return NULL;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileManagerLocal::FreeAAS
|
||||
================
|
||||
*/
|
||||
void idAASFileManagerLocal::FreeAAS( idAASFile *file ) {
|
||||
delete file;
|
||||
}
|
||||
50
neo/tools/compilers/aas/AASFileManager.h
Normal file
50
neo/tools/compilers/aas/AASFileManager.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __AASFILEMANAGER_H__
|
||||
#define __AASFILEMANAGER_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
AAS File Manager
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idAASFileManager {
|
||||
public:
|
||||
virtual ~idAASFileManager( void ) {}
|
||||
|
||||
virtual idAASFile * LoadAAS( const char *fileName, unsigned int mapFileCRC ) = 0;
|
||||
virtual void FreeAAS( idAASFile *file ) = 0;
|
||||
};
|
||||
|
||||
extern idAASFileManager * AASFileManager;
|
||||
|
||||
#endif /* !__AASFILEMANAGER_H__ */
|
||||
99
neo/tools/compilers/aas/AASFile_local.h
Normal file
99
neo/tools/compilers/aas/AASFile_local.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __AASFILELOCAL_H__
|
||||
#define __AASFILELOCAL_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
AAS File Local
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idAASFileLocal : public idAASFile {
|
||||
friend class idAASBuild;
|
||||
friend class idAASReach;
|
||||
friend class idAASCluster;
|
||||
public:
|
||||
idAASFileLocal( void );
|
||||
virtual ~idAASFileLocal( void );
|
||||
|
||||
public:
|
||||
virtual idVec3 EdgeCenter( int edgeNum ) const;
|
||||
virtual idVec3 FaceCenter( int faceNum ) const;
|
||||
virtual idVec3 AreaCenter( int areaNum ) const;
|
||||
|
||||
virtual idBounds EdgeBounds( int edgeNum ) const;
|
||||
virtual idBounds FaceBounds( int faceNum ) const;
|
||||
virtual idBounds AreaBounds( int areaNum ) const;
|
||||
|
||||
virtual int PointAreaNum( const idVec3 &origin ) const;
|
||||
virtual int PointReachableAreaNum( const idVec3 &origin, const idBounds &searchBounds, const int areaFlags, const int excludeTravelFlags ) const;
|
||||
virtual int BoundsReachableAreaNum( const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const;
|
||||
virtual void PushPointIntoAreaNum( int areaNum, idVec3 &point ) const;
|
||||
virtual bool Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const;
|
||||
virtual void PrintInfo( void ) const;
|
||||
|
||||
public:
|
||||
bool Load( const idStr &fileName, unsigned int mapFileCRC );
|
||||
bool Write( const idStr &fileName, unsigned int mapFileCRC );
|
||||
|
||||
int MemorySize( void ) const;
|
||||
void ReportRoutingEfficiency( void ) const;
|
||||
void Optimize( void );
|
||||
void LinkReversedReachability( void );
|
||||
void FinishAreas( void );
|
||||
|
||||
void Clear( void );
|
||||
void DeleteReachabilities( void );
|
||||
void DeleteClusters( void );
|
||||
|
||||
private:
|
||||
bool ParseIndex( idLexer &src, idList<aasIndex_t> &indexes );
|
||||
bool ParsePlanes( idLexer &src );
|
||||
bool ParseVertices( idLexer &src );
|
||||
bool ParseEdges( idLexer &src );
|
||||
bool ParseFaces( idLexer &src );
|
||||
bool ParseReachabilities( idLexer &src, int areaNum );
|
||||
bool ParseAreas( idLexer &src );
|
||||
bool ParseNodes( idLexer &src );
|
||||
bool ParsePortals( idLexer &src );
|
||||
bool ParseClusters( idLexer &src );
|
||||
|
||||
private:
|
||||
int BoundsReachableAreaNum_r( int nodeNum, const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const;
|
||||
void MaxTreeDepth_r( int nodeNum, int &depth, int &maxDepth ) const;
|
||||
int MaxTreeDepth( void ) const;
|
||||
int AreaContentsTravelFlags( int areaNum ) const;
|
||||
idVec3 AreaReachableGoal( int areaNum ) const;
|
||||
int NumReachabilities( void ) const;
|
||||
};
|
||||
|
||||
#endif /* !__AASFILELOCAL_H__ */
|
||||
154
neo/tools/compilers/aas/AASFile_optimize.cpp
Normal file
154
neo/tools/compilers/aas/AASFile_optimize.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASFile.h"
|
||||
#include "AASFile_local.h"
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// optimize file
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileLocal::Optimize
|
||||
================
|
||||
*/
|
||||
void idAASFileLocal::Optimize( void ) {
|
||||
int i, j, k, faceNum, edgeNum, areaFirstFace, faceFirstEdge;
|
||||
aasArea_t *area;
|
||||
aasFace_t *face;
|
||||
aasEdge_t *edge;
|
||||
idReachability *reach;
|
||||
idList<int> vertexRemap;
|
||||
idList<int> edgeRemap;
|
||||
idList<int> faceRemap;
|
||||
idList<aasVertex_t> newVertices;
|
||||
idList<aasEdge_t> newEdges;
|
||||
idList<aasIndex_t> newEdgeIndex;
|
||||
idList<aasFace_t> newFaces;
|
||||
idList<aasIndex_t> newFaceIndex;
|
||||
|
||||
vertexRemap.AssureSize( vertices.Num(), -1 );
|
||||
edgeRemap.AssureSize( edges.Num(), 0 );
|
||||
faceRemap.AssureSize( faces.Num(), 0 );
|
||||
|
||||
newVertices.Resize( vertices.Num() );
|
||||
newEdges.Resize( edges.Num() );
|
||||
newEdges.SetNum( 1, false );
|
||||
newEdgeIndex.Resize( edgeIndex.Num() );
|
||||
newFaces.Resize( faces.Num() );
|
||||
newFaces.SetNum( 1, false );
|
||||
newFaceIndex.Resize( faceIndex.Num() );
|
||||
|
||||
for ( i = 0; i < areas.Num(); i++ ) {
|
||||
area = &areas[i];
|
||||
|
||||
areaFirstFace = newFaceIndex.Num();
|
||||
for ( j = 0; j < area->numFaces; j++ ) {
|
||||
faceNum = faceIndex[area->firstFace + j];
|
||||
face = &faces[ abs(faceNum) ];
|
||||
|
||||
// store face
|
||||
if ( !faceRemap[ abs(faceNum) ] ) {
|
||||
faceRemap[ abs(faceNum) ] = newFaces.Num();
|
||||
newFaces.Append( *face );
|
||||
|
||||
// don't store edges for faces we don't care about
|
||||
if ( !( face->flags & ( FACE_FLOOR|FACE_LADDER ) ) ) {
|
||||
|
||||
newFaces[ newFaces.Num()-1 ].firstEdge = 0;
|
||||
newFaces[ newFaces.Num()-1 ].numEdges = 0;
|
||||
|
||||
} else {
|
||||
|
||||
// store edges
|
||||
faceFirstEdge = newEdgeIndex.Num();
|
||||
for ( k = 0; k < face->numEdges; k++ ) {
|
||||
edgeNum = edgeIndex[ face->firstEdge + k ];
|
||||
edge = &edges[ abs(edgeNum) ];
|
||||
|
||||
if ( !edgeRemap[ abs(edgeNum) ] ) {
|
||||
if ( edgeNum < 0 ) {
|
||||
edgeRemap[ abs(edgeNum) ] = -newEdges.Num();
|
||||
}
|
||||
else {
|
||||
edgeRemap[ abs(edgeNum) ] = newEdges.Num();
|
||||
}
|
||||
|
||||
// remap vertices if not yet remapped
|
||||
if ( vertexRemap[ edge->vertexNum[0] ] == -1 ) {
|
||||
vertexRemap[ edge->vertexNum[0] ] = newVertices.Num();
|
||||
newVertices.Append( vertices[ edge->vertexNum[0] ] );
|
||||
}
|
||||
if ( vertexRemap[ edge->vertexNum[1] ] == -1 ) {
|
||||
vertexRemap[ edge->vertexNum[1] ] = newVertices.Num();
|
||||
newVertices.Append( vertices[ edge->vertexNum[1] ] );
|
||||
}
|
||||
|
||||
newEdges.Append( *edge );
|
||||
newEdges[ newEdges.Num()-1 ].vertexNum[0] = vertexRemap[ edge->vertexNum[0] ];
|
||||
newEdges[ newEdges.Num()-1 ].vertexNum[1] = vertexRemap[ edge->vertexNum[1] ];
|
||||
}
|
||||
|
||||
newEdgeIndex.Append( edgeRemap[ abs(edgeNum) ] );
|
||||
}
|
||||
|
||||
newFaces[ newFaces.Num()-1 ].firstEdge = faceFirstEdge;
|
||||
newFaces[ newFaces.Num()-1 ].numEdges = newEdgeIndex.Num() - faceFirstEdge;
|
||||
}
|
||||
}
|
||||
|
||||
if ( faceNum < 0 ) {
|
||||
newFaceIndex.Append( -faceRemap[ abs(faceNum) ] );
|
||||
} else {
|
||||
newFaceIndex.Append( faceRemap[ abs(faceNum) ] );
|
||||
}
|
||||
}
|
||||
|
||||
area->firstFace = areaFirstFace;
|
||||
area->numFaces = newFaceIndex.Num() - areaFirstFace;
|
||||
|
||||
// remap the reachability edges
|
||||
for ( reach = area->reach; reach; reach = reach->next ) {
|
||||
reach->edgeNum = abs( edgeRemap[reach->edgeNum] );
|
||||
}
|
||||
}
|
||||
|
||||
// store new list
|
||||
vertices = newVertices;
|
||||
edges = newEdges;
|
||||
edgeIndex = newEdgeIndex;
|
||||
faces = newFaces;
|
||||
faceIndex = newFaceIndex;
|
||||
}
|
||||
608
neo/tools/compilers/aas/AASFile_sample.cpp
Normal file
608
neo/tools/compilers/aas/AASFile_sample.cpp
Normal file
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASFile.h"
|
||||
#include "AASFile_local.h"
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// Environment Sampling
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileLocal::EdgeCenter
|
||||
================
|
||||
*/
|
||||
idVec3 idAASFileLocal::EdgeCenter( int edgeNum ) const {
|
||||
const aasEdge_t *edge;
|
||||
edge = &edges[edgeNum];
|
||||
return ( vertices[edge->vertexNum[0]] + vertices[edge->vertexNum[1]] ) * 0.5f;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileLocal::FaceCenter
|
||||
================
|
||||
*/
|
||||
idVec3 idAASFileLocal::FaceCenter( int faceNum ) const {
|
||||
int i, edgeNum;
|
||||
const aasFace_t *face;
|
||||
const aasEdge_t *edge;
|
||||
idVec3 center;
|
||||
|
||||
center = vec3_origin;
|
||||
|
||||
face = &faces[faceNum];
|
||||
if ( face->numEdges > 0 ) {
|
||||
for ( i = 0; i < face->numEdges; i++ ) {
|
||||
edgeNum = edgeIndex[ face->firstEdge + i ];
|
||||
edge = &edges[ abs( edgeNum ) ];
|
||||
center += vertices[ edge->vertexNum[ INTSIGNBITSET(edgeNum) ] ];
|
||||
}
|
||||
center /= face->numEdges;
|
||||
}
|
||||
return center;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileLocal::AreaCenter
|
||||
================
|
||||
*/
|
||||
idVec3 idAASFileLocal::AreaCenter( int areaNum ) const {
|
||||
int i, faceNum;
|
||||
const aasArea_t *area;
|
||||
idVec3 center;
|
||||
|
||||
center = vec3_origin;
|
||||
|
||||
area = &areas[areaNum];
|
||||
if ( area->numFaces > 0 ) {
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = faceIndex[area->firstFace + i];
|
||||
center += FaceCenter( abs(faceNum) );
|
||||
}
|
||||
center /= area->numFaces;
|
||||
}
|
||||
return center;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::AreaReachableGoal
|
||||
============
|
||||
*/
|
||||
idVec3 idAASFileLocal::AreaReachableGoal( int areaNum ) const {
|
||||
int i, faceNum, numFaces;
|
||||
const aasArea_t *area;
|
||||
idVec3 center;
|
||||
idVec3 start, end;
|
||||
aasTrace_t trace;
|
||||
|
||||
area = &areas[areaNum];
|
||||
|
||||
if ( !(area->flags & (AREA_REACHABLE_WALK|AREA_REACHABLE_FLY)) || (area->flags & AREA_LIQUID) ) {
|
||||
return AreaCenter( areaNum );
|
||||
}
|
||||
|
||||
center = vec3_origin;
|
||||
|
||||
numFaces = 0;
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = faceIndex[area->firstFace + i];
|
||||
if ( !(faces[abs(faceNum)].flags & FACE_FLOOR) ) {
|
||||
continue;
|
||||
}
|
||||
center += FaceCenter( abs(faceNum) );
|
||||
numFaces++;
|
||||
}
|
||||
if ( numFaces > 0 ) {
|
||||
center /= numFaces;
|
||||
}
|
||||
center[2] += 1.0f;
|
||||
end = center;
|
||||
end[2] -= 1024;
|
||||
Trace( trace, center, end );
|
||||
|
||||
return trace.endpos;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileLocal::EdgeBounds
|
||||
================
|
||||
*/
|
||||
idBounds idAASFileLocal::EdgeBounds( int edgeNum ) const {
|
||||
const aasEdge_t *edge;
|
||||
idBounds bounds;
|
||||
|
||||
edge = &edges[ abs( edgeNum ) ];
|
||||
bounds[0] = bounds[1] = vertices[ edge->vertexNum[0] ];
|
||||
bounds += vertices[ edge->vertexNum[1] ];
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileLocal::FaceBounds
|
||||
================
|
||||
*/
|
||||
idBounds idAASFileLocal::FaceBounds( int faceNum ) const {
|
||||
int i, edgeNum;
|
||||
const aasFace_t *face;
|
||||
const aasEdge_t *edge;
|
||||
idBounds bounds;
|
||||
|
||||
face = &faces[faceNum];
|
||||
bounds.Clear();
|
||||
|
||||
for ( i = 0; i < face->numEdges; i++ ) {
|
||||
edgeNum = edgeIndex[ face->firstEdge + i ];
|
||||
edge = &edges[ abs( edgeNum ) ];
|
||||
bounds.AddPoint( vertices[ edge->vertexNum[ INTSIGNBITSET(edgeNum) ] ] );
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASFileLocal::AreaBounds
|
||||
================
|
||||
*/
|
||||
idBounds idAASFileLocal::AreaBounds( int areaNum ) const {
|
||||
int i, faceNum;
|
||||
const aasArea_t *area;
|
||||
idBounds bounds;
|
||||
|
||||
area = &areas[areaNum];
|
||||
bounds.Clear();
|
||||
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = faceIndex[area->firstFace + i];
|
||||
bounds += FaceBounds( abs(faceNum) );
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::PointAreaNum
|
||||
============
|
||||
*/
|
||||
int idAASFileLocal::PointAreaNum( const idVec3 &origin ) const {
|
||||
int nodeNum;
|
||||
const aasNode_t *node;
|
||||
|
||||
nodeNum = 1;
|
||||
do {
|
||||
node = &nodes[nodeNum];
|
||||
if ( planeList[node->planeNum].Side( origin ) == PLANESIDE_BACK ) {
|
||||
nodeNum = node->children[1];
|
||||
}
|
||||
else {
|
||||
nodeNum = node->children[0];
|
||||
}
|
||||
if ( nodeNum < 0 ) {
|
||||
return -nodeNum;
|
||||
}
|
||||
} while( nodeNum );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::PointReachableAreaNum
|
||||
============
|
||||
*/
|
||||
int idAASFileLocal::PointReachableAreaNum( const idVec3 &origin, const idBounds &searchBounds, const int areaFlags, const int excludeTravelFlags ) const {
|
||||
int areaList[32], areaNum, i;
|
||||
idVec3 start, end, pointList[32];
|
||||
aasTrace_t trace;
|
||||
idBounds bounds;
|
||||
float frac;
|
||||
|
||||
start = origin;
|
||||
|
||||
trace.areas = areaList;
|
||||
trace.points = pointList;
|
||||
trace.maxAreas = sizeof( areaList ) / sizeof( int );
|
||||
trace.getOutOfSolid = true;
|
||||
|
||||
areaNum = PointAreaNum( start );
|
||||
if ( areaNum ) {
|
||||
if ( ( areas[areaNum].flags & areaFlags ) && ( ( areas[areaNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
|
||||
return areaNum;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// trace up
|
||||
end = start;
|
||||
end[2] += 32.0f;
|
||||
Trace( trace, start, end );
|
||||
if ( trace.numAreas >= 1 ) {
|
||||
if ( ( areas[0].flags & areaFlags ) && ( ( areas[0].travelFlags & excludeTravelFlags ) == 0 ) ) {
|
||||
return areaList[0];
|
||||
}
|
||||
start = pointList[0];
|
||||
start[2] += 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// trace down
|
||||
end = start;
|
||||
end[2] -= 32.0f;
|
||||
Trace( trace, start, end );
|
||||
if ( trace.lastAreaNum ) {
|
||||
if ( ( areas[trace.lastAreaNum].flags & areaFlags ) && ( ( areas[trace.lastAreaNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
|
||||
return trace.lastAreaNum;
|
||||
}
|
||||
start = trace.endpos;
|
||||
}
|
||||
|
||||
// expand bounds until an area is found
|
||||
for ( i = 1; i <= 12; i++ ) {
|
||||
frac = i * ( 1.0f / 12.0f );
|
||||
bounds[0] = origin + searchBounds[0] * frac;
|
||||
bounds[1] = origin + searchBounds[1] * frac;
|
||||
areaNum = BoundsReachableAreaNum( bounds, areaFlags, excludeTravelFlags );
|
||||
if ( areaNum && ( areas[areaNum].flags & areaFlags ) && ( ( areas[areaNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
|
||||
return areaNum;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::BoundsReachableAreaNum_r
|
||||
============
|
||||
*/
|
||||
int idAASFileLocal::BoundsReachableAreaNum_r( int nodeNum, const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const {
|
||||
int res;
|
||||
const aasNode_t *node;
|
||||
|
||||
while( nodeNum ) {
|
||||
if ( nodeNum < 0 ) {
|
||||
if ( ( areas[-nodeNum].flags & areaFlags ) && ( ( areas[-nodeNum].travelFlags & excludeTravelFlags ) == 0 ) ) {
|
||||
return -nodeNum;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
node = &nodes[nodeNum];
|
||||
res = bounds.PlaneSide( planeList[node->planeNum] );
|
||||
if ( res == PLANESIDE_BACK ) {
|
||||
nodeNum = node->children[1];
|
||||
}
|
||||
else if ( res == PLANESIDE_FRONT ) {
|
||||
nodeNum = node->children[0];
|
||||
}
|
||||
else {
|
||||
nodeNum = BoundsReachableAreaNum_r( node->children[1], bounds, areaFlags, excludeTravelFlags );
|
||||
if ( nodeNum ) {
|
||||
return nodeNum;
|
||||
}
|
||||
nodeNum = node->children[0];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::BoundsReachableAreaNum
|
||||
============
|
||||
*/
|
||||
int idAASFileLocal::BoundsReachableAreaNum( const idBounds &bounds, const int areaFlags, const int excludeTravelFlags ) const {
|
||||
|
||||
return BoundsReachableAreaNum_r( 1, bounds, areaFlags, excludeTravelFlags );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::PushPointIntoAreaNum
|
||||
============
|
||||
*/
|
||||
void idAASFileLocal::PushPointIntoAreaNum( int areaNum, idVec3 &point ) const {
|
||||
int i, faceNum;
|
||||
const aasArea_t *area;
|
||||
const aasFace_t *face;
|
||||
|
||||
area = &areas[areaNum];
|
||||
|
||||
// push the point to the right side of all area face planes
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = faceIndex[area->firstFace + i];
|
||||
face = &faces[abs( faceNum )];
|
||||
|
||||
const idPlane &plane = planeList[face->planeNum ^ INTSIGNBITSET( faceNum )];
|
||||
float dist = plane.Distance( point );
|
||||
|
||||
// project the point onto the face plane if it is on the wrong side
|
||||
if ( dist < 0.0f ) {
|
||||
point -= dist * plane.Normal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::Trace
|
||||
============
|
||||
*/
|
||||
#define TRACEPLANE_EPSILON 0.125f
|
||||
|
||||
typedef struct aasTraceStack_s
|
||||
{
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
int planeNum;
|
||||
int nodeNum;
|
||||
} aasTraceStack_t;
|
||||
|
||||
bool idAASFileLocal::Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const {
|
||||
int side, nodeNum, tmpPlaneNum;
|
||||
double front, back, frac;
|
||||
idVec3 cur_start, cur_end, cur_mid, v1, v2;
|
||||
aasTraceStack_t tracestack[MAX_AAS_TREE_DEPTH];
|
||||
aasTraceStack_t *tstack_p;
|
||||
const aasNode_t *node;
|
||||
const idPlane *plane;
|
||||
|
||||
trace.numAreas = 0;
|
||||
trace.lastAreaNum = 0;
|
||||
trace.blockingAreaNum = 0;
|
||||
|
||||
tstack_p = tracestack;
|
||||
tstack_p->start = start;
|
||||
tstack_p->end = end;
|
||||
tstack_p->planeNum = 0;
|
||||
tstack_p->nodeNum = 1; //start with the root of the tree
|
||||
tstack_p++;
|
||||
|
||||
while( 1 ) {
|
||||
|
||||
tstack_p--;
|
||||
// if the trace stack is empty
|
||||
if ( tstack_p < tracestack ) {
|
||||
if ( !trace.lastAreaNum ) {
|
||||
// completely in solid
|
||||
trace.fraction = 0.0f;
|
||||
trace.endpos = start;
|
||||
}
|
||||
else {
|
||||
// nothing was hit
|
||||
trace.fraction = 1.0f;
|
||||
trace.endpos = end;
|
||||
}
|
||||
trace.planeNum = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// number of the current node to test the line against
|
||||
nodeNum = tstack_p->nodeNum;
|
||||
|
||||
// if it is an area
|
||||
if ( nodeNum < 0) {
|
||||
// if can't enter the area
|
||||
if ( ( areas[-nodeNum].flags & trace.flags ) || ( areas[-nodeNum].travelFlags & trace.travelFlags ) ) {
|
||||
if ( !trace.lastAreaNum ) {
|
||||
trace.fraction = 0.0f;
|
||||
v1 = vec3_origin;
|
||||
} else {
|
||||
v1 = end - start;
|
||||
v2 = tstack_p->start - start;
|
||||
trace.fraction = v2.Length() / v1.Length();
|
||||
}
|
||||
trace.endpos = tstack_p->start;
|
||||
trace.blockingAreaNum = -nodeNum;
|
||||
trace.planeNum = tstack_p->planeNum;
|
||||
// always take the plane with normal facing towards the trace start
|
||||
plane = &planeList[trace.planeNum];
|
||||
if ( v1 * plane->Normal() > 0.0f ) {
|
||||
trace.planeNum ^= 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
trace.lastAreaNum = -nodeNum;
|
||||
if ( trace.numAreas < trace.maxAreas ) {
|
||||
if ( trace.areas ) {
|
||||
trace.areas[trace.numAreas] = -nodeNum;
|
||||
}
|
||||
if ( trace.points ) {
|
||||
trace.points[trace.numAreas] = tstack_p->start;
|
||||
}
|
||||
trace.numAreas++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it is a solid leaf
|
||||
if ( !nodeNum ) {
|
||||
if ( !trace.lastAreaNum ) {
|
||||
trace.fraction = 0.0f;
|
||||
v1 = vec3_origin;
|
||||
} else {
|
||||
v1 = end - start;
|
||||
v2 = tstack_p->start - start;
|
||||
trace.fraction = v2.Length() / v1.Length();
|
||||
}
|
||||
trace.endpos = tstack_p->start;
|
||||
trace.blockingAreaNum = 0; // hit solid leaf
|
||||
trace.planeNum = tstack_p->planeNum;
|
||||
// always take the plane with normal facing towards the trace start
|
||||
plane = &planeList[trace.planeNum];
|
||||
if ( v1 * plane->Normal() > 0.0f ) {
|
||||
trace.planeNum ^= 1;
|
||||
}
|
||||
if ( !trace.lastAreaNum && trace.getOutOfSolid ) {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// the node to test against
|
||||
node = &nodes[nodeNum];
|
||||
// start point of current line to test against node
|
||||
cur_start = tstack_p->start;
|
||||
// end point of the current line to test against node
|
||||
cur_end = tstack_p->end;
|
||||
// the current node plane
|
||||
plane = &planeList[node->planeNum];
|
||||
|
||||
front = plane->Distance( cur_start );
|
||||
back = plane->Distance( cur_end );
|
||||
|
||||
// if the whole to be traced line is totally at the front of this node
|
||||
// only go down the tree with the front child
|
||||
if ( front >= -ON_EPSILON && back >= -ON_EPSILON ) {
|
||||
// keep the current start and end point on the stack and go down the tree with the front child
|
||||
tstack_p->nodeNum = node->children[0];
|
||||
tstack_p++;
|
||||
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
|
||||
common->Error("idAASFileLocal::Trace: stack overflow\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if the whole to be traced line is totally at the back of this node
|
||||
// only go down the tree with the back child
|
||||
else if ( front < ON_EPSILON && back < ON_EPSILON ) {
|
||||
// keep the current start and end point on the stack and go down the tree with the back child
|
||||
tstack_p->nodeNum = node->children[1];
|
||||
tstack_p++;
|
||||
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
|
||||
common->Error("idAASFileLocal::Trace: stack overflow\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// go down the tree both at the front and back of the node
|
||||
else {
|
||||
tmpPlaneNum = tstack_p->planeNum;
|
||||
// calculate the hit point with the node plane
|
||||
// put the cross point TRACEPLANE_EPSILON on the near side
|
||||
if (front < 0) {
|
||||
frac = (front + TRACEPLANE_EPSILON) / ( front - back );
|
||||
}
|
||||
else {
|
||||
frac = (front - TRACEPLANE_EPSILON) / ( front - back );
|
||||
}
|
||||
|
||||
if (frac < 0) {
|
||||
frac = 0.001f; //0
|
||||
}
|
||||
else if (frac > 1) {
|
||||
frac = 0.999f; //1
|
||||
}
|
||||
|
||||
cur_mid = cur_start + ( cur_end - cur_start ) * frac;
|
||||
|
||||
// side the front part of the line is on
|
||||
side = front < 0;
|
||||
|
||||
// first put the end part of the line on the stack (back side)
|
||||
tstack_p->start = cur_mid;
|
||||
tstack_p->planeNum = node->planeNum;
|
||||
tstack_p->nodeNum = node->children[!side];
|
||||
tstack_p++;
|
||||
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
|
||||
common->Error("idAASFileLocal::Trace: stack overflow\n" );
|
||||
return false;
|
||||
}
|
||||
// now put the part near the start of the line on the stack so we will
|
||||
// continue with that part first.
|
||||
tstack_p->start = cur_start;
|
||||
tstack_p->end = cur_mid;
|
||||
tstack_p->planeNum = tmpPlaneNum;
|
||||
tstack_p->nodeNum = node->children[side];
|
||||
tstack_p++;
|
||||
if ( tstack_p >= &tracestack[MAX_AAS_TREE_DEPTH] ) {
|
||||
common->Error("idAASFileLocal::Trace: stack overflow\n" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASLocal::AreaContentsTravelFlags
|
||||
============
|
||||
*/
|
||||
int idAASFileLocal::AreaContentsTravelFlags( int areaNum ) const {
|
||||
if ( areas[areaNum].contents & AREACONTENTS_WATER ) {
|
||||
return TFL_WATER;
|
||||
}
|
||||
return TFL_AIR;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::MaxTreeDepth_r
|
||||
============
|
||||
*/
|
||||
void idAASFileLocal::MaxTreeDepth_r( int nodeNum, int &depth, int &maxDepth ) const {
|
||||
const aasNode_t *node;
|
||||
|
||||
if ( nodeNum <= 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
depth++;
|
||||
if ( depth > maxDepth ) {
|
||||
maxDepth = depth;
|
||||
}
|
||||
|
||||
node = &nodes[nodeNum];
|
||||
MaxTreeDepth_r( node->children[0], depth, maxDepth );
|
||||
MaxTreeDepth_r( node->children[1], depth, maxDepth );
|
||||
|
||||
depth--;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
idAASFileLocal::MaxTreeDepth
|
||||
============
|
||||
*/
|
||||
int idAASFileLocal::MaxTreeDepth( void ) const {
|
||||
int depth, maxDepth;
|
||||
|
||||
depth = maxDepth = 0;
|
||||
MaxTreeDepth_r( 1, depth, maxDepth );
|
||||
return maxDepth;
|
||||
}
|
||||
945
neo/tools/compilers/aas/AASReach.cpp
Normal file
945
neo/tools/compilers/aas/AASReach.cpp
Normal file
@@ -0,0 +1,945 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "AASFile.h"
|
||||
#include "AASFile_local.h"
|
||||
#include "AASReach.h"
|
||||
|
||||
#define INSIDEUNITS 2.0f
|
||||
#define INSIDEUNITS_WALKEND 0.5f
|
||||
#define INSIDEUNITS_WALKSTART 0.1f
|
||||
#define INSIDEUNITS_SWIMEND 0.5f
|
||||
#define INSIDEUNITS_FLYEND 0.5f
|
||||
#define INSIDEUNITS_WATERJUMP 15.0f
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::ReachabilityExists
|
||||
================
|
||||
*/
|
||||
bool idAASReach::ReachabilityExists( int fromAreaNum, int toAreaNum ) {
|
||||
aasArea_t *area;
|
||||
idReachability *reach;
|
||||
|
||||
area = &file->areas[fromAreaNum];
|
||||
for ( reach = area->reach; reach; reach = reach->next ) {
|
||||
if ( reach->toAreaNum == toAreaNum ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::CanSwimInArea
|
||||
================
|
||||
*/
|
||||
ID_INLINE bool idAASReach::CanSwimInArea( int areaNum ) {
|
||||
return ( file->areas[areaNum].contents & AREACONTENTS_WATER ) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::AreaHasFloor
|
||||
================
|
||||
*/
|
||||
ID_INLINE bool idAASReach::AreaHasFloor( int areaNum ) {
|
||||
return ( file->areas[areaNum].flags & AREA_FLOOR ) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::AreaIsClusterPortal
|
||||
================
|
||||
*/
|
||||
ID_INLINE bool idAASReach::AreaIsClusterPortal( int areaNum ) {
|
||||
return ( file->areas[areaNum].contents & AREACONTENTS_CLUSTERPORTAL ) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::AddReachabilityToArea
|
||||
================
|
||||
*/
|
||||
void idAASReach::AddReachabilityToArea( idReachability *reach, int areaNum ) {
|
||||
aasArea_t *area;
|
||||
|
||||
area = &file->areas[areaNum];
|
||||
reach->next = area->reach;
|
||||
area->reach = reach;
|
||||
numReachabilities++;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::Reachability_Fly
|
||||
================
|
||||
*/
|
||||
void idAASReach::Reachability_Fly( int areaNum ) {
|
||||
int i, faceNum, otherAreaNum;
|
||||
aasArea_t *area;
|
||||
aasFace_t *face;
|
||||
idReachability_Fly *reach;
|
||||
|
||||
area = &file->areas[areaNum];
|
||||
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = file->faceIndex[area->firstFace + i];
|
||||
face = &file->faces[abs(faceNum)];
|
||||
|
||||
otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
|
||||
|
||||
if ( otherAreaNum == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ReachabilityExists( areaNum, otherAreaNum ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create reachability going through this face
|
||||
reach = new idReachability_Fly();
|
||||
reach->travelType = TFL_FLY;
|
||||
reach->toAreaNum = otherAreaNum;
|
||||
reach->fromAreaNum = areaNum;
|
||||
reach->edgeNum = 0;
|
||||
reach->travelTime = 1;
|
||||
reach->start = file->FaceCenter( abs(faceNum) );
|
||||
if ( faceNum < 0 ) {
|
||||
reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND;
|
||||
} else {
|
||||
reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_FLYEND;
|
||||
}
|
||||
AddReachabilityToArea( reach, areaNum );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::Reachability_Swim
|
||||
================
|
||||
*/
|
||||
void idAASReach::Reachability_Swim( int areaNum ) {
|
||||
int i, faceNum, otherAreaNum;
|
||||
aasArea_t *area;
|
||||
aasFace_t *face;
|
||||
idReachability_Swim *reach;
|
||||
|
||||
if ( !CanSwimInArea( areaNum ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
area = &file->areas[areaNum];
|
||||
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = file->faceIndex[area->firstFace + i];
|
||||
face = &file->faces[abs(faceNum)];
|
||||
|
||||
otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
|
||||
|
||||
if ( otherAreaNum == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !CanSwimInArea( otherAreaNum ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ReachabilityExists( areaNum, otherAreaNum ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create reachability going through this face
|
||||
reach = new idReachability_Swim();
|
||||
reach->travelType = TFL_SWIM;
|
||||
reach->toAreaNum = otherAreaNum;
|
||||
reach->fromAreaNum = areaNum;
|
||||
reach->edgeNum = 0;
|
||||
reach->travelTime = 1;
|
||||
reach->start = file->FaceCenter( abs(faceNum) );
|
||||
if ( faceNum < 0 ) {
|
||||
reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND;
|
||||
} else {
|
||||
reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND;
|
||||
}
|
||||
AddReachabilityToArea( reach, areaNum );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::Reachability_EqualFloorHeight
|
||||
================
|
||||
*/
|
||||
void idAASReach::Reachability_EqualFloorHeight( int areaNum ) {
|
||||
int i, k, l, m, n, faceNum, face1Num, face2Num, otherAreaNum, edge1Num, edge2Num;
|
||||
aasArea_t *area, *otherArea;
|
||||
aasFace_t *face, *face1, *face2;
|
||||
idReachability_Walk *reach;
|
||||
|
||||
if ( !AreaHasFloor( areaNum ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
area = &file->areas[areaNum];
|
||||
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = file->faceIndex[area->firstFace + i];
|
||||
face = &file->faces[abs(faceNum)];
|
||||
|
||||
otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)];
|
||||
if ( !AreaHasFloor( otherAreaNum ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
otherArea = &file->areas[otherAreaNum];
|
||||
|
||||
for ( k = 0; k < area->numFaces; k++ ) {
|
||||
face1Num = file->faceIndex[area->firstFace + k];
|
||||
face1 = &file->faces[abs(face1Num)];
|
||||
|
||||
if ( !( face1->flags & FACE_FLOOR ) ) {
|
||||
continue;
|
||||
}
|
||||
for ( l = 0; l < otherArea->numFaces; l++ ) {
|
||||
face2Num = file->faceIndex[otherArea->firstFace + l];
|
||||
face2 = &file->faces[abs(face2Num)];
|
||||
|
||||
if ( !( face2->flags & FACE_FLOOR ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( m = 0; m < face1->numEdges; m++ ) {
|
||||
edge1Num = abs(file->edgeIndex[face1->firstEdge + m]);
|
||||
for ( n = 0; n < face2->numEdges; n++ ) {
|
||||
edge2Num = abs(file->edgeIndex[face2->firstEdge + n]);
|
||||
if ( edge1Num == edge2Num ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( n < face2->numEdges ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( m < face1->numEdges ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( l < otherArea->numFaces ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( k < area->numFaces ) {
|
||||
// create reachability
|
||||
reach = new idReachability_Walk();
|
||||
reach->travelType = TFL_WALK;
|
||||
reach->toAreaNum = otherAreaNum;
|
||||
reach->fromAreaNum = areaNum;
|
||||
reach->edgeNum = abs( edge1Num );
|
||||
reach->travelTime = 1;
|
||||
reach->start = file->EdgeCenter( edge1Num );
|
||||
if ( faceNum < 0 ) {
|
||||
reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND;
|
||||
}
|
||||
else {
|
||||
reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND;
|
||||
}
|
||||
AddReachabilityToArea( reach, areaNum );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::Reachability_Step_Barrier_WaterJump_WalkOffLedge
|
||||
================
|
||||
*/
|
||||
bool idAASReach::Reachability_Step_Barrier_WaterJump_WalkOffLedge( int area1num, int area2num ) {
|
||||
int i, j, k, l, edge1Num, edge2Num, areas[10];
|
||||
int floor_bestArea1FloorEdgeNum, floor_bestArea2FloorEdgeNum, floor_foundReach;
|
||||
int water_bestArea1FloorEdgeNum, water_bestArea2FloorEdgeNum, water_foundReach;
|
||||
int side1, faceSide1, floorFace1Num;
|
||||
float dist, dist1, dist2, diff, invGravityDot, orthogonalDot;
|
||||
float x1, x2, x3, x4, y1, y2, y3, y4, tmp, y;
|
||||
float length, floor_bestLength, water_bestLength, floor_bestDist, water_bestDist;
|
||||
idVec3 v1, v2, v3, v4, tmpv, p1area1, p1area2, p2area1, p2area2;
|
||||
idVec3 normal, orthogonal, edgeVec, start, end;
|
||||
idVec3 floor_bestStart, floor_bestEnd, floor_bestNormal;
|
||||
idVec3 water_bestStart, water_bestEnd, water_bestNormal;
|
||||
idVec3 testPoint;
|
||||
idPlane *plane;
|
||||
aasArea_t *area1, *area2;
|
||||
aasFace_t *floorFace1, *floorFace2, *floor_bestFace1, *water_bestFace1;
|
||||
aasEdge_t *edge1, *edge2;
|
||||
idReachability_Walk *walkReach;
|
||||
idReachability_BarrierJump *barrierJumpReach;
|
||||
idReachability_WaterJump *waterJumpReach;
|
||||
idReachability_WalkOffLedge *walkOffLedgeReach;
|
||||
aasTrace_t trace;
|
||||
|
||||
// must be able to walk or swim in the first area
|
||||
if ( !AreaHasFloor( area1num ) && !CanSwimInArea( area1num ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !AreaHasFloor( area2num ) && !CanSwimInArea( area2num ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
area1 = &file->areas[area1num];
|
||||
area2 = &file->areas[area2num];
|
||||
|
||||
// if the areas are not near anough in the x-y direction
|
||||
for ( i = 0; i < 2; i++ ) {
|
||||
if ( area1->bounds[0][i] > area2->bounds[1][i] + 2.0f ) {
|
||||
return false;
|
||||
}
|
||||
if ( area1->bounds[1][i] < area2->bounds[0][i] - 2.0f ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
floor_foundReach = false;
|
||||
floor_bestDist = 99999;
|
||||
floor_bestLength = 0;
|
||||
floor_bestArea2FloorEdgeNum = 0;
|
||||
|
||||
water_foundReach = false;
|
||||
water_bestDist = 99999;
|
||||
water_bestLength = 0;
|
||||
water_bestArea2FloorEdgeNum = 0;
|
||||
|
||||
for ( i = 0; i < area1->numFaces; i++ ) {
|
||||
floorFace1Num = file->faceIndex[area1->firstFace + i];
|
||||
faceSide1 = floorFace1Num < 0;
|
||||
floorFace1 = &file->faces[abs(floorFace1Num)];
|
||||
|
||||
// if this isn't a floor face
|
||||
if ( !(floorFace1->flags & FACE_FLOOR) ) {
|
||||
|
||||
// if we can swim in the first area
|
||||
if ( CanSwimInArea( area1num ) ) {
|
||||
|
||||
// face plane must be more or less horizontal
|
||||
plane = &file->planeList[ floorFace1->planeNum ^ (!faceSide1) ];
|
||||
if ( plane->Normal() * file->settings.invGravityDir < file->settings.minFloorCos ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if we can't swim in the area it must be a ground face
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for ( k = 0; k < floorFace1->numEdges; k++ ) {
|
||||
edge1Num = file->edgeIndex[floorFace1->firstEdge + k];
|
||||
side1 = (edge1Num < 0);
|
||||
// NOTE: for water faces we must take the side area 1 is on into
|
||||
// account because the face is shared and doesn't have to be oriented correctly
|
||||
if ( !(floorFace1->flags & FACE_FLOOR) ) {
|
||||
side1 = (side1 == faceSide1);
|
||||
}
|
||||
edge1Num = abs(edge1Num);
|
||||
edge1 = &file->edges[edge1Num];
|
||||
// vertices of the edge
|
||||
v1 = file->vertices[edge1->vertexNum[!side1]];
|
||||
v2 = file->vertices[edge1->vertexNum[side1]];
|
||||
// get a vertical plane through the edge
|
||||
// NOTE: normal is pointing into area 2 because the face edges are stored counter clockwise
|
||||
edgeVec = v2 - v1;
|
||||
normal = edgeVec.Cross( file->settings.invGravityDir );
|
||||
normal.Normalize();
|
||||
dist = normal * v1;
|
||||
|
||||
// check the faces from the second area
|
||||
for ( j = 0; j < area2->numFaces; j++ ) {
|
||||
floorFace2 = &file->faces[abs(file->faceIndex[area2->firstFace + j])];
|
||||
// must be a ground face
|
||||
if ( !(floorFace2->flags & FACE_FLOOR) ) {
|
||||
continue;
|
||||
}
|
||||
// check the edges of this ground face
|
||||
for ( l = 0; l < floorFace2->numEdges; l++ ) {
|
||||
edge2Num = abs(file->edgeIndex[floorFace2->firstEdge + l]);
|
||||
edge2 = &file->edges[edge2Num];
|
||||
// vertices of the edge
|
||||
v3 = file->vertices[edge2->vertexNum[0]];
|
||||
v4 = file->vertices[edge2->vertexNum[1]];
|
||||
// check the distance between the two points and the vertical plane through the edge of area1
|
||||
diff = normal * v3 - dist;
|
||||
if ( diff < -0.2f || diff > 0.2f ) {
|
||||
continue;
|
||||
}
|
||||
diff = normal * v4 - dist;
|
||||
if ( diff < -0.2f || diff > 0.2f ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// project the two ground edges into the step side plane
|
||||
// and calculate the shortest distance between the two
|
||||
// edges if they overlap in the direction orthogonal to
|
||||
// the gravity direction
|
||||
orthogonal = file->settings.invGravityDir.Cross( normal );
|
||||
invGravityDot = file->settings.invGravityDir * file->settings.invGravityDir;
|
||||
orthogonalDot = orthogonal * orthogonal;
|
||||
// projection into the step plane
|
||||
// NOTE: since gravity is vertical this is just the z coordinate
|
||||
y1 = v1[2];//(v1 * file->settings.invGravity) / invGravityDot;
|
||||
y2 = v2[2];//(v2 * file->settings.invGravity) / invGravityDot;
|
||||
y3 = v3[2];//(v3 * file->settings.invGravity) / invGravityDot;
|
||||
y4 = v4[2];//(v4 * file->settings.invGravity) / invGravityDot;
|
||||
|
||||
x1 = (v1 * orthogonal) / orthogonalDot;
|
||||
x2 = (v2 * orthogonal) / orthogonalDot;
|
||||
x3 = (v3 * orthogonal) / orthogonalDot;
|
||||
x4 = (v4 * orthogonal) / orthogonalDot;
|
||||
|
||||
if ( x1 > x2 ) {
|
||||
tmp = x1; x1 = x2; x2 = tmp;
|
||||
tmp = y1; y1 = y2; y2 = tmp;
|
||||
tmpv = v1; v1 = v2; v2 = tmpv;
|
||||
}
|
||||
if ( x3 > x4 ) {
|
||||
tmp = x3; x3 = x4; x4 = tmp;
|
||||
tmp = y3; y3 = y4; y4 = tmp;
|
||||
tmpv = v3; v3 = v4; v4 = tmpv;
|
||||
}
|
||||
// if the two projected edge lines have no overlap
|
||||
if ( x2 <= x3 || x4 <= x1 ) {
|
||||
continue;
|
||||
}
|
||||
// if the two lines fully overlap
|
||||
if ( (x1 - 0.5f < x3 && x4 < x2 + 0.5f) && (x3 - 0.5f < x1 && x2 < x4 + 0.5f) ) {
|
||||
dist1 = y3 - y1;
|
||||
dist2 = y4 - y2;
|
||||
p1area1 = v1;
|
||||
p2area1 = v2;
|
||||
p1area2 = v3;
|
||||
p2area2 = v4;
|
||||
}
|
||||
else {
|
||||
// if the points are equal
|
||||
if ( x1 > x3 - 0.1f && x1 < x3 + 0.1f ) {
|
||||
dist1 = y3 - y1;
|
||||
p1area1 = v1;
|
||||
p1area2 = v3;
|
||||
}
|
||||
else if ( x1 < x3 ) {
|
||||
y = y1 + (x3 - x1) * (y2 - y1) / (x2 - x1);
|
||||
dist1 = y3 - y;
|
||||
p1area1 = v3;
|
||||
p1area1[2] = y;
|
||||
p1area2 = v3;
|
||||
}
|
||||
else {
|
||||
y = y3 + (x1 - x3) * (y4 - y3) / (x4 - x3);
|
||||
dist1 = y - y1;
|
||||
p1area1 = v1;
|
||||
p1area2 = v1;
|
||||
p1area2[2] = y;
|
||||
}
|
||||
// if the points are equal
|
||||
if ( x2 > x4 - 0.1f && x2 < x4 + 0.1f ) {
|
||||
dist2 = y4 - y2;
|
||||
p2area1 = v2;
|
||||
p2area2 = v4;
|
||||
}
|
||||
else if ( x2 < x4 ) {
|
||||
y = y3 + (x2 - x3) * (y4 - y3) / (x4 - x3);
|
||||
dist2 = y - y2;
|
||||
p2area1 = v2;
|
||||
p2area2 = v2;
|
||||
p2area2[2] = y;
|
||||
}
|
||||
else {
|
||||
y = y1 + (x4 - x1) * (y2 - y1) / (x2 - x1);
|
||||
dist2 = y4 - y;
|
||||
p2area1 = v4;
|
||||
p2area1[2] = y;
|
||||
p2area2 = v4;
|
||||
}
|
||||
}
|
||||
|
||||
// if both distances are pretty much equal then we take the middle of the points
|
||||
if ( dist1 > dist2 - 1.0f && dist1 < dist2 + 1.0f ) {
|
||||
dist = dist1;
|
||||
start = ( p1area1 + p2area1 ) * 0.5f;
|
||||
end = ( p1area2 + p2area2 ) * 0.5f;
|
||||
}
|
||||
else if (dist1 < dist2) {
|
||||
dist = dist1;
|
||||
start = p1area1;
|
||||
end = p1area2;
|
||||
}
|
||||
else {
|
||||
dist = dist2;
|
||||
start = p2area1;
|
||||
end = p2area2;
|
||||
}
|
||||
|
||||
// get the length of the overlapping part of the edges of the two areas
|
||||
length = (p2area2 - p1area2).Length();
|
||||
|
||||
if ( floorFace1->flags & FACE_FLOOR ) {
|
||||
// if the vertical distance is smaller
|
||||
if ( dist < floor_bestDist ||
|
||||
// or the vertical distance is pretty much the same
|
||||
// but the overlapping part of the edges is longer
|
||||
(dist < floor_bestDist + 1.0f && length > floor_bestLength) ) {
|
||||
floor_bestDist = dist;
|
||||
floor_bestLength = length;
|
||||
floor_foundReach = true;
|
||||
floor_bestArea1FloorEdgeNum = edge1Num;
|
||||
floor_bestArea2FloorEdgeNum = edge2Num;
|
||||
floor_bestFace1 = floorFace1;
|
||||
floor_bestStart = start;
|
||||
floor_bestNormal = normal;
|
||||
floor_bestEnd = end;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// if the vertical distance is smaller
|
||||
if ( dist < water_bestDist ||
|
||||
//or the vertical distance is pretty much the same
|
||||
//but the overlapping part of the edges is longer
|
||||
(dist < water_bestDist + 1.0f && length > water_bestLength) ) {
|
||||
water_bestDist = dist;
|
||||
water_bestLength = length;
|
||||
water_foundReach = true;
|
||||
water_bestArea1FloorEdgeNum = edge1Num;
|
||||
water_bestArea2FloorEdgeNum = edge2Num;
|
||||
water_bestFace1 = floorFace1;
|
||||
water_bestStart = start; // best start point in area1
|
||||
water_bestNormal = normal; // normal is pointing into area2
|
||||
water_bestEnd = end; // best point towards area2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// NOTE: swim reachabilities should already be filtered out
|
||||
//
|
||||
// Steps
|
||||
//
|
||||
// ---------
|
||||
// | step height -> TFL_WALK
|
||||
// --------|
|
||||
//
|
||||
// ---------
|
||||
// ~~~~~~~~| step height and low water -> TFL_WALK
|
||||
// --------|
|
||||
//
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
// ---------
|
||||
// | step height and low water up to the step -> TFL_WALK
|
||||
// --------|
|
||||
//
|
||||
// check for a step reachability
|
||||
if ( floor_foundReach ) {
|
||||
// if area2 is higher but lower than the maximum step height
|
||||
// NOTE: floor_bestDist >= 0 also catches equal floor reachabilities
|
||||
if ( floor_bestDist >= 0 && floor_bestDist < file->settings.maxStepHeight ) {
|
||||
// create walk reachability from area1 to area2
|
||||
walkReach = new idReachability_Walk();
|
||||
walkReach->travelType = TFL_WALK;
|
||||
walkReach->toAreaNum = area2num;
|
||||
walkReach->fromAreaNum = area1num;
|
||||
walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
|
||||
walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
|
||||
walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
|
||||
walkReach->travelTime = 0;
|
||||
if ( area2->flags & AREA_CROUCH ) {
|
||||
walkReach->travelTime += file->settings.tt_startCrouching;
|
||||
}
|
||||
AddReachabilityToArea( walkReach, area1num );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Water Jumps
|
||||
//
|
||||
// ---------
|
||||
// |
|
||||
// ~~~~~~~~|
|
||||
// |
|
||||
// | higher than step height and water up to waterjump height -> TFL_WATERJUMP
|
||||
// --------|
|
||||
//
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
// ---------
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// | higher than step height and low water up to the step -> TFL_WATERJUMP
|
||||
// --------|
|
||||
//
|
||||
// check for a waterjump reachability
|
||||
if ( water_foundReach ) {
|
||||
// get a test point a little bit towards area1
|
||||
testPoint = water_bestEnd - INSIDEUNITS * water_bestNormal;
|
||||
// go down the maximum waterjump height
|
||||
testPoint[2] -= file->settings.maxWaterJumpHeight;
|
||||
// if there IS water the sv_maxwaterjump height below the bestend point
|
||||
if ( area1->flags & AREA_LIQUID ) {
|
||||
// don't create rediculous water jump reachabilities from areas very far below the water surface
|
||||
if ( water_bestDist < file->settings.maxWaterJumpHeight + 24 ) {
|
||||
// water jumping from or towards a crouch only areas is not possible
|
||||
if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) {
|
||||
// create water jump reachability from area1 to area2
|
||||
waterJumpReach = new idReachability_WaterJump();
|
||||
waterJumpReach->travelType = TFL_WATERJUMP;
|
||||
waterJumpReach->toAreaNum = area2num;
|
||||
waterJumpReach->fromAreaNum = area1num;
|
||||
waterJumpReach->start = water_bestStart;
|
||||
waterJumpReach->end = water_bestEnd + INSIDEUNITS_WATERJUMP * water_bestNormal;
|
||||
waterJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
|
||||
waterJumpReach->travelTime = file->settings.tt_waterJump;
|
||||
AddReachabilityToArea( waterJumpReach, area1num );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Barrier Jumps
|
||||
//
|
||||
// ---------
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// | higher than max step height lower than max barrier height -> TFL_BARRIERJUMP
|
||||
// --------|
|
||||
//
|
||||
// ---------
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// ~~~~~~~~| higher than max step height lower than max barrier height
|
||||
// --------| and a thin layer of water in the area to jump from -> TFL_BARRIERJUMP
|
||||
//
|
||||
// check for a barrier jump reachability
|
||||
if ( floor_foundReach ) {
|
||||
//if area2 is higher but lower than the maximum barrier jump height
|
||||
if ( floor_bestDist > 0 && floor_bestDist < file->settings.maxBarrierHeight ) {
|
||||
//if no water in area1 or a very thin layer of water on the ground
|
||||
if ( !water_foundReach || (floor_bestDist - water_bestDist < 16) ) {
|
||||
// cannot perform a barrier jump towards or from a crouch area
|
||||
if ( !(area1->flags & AREA_CROUCH) && !(area2->flags & AREA_CROUCH) ) {
|
||||
// create barrier jump reachability from area1 to area2
|
||||
barrierJumpReach = new idReachability_BarrierJump();
|
||||
barrierJumpReach->travelType = TFL_BARRIERJUMP;
|
||||
barrierJumpReach->toAreaNum = area2num;
|
||||
barrierJumpReach->fromAreaNum = area1num;
|
||||
barrierJumpReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
|
||||
barrierJumpReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
|
||||
barrierJumpReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
|
||||
barrierJumpReach->travelTime = file->settings.tt_barrierJump;
|
||||
AddReachabilityToArea( barrierJumpReach, area1num );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// Walk and Walk Off Ledge
|
||||
//
|
||||
// --------|
|
||||
// | can walk or step back -> TFL_WALK
|
||||
// ---------
|
||||
//
|
||||
// --------|
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// | cannot walk/step back -> TFL_WALKOFFLEDGE
|
||||
// ---------
|
||||
//
|
||||
// --------|
|
||||
// |
|
||||
// |~~~~~~~~
|
||||
// |
|
||||
// | cannot step back but can waterjump back -> TFL_WALKOFFLEDGE
|
||||
// --------- FIXME: create TFL_WALK reach??
|
||||
//
|
||||
// check for a walk or walk off ledge reachability
|
||||
if ( floor_foundReach ) {
|
||||
if ( floor_bestDist < 0 ) {
|
||||
if ( floor_bestDist > -file->settings.maxStepHeight ) {
|
||||
// create walk reachability from area1 to area2
|
||||
walkReach = new idReachability_Walk();
|
||||
walkReach->travelType = TFL_WALK;
|
||||
walkReach->toAreaNum = area2num;
|
||||
walkReach->fromAreaNum = area1num;
|
||||
walkReach->start = floor_bestStart + INSIDEUNITS_WALKSTART * floor_bestNormal;
|
||||
walkReach->end = floor_bestEnd + INSIDEUNITS_WALKEND * floor_bestNormal;
|
||||
walkReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
|
||||
walkReach->travelTime = 1;
|
||||
AddReachabilityToArea( walkReach, area1num );
|
||||
return true;
|
||||
}
|
||||
// if no maximum fall height set or less than the max
|
||||
if ( !file->settings.maxFallHeight || idMath::Fabs(floor_bestDist) < file->settings.maxFallHeight ) {
|
||||
// trace a bounding box vertically to check for solids
|
||||
floor_bestEnd += INSIDEUNITS * floor_bestNormal;
|
||||
start = floor_bestEnd;
|
||||
start[2] = floor_bestStart[2];
|
||||
end = floor_bestEnd;
|
||||
end[2] += 4;
|
||||
trace.areas = areas;
|
||||
trace.maxAreas = sizeof(areas) / sizeof(int);
|
||||
file->Trace( trace, start, end );
|
||||
// if the trace didn't start in solid and nothing was hit
|
||||
if ( trace.lastAreaNum && trace.fraction >= 1.0f ) {
|
||||
// the trace end point must be in the goal area
|
||||
if ( trace.lastAreaNum == area2num ) {
|
||||
// don't create reachability if going through a cluster portal
|
||||
for (i = 0; i < trace.numAreas; i++) {
|
||||
if ( AreaIsClusterPortal( trace.areas[i] ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i >= trace.numAreas ) {
|
||||
// create a walk off ledge reachability from area1 to area2
|
||||
walkOffLedgeReach = new idReachability_WalkOffLedge();
|
||||
walkOffLedgeReach->travelType = TFL_WALKOFFLEDGE;
|
||||
walkOffLedgeReach->toAreaNum = area2num;
|
||||
walkOffLedgeReach->fromAreaNum = area1num;
|
||||
walkOffLedgeReach->start = floor_bestStart;
|
||||
walkOffLedgeReach->end = floor_bestEnd;
|
||||
walkOffLedgeReach->edgeNum = abs( floor_bestArea1FloorEdgeNum );
|
||||
walkOffLedgeReach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(floor_bestDist) * 50 / file->settings.gravityValue;
|
||||
AddReachabilityToArea( walkOffLedgeReach, area1num );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::Reachability_WalkOffLedge
|
||||
================
|
||||
*/
|
||||
void idAASReach::Reachability_WalkOffLedge( int areaNum ) {
|
||||
int i, j, faceNum, edgeNum, side, reachAreaNum, p, areas[10];
|
||||
aasArea_t *area;
|
||||
aasFace_t *face;
|
||||
aasEdge_t *edge;
|
||||
idPlane *plane;
|
||||
idVec3 v1, v2, mid, dir, testEnd;
|
||||
idReachability_WalkOffLedge *reach;
|
||||
aasTrace_t trace;
|
||||
|
||||
if ( !AreaHasFloor( areaNum ) || CanSwimInArea( areaNum ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
area = &file->areas[areaNum];
|
||||
|
||||
for ( i = 0; i < area->numFaces; i++ ) {
|
||||
faceNum = file->faceIndex[area->firstFace + i];
|
||||
face = &file->faces[abs(faceNum)];
|
||||
|
||||
// face must be a floor face
|
||||
if ( !(face->flags & FACE_FLOOR) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( j = 0; j < face->numEdges; j++ ) {
|
||||
|
||||
edgeNum = file->edgeIndex[face->firstEdge + j];
|
||||
edge = &file->edges[abs(edgeNum)];
|
||||
|
||||
//if ( !(edge->flags & EDGE_LEDGE) ) {
|
||||
// continue;
|
||||
//}
|
||||
|
||||
side = edgeNum < 0;
|
||||
|
||||
v1 = file->vertices[edge->vertexNum[side]];
|
||||
v2 = file->vertices[edge->vertexNum[!side]];
|
||||
|
||||
plane = &file->planeList[face->planeNum ^ INTSIGNBITSET(faceNum) ];
|
||||
|
||||
// get the direction into the other area
|
||||
dir = plane->Normal().Cross( v2 - v1 );
|
||||
dir.Normalize();
|
||||
|
||||
mid = ( v1 + v2 ) * 0.5f;
|
||||
testEnd = mid + INSIDEUNITS_WALKEND * dir;
|
||||
testEnd[2] -= file->settings.maxFallHeight + 1.0f;
|
||||
trace.areas = areas;
|
||||
trace.maxAreas = sizeof(areas) / sizeof(int);
|
||||
file->Trace( trace, mid, testEnd );
|
||||
|
||||
reachAreaNum = trace.lastAreaNum;
|
||||
if ( !reachAreaNum || reachAreaNum == areaNum ) {
|
||||
continue;
|
||||
}
|
||||
if ( idMath::Fabs( mid[2] - trace.endpos[2] ) > file->settings.maxFallHeight ) {
|
||||
continue;
|
||||
}
|
||||
if ( !AreaHasFloor( reachAreaNum ) && !CanSwimInArea( reachAreaNum ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( ReachabilityExists( areaNum, reachAreaNum) ) {
|
||||
continue;
|
||||
}
|
||||
// if not going through a cluster portal
|
||||
for ( p = 0; p < trace.numAreas; p++ ) {
|
||||
if ( AreaIsClusterPortal( trace.areas[p] ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( p < trace.numAreas ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reach = new idReachability_WalkOffLedge();
|
||||
reach->travelType = TFL_WALKOFFLEDGE;
|
||||
reach->toAreaNum = reachAreaNum;
|
||||
reach->fromAreaNum = areaNum;
|
||||
reach->start = mid;
|
||||
reach->end = trace.endpos;
|
||||
reach->edgeNum = abs( edgeNum );
|
||||
reach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(mid[2] - trace.endpos[2]) * 50 / file->settings.gravityValue;
|
||||
AddReachabilityToArea( reach, areaNum );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::FlagReachableAreas
|
||||
================
|
||||
*/
|
||||
void idAASReach::FlagReachableAreas( idAASFileLocal *file ) {
|
||||
int i, numReachableAreas;
|
||||
|
||||
numReachableAreas = 0;
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
|
||||
if ( ( file->areas[i].flags & ( AREA_FLOOR | AREA_LADDER ) ) ||
|
||||
( file->areas[i].contents & AREACONTENTS_WATER ) ) {
|
||||
file->areas[i].flags |= AREA_REACHABLE_WALK;
|
||||
}
|
||||
if ( file->GetSettings().allowFlyReachabilities ) {
|
||||
file->areas[i].flags |= AREA_REACHABLE_FLY;
|
||||
}
|
||||
numReachableAreas++;
|
||||
}
|
||||
|
||||
common->Printf( "%6d reachable areas\n", numReachableAreas );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idAASReach::Build
|
||||
================
|
||||
*/
|
||||
bool idAASReach::Build( const idMapFile *mapFile, idAASFileLocal *file ) {
|
||||
int i, j, lastPercent, percent;
|
||||
|
||||
this->mapFile = mapFile;
|
||||
this->file = file;
|
||||
numReachabilities = 0;
|
||||
|
||||
common->Printf( "[Reachability]\n" );
|
||||
|
||||
// delete all existing reachabilities
|
||||
file->DeleteReachabilities();
|
||||
|
||||
FlagReachableAreas( file );
|
||||
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( file->GetSettings().allowSwimReachabilities ) {
|
||||
Reachability_Swim( i );
|
||||
}
|
||||
Reachability_EqualFloorHeight( i );
|
||||
}
|
||||
|
||||
lastPercent = -1;
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
|
||||
if ( !( file->areas[i].flags & AREA_REACHABLE_WALK ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( j = 0; j < file->areas.Num(); j++ ) {
|
||||
if ( i == j ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !( file->areas[j].flags & AREA_REACHABLE_WALK ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ReachabilityExists( i, j ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( Reachability_Step_Barrier_WaterJump_WalkOffLedge( i, j ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Reachability_WalkOffLedge( i );
|
||||
|
||||
percent = 100 * i / file->areas.Num();
|
||||
if ( percent > lastPercent ) {
|
||||
common->Printf( "\r%6d%%", percent );
|
||||
lastPercent = percent;
|
||||
}
|
||||
}
|
||||
|
||||
if ( file->GetSettings().allowFlyReachabilities ) {
|
||||
for ( i = 1; i < file->areas.Num(); i++ ) {
|
||||
Reachability_Fly( i );
|
||||
}
|
||||
}
|
||||
|
||||
file->LinkReversedReachability();
|
||||
|
||||
common->Printf( "\r%6d reachabilities\n", numReachabilities );
|
||||
|
||||
return true;
|
||||
}
|
||||
67
neo/tools/compilers/aas/AASReach.h
Normal file
67
neo/tools/compilers/aas/AASReach.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __AASREACH_H__
|
||||
#define __AASREACH_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Reachabilities
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idAASReach {
|
||||
|
||||
public:
|
||||
bool Build( const idMapFile *mapFile, idAASFileLocal *file );
|
||||
|
||||
private:
|
||||
const idMapFile * mapFile;
|
||||
idAASFileLocal * file;
|
||||
int numReachabilities;
|
||||
bool allowSwimReachabilities;
|
||||
bool allowFlyReachabilities;
|
||||
|
||||
private: // reachability
|
||||
void FlagReachableAreas( idAASFileLocal *file );
|
||||
bool ReachabilityExists( int fromAreaNum, int toAreaNum );
|
||||
bool CanSwimInArea( int areaNum );
|
||||
bool AreaHasFloor( int areaNum );
|
||||
bool AreaIsClusterPortal( int areaNum );
|
||||
void AddReachabilityToArea( idReachability *reach, int areaNum );
|
||||
void Reachability_Fly( int areaNum );
|
||||
void Reachability_Swim( int areaNum );
|
||||
void Reachability_EqualFloorHeight( int areaNum );
|
||||
bool Reachability_Step_Barrier_WaterJump_WalkOffLedge( int fromAreaNum, int toAreaNum );
|
||||
void Reachability_WalkOffLedge( int areaNum );
|
||||
|
||||
};
|
||||
|
||||
#endif /* !__AASREACH_H__ */
|
||||
1582
neo/tools/compilers/aas/Brush.cpp
Normal file
1582
neo/tools/compilers/aas/Brush.cpp
Normal file
File diff suppressed because it is too large
Load Diff
231
neo/tools/compilers/aas/Brush.h
Normal file
231
neo/tools/compilers/aas/Brush.h
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __BRUSH_H__
|
||||
#define __BRUSH_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Brushes
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
|
||||
#define BRUSH_PLANESIDE_FRONT 1
|
||||
#define BRUSH_PLANESIDE_BACK 2
|
||||
#define BRUSH_PLANESIDE_BOTH ( BRUSH_PLANESIDE_FRONT | BRUSH_PLANESIDE_BACK )
|
||||
#define BRUSH_PLANESIDE_FACING 4
|
||||
|
||||
class idBrush;
|
||||
class idBrushList;
|
||||
|
||||
void DisplayRealTimeString( char *string, ... ) id_attribute((format(printf,1,2)));
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idBrushSide
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
#define SFL_SPLIT 0x0001
|
||||
#define SFL_BEVEL 0x0002
|
||||
#define SFL_USED_SPLITTER 0x0004
|
||||
#define SFL_TESTED_SPLITTER 0x0008
|
||||
|
||||
class idBrushSide {
|
||||
|
||||
friend class idBrush;
|
||||
|
||||
public:
|
||||
idBrushSide( void );
|
||||
idBrushSide( const idPlane &plane, int planeNum );
|
||||
~idBrushSide( void );
|
||||
|
||||
int GetFlags( void ) const { return flags; }
|
||||
void SetFlag( int flag ) { flags |= flag; }
|
||||
void RemoveFlag( int flag ) { flags &= ~flag; }
|
||||
const idPlane & GetPlane( void ) const { return plane; }
|
||||
void SetPlaneNum( int num ) { planeNum = num; }
|
||||
int GetPlaneNum( void ) { return planeNum; }
|
||||
const idWinding * GetWinding( void ) const { return winding; }
|
||||
idBrushSide * Copy( void ) const;
|
||||
int Split( const idPlane &splitPlane, idBrushSide **front, idBrushSide **back ) const;
|
||||
|
||||
private:
|
||||
int flags;
|
||||
int planeNum;
|
||||
idPlane plane;
|
||||
idWinding * winding;
|
||||
};
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idBrush
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
#define BFL_NO_VALID_SPLITTERS 0x0001
|
||||
|
||||
class idBrush {
|
||||
|
||||
friend class idBrushList;
|
||||
|
||||
public:
|
||||
idBrush( void );
|
||||
~idBrush( void );
|
||||
|
||||
int GetFlags( void ) const { return flags; }
|
||||
void SetFlag( int flag ) { flags |= flag; }
|
||||
void RemoveFlag( int flag ) { flags &= ~flag; }
|
||||
void SetEntityNum( int num ) { entityNum = num; }
|
||||
void SetPrimitiveNum( int num ) { primitiveNum = num; }
|
||||
void SetContents( int contents ) { this->contents = contents; }
|
||||
int GetContents( void ) const { return contents; }
|
||||
const idBounds & GetBounds( void ) const { return bounds; }
|
||||
float GetVolume( void ) const;
|
||||
int GetNumSides( void ) const { return sides.Num(); }
|
||||
idBrushSide * GetSide( int i ) const { return sides[i]; }
|
||||
void SetPlaneSide( int s ) { planeSide = s; }
|
||||
void SavePlaneSide( void ) { savedPlaneSide = planeSide; }
|
||||
int GetSavedPlaneSide( void ) const { return savedPlaneSide; }
|
||||
bool FromSides( idList<idBrushSide *> &sideList );
|
||||
bool FromWinding( const idWinding &w, const idPlane &windingPlane );
|
||||
bool FromBounds( const idBounds &bounds );
|
||||
void Transform( const idVec3 &origin, const idMat3 &axis );
|
||||
idBrush * Copy( void ) const;
|
||||
bool TryMerge( const idBrush *brush, const idPlaneSet &planeList );
|
||||
// returns true if the brushes did intersect
|
||||
bool Subtract( const idBrush *b, idBrushList &list ) const;
|
||||
// split the brush into a front and back brush
|
||||
int Split( const idPlane &plane, int planeNum, idBrush **front, idBrush **back ) const;
|
||||
// expand the brush for an axial bounding box
|
||||
void ExpandForAxialBox( const idBounds &bounds );
|
||||
// next brush in list
|
||||
idBrush * Next( void ) const { return next; }
|
||||
|
||||
private:
|
||||
mutable idBrush * next; // next brush in list
|
||||
int entityNum; // entity number in editor
|
||||
int primitiveNum; // primitive number in editor
|
||||
int flags; // brush flags
|
||||
bool windingsValid; // set when side windings are valid
|
||||
int contents; // contents of brush
|
||||
int planeSide; // side of a plane this brush is on
|
||||
int savedPlaneSide; // saved plane side
|
||||
idBounds bounds; // brush bounds
|
||||
idList<idBrushSide *> sides; // list with sides
|
||||
|
||||
private:
|
||||
bool CreateWindings( void );
|
||||
void BoundBrush( const idBrush *original = NULL );
|
||||
void AddBevelsForAxialBox( void );
|
||||
bool RemoveSidesWithoutWinding( void );
|
||||
};
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idBrushList
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
class idBrushList {
|
||||
public:
|
||||
idBrushList( void );
|
||||
~idBrushList( void );
|
||||
|
||||
int Num( void ) const { return numBrushes; }
|
||||
int NumSides( void ) const { return numBrushSides; }
|
||||
idBrush * Head( void ) const { return head; }
|
||||
idBrush * Tail( void ) const { return tail; }
|
||||
void Clear( void ) { head = tail = NULL; numBrushes = 0; }
|
||||
bool IsEmpty( void ) const { return (numBrushes == 0); }
|
||||
idBounds GetBounds( void ) const;
|
||||
// add brush to the tail of the list
|
||||
void AddToTail( idBrush *brush );
|
||||
// add list to the tail of the list
|
||||
void AddToTail( idBrushList &list );
|
||||
// add brush to the front of the list
|
||||
void AddToFront( idBrush *brush );
|
||||
// add list to the front of the list
|
||||
void AddToFront( idBrushList &list );
|
||||
// remove the brush from the list
|
||||
void Remove( idBrush *brush );
|
||||
// remove the brush from the list and delete the brush
|
||||
void Delete( idBrush *brush);
|
||||
// returns a copy of the brush list
|
||||
idBrushList * Copy( void ) const;
|
||||
// delete all brushes in the list
|
||||
void Free( void );
|
||||
// split the brushes in the list into two lists
|
||||
void Split( const idPlane &plane, int planeNum, idBrushList &frontList, idBrushList &backList, bool useBrushSavedPlaneSide = false );
|
||||
// chop away all brush overlap
|
||||
void Chop( bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ) );
|
||||
// merge brushes
|
||||
void Merge( bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) );
|
||||
// set the given flag on all brush sides facing the plane
|
||||
void SetFlagOnFacingBrushSides( const idPlane &plane, int flag );
|
||||
// get a list with planes for all brushes in the list
|
||||
void CreatePlaneList( idPlaneSet &planeList ) const;
|
||||
// write a brush map with the brushes in the list
|
||||
void WriteBrushMap( const idStr &fileName, const idStr &ext ) const;
|
||||
|
||||
private:
|
||||
idBrush * head;
|
||||
idBrush * tail;
|
||||
int numBrushes;
|
||||
int numBrushSides;
|
||||
};
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idBrushMap
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
class idBrushMap {
|
||||
|
||||
public:
|
||||
idBrushMap( const idStr &fileName, const idStr &ext );
|
||||
~idBrushMap( void );
|
||||
void SetTexture( const idStr &textureName ) { texture = textureName; }
|
||||
void WriteBrush( const idBrush *brush );
|
||||
void WriteBrushList( const idBrushList &brushList );
|
||||
|
||||
private:
|
||||
idFile * fp;
|
||||
idStr texture;
|
||||
int brushCount;
|
||||
};
|
||||
|
||||
#endif /* !__BRUSH_H__ */
|
||||
2151
neo/tools/compilers/aas/BrushBSP.cpp
Normal file
2151
neo/tools/compilers/aas/BrushBSP.cpp
Normal file
File diff suppressed because it is too large
Load Diff
233
neo/tools/compilers/aas/BrushBSP.h
Normal file
233
neo/tools/compilers/aas/BrushBSP.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __BRUSHBSP_H__
|
||||
#define __BRUSHBSP_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
BrushBSP
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
class idBrushBSP;
|
||||
class idBrushBSPNode;
|
||||
class idBrushBSPPortal;
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idBrushBSPPortal
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
class idBrushBSPPortal {
|
||||
|
||||
friend class idBrushBSP;
|
||||
friend class idBrushBSPNode;
|
||||
|
||||
public:
|
||||
idBrushBSPPortal( void );
|
||||
~idBrushBSPPortal( void );
|
||||
void AddToNodes( idBrushBSPNode *front, idBrushBSPNode *back );
|
||||
void RemoveFromNode( idBrushBSPNode *l );
|
||||
void Flip( void );
|
||||
int Split( const idPlane &splitPlane, idBrushBSPPortal **front, idBrushBSPPortal **back );
|
||||
idWinding * GetWinding( void ) const { return winding; }
|
||||
const idPlane & GetPlane( void ) const { return plane; }
|
||||
void SetFaceNum( int num ) { faceNum = num; }
|
||||
int GetFaceNum( void ) const { return faceNum; }
|
||||
int GetFlags( void ) const { return flags; }
|
||||
void SetFlag( int flag ) { flags |= flag; }
|
||||
void RemoveFlag( int flag ) { flags &= ~flag; }
|
||||
idBrushBSPPortal * Next( int side ) const { return next[side]; }
|
||||
idBrushBSPNode * GetNode( int side ) const { return nodes[side]; }
|
||||
|
||||
private:
|
||||
idPlane plane; // portal plane
|
||||
int planeNum; // number of plane this portal is on
|
||||
idWinding * winding; // portal winding
|
||||
idBrushBSPNode * nodes[2]; // nodes this portal seperates
|
||||
idBrushBSPPortal * next[2]; // next portal in list for both nodes
|
||||
int flags; // portal flags
|
||||
int faceNum; // number of the face created for this portal
|
||||
};
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idBrushBSPNode
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
#define NODE_VISITED BIT(30)
|
||||
#define NODE_DONE BIT(31)
|
||||
|
||||
class idBrushBSPNode {
|
||||
|
||||
friend class idBrushBSP;
|
||||
friend class idBrushBSPPortal;
|
||||
|
||||
public:
|
||||
idBrushBSPNode( void );
|
||||
~idBrushBSPNode( void );
|
||||
void SetContentsFromBrushes( void );
|
||||
idBounds GetPortalBounds( void );
|
||||
idBrushBSPNode * GetChild( int index ) const { return children[index]; }
|
||||
idBrushBSPNode * GetParent( void ) const { return parent; }
|
||||
void SetContents( int contents ) { this->contents = contents; }
|
||||
int GetContents( void ) const { return contents; }
|
||||
const idPlane & GetPlane( void ) const { return plane; }
|
||||
idBrushBSPPortal * GetPortals( void ) const { return portals; }
|
||||
void SetAreaNum( int num ) { areaNum = num; }
|
||||
int GetAreaNum( void ) const { return areaNum; }
|
||||
int GetFlags( void ) const { return flags; }
|
||||
void SetFlag( int flag ) { flags |= flag; }
|
||||
void RemoveFlag( int flag ) { flags &= ~flag; }
|
||||
bool TestLeafNode( void );
|
||||
// remove the flag from nodes found by flooding through portals to nodes with the flag set
|
||||
void RemoveFlagFlood( int flag );
|
||||
// recurse down the tree and remove the flag from all visited nodes
|
||||
void RemoveFlagRecurse( int flag );
|
||||
// first recurse down the tree and flood from there
|
||||
void RemoveFlagRecurseFlood( int flag );
|
||||
// returns side of the plane the node is on
|
||||
int PlaneSide( const idPlane &plane, float epsilon = ON_EPSILON ) const;
|
||||
// split the leaf node with a plane
|
||||
bool Split( const idPlane &splitPlane, int splitPlaneNum );
|
||||
|
||||
|
||||
private:
|
||||
idPlane plane; // split plane if this is not a leaf node
|
||||
idBrush * volume; // node volume
|
||||
int contents; // node contents
|
||||
idBrushList brushList; // list with brushes for this node
|
||||
idBrushBSPNode * parent; // parent of this node
|
||||
idBrushBSPNode * children[2]; // both are NULL if this is a leaf node
|
||||
idBrushBSPPortal * portals; // portals of this node
|
||||
int flags; // node flags
|
||||
int areaNum; // number of the area created for this node
|
||||
int occupied; // true when portal is occupied
|
||||
};
|
||||
|
||||
|
||||
//===============================================================
|
||||
//
|
||||
// idBrushBSP
|
||||
//
|
||||
//===============================================================
|
||||
|
||||
class idBrushBSP {
|
||||
|
||||
public:
|
||||
idBrushBSP( void );
|
||||
~idBrushBSP( void );
|
||||
// build a bsp tree from a set of brushes
|
||||
void Build( idBrushList brushList, int skipContents,
|
||||
bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ),
|
||||
bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) );
|
||||
// remove splits in subspaces with the given contents
|
||||
void PruneTree( int contents );
|
||||
// portalize the bsp tree
|
||||
void Portalize( void );
|
||||
// remove subspaces outside the map not reachable by entities
|
||||
bool RemoveOutside( const idMapFile *mapFile, int contents, const idStrList &classNames );
|
||||
// write file with a trace going through a leak
|
||||
void LeakFile( const idStr &fileName );
|
||||
// try to merge portals
|
||||
void MergePortals( int skipContents );
|
||||
// try to merge the two leaf nodes at either side of the portal
|
||||
bool TryMergeLeafNodes( idBrushBSPPortal *portal, int side );
|
||||
void PruneMergedTree_r( idBrushBSPNode *node );
|
||||
// melt portal windings
|
||||
void MeltPortals( int skipContents );
|
||||
// write a map file with a brush for every leaf node that has the given contents
|
||||
void WriteBrushMap( const idStr &fileName, const idStr &ext, int contents );
|
||||
// bounds for the whole tree
|
||||
const idBounds & GetTreeBounds( void ) const { return treeBounds; }
|
||||
// root node of the tree
|
||||
idBrushBSPNode * GetRootNode( void ) const { return root; }
|
||||
|
||||
private:
|
||||
idBrushBSPNode * root;
|
||||
idBrushBSPNode * outside;
|
||||
idBounds treeBounds;
|
||||
idPlaneSet portalPlanes;
|
||||
int numGridCells;
|
||||
int numSplits;
|
||||
int numGridCellSplits;
|
||||
int numPrunedSplits;
|
||||
int numPortals;
|
||||
int solidLeafNodes;
|
||||
int outsideLeafNodes;
|
||||
int insideLeafNodes;
|
||||
int numMergedPortals;
|
||||
int numInsertedPoints;
|
||||
idVec3 leakOrigin;
|
||||
int brushMapContents;
|
||||
idBrushMap * brushMap;
|
||||
|
||||
bool (*BrushChopAllowed)( idBrush *b1, idBrush *b2 );
|
||||
bool (*BrushMergeAllowed)( idBrush *b1, idBrush *b2 );
|
||||
|
||||
private:
|
||||
void RemoveMultipleLeafNodeReferences_r( idBrushBSPNode *node );
|
||||
void Free_r( idBrushBSPNode *node );
|
||||
void IncreaseNumSplits( void );
|
||||
bool IsValidSplitter( const idBrushSide *side );
|
||||
int BrushSplitterStats( const idBrush *brush, int planeNum, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &stats );
|
||||
int FindSplitter( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &bestStats );
|
||||
void SetSplitterUsed( idBrushBSPNode *node, int planeNum );
|
||||
idBrushBSPNode * BuildBrushBSP_r( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, int skipContents );
|
||||
idBrushBSPNode * ProcessGridCell( idBrushBSPNode *node, int skipContents );
|
||||
void BuildGrid_r( idList<idBrushBSPNode *> &gridCells, idBrushBSPNode *node );
|
||||
void PruneTree_r( idBrushBSPNode *node, int contents );
|
||||
void MakeOutsidePortals( void );
|
||||
idWinding * BaseWindingForNode( idBrushBSPNode *node );
|
||||
void MakeNodePortal( idBrushBSPNode *node );
|
||||
void SplitNodePortals( idBrushBSPNode *node );
|
||||
void MakeTreePortals_r( idBrushBSPNode *node );
|
||||
void FloodThroughPortals_r( idBrushBSPNode *node, int contents, int depth );
|
||||
bool FloodFromOrigin( const idVec3 &origin, int contents );
|
||||
bool FloodFromEntities( const idMapFile *mapFile, int contents, const idStrList &classNames );
|
||||
void RemoveOutside_r( idBrushBSPNode *node, int contents );
|
||||
void SetPortalPlanes_r( idBrushBSPNode *node, idPlaneSet &planeList );
|
||||
void SetPortalPlanes( void );
|
||||
void MergePortals_r( idBrushBSPNode *node, int skipContents );
|
||||
void MergeLeafNodePortals( idBrushBSPNode *node, int skipContents );
|
||||
void UpdateTreeAfterMerge_r( idBrushBSPNode *node, const idBounds &bounds, idBrushBSPNode *oldNode, idBrushBSPNode *newNode );
|
||||
void RemoveLeafNodeColinearPoints( idBrushBSPNode *node );
|
||||
void RemoveColinearPoints_r( idBrushBSPNode *node, int skipContents );
|
||||
void MeltFlood_r( idBrushBSPNode *node, int skipContents, idBounds &bounds, idVectorSet<idVec3,3> &vertexList );
|
||||
void MeltLeafNodePortals( idBrushBSPNode *node, int skipContents, idVectorSet<idVec3,3> &vertexList );
|
||||
void MeltPortals_r( idBrushBSPNode *node, int skipContents, idVectorSet<idVec3,3> &vertexList );
|
||||
};
|
||||
|
||||
#endif /* !__BRUSHBSP_H__ */
|
||||
55
neo/tools/compilers/compiler_public.h
Normal file
55
neo/tools/compilers/compiler_public.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __COMPILER_PUBLIC_H__
|
||||
#define __COMPILER_PUBLIC_H__
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Compilers for map, model, video etc. processing.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
// map processing (also see SuperOptimizeOccluders in tr_local.h)
|
||||
void Dmap_f( const idCmdArgs &args );
|
||||
|
||||
// bump map generation
|
||||
void RenderBump_f( const idCmdArgs &args );
|
||||
void RenderBumpFlat_f( const idCmdArgs &args );
|
||||
|
||||
// AAS file compiler
|
||||
void RunAAS_f( const idCmdArgs &args );
|
||||
void RunAASDir_f( const idCmdArgs &args );
|
||||
void RunReach_f( const idCmdArgs &args );
|
||||
|
||||
// video file encoding
|
||||
void RoQFileEncode_f( const idCmdArgs &args );
|
||||
|
||||
#endif /* !__COMPILER_PUBLIC_H__ */
|
||||
404
neo/tools/compilers/dmap/dmap.cpp
Normal file
404
neo/tools/compilers/dmap/dmap.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
dmapGlobals_t dmapGlobals;
|
||||
|
||||
/*
|
||||
============
|
||||
ProcessModel
|
||||
============
|
||||
*/
|
||||
bool ProcessModel( uEntity_t *e, bool floodFill ) {
|
||||
bspface_t *faces;
|
||||
|
||||
// build a bsp tree using all of the sides
|
||||
// of all of the structural brushes
|
||||
faces = MakeStructuralBspFaceList ( e->primitives );
|
||||
e->tree = FaceBSP( faces );
|
||||
|
||||
// create portals at every leaf intersection
|
||||
// to allow flood filling
|
||||
MakeTreePortals( e->tree );
|
||||
|
||||
// classify the leafs as opaque or areaportal
|
||||
FilterBrushesIntoTree( e );
|
||||
|
||||
// see if the bsp is completely enclosed
|
||||
if ( floodFill && !dmapGlobals.noFlood ) {
|
||||
if ( FloodEntities( e->tree ) ) {
|
||||
// set the outside leafs to opaque
|
||||
FillOutside( e );
|
||||
} else {
|
||||
common->Printf ( "**********************\n" );
|
||||
common->Warning( "******* leaked *******" );
|
||||
common->Printf ( "**********************\n" );
|
||||
LeakFile( e->tree );
|
||||
// bail out here. If someone really wants to
|
||||
// process a map that leaks, they should use
|
||||
// -noFlood
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// get minimum convex hulls for each visible side
|
||||
// this must be done before creating area portals,
|
||||
// because the visible hull is used as the portal
|
||||
ClipSidesByTree( e );
|
||||
|
||||
// determine areas before clipping tris into the
|
||||
// tree, so tris will never cross area boundaries
|
||||
FloodAreas( e );
|
||||
|
||||
// we now have a BSP tree with solid and non-solid leafs marked with areas
|
||||
// all primitives will now be clipped into this, throwing away
|
||||
// fragments in the solid areas
|
||||
PutPrimitivesInAreas( e );
|
||||
|
||||
// now build shadow volumes for the lights and split
|
||||
// the optimize lists by the light beam trees
|
||||
// so there won't be unneeded overdraw in the static
|
||||
// case
|
||||
Prelight( e );
|
||||
|
||||
// optimizing is a superset of fixing tjunctions
|
||||
if ( !dmapGlobals.noOptimize ) {
|
||||
OptimizeEntity( e );
|
||||
} else if ( !dmapGlobals.noTJunc ) {
|
||||
FixEntityTjunctions( e );
|
||||
}
|
||||
|
||||
// now fix t junctions across areas
|
||||
FixGlobalTjunctions( e );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
ProcessModels
|
||||
============
|
||||
*/
|
||||
bool ProcessModels( void ) {
|
||||
bool oldVerbose;
|
||||
uEntity_t *entity;
|
||||
|
||||
oldVerbose = dmapGlobals.verbose;
|
||||
|
||||
for ( dmapGlobals.entityNum = 0 ; dmapGlobals.entityNum < dmapGlobals.num_entities ; dmapGlobals.entityNum++ ) {
|
||||
|
||||
entity = &dmapGlobals.uEntities[dmapGlobals.entityNum];
|
||||
if ( !entity->primitives ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
common->Printf( "############### entity %i ###############\n", dmapGlobals.entityNum );
|
||||
|
||||
// if we leaked, stop without any more processing
|
||||
if ( !ProcessModel( entity, (bool)(dmapGlobals.entityNum == 0 ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we usually don't want to see output for submodels unless
|
||||
// something strange is going on
|
||||
if ( !dmapGlobals.verboseentities ) {
|
||||
dmapGlobals.verbose = false;
|
||||
}
|
||||
}
|
||||
|
||||
dmapGlobals.verbose = oldVerbose;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
DmapHelp
|
||||
============
|
||||
*/
|
||||
void DmapHelp( void ) {
|
||||
common->Printf(
|
||||
|
||||
"Usage: dmap [options] mapfile\n"
|
||||
"Options:\n"
|
||||
"noCurves = don't process curves\n"
|
||||
"noCM = don't create collision map\n"
|
||||
"noAAS = don't create AAS files\n"
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
ResetDmapGlobals
|
||||
============
|
||||
*/
|
||||
void ResetDmapGlobals( void ) {
|
||||
dmapGlobals.mapFileBase[0] = '\0';
|
||||
dmapGlobals.dmapFile = NULL;
|
||||
dmapGlobals.mapPlanes.Clear();
|
||||
dmapGlobals.num_entities = 0;
|
||||
dmapGlobals.uEntities = NULL;
|
||||
dmapGlobals.entityNum = 0;
|
||||
dmapGlobals.mapLights.Clear();
|
||||
dmapGlobals.verbose = false;
|
||||
dmapGlobals.glview = false;
|
||||
dmapGlobals.noOptimize = false;
|
||||
dmapGlobals.verboseentities = false;
|
||||
dmapGlobals.noCurves = false;
|
||||
dmapGlobals.fullCarve = false;
|
||||
dmapGlobals.noModelBrushes = false;
|
||||
dmapGlobals.noTJunc = false;
|
||||
dmapGlobals.nomerge = false;
|
||||
dmapGlobals.noFlood = false;
|
||||
dmapGlobals.noClipSides = false;
|
||||
dmapGlobals.noLightCarve = false;
|
||||
dmapGlobals.noShadow = false;
|
||||
dmapGlobals.shadowOptLevel = SO_NONE;
|
||||
dmapGlobals.drawBounds.Clear();
|
||||
dmapGlobals.drawflag = false;
|
||||
dmapGlobals.totalShadowTriangles = 0;
|
||||
dmapGlobals.totalShadowVerts = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Dmap
|
||||
============
|
||||
*/
|
||||
void Dmap( const idCmdArgs &args ) {
|
||||
int i;
|
||||
int start, end;
|
||||
char path[1024];
|
||||
idStr passedName;
|
||||
bool leaked = false;
|
||||
bool noCM = false;
|
||||
bool noAAS = false;
|
||||
|
||||
ResetDmapGlobals();
|
||||
|
||||
if ( args.Argc() < 2 ) {
|
||||
DmapHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
common->Printf("---- dmap ----\n");
|
||||
|
||||
dmapGlobals.fullCarve = true;
|
||||
dmapGlobals.shadowOptLevel = SO_MERGE_SURFACES; // create shadows by merging all surfaces, but no super optimization
|
||||
// dmapGlobals.shadowOptLevel = SO_CLIP_OCCLUDERS; // remove occluders that are completely covered
|
||||
// dmapGlobals.shadowOptLevel = SO_SIL_OPTIMIZE;
|
||||
// dmapGlobals.shadowOptLevel = SO_CULL_OCCLUDED;
|
||||
|
||||
dmapGlobals.noLightCarve = true;
|
||||
|
||||
for ( i = 1 ; i < args.Argc() ; i++ ) {
|
||||
const char *s;
|
||||
|
||||
s = args.Argv(i);
|
||||
if ( s[0] == '-' ) {
|
||||
s++;
|
||||
if ( s[0] == '\0' ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !idStr::Icmp( s,"glview" ) ) {
|
||||
dmapGlobals.glview = true;
|
||||
} else if ( !idStr::Icmp( s, "v" ) ) {
|
||||
common->Printf( "verbose = true\n" );
|
||||
dmapGlobals.verbose = true;
|
||||
} else if ( !idStr::Icmp( s, "draw" ) ) {
|
||||
common->Printf( "drawflag = true\n" );
|
||||
dmapGlobals.drawflag = true;
|
||||
} else if ( !idStr::Icmp( s, "noFlood" ) ) {
|
||||
common->Printf( "noFlood = true\n" );
|
||||
dmapGlobals.noFlood = true;
|
||||
} else if ( !idStr::Icmp( s, "noLightCarve" ) ) {
|
||||
common->Printf( "noLightCarve = true\n" );
|
||||
dmapGlobals.noLightCarve = true;
|
||||
} else if ( !idStr::Icmp( s, "lightCarve" ) ) {
|
||||
common->Printf( "noLightCarve = false\n" );
|
||||
dmapGlobals.noLightCarve = false;
|
||||
} else if ( !idStr::Icmp( s, "noOpt" ) ) {
|
||||
common->Printf( "noOptimize = true\n" );
|
||||
dmapGlobals.noOptimize = true;
|
||||
} else if ( !idStr::Icmp( s, "verboseentities" ) ) {
|
||||
common->Printf( "verboseentities = true\n");
|
||||
dmapGlobals.verboseentities = true;
|
||||
} else if ( !idStr::Icmp( s, "noCurves" ) ) {
|
||||
common->Printf( "noCurves = true\n");
|
||||
dmapGlobals.noCurves = true;
|
||||
} else if ( !idStr::Icmp( s, "noModels" ) ) {
|
||||
common->Printf( "noModels = true\n" );
|
||||
dmapGlobals.noModelBrushes = true;
|
||||
} else if ( !idStr::Icmp( s, "noClipSides" ) ) {
|
||||
common->Printf( "noClipSides = true\n" );
|
||||
dmapGlobals.noClipSides = true;
|
||||
} else if ( !idStr::Icmp( s, "noCarve" ) ) {
|
||||
common->Printf( "noCarve = true\n" );
|
||||
dmapGlobals.fullCarve = false;
|
||||
} else if ( !idStr::Icmp( s, "shadowOpt" ) ) {
|
||||
dmapGlobals.shadowOptLevel = (shadowOptLevel_t)atoi( args.Argv( i+1 ) );
|
||||
common->Printf( "shadowOpt = %i\n",dmapGlobals.shadowOptLevel );
|
||||
i += 1;
|
||||
} else if ( !idStr::Icmp( s, "noTjunc" ) ) {
|
||||
// triangle optimization won't work properly without tjunction fixing
|
||||
common->Printf ("noTJunc = true\n" );
|
||||
dmapGlobals.noTJunc = true;
|
||||
dmapGlobals.noOptimize = true;
|
||||
common->Printf ("forcing noOptimize = true\n" );
|
||||
} else if ( !idStr::Icmp( s, "noCM" ) ) {
|
||||
noCM = true;
|
||||
common->Printf( "noCM = true\n" );
|
||||
} else if ( !idStr::Icmp( s, "noAAS" ) ) {
|
||||
noAAS = true;
|
||||
common->Printf( "noAAS = true\n" );
|
||||
} else if ( !idStr::Icmp( s, "editorOutput" ) ) {
|
||||
#ifdef _WIN32
|
||||
com_outputMsg = true;
|
||||
#endif
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i >= args.Argc() ) {
|
||||
common->Error( "usage: dmap [options] mapfile" );
|
||||
}
|
||||
|
||||
passedName = args.Argv(i); // may have an extension
|
||||
passedName.BackSlashesToSlashes();
|
||||
if ( passedName.Icmpn( "maps/", 4 ) != 0 ) {
|
||||
passedName = "maps/" + passedName;
|
||||
}
|
||||
|
||||
idStr stripped = passedName;
|
||||
stripped.StripFileExtension();
|
||||
idStr::Copynz( dmapGlobals.mapFileBase, stripped, sizeof(dmapGlobals.mapFileBase) );
|
||||
|
||||
bool region = false;
|
||||
// if this isn't a regioned map, delete the last saved region map
|
||||
if ( passedName.Right( 4 ) != ".reg" ) {
|
||||
sprintf( path, "%s.reg", dmapGlobals.mapFileBase );
|
||||
fileSystem->RemoveFile( path );
|
||||
} else {
|
||||
region = true;
|
||||
}
|
||||
|
||||
|
||||
passedName = stripped;
|
||||
|
||||
// delete any old line leak files
|
||||
sprintf( path, "%s.lin", dmapGlobals.mapFileBase );
|
||||
fileSystem->RemoveFile( path );
|
||||
|
||||
|
||||
//
|
||||
// start from scratch
|
||||
//
|
||||
start = Sys_Milliseconds();
|
||||
|
||||
if ( !LoadDMapFile( passedName ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ProcessModels() ) {
|
||||
WriteOutputFile();
|
||||
} else {
|
||||
leaked = true;
|
||||
}
|
||||
|
||||
FreeDMapFile();
|
||||
|
||||
common->Printf( "%i total shadow triangles\n", dmapGlobals.totalShadowTriangles );
|
||||
common->Printf( "%i total shadow verts\n", dmapGlobals.totalShadowVerts );
|
||||
|
||||
end = Sys_Milliseconds();
|
||||
common->Printf( "-----------------------\n" );
|
||||
common->Printf( "%5.0f seconds for dmap\n", ( end - start ) * 0.001f );
|
||||
|
||||
if ( !leaked ) {
|
||||
|
||||
if ( !noCM ) {
|
||||
|
||||
// make sure the collision model manager is not used by the game
|
||||
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
|
||||
|
||||
// create the collision map
|
||||
start = Sys_Milliseconds();
|
||||
|
||||
collisionModelManager->LoadMap( dmapGlobals.dmapFile );
|
||||
collisionModelManager->FreeMap();
|
||||
|
||||
end = Sys_Milliseconds();
|
||||
common->Printf( "-------------------------------------\n" );
|
||||
common->Printf( "%5.0f seconds to create collision map\n", ( end - start ) * 0.001f );
|
||||
}
|
||||
|
||||
if ( !noAAS && !region ) {
|
||||
// create AAS files
|
||||
RunAAS_f( args );
|
||||
}
|
||||
}
|
||||
|
||||
// free the common .map representation
|
||||
delete dmapGlobals.dmapFile;
|
||||
|
||||
// clear the map plane list
|
||||
dmapGlobals.mapPlanes.Clear();
|
||||
|
||||
#ifdef _WIN32
|
||||
if ( com_outputMsg && com_hwndMsg != NULL ) {
|
||||
unsigned int msg = ::RegisterWindowMessage( DMAP_DONE );
|
||||
::PostMessage( com_hwndMsg, msg, 0, 0 );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Dmap_f
|
||||
============
|
||||
*/
|
||||
void Dmap_f( const idCmdArgs &args ) {
|
||||
|
||||
common->ClearWarnings( "running dmap" );
|
||||
|
||||
// refresh the screen each time we print so it doesn't look
|
||||
// like it is hung
|
||||
common->SetRefreshOnPrint( true );
|
||||
Dmap( args );
|
||||
common->SetRefreshOnPrint( false );
|
||||
|
||||
common->PrintWarnings();
|
||||
}
|
||||
485
neo/tools/compilers/dmap/dmap.h
Normal file
485
neo/tools/compilers/dmap/dmap.h
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../renderer/tr_local.h"
|
||||
|
||||
|
||||
typedef struct primitive_s {
|
||||
struct primitive_s *next;
|
||||
|
||||
// only one of these will be non-NULL
|
||||
struct bspbrush_s * brush;
|
||||
struct mapTri_s * tris;
|
||||
} primitive_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct optimizeGroup_s *groups;
|
||||
// we might want to add other fields later
|
||||
} uArea_t;
|
||||
|
||||
typedef struct {
|
||||
idMapEntity * mapEntity; // points into mapFile_t data
|
||||
|
||||
idVec3 origin;
|
||||
primitive_t * primitives;
|
||||
struct tree_s * tree;
|
||||
|
||||
int numAreas;
|
||||
uArea_t * areas;
|
||||
} uEntity_t;
|
||||
|
||||
|
||||
// chains of mapTri_t are the general unit of processing
|
||||
typedef struct mapTri_s {
|
||||
struct mapTri_s * next;
|
||||
|
||||
const idMaterial * material;
|
||||
void * mergeGroup; // we want to avoid merging triangles
|
||||
// from different fixed groups, like guiSurfs and mirrors
|
||||
int planeNum; // not set universally, just in some areas
|
||||
|
||||
idDrawVert v[3];
|
||||
const struct hashVert_s *hashVert[3];
|
||||
struct optVertex_s *optVert[3];
|
||||
} mapTri_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int width, height;
|
||||
idDrawVert * verts;
|
||||
} mesh_t;
|
||||
|
||||
|
||||
#define MAX_PATCH_SIZE 32
|
||||
|
||||
#define PLANENUM_LEAF -1
|
||||
|
||||
typedef struct parseMesh_s {
|
||||
struct parseMesh_s *next;
|
||||
mesh_t mesh;
|
||||
const idMaterial * material;
|
||||
} parseMesh_t;
|
||||
|
||||
typedef struct bspface_s {
|
||||
struct bspface_s * next;
|
||||
int planenum;
|
||||
bool portal; // all portals will be selected before
|
||||
// any non-portals
|
||||
bool checked; // used by SelectSplitPlaneNum()
|
||||
idWinding * w;
|
||||
} bspface_t;
|
||||
|
||||
typedef struct {
|
||||
idVec4 v[2]; // the offset value will always be in the 0.0 to 1.0 range
|
||||
} textureVectors_t;
|
||||
|
||||
typedef struct side_s {
|
||||
int planenum;
|
||||
|
||||
const idMaterial * material;
|
||||
textureVectors_t texVec;
|
||||
|
||||
idWinding * winding; // only clipped to the other sides of the brush
|
||||
idWinding * visibleHull; // also clipped to the solid parts of the world
|
||||
} side_t;
|
||||
|
||||
|
||||
typedef struct bspbrush_s {
|
||||
struct bspbrush_s * next;
|
||||
struct bspbrush_s * original; // chopped up brushes will reference the originals
|
||||
|
||||
int entitynum; // editor numbering for messages
|
||||
int brushnum; // editor numbering for messages
|
||||
|
||||
const idMaterial * contentShader; // one face's shader will determine the volume attributes
|
||||
|
||||
int contents;
|
||||
bool opaque;
|
||||
int outputNumber; // set when the brush is written to the file list
|
||||
|
||||
idBounds bounds;
|
||||
int numsides;
|
||||
side_t sides[6]; // variably sized
|
||||
} uBrush_t;
|
||||
|
||||
|
||||
typedef struct drawSurfRef_s {
|
||||
struct drawSurfRef_s * nextRef;
|
||||
int outputNumber;
|
||||
} drawSurfRef_t;
|
||||
|
||||
|
||||
typedef struct node_s {
|
||||
// both leafs and nodes
|
||||
int planenum; // -1 = leaf node
|
||||
struct node_s * parent;
|
||||
idBounds bounds; // valid after portalization
|
||||
|
||||
// nodes only
|
||||
side_t * side; // the side that created the node
|
||||
struct node_s * children[2];
|
||||
int nodeNumber; // set after pruning
|
||||
|
||||
// leafs only
|
||||
bool opaque; // view can never be inside
|
||||
|
||||
uBrush_t * brushlist; // fragments of all brushes in this leaf
|
||||
// needed for FindSideForPortal
|
||||
|
||||
int area; // determined by flood filling up to areaportals
|
||||
int occupied; // 1 or greater can reach entity
|
||||
uEntity_t * occupant; // for leak file testing
|
||||
|
||||
struct uPortal_s * portals; // also on nodes during construction
|
||||
} node_t;
|
||||
|
||||
|
||||
typedef struct uPortal_s {
|
||||
idPlane plane;
|
||||
node_t *onnode; // NULL = outside box
|
||||
node_t *nodes[2]; // [0] = front side of plane
|
||||
struct uPortal_s *next[2];
|
||||
idWinding *winding;
|
||||
} uPortal_t;
|
||||
|
||||
// a tree_t is created by FaceBSP()
|
||||
typedef struct tree_s {
|
||||
node_t *headnode;
|
||||
node_t outside_node;
|
||||
idBounds bounds;
|
||||
} tree_t;
|
||||
|
||||
#define MAX_QPATH 256 // max length of a game pathname
|
||||
|
||||
typedef struct {
|
||||
idRenderLightLocal def;
|
||||
char name[MAX_QPATH]; // for naming the shadow volume surface and interactions
|
||||
srfTriangles_t *shadowTris;
|
||||
} mapLight_t;
|
||||
|
||||
#define MAX_GROUP_LIGHTS 16
|
||||
|
||||
typedef struct optimizeGroup_s {
|
||||
struct optimizeGroup_s *nextGroup;
|
||||
|
||||
idBounds bounds; // set in CarveGroupsByLight
|
||||
|
||||
// all of these must match to add a triangle to the triList
|
||||
bool smoothed; // curves will never merge with brushes
|
||||
int planeNum;
|
||||
int areaNum;
|
||||
const idMaterial * material;
|
||||
int numGroupLights;
|
||||
mapLight_t * groupLights[MAX_GROUP_LIGHTS]; // lights effecting this list
|
||||
void * mergeGroup; // if this differs (guiSurfs, mirrors, etc), the
|
||||
// groups will not be combined into model surfaces
|
||||
// after optimization
|
||||
textureVectors_t texVec;
|
||||
|
||||
bool surfaceEmited;
|
||||
|
||||
mapTri_t * triList;
|
||||
mapTri_t * regeneratedTris; // after each island optimization
|
||||
idVec3 axis[2]; // orthogonal to the plane, so optimization can be 2D
|
||||
} optimizeGroup_t;
|
||||
|
||||
// all primitives from the map are added to optimzeGroups, creating new ones as needed
|
||||
// each optimizeGroup is then split into the map areas, creating groups in each area
|
||||
// each optimizeGroup is then divided by each light, creating more groups
|
||||
// the final list of groups is then tjunction fixed against all groups, then optimized internally
|
||||
// multiple optimizeGroups will be merged together into .proc surfaces, but no further optimization
|
||||
// is done on them
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// dmap.cpp
|
||||
|
||||
typedef enum {
|
||||
SO_NONE, // 0
|
||||
SO_MERGE_SURFACES, // 1
|
||||
SO_CULL_OCCLUDED, // 2
|
||||
SO_CLIP_OCCLUDERS, // 3
|
||||
SO_CLIP_SILS, // 4
|
||||
SO_SIL_OPTIMIZE // 5
|
||||
} shadowOptLevel_t;
|
||||
|
||||
typedef struct {
|
||||
// mapFileBase will contain the qpath without any extension: "maps/test_box"
|
||||
char mapFileBase[1024];
|
||||
|
||||
idMapFile *dmapFile;
|
||||
|
||||
idPlaneSet mapPlanes;
|
||||
|
||||
int num_entities;
|
||||
uEntity_t *uEntities;
|
||||
|
||||
int entityNum;
|
||||
|
||||
idList<mapLight_t*> mapLights;
|
||||
|
||||
bool verbose;
|
||||
|
||||
bool glview;
|
||||
bool noOptimize;
|
||||
bool verboseentities;
|
||||
bool noCurves;
|
||||
bool fullCarve;
|
||||
bool noModelBrushes;
|
||||
bool noTJunc;
|
||||
bool nomerge;
|
||||
bool noFlood;
|
||||
bool noClipSides; // don't cut sides by solid leafs, use the entire thing
|
||||
bool noLightCarve; // extra triangle subdivision by light frustums
|
||||
shadowOptLevel_t shadowOptLevel;
|
||||
bool noShadow; // don't create optimized shadow volumes
|
||||
|
||||
idBounds drawBounds;
|
||||
bool drawflag;
|
||||
|
||||
int totalShadowTriangles;
|
||||
int totalShadowVerts;
|
||||
} dmapGlobals_t;
|
||||
|
||||
extern dmapGlobals_t dmapGlobals;
|
||||
|
||||
int FindFloatPlane( const idPlane &plane, bool *fixedDegeneracies = NULL );
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// brush.cpp
|
||||
|
||||
#ifndef CLIP_EPSILON
|
||||
#define CLIP_EPSILON 0.1f
|
||||
#endif
|
||||
|
||||
#define PSIDE_FRONT 1
|
||||
#define PSIDE_BACK 2
|
||||
#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
|
||||
#define PSIDE_FACING 4
|
||||
|
||||
int CountBrushList (uBrush_t *brushes);
|
||||
uBrush_t *AllocBrush (int numsides);
|
||||
void FreeBrush (uBrush_t *brushes);
|
||||
void FreeBrushList (uBrush_t *brushes);
|
||||
uBrush_t *CopyBrush (uBrush_t *brush);
|
||||
void DrawBrushList (uBrush_t *brush);
|
||||
void PrintBrush (uBrush_t *brush);
|
||||
bool BoundBrush (uBrush_t *brush);
|
||||
bool CreateBrushWindings (uBrush_t *brush);
|
||||
uBrush_t *BrushFromBounds( const idBounds &bounds );
|
||||
float BrushVolume (uBrush_t *brush);
|
||||
void WriteBspBrushMap( const char *name, uBrush_t *list );
|
||||
|
||||
void FilterBrushesIntoTree( uEntity_t *e );
|
||||
|
||||
void SplitBrush( uBrush_t *brush, int planenum, uBrush_t **front, uBrush_t **back);
|
||||
node_t *AllocNode( void );
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// map.cpp
|
||||
|
||||
bool LoadDMapFile( const char *filename );
|
||||
void FreeOptimizeGroupList( optimizeGroup_t *groups );
|
||||
void FreeDMapFile( void );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// draw.cpp -- draw debug views either directly, or through glserv.exe
|
||||
|
||||
void Draw_ClearWindow( void );
|
||||
void DrawWinding( const idWinding *w );
|
||||
void DrawAuxWinding( const idWinding *w );
|
||||
|
||||
void DrawLine( idVec3 v1, idVec3 v2, int color );
|
||||
|
||||
void GLS_BeginScene( void );
|
||||
void GLS_Winding( const idWinding *w, int code );
|
||||
void GLS_Triangle( const mapTri_t *tri, int code );
|
||||
void GLS_EndScene( void );
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// portals.cpp
|
||||
|
||||
#define MAX_INTER_AREA_PORTALS 1024
|
||||
|
||||
typedef struct {
|
||||
int area0, area1;
|
||||
side_t *side;
|
||||
} interAreaPortal_t;
|
||||
|
||||
extern interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS];
|
||||
extern int numInterAreaPortals;
|
||||
|
||||
bool FloodEntities( tree_t *tree );
|
||||
void FillOutside( uEntity_t *e );
|
||||
void FloodAreas( uEntity_t *e );
|
||||
void MakeTreePortals( tree_t *tree );
|
||||
void FreePortal( uPortal_t *p );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// glfile.cpp -- write a debug file to be viewd with glview.exe
|
||||
|
||||
void OutputWinding( idWinding *w, idFile *glview );
|
||||
void WriteGLView( tree_t *tree, char *source );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// leakfile.cpp
|
||||
|
||||
void LeakFile( tree_t *tree );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// facebsp.cpp
|
||||
|
||||
tree_t *AllocTree( void );
|
||||
|
||||
void FreeTree( tree_t *tree );
|
||||
|
||||
void FreeTree_r( node_t *node );
|
||||
void FreeTreePortals_r( node_t *node );
|
||||
|
||||
|
||||
bspface_t *MakeStructuralBspFaceList( primitive_t *list );
|
||||
bspface_t *MakeVisibleBspFaceList( primitive_t *list );
|
||||
tree_t *FaceBSP( bspface_t *list );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// surface.cpp
|
||||
|
||||
mapTri_t *CullTrisInOpaqueLeafs( mapTri_t *triList, tree_t *tree );
|
||||
void ClipSidesByTree( uEntity_t *e );
|
||||
void SplitTrisToSurfaces( mapTri_t *triList, tree_t *tree );
|
||||
void PutPrimitivesInAreas( uEntity_t *e );
|
||||
void Prelight( uEntity_t *e );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// tritjunction.cpp
|
||||
|
||||
struct hashVert_s *GetHashVert( idVec3 &v );
|
||||
void HashTriangles( optimizeGroup_t *groupList );
|
||||
void FreeTJunctionHash( void );
|
||||
int CountGroupListTris( const optimizeGroup_t *groupList );
|
||||
void FixEntityTjunctions( uEntity_t *e );
|
||||
void FixAreaGroupsTjunctions( optimizeGroup_t *groupList );
|
||||
void FixGlobalTjunctions( uEntity_t *e );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// optimize.cpp -- trianlge mesh reoptimization
|
||||
|
||||
// the shadow volume optimizer call internal optimizer routines, normal triangles
|
||||
// will just be done by OptimizeEntity()
|
||||
|
||||
|
||||
typedef struct optVertex_s {
|
||||
idDrawVert v;
|
||||
idVec3 pv; // projected against planar axis, third value is 0
|
||||
struct optEdge_s *edges;
|
||||
struct optVertex_s *islandLink;
|
||||
bool addedToIsland;
|
||||
bool emited; // when regenerating triangles
|
||||
} optVertex_t;
|
||||
|
||||
typedef struct optEdge_s {
|
||||
optVertex_t *v1, *v2;
|
||||
struct optEdge_s *islandLink;
|
||||
bool addedToIsland;
|
||||
bool created; // not one of the original edges
|
||||
bool combined; // combined from two or more colinear edges
|
||||
struct optTri_s *frontTri, *backTri;
|
||||
struct optEdge_s *v1link, *v2link;
|
||||
} optEdge_t;
|
||||
|
||||
typedef struct optTri_s {
|
||||
struct optTri_s *next;
|
||||
idVec3 midpoint;
|
||||
optVertex_t *v[3];
|
||||
bool filled;
|
||||
} optTri_t;
|
||||
|
||||
typedef struct {
|
||||
optimizeGroup_t *group;
|
||||
optVertex_t *verts;
|
||||
optEdge_t *edges;
|
||||
optTri_t *tris;
|
||||
} optIsland_t;
|
||||
|
||||
|
||||
void OptimizeEntity( uEntity_t *e );
|
||||
void OptimizeGroupList( optimizeGroup_t *groupList );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// tritools.cpp
|
||||
|
||||
mapTri_t *AllocTri( void );
|
||||
void FreeTri( mapTri_t *tri );
|
||||
int CountTriList( const mapTri_t *list );
|
||||
mapTri_t *MergeTriLists( mapTri_t *a, mapTri_t *b );
|
||||
mapTri_t *CopyTriList( const mapTri_t *a );
|
||||
void FreeTriList( mapTri_t *a );
|
||||
mapTri_t *CopyMapTri( const mapTri_t *tri );
|
||||
float MapTriArea( const mapTri_t *tri );
|
||||
mapTri_t *RemoveBadTris( const mapTri_t *tri );
|
||||
void BoundTriList( const mapTri_t *list, idBounds &b );
|
||||
void DrawTri( const mapTri_t *tri );
|
||||
void FlipTriList( mapTri_t *tris );
|
||||
void TriVertsFromOriginal( mapTri_t *tri, const mapTri_t *original );
|
||||
void PlaneForTri( const mapTri_t *tri, idPlane &plane );
|
||||
idWinding *WindingForTri( const mapTri_t *tri );
|
||||
mapTri_t *WindingToTriList( const idWinding *w, const mapTri_t *originalTri );
|
||||
void ClipTriList( const mapTri_t *list, const idPlane &plane, float epsilon, mapTri_t **front, mapTri_t **back );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// output.cpp
|
||||
|
||||
srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris );
|
||||
void WriteOutputFile( void );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// shadowopt.cpp
|
||||
|
||||
srfTriangles_t *CreateLightShadow( optimizeGroup_t *shadowerGroups, const mapLight_t *light );
|
||||
void FreeBeamTree( struct beamTree_s *beamTree );
|
||||
|
||||
void CarveTriByBeamTree( const struct beamTree_s *beamTree, const mapTri_t *tri, mapTri_t **lit, mapTri_t **unLit );
|
||||
499
neo/tools/compilers/dmap/facebsp.cpp
Normal file
499
neo/tools/compilers/dmap/facebsp.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
int c_faceLeafs;
|
||||
|
||||
|
||||
extern int c_nodes;
|
||||
|
||||
void RemovePortalFromNode( uPortal_t *portal, node_t *l );
|
||||
|
||||
node_t *NodeForPoint( node_t *node, idVec3 origin ) {
|
||||
float d;
|
||||
|
||||
while( node->planenum != PLANENUM_LEAF ) {
|
||||
idPlane &plane = dmapGlobals.mapPlanes[node->planenum];
|
||||
d = plane.Distance( origin );
|
||||
if ( d >= 0 ) {
|
||||
node = node->children[0];
|
||||
} else {
|
||||
node = node->children[1];
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTreePortals_r
|
||||
=============
|
||||
*/
|
||||
void FreeTreePortals_r (node_t *node)
|
||||
{
|
||||
uPortal_t *p, *nextp;
|
||||
int s;
|
||||
|
||||
// free children
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FreeTreePortals_r (node->children[0]);
|
||||
FreeTreePortals_r (node->children[1]);
|
||||
}
|
||||
|
||||
// free portals
|
||||
for (p=node->portals ; p ; p=nextp)
|
||||
{
|
||||
s = (p->nodes[1] == node);
|
||||
nextp = p->next[s];
|
||||
|
||||
RemovePortalFromNode (p, p->nodes[!s]);
|
||||
FreePortal (p);
|
||||
}
|
||||
node->portals = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTree_r
|
||||
=============
|
||||
*/
|
||||
void FreeTree_r (node_t *node)
|
||||
{
|
||||
// free children
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FreeTree_r (node->children[0]);
|
||||
FreeTree_r (node->children[1]);
|
||||
}
|
||||
|
||||
// free brushes
|
||||
FreeBrushList (node->brushlist);
|
||||
|
||||
// free the node
|
||||
c_nodes--;
|
||||
Mem_Free (node);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTree
|
||||
=============
|
||||
*/
|
||||
void FreeTree( tree_t *tree ) {
|
||||
if ( !tree ) {
|
||||
return;
|
||||
}
|
||||
FreeTreePortals_r (tree->headnode);
|
||||
FreeTree_r (tree->headnode);
|
||||
Mem_Free (tree);
|
||||
}
|
||||
|
||||
//===============================================================
|
||||
|
||||
void PrintTree_r (node_t *node, int depth)
|
||||
{
|
||||
int i;
|
||||
uBrush_t *bb;
|
||||
|
||||
for (i=0 ; i<depth ; i++)
|
||||
common->Printf(" ");
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
{
|
||||
if (!node->brushlist)
|
||||
common->Printf("NULL\n");
|
||||
else
|
||||
{
|
||||
for (bb=node->brushlist ; bb ; bb=bb->next)
|
||||
common->Printf("%i ", bb->original->brushnum);
|
||||
common->Printf("\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
idPlane &plane = dmapGlobals.mapPlanes[node->planenum];
|
||||
common->Printf( "#%i (%5.2f %5.2f %5.2f %5.2f)\n", node->planenum,
|
||||
plane[0], plane[1], plane[2], plane[3] );
|
||||
PrintTree_r( node->children[0], depth+1 );
|
||||
PrintTree_r( node->children[1], depth+1 );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
AllocBspFace
|
||||
================
|
||||
*/
|
||||
bspface_t *AllocBspFace( void ) {
|
||||
bspface_t *f;
|
||||
|
||||
f = (bspface_t *)Mem_Alloc(sizeof(*f));
|
||||
memset( f, 0, sizeof(*f) );
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FreeBspFace
|
||||
================
|
||||
*/
|
||||
void FreeBspFace( bspface_t *f ) {
|
||||
if ( f->w ) {
|
||||
delete f->w;
|
||||
}
|
||||
Mem_Free( f );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
SelectSplitPlaneNum
|
||||
================
|
||||
*/
|
||||
#define BLOCK_SIZE 1024
|
||||
int SelectSplitPlaneNum( node_t *node, bspface_t *list ) {
|
||||
bspface_t *split;
|
||||
bspface_t *check;
|
||||
bspface_t *bestSplit;
|
||||
int splits, facing, front, back;
|
||||
int side;
|
||||
idPlane *mapPlane;
|
||||
int value, bestValue;
|
||||
idPlane plane;
|
||||
int planenum;
|
||||
bool havePortals;
|
||||
float dist;
|
||||
idVec3 halfSize;
|
||||
|
||||
// if it is crossing a 1k block boundary, force a split
|
||||
// this prevents epsilon problems from extending an
|
||||
// arbitrary distance across the map
|
||||
|
||||
halfSize = ( node->bounds[1] - node->bounds[0] ) * 0.5f;
|
||||
for ( int axis = 0; axis < 3; axis++ ) {
|
||||
if ( halfSize[axis] > BLOCK_SIZE ) {
|
||||
dist = BLOCK_SIZE * ( floor( ( node->bounds[0][axis] + halfSize[axis] ) / BLOCK_SIZE ) + 1.0f );
|
||||
} else {
|
||||
dist = BLOCK_SIZE * ( floor( node->bounds[0][axis] / BLOCK_SIZE ) + 1.0f );
|
||||
}
|
||||
if ( dist > node->bounds[0][axis] + 1.0f && dist < node->bounds[1][axis] - 1.0f ) {
|
||||
plane[0] = plane[1] = plane[2] = 0.0f;
|
||||
plane[axis] = 1.0f;
|
||||
plane[3] = -dist;
|
||||
planenum = FindFloatPlane( plane );
|
||||
return planenum;
|
||||
}
|
||||
}
|
||||
|
||||
// pick one of the face planes
|
||||
// if we have any portal faces at all, only
|
||||
// select from them, otherwise select from
|
||||
// all faces
|
||||
bestValue = -999999;
|
||||
bestSplit = list;
|
||||
|
||||
havePortals = false;
|
||||
for ( split = list ; split ; split = split->next ) {
|
||||
split->checked = false;
|
||||
if ( split->portal ) {
|
||||
havePortals = true;
|
||||
}
|
||||
}
|
||||
|
||||
for ( split = list ; split ; split = split->next ) {
|
||||
if ( split->checked ) {
|
||||
continue;
|
||||
}
|
||||
if ( havePortals != split->portal ) {
|
||||
continue;
|
||||
}
|
||||
mapPlane = &dmapGlobals.mapPlanes[ split->planenum ];
|
||||
splits = 0;
|
||||
facing = 0;
|
||||
front = 0;
|
||||
back = 0;
|
||||
for ( check = list ; check ; check = check->next ) {
|
||||
if ( check->planenum == split->planenum ) {
|
||||
facing++;
|
||||
check->checked = true; // won't need to test this plane again
|
||||
continue;
|
||||
}
|
||||
side = check->w->PlaneSide( *mapPlane );
|
||||
if ( side == SIDE_CROSS ) {
|
||||
splits++;
|
||||
} else if ( side == SIDE_FRONT ) {
|
||||
front++;
|
||||
} else if ( side == SIDE_BACK ) {
|
||||
back++;
|
||||
}
|
||||
}
|
||||
value = 5*facing - 5*splits; // - abs(front-back);
|
||||
if ( mapPlane->Type() < PLANETYPE_TRUEAXIAL ) {
|
||||
value+=5; // axial is better
|
||||
}
|
||||
|
||||
if ( value > bestValue ) {
|
||||
bestValue = value;
|
||||
bestSplit = split;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bestValue == -999999 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bestSplit->planenum;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
BuildFaceTree_r
|
||||
================
|
||||
*/
|
||||
void BuildFaceTree_r( node_t *node, bspface_t *list ) {
|
||||
bspface_t *split;
|
||||
bspface_t *next;
|
||||
int side;
|
||||
bspface_t *newFace;
|
||||
bspface_t *childLists[2];
|
||||
idWinding *frontWinding, *backWinding;
|
||||
int i;
|
||||
int splitPlaneNum;
|
||||
|
||||
splitPlaneNum = SelectSplitPlaneNum( node, list );
|
||||
// if we don't have any more faces, this is a node
|
||||
if ( splitPlaneNum == -1 ) {
|
||||
node->planenum = PLANENUM_LEAF;
|
||||
c_faceLeafs++;
|
||||
return;
|
||||
}
|
||||
|
||||
// partition the list
|
||||
node->planenum = splitPlaneNum;
|
||||
idPlane &plane = dmapGlobals.mapPlanes[ splitPlaneNum ];
|
||||
childLists[0] = NULL;
|
||||
childLists[1] = NULL;
|
||||
for ( split = list ; split ; split = next ) {
|
||||
next = split->next;
|
||||
|
||||
if ( split->planenum == node->planenum ) {
|
||||
FreeBspFace( split );
|
||||
continue;
|
||||
}
|
||||
|
||||
side = split->w->PlaneSide( plane );
|
||||
|
||||
if ( side == SIDE_CROSS ) {
|
||||
split->w->Split( plane, CLIP_EPSILON * 2, &frontWinding, &backWinding );
|
||||
if ( frontWinding ) {
|
||||
newFace = AllocBspFace();
|
||||
newFace->w = frontWinding;
|
||||
newFace->next = childLists[0];
|
||||
newFace->planenum = split->planenum;
|
||||
childLists[0] = newFace;
|
||||
}
|
||||
if ( backWinding ) {
|
||||
newFace = AllocBspFace();
|
||||
newFace->w = backWinding;
|
||||
newFace->next = childLists[1];
|
||||
newFace->planenum = split->planenum;
|
||||
childLists[1] = newFace;
|
||||
}
|
||||
FreeBspFace( split );
|
||||
} else if ( side == SIDE_FRONT ) {
|
||||
split->next = childLists[0];
|
||||
childLists[0] = split;
|
||||
} else if ( side == SIDE_BACK ) {
|
||||
split->next = childLists[1];
|
||||
childLists[1] = split;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// recursively process children
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
node->children[i] = AllocNode();
|
||||
node->children[i]->parent = node;
|
||||
node->children[i]->bounds = node->bounds;
|
||||
}
|
||||
|
||||
// split the bounds if we have a nice axial plane
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
if ( idMath::Fabs( plane[i] - 1.0 ) < 0.001 ) {
|
||||
node->children[0]->bounds[0][i] = plane.Dist();
|
||||
node->children[1]->bounds[1][i] = plane.Dist();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
BuildFaceTree_r ( node->children[i], childLists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
FaceBSP
|
||||
|
||||
List will be freed before returning
|
||||
================
|
||||
*/
|
||||
tree_t *FaceBSP( bspface_t *list ) {
|
||||
tree_t *tree;
|
||||
bspface_t *face;
|
||||
int i;
|
||||
int count;
|
||||
int start, end;
|
||||
|
||||
start = Sys_Milliseconds();
|
||||
|
||||
common->Printf( "--- FaceBSP ---\n" );
|
||||
|
||||
tree = AllocTree ();
|
||||
|
||||
count = 0;
|
||||
tree->bounds.Clear();
|
||||
for ( face = list ; face ; face = face->next ) {
|
||||
count++;
|
||||
for ( i = 0 ; i < face->w->GetNumPoints() ; i++ ) {
|
||||
tree->bounds.AddPoint( (*face->w)[i].ToVec3() );
|
||||
}
|
||||
}
|
||||
common->Printf( "%5i faces\n", count );
|
||||
|
||||
tree->headnode = AllocNode();
|
||||
tree->headnode->bounds = tree->bounds;
|
||||
c_faceLeafs = 0;
|
||||
|
||||
BuildFaceTree_r ( tree->headnode, list );
|
||||
|
||||
common->Printf( "%5i leafs\n", c_faceLeafs );
|
||||
|
||||
end = Sys_Milliseconds();
|
||||
|
||||
common->Printf( "%5.1f seconds faceBsp\n", ( end - start ) / 1000.0 );
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
MakeStructuralBspFaceList
|
||||
=================
|
||||
*/
|
||||
bspface_t *MakeStructuralBspFaceList( primitive_t *list ) {
|
||||
uBrush_t *b;
|
||||
int i;
|
||||
side_t *s;
|
||||
idWinding *w;
|
||||
bspface_t *f, *flist;
|
||||
|
||||
flist = NULL;
|
||||
for ( ; list ; list = list->next ) {
|
||||
b = list->brush;
|
||||
if ( !b ) {
|
||||
continue;
|
||||
}
|
||||
if ( !b->opaque && !( b->contents & CONTENTS_AREAPORTAL ) ) {
|
||||
continue;
|
||||
}
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
s = &b->sides[i];
|
||||
w = s->winding;
|
||||
if ( !w ) {
|
||||
continue;
|
||||
}
|
||||
if ( ( b->contents & CONTENTS_AREAPORTAL ) && ! ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
|
||||
continue;
|
||||
}
|
||||
f = AllocBspFace();
|
||||
if ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) {
|
||||
f->portal = true;
|
||||
}
|
||||
f->w = w->Copy();
|
||||
f->planenum = s->planenum & ~1;
|
||||
f->next = flist;
|
||||
flist = f;
|
||||
}
|
||||
}
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
MakeVisibleBspFaceList
|
||||
=================
|
||||
*/
|
||||
bspface_t *MakeVisibleBspFaceList( primitive_t *list ) {
|
||||
uBrush_t *b;
|
||||
int i;
|
||||
side_t *s;
|
||||
idWinding *w;
|
||||
bspface_t *f, *flist;
|
||||
|
||||
flist = NULL;
|
||||
for ( ; list ; list = list->next ) {
|
||||
b = list->brush;
|
||||
if ( !b ) {
|
||||
continue;
|
||||
}
|
||||
if ( !b->opaque && !( b->contents & CONTENTS_AREAPORTAL ) ) {
|
||||
continue;
|
||||
}
|
||||
for ( i = 0 ; i < b->numsides ; i++ ) {
|
||||
s = &b->sides[i];
|
||||
w = s->visibleHull;
|
||||
if ( !w ) {
|
||||
continue;
|
||||
}
|
||||
f = AllocBspFace();
|
||||
if ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) {
|
||||
f->portal = true;
|
||||
}
|
||||
f->w = w->Copy();
|
||||
f->planenum = s->planenum & ~1;
|
||||
f->next = flist;
|
||||
flist = f;
|
||||
}
|
||||
}
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
292
neo/tools/compilers/dmap/gldraw.cpp
Normal file
292
neo/tools/compilers/dmap/gldraw.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
//#include <GL/glaux.h>
|
||||
|
||||
#define WIN_SIZE 1024
|
||||
|
||||
void Draw_ClearWindow( void ) {
|
||||
|
||||
if ( !dmapGlobals.drawflag ) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDrawBuffer( GL_FRONT );
|
||||
|
||||
RB_SetGL2D();
|
||||
|
||||
glClearColor( 0.5, 0.5, 0.5, 0 );
|
||||
glClear( GL_COLOR_BUFFER_BIT );
|
||||
|
||||
#if 0
|
||||
int w, h, g;
|
||||
float mx, my;
|
||||
|
||||
w = (dmapGlobals.drawBounds.b[1][0] - dmapGlobals.drawBounds.b[0][0]);
|
||||
h = (dmapGlobals.drawBounds.b[1][1] - dmapGlobals.drawBounds.b[0][1]);
|
||||
|
||||
mx = dmapGlobals.drawBounds.b[0][0] + w/2;
|
||||
my = dmapGlobals.drawBounds.b[1][1] + h/2;
|
||||
|
||||
g = w > h ? w : h;
|
||||
|
||||
glLoadIdentity ();
|
||||
gluPerspective (90, 1, 2, 16384);
|
||||
gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
|
||||
#else
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
glLoadIdentity ();
|
||||
glOrtho( dmapGlobals.drawBounds[0][0], dmapGlobals.drawBounds[1][0],
|
||||
dmapGlobals.drawBounds[0][1], dmapGlobals.drawBounds[1][1],
|
||||
-1, 1 );
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
glLoadIdentity();
|
||||
#endif
|
||||
glColor3f (0,0,0);
|
||||
// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
|
||||
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
|
||||
glDisable (GL_DEPTH_TEST);
|
||||
// glEnable (GL_BLEND);
|
||||
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
#if 0
|
||||
//glColor4f (1,0,0,0.5);
|
||||
// glBegin( GL_LINE_LOOP );
|
||||
glBegin( GL_QUADS );
|
||||
|
||||
glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[0][1] + 20 );
|
||||
glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[0][1] + 20 );
|
||||
glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[1][1] - 20 );
|
||||
glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[1][1] - 20 );
|
||||
|
||||
glEnd ();
|
||||
#endif
|
||||
|
||||
glFlush ();
|
||||
|
||||
}
|
||||
|
||||
void Draw_SetRed (void)
|
||||
{
|
||||
if (!dmapGlobals.drawflag)
|
||||
return;
|
||||
|
||||
glColor3f (1,0,0);
|
||||
}
|
||||
|
||||
void Draw_SetGrey (void)
|
||||
{
|
||||
if (!dmapGlobals.drawflag)
|
||||
return;
|
||||
|
||||
glColor3f( 0.5f, 0.5f, 0.5f);
|
||||
}
|
||||
|
||||
void Draw_SetBlack (void)
|
||||
{
|
||||
if (!dmapGlobals.drawflag)
|
||||
return;
|
||||
|
||||
glColor3f( 0.0f, 0.0f, 0.0f );
|
||||
}
|
||||
|
||||
void DrawWinding ( const idWinding *w )
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dmapGlobals.drawflag)
|
||||
return;
|
||||
|
||||
glColor3f( 0.3f, 0.0f, 0.0f );
|
||||
glBegin (GL_POLYGON);
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ )
|
||||
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glColor3f( 1, 0, 0 );
|
||||
glBegin (GL_LINE_LOOP);
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ )
|
||||
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glFlush ();
|
||||
}
|
||||
|
||||
void DrawAuxWinding ( const idWinding *w)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dmapGlobals.drawflag)
|
||||
return;
|
||||
|
||||
glColor3f( 0.0f, 0.3f, 0.0f );
|
||||
glBegin (GL_POLYGON);
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ )
|
||||
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glColor3f( 0.0f, 1.0f, 0.0f );
|
||||
glBegin (GL_LINE_LOOP);
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ )
|
||||
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
|
||||
glEnd ();
|
||||
|
||||
glFlush ();
|
||||
}
|
||||
|
||||
void DrawLine( idVec3 v1, idVec3 v2, int color ) {
|
||||
if (!dmapGlobals.drawflag)
|
||||
return;
|
||||
|
||||
switch( color ) {
|
||||
case 0: glColor3f( 0, 0, 0 ); break;
|
||||
case 1: glColor3f( 0, 0, 1 ); break;
|
||||
case 2: glColor3f( 0, 1, 0 ); break;
|
||||
case 3: glColor3f( 0, 1, 1 ); break;
|
||||
case 4: glColor3f( 1, 0, 0 ); break;
|
||||
case 5: glColor3f( 1, 0, 1 ); break;
|
||||
case 6: glColor3f( 1, 1, 0 ); break;
|
||||
case 7: glColor3f( 1, 1, 1 ); break;
|
||||
}
|
||||
|
||||
|
||||
glBegin( GL_LINES );
|
||||
|
||||
glVertex3fv( v1.ToFloatPtr() );
|
||||
glVertex3fv( v2.ToFloatPtr() );
|
||||
|
||||
glEnd();
|
||||
glFlush();
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
#define GLSERV_PORT 25001
|
||||
|
||||
bool wins_init;
|
||||
int draw_socket;
|
||||
|
||||
void GLS_BeginScene (void)
|
||||
{
|
||||
WSADATA winsockdata;
|
||||
WORD wVersionRequested;
|
||||
struct sockaddr_in address;
|
||||
int r;
|
||||
|
||||
if (!wins_init)
|
||||
{
|
||||
wins_init = true;
|
||||
|
||||
wVersionRequested = MAKEWORD(1, 1);
|
||||
|
||||
r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
|
||||
|
||||
if (r)
|
||||
common->Error( "Winsock initialization failed.");
|
||||
|
||||
}
|
||||
|
||||
// connect a socket to the server
|
||||
|
||||
draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (draw_socket == -1)
|
||||
common->Error( "draw_socket failed");
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = GLSERV_PORT;
|
||||
r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
|
||||
if (r == -1)
|
||||
{
|
||||
closesocket (draw_socket);
|
||||
draw_socket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GLS_Winding( const idWinding *w, int code )
|
||||
{
|
||||
byte buf[1024];
|
||||
int i, j;
|
||||
|
||||
if (!draw_socket)
|
||||
return;
|
||||
|
||||
((int *)buf)[0] = w->GetNumPoints();
|
||||
((int *)buf)[1] = code;
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ )
|
||||
for (j=0 ; j<3 ; j++)
|
||||
((float *)buf)[2+i*3+j] = (*w)[i][j];
|
||||
|
||||
send (draw_socket, (const char *)buf, w->GetNumPoints() * 12 + 8, 0);
|
||||
}
|
||||
|
||||
void GLS_Triangle( const mapTri_t *tri, int code ) {
|
||||
idWinding w;
|
||||
|
||||
w.SetNumPoints( 3 );
|
||||
VectorCopy( tri->v[0].xyz, w[0] );
|
||||
VectorCopy( tri->v[1].xyz, w[1] );
|
||||
VectorCopy( tri->v[2].xyz, w[2] );
|
||||
GLS_Winding( &w, code );
|
||||
}
|
||||
|
||||
void GLS_EndScene (void)
|
||||
{
|
||||
closesocket (draw_socket);
|
||||
draw_socket = 0;
|
||||
}
|
||||
#else
|
||||
void Draw_ClearWindow( void ) {
|
||||
}
|
||||
|
||||
void DrawWinding( const idWinding *w) {
|
||||
}
|
||||
|
||||
void DrawAuxWinding ( const idWinding *w) {
|
||||
}
|
||||
|
||||
void GLS_Winding( const idWinding *w, int code ) {
|
||||
}
|
||||
|
||||
void GLS_BeginScene (void) {
|
||||
}
|
||||
|
||||
void GLS_EndScene (void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
158
neo/tools/compilers/dmap/glfile.cpp
Normal file
158
neo/tools/compilers/dmap/glfile.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
int c_glfaces;
|
||||
|
||||
int PortalVisibleSides( uPortal_t *p )
|
||||
{
|
||||
int fcon, bcon;
|
||||
|
||||
if (!p->onnode)
|
||||
return 0; // outside
|
||||
|
||||
fcon = p->nodes[0]->opaque;
|
||||
bcon = p->nodes[1]->opaque;
|
||||
|
||||
// same contents never create a face
|
||||
if (fcon == bcon)
|
||||
return 0;
|
||||
|
||||
if (!fcon)
|
||||
return 1;
|
||||
if (!bcon)
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OutputWinding( idWinding *w, idFile *glview )
|
||||
{
|
||||
static int level = 128;
|
||||
float light;
|
||||
int i;
|
||||
|
||||
glview->WriteFloatString( "%i\n", w->GetNumPoints() );
|
||||
level += 28;
|
||||
light = (level&255)/255.0;
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ ) {
|
||||
glview->WriteFloatString( "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
|
||||
(*w)[i][0],
|
||||
(*w)[i][1],
|
||||
(*w)[i][2],
|
||||
light,
|
||||
light,
|
||||
light );
|
||||
}
|
||||
glview->WriteFloatString( "\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
OutputPortal
|
||||
=============
|
||||
*/
|
||||
void OutputPortal( uPortal_t *p, idFile *glview ) {
|
||||
idWinding *w;
|
||||
int sides;
|
||||
|
||||
sides = PortalVisibleSides( p );
|
||||
if ( !sides ) {
|
||||
return;
|
||||
}
|
||||
|
||||
c_glfaces++;
|
||||
|
||||
w = p->winding;
|
||||
|
||||
if ( sides == 2 ) { // back side
|
||||
w = w->Reverse();
|
||||
}
|
||||
|
||||
OutputWinding( w, glview );
|
||||
|
||||
if ( sides == 2 ) {
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
WriteGLView_r
|
||||
=============
|
||||
*/
|
||||
void WriteGLView_r( node_t *node, idFile *glview )
|
||||
{
|
||||
uPortal_t *p, *nextp;
|
||||
|
||||
if ( node->planenum != PLANENUM_LEAF )
|
||||
{
|
||||
WriteGLView_r( node->children[0], glview );
|
||||
WriteGLView_r( node->children[1], glview );
|
||||
return;
|
||||
}
|
||||
|
||||
// write all the portals
|
||||
for ( p = node->portals; p; p = nextp )
|
||||
{
|
||||
if ( p->nodes[0] == node )
|
||||
{
|
||||
OutputPortal( p, glview );
|
||||
nextp = p->next[0];
|
||||
}
|
||||
else {
|
||||
nextp = p->next[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
WriteGLView
|
||||
=============
|
||||
*/
|
||||
void WriteGLView( tree_t *tree, char *source )
|
||||
{
|
||||
idFile *glview;
|
||||
|
||||
c_glfaces = 0;
|
||||
common->Printf( "Writing %s\n", source );
|
||||
|
||||
glview = fileSystem->OpenExplicitFileWrite( source );
|
||||
if ( !glview ) {
|
||||
common->Error( "Couldn't open %s", source );
|
||||
}
|
||||
WriteGLView_r( tree->headnode, glview );
|
||||
fileSystem->CloseFile( glview );
|
||||
|
||||
common->Printf( "%5i c_glfaces\n", c_glfaces );
|
||||
}
|
||||
|
||||
112
neo/tools/compilers/dmap/leakfile.cpp
Normal file
112
neo/tools/compilers/dmap/leakfile.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
LEAF FILE GENERATION
|
||||
|
||||
Save out name.line for qe3 to read
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
LeakFile
|
||||
|
||||
Finds the shortest possible chain of portals
|
||||
that leads from the outside leaf to a specifically
|
||||
occupied leaf
|
||||
=============
|
||||
*/
|
||||
void LeakFile (tree_t *tree)
|
||||
{
|
||||
idVec3 mid;
|
||||
FILE *linefile;
|
||||
idStr filename;
|
||||
idStr ospath;
|
||||
node_t *node;
|
||||
int count;
|
||||
|
||||
if (!tree->outside_node.occupied)
|
||||
return;
|
||||
|
||||
common->Printf ("--- LeakFile ---\n");
|
||||
|
||||
//
|
||||
// write the points to the file
|
||||
//
|
||||
sprintf( filename, "%s.lin", dmapGlobals.mapFileBase );
|
||||
ospath = fileSystem->RelativePathToOSPath( filename );
|
||||
linefile = fopen( ospath, "w" );
|
||||
if ( !linefile ) {
|
||||
common->Error( "Couldn't open %s\n", filename.c_str() );
|
||||
}
|
||||
|
||||
count = 0;
|
||||
node = &tree->outside_node;
|
||||
while (node->occupied > 1)
|
||||
{
|
||||
int next;
|
||||
uPortal_t *p, *nextportal;
|
||||
node_t *nextnode;
|
||||
int s;
|
||||
|
||||
// find the best portal exit
|
||||
next = node->occupied;
|
||||
for (p=node->portals ; p ; p = p->next[!s])
|
||||
{
|
||||
s = (p->nodes[0] == node);
|
||||
if (p->nodes[s]->occupied
|
||||
&& p->nodes[s]->occupied < next)
|
||||
{
|
||||
nextportal = p;
|
||||
nextnode = p->nodes[s];
|
||||
next = nextnode->occupied;
|
||||
}
|
||||
}
|
||||
node = nextnode;
|
||||
mid = nextportal->winding->GetCenter();
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
count++;
|
||||
}
|
||||
// add the occupant center
|
||||
node->occupant->mapEntity->epairs.GetVector( "origin", "", mid );
|
||||
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
common->Printf ("%5i point linefile\n", count+1);
|
||||
|
||||
fclose (linefile);
|
||||
}
|
||||
|
||||
648
neo/tools/compilers/dmap/map.cpp
Normal file
648
neo/tools/compilers/dmap/map.cpp
Normal file
@@ -0,0 +1,648 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
/*
|
||||
|
||||
After parsing, there will be a list of entities that each has
|
||||
a list of primitives.
|
||||
|
||||
Primitives are either brushes, triangle soups, or model references.
|
||||
|
||||
Curves are tesselated to triangle soups at load time, but model
|
||||
references are
|
||||
Brushes will have
|
||||
|
||||
brushes, each of which has a side definition.
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
// private declarations
|
||||
//
|
||||
|
||||
#define MAX_BUILD_SIDES 300
|
||||
|
||||
static int entityPrimitive; // to track editor brush numbers
|
||||
static int c_numMapPatches;
|
||||
static int c_areaportals;
|
||||
|
||||
static uEntity_t *uEntity;
|
||||
|
||||
// brushes are parsed into a temporary array of sides,
|
||||
// which will have duplicates removed before the final brush is allocated
|
||||
static uBrush_t *buildBrush;
|
||||
|
||||
|
||||
#define NORMAL_EPSILON 0.00001f
|
||||
#define DIST_EPSILON 0.01f
|
||||
|
||||
|
||||
/*
|
||||
===========
|
||||
FindFloatPlane
|
||||
===========
|
||||
*/
|
||||
int FindFloatPlane( const idPlane &plane, bool *fixedDegeneracies ) {
|
||||
idPlane p = plane;
|
||||
bool fixed = p.FixDegeneracies( DIST_EPSILON );
|
||||
if ( fixed && fixedDegeneracies ) {
|
||||
*fixedDegeneracies = true;
|
||||
}
|
||||
return dmapGlobals.mapPlanes.FindPlane( p, NORMAL_EPSILON, DIST_EPSILON );
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
SetBrushContents
|
||||
|
||||
The contents on all sides of a brush should be the same
|
||||
Sets contentsShader, contents, opaque
|
||||
===========
|
||||
*/
|
||||
static void SetBrushContents( uBrush_t *b ) {
|
||||
int contents, c2;
|
||||
side_t *s;
|
||||
int i;
|
||||
bool mixed;
|
||||
|
||||
s = &b->sides[0];
|
||||
contents = s->material->GetContentFlags();
|
||||
|
||||
b->contentShader = s->material;
|
||||
mixed = false;
|
||||
|
||||
// a brush is only opaque if all sides are opaque
|
||||
b->opaque = true;
|
||||
|
||||
for ( i=1 ; i<b->numsides ; i++, s++ ) {
|
||||
s = &b->sides[i];
|
||||
|
||||
if ( !s->material ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c2 = s->material->GetContentFlags();
|
||||
if (c2 != contents) {
|
||||
mixed = true;
|
||||
contents |= c2;
|
||||
}
|
||||
|
||||
if ( s->material->Coverage() != MC_OPAQUE ) {
|
||||
b->opaque = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( contents & CONTENTS_AREAPORTAL ) {
|
||||
c_areaportals++;
|
||||
}
|
||||
|
||||
b->contents = contents;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
===============
|
||||
FreeBuildBrush
|
||||
===============
|
||||
*/
|
||||
static void FreeBuildBrush( void ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < buildBrush->numsides ; i++ ) {
|
||||
if ( buildBrush->sides[i].winding ) {
|
||||
delete buildBrush->sides[i].winding;
|
||||
}
|
||||
}
|
||||
buildBrush->numsides = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
FinishBrush
|
||||
|
||||
Produces a final brush based on the buildBrush->sides array
|
||||
and links it to the current entity
|
||||
===============
|
||||
*/
|
||||
static uBrush_t *FinishBrush( void ) {
|
||||
uBrush_t *b;
|
||||
primitive_t *prim;
|
||||
|
||||
// create windings for sides and bounds for brush
|
||||
if ( !CreateBrushWindings( buildBrush ) ) {
|
||||
// don't keep this brush
|
||||
FreeBuildBrush();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( buildBrush->contents & CONTENTS_AREAPORTAL ) {
|
||||
if (dmapGlobals.num_entities != 1) {
|
||||
common->Printf("Entity %i, Brush %i: areaportals only allowed in world\n"
|
||||
, dmapGlobals.num_entities - 1, entityPrimitive);
|
||||
FreeBuildBrush();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// keep it
|
||||
b = CopyBrush( buildBrush );
|
||||
|
||||
FreeBuildBrush();
|
||||
|
||||
b->entitynum = dmapGlobals.num_entities-1;
|
||||
b->brushnum = entityPrimitive;
|
||||
|
||||
b->original = b;
|
||||
|
||||
prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) );
|
||||
memset( prim, 0, sizeof( *prim ) );
|
||||
prim->next = uEntity->primitives;
|
||||
uEntity->primitives = prim;
|
||||
|
||||
prim->brush = b;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
AdjustEntityForOrigin
|
||||
================
|
||||
*/
|
||||
static void AdjustEntityForOrigin( uEntity_t *ent ) {
|
||||
primitive_t *prim;
|
||||
uBrush_t *b;
|
||||
int i;
|
||||
side_t *s;
|
||||
|
||||
for ( prim = ent->primitives ; prim ; prim = prim->next ) {
|
||||
b = prim->brush;
|
||||
if ( !b ) {
|
||||
continue;
|
||||
}
|
||||
for ( i = 0; i < b->numsides; i++ ) {
|
||||
idPlane plane;
|
||||
|
||||
s = &b->sides[i];
|
||||
|
||||
plane = dmapGlobals.mapPlanes[s->planenum];
|
||||
plane[3] += plane.Normal() * ent->origin;
|
||||
|
||||
s->planenum = FindFloatPlane( plane );
|
||||
|
||||
s->texVec.v[0][3] += DotProduct( ent->origin, s->texVec.v[0] );
|
||||
s->texVec.v[1][3] += DotProduct( ent->origin, s->texVec.v[1] );
|
||||
|
||||
// remove any integral shift
|
||||
s->texVec.v[0][3] -= floor( s->texVec.v[0][3] );
|
||||
s->texVec.v[1][3] -= floor( s->texVec.v[1][3] );
|
||||
}
|
||||
CreateBrushWindings(b);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
RemoveDuplicateBrushPlanes
|
||||
|
||||
Returns false if the brush has a mirrored set of planes,
|
||||
meaning it encloses no volume.
|
||||
Also removes planes without any normal
|
||||
=================
|
||||
*/
|
||||
static bool RemoveDuplicateBrushPlanes( uBrush_t * b ) {
|
||||
int i, j, k;
|
||||
side_t *sides;
|
||||
|
||||
sides = b->sides;
|
||||
|
||||
for ( i = 1 ; i < b->numsides ; i++ ) {
|
||||
|
||||
// check for a degenerate plane
|
||||
if ( sides[i].planenum == -1) {
|
||||
common->Printf("Entity %i, Brush %i: degenerate plane\n"
|
||||
, b->entitynum, b->brushnum);
|
||||
// remove it
|
||||
for ( k = i + 1 ; k < b->numsides ; k++ ) {
|
||||
sides[k-1] = sides[k];
|
||||
}
|
||||
b->numsides--;
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for duplication and mirroring
|
||||
for ( j = 0 ; j < i ; j++ ) {
|
||||
if ( sides[i].planenum == sides[j].planenum ) {
|
||||
common->Printf("Entity %i, Brush %i: duplicate plane\n"
|
||||
, b->entitynum, b->brushnum);
|
||||
// remove the second duplicate
|
||||
for ( k = i + 1 ; k < b->numsides ; k++ ) {
|
||||
sides[k-1] = sides[k];
|
||||
}
|
||||
b->numsides--;
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
|
||||
// mirror plane, brush is invalid
|
||||
common->Printf("Entity %i, Brush %i: mirrored plane\n"
|
||||
, b->entitynum, b->brushnum);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
ParseBrush
|
||||
=================
|
||||
*/
|
||||
static void ParseBrush( const idMapBrush *mapBrush, int primitiveNum ) {
|
||||
uBrush_t *b;
|
||||
side_t *s;
|
||||
const idMapBrushSide *ms;
|
||||
int i;
|
||||
bool fixedDegeneracies = false;
|
||||
|
||||
buildBrush->entitynum = dmapGlobals.num_entities-1;
|
||||
buildBrush->brushnum = entityPrimitive;
|
||||
buildBrush->numsides = mapBrush->GetNumSides();
|
||||
for ( i = 0 ; i < mapBrush->GetNumSides() ; i++ ) {
|
||||
s = &buildBrush->sides[i];
|
||||
ms = mapBrush->GetSide(i);
|
||||
|
||||
memset( s, 0, sizeof( *s ) );
|
||||
s->planenum = FindFloatPlane( ms->GetPlane(), &fixedDegeneracies );
|
||||
s->material = declManager->FindMaterial( ms->GetMaterial() );
|
||||
ms->GetTextureVectors( s->texVec.v );
|
||||
// remove any integral shift, which will help with grouping
|
||||
s->texVec.v[0][3] -= floor( s->texVec.v[0][3] );
|
||||
s->texVec.v[1][3] -= floor( s->texVec.v[1][3] );
|
||||
}
|
||||
|
||||
// if there are mirrored planes, the entire brush is invalid
|
||||
if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get the content for the entire brush
|
||||
SetBrushContents( buildBrush );
|
||||
|
||||
b = FinishBrush();
|
||||
if ( !b ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( fixedDegeneracies && dmapGlobals.verboseentities ) {
|
||||
common->Warning( "brush %d has degenerate plane equations", primitiveNum );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ParseSurface
|
||||
================
|
||||
*/
|
||||
static void ParseSurface( const idMapPatch *patch, const idSurface *surface, const idMaterial *material ) {
|
||||
int i;
|
||||
mapTri_t *tri;
|
||||
primitive_t *prim;
|
||||
|
||||
prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) );
|
||||
memset( prim, 0, sizeof( *prim ) );
|
||||
prim->next = uEntity->primitives;
|
||||
uEntity->primitives = prim;
|
||||
|
||||
for ( i = 0; i < surface->GetNumIndexes(); i += 3 ) {
|
||||
tri = AllocTri();
|
||||
tri->v[2] = (*surface)[surface->GetIndexes()[i+0]];
|
||||
tri->v[1] = (*surface)[surface->GetIndexes()[i+2]];
|
||||
tri->v[0] = (*surface)[surface->GetIndexes()[i+1]];
|
||||
tri->material = material;
|
||||
tri->next = prim->tris;
|
||||
prim->tris = tri;
|
||||
}
|
||||
|
||||
// set merge groups if needed, to prevent multiple sides from being
|
||||
// merged into a single surface in the case of gui shaders, mirrors, and autosprites
|
||||
if ( material->IsDiscrete() ) {
|
||||
for ( tri = prim->tris ; tri ; tri = tri->next ) {
|
||||
tri->mergeGroup = (void *)patch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ParsePatch
|
||||
================
|
||||
*/
|
||||
static void ParsePatch( const idMapPatch *patch, int primitiveNum ) {
|
||||
const idMaterial *mat;
|
||||
|
||||
if ( dmapGlobals.noCurves ) {
|
||||
return;
|
||||
}
|
||||
|
||||
c_numMapPatches++;
|
||||
|
||||
mat = declManager->FindMaterial( patch->GetMaterial() );
|
||||
|
||||
idSurface_Patch *cp = new idSurface_Patch( *patch );
|
||||
|
||||
if ( patch->GetExplicitlySubdivided() ) {
|
||||
cp->SubdivideExplicit( patch->GetHorzSubdivisions(), patch->GetVertSubdivisions(), true );
|
||||
} else {
|
||||
cp->Subdivide( DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, true );
|
||||
}
|
||||
|
||||
ParseSurface( patch, cp, mat );
|
||||
|
||||
delete cp;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ProcessMapEntity
|
||||
================
|
||||
*/
|
||||
static bool ProcessMapEntity( idMapEntity *mapEnt ) {
|
||||
idMapPrimitive *prim;
|
||||
|
||||
uEntity = &dmapGlobals.uEntities[dmapGlobals.num_entities];
|
||||
memset( uEntity, 0, sizeof(*uEntity) );
|
||||
uEntity->mapEntity = mapEnt;
|
||||
dmapGlobals.num_entities++;
|
||||
|
||||
for ( entityPrimitive = 0; entityPrimitive < mapEnt->GetNumPrimitives(); entityPrimitive++ ) {
|
||||
prim = mapEnt->GetPrimitive(entityPrimitive);
|
||||
|
||||
if ( prim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
|
||||
ParseBrush( static_cast<idMapBrush*>(prim), entityPrimitive );
|
||||
}
|
||||
else if ( prim->GetType() == idMapPrimitive::TYPE_PATCH ) {
|
||||
ParsePatch( static_cast<idMapPatch*>(prim), entityPrimitive );
|
||||
}
|
||||
}
|
||||
|
||||
// never put an origin on the world, even if the editor left one there
|
||||
if ( dmapGlobals.num_entities != 1 ) {
|
||||
uEntity->mapEntity->epairs.GetVector( "origin", "", uEntity->origin );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
/*
|
||||
==============
|
||||
CreateMapLight
|
||||
|
||||
==============
|
||||
*/
|
||||
static void CreateMapLight( const idMapEntity *mapEnt ) {
|
||||
mapLight_t *light;
|
||||
bool dynamic;
|
||||
|
||||
// designers can add the "noPrelight" flag to signal that
|
||||
// the lights will move around, so we don't want
|
||||
// to bother chopping up the surfaces under it or creating
|
||||
// shadow volumes
|
||||
mapEnt->epairs.GetBool( "noPrelight", "0", dynamic );
|
||||
if ( dynamic ) {
|
||||
return;
|
||||
}
|
||||
|
||||
light = new mapLight_t;
|
||||
light->name[0] = '\0';
|
||||
light->shadowTris = NULL;
|
||||
|
||||
// parse parms exactly as the game do
|
||||
// use the game's epair parsing code so
|
||||
// we can use the same renderLight generation
|
||||
gameEdit->ParseSpawnArgsToRenderLight( &mapEnt->epairs, &light->def.parms );
|
||||
|
||||
R_DeriveLightData( &light->def );
|
||||
|
||||
// get the name for naming the shadow surfaces
|
||||
const char *name;
|
||||
|
||||
mapEnt->epairs.GetString( "name", "", &name );
|
||||
|
||||
idStr::Copynz( light->name, name, sizeof( light->name ) );
|
||||
if ( !light->name[0] ) {
|
||||
common->Error( "Light at (%f,%f,%f) didn't have a name",
|
||||
light->def.parms.origin[0], light->def.parms.origin[1], light->def.parms.origin[2] );
|
||||
}
|
||||
#if 0
|
||||
// use the renderer code to get the bounding planes for the light
|
||||
// based on all the parameters
|
||||
R_RenderLightFrustum( light->parms, light->frustum );
|
||||
light->lightShader = light->parms.shader;
|
||||
#endif
|
||||
|
||||
dmapGlobals.mapLights.Append( light );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CreateMapLights
|
||||
|
||||
==============
|
||||
*/
|
||||
static void CreateMapLights( const idMapFile *dmapFile ) {
|
||||
int i;
|
||||
const idMapEntity *mapEnt;
|
||||
const char *value;
|
||||
|
||||
for ( i = 0 ; i < dmapFile->GetNumEntities() ; i++ ) {
|
||||
mapEnt = dmapFile->GetEntity(i);
|
||||
mapEnt->epairs.GetString( "classname", "", &value);
|
||||
if ( !idStr::Icmp( value, "light" ) ) {
|
||||
CreateMapLight( mapEnt );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
LoadDMapFile
|
||||
================
|
||||
*/
|
||||
bool LoadDMapFile( const char *filename ) {
|
||||
primitive_t *prim;
|
||||
idBounds mapBounds;
|
||||
int brushes, triSurfs;
|
||||
int i;
|
||||
int size;
|
||||
|
||||
common->Printf( "--- LoadDMapFile ---\n" );
|
||||
common->Printf( "loading %s\n", filename );
|
||||
|
||||
// load and parse the map file into canonical form
|
||||
dmapGlobals.dmapFile = new idMapFile();
|
||||
if ( !dmapGlobals.dmapFile->Parse(filename) ) {
|
||||
delete dmapGlobals.dmapFile;
|
||||
dmapGlobals.dmapFile = NULL;
|
||||
common->Warning( "Couldn't load map file: '%s'", filename );
|
||||
return false;
|
||||
}
|
||||
|
||||
dmapGlobals.mapPlanes.Clear();
|
||||
dmapGlobals.mapPlanes.SetGranularity( 1024 );
|
||||
|
||||
// process the canonical form into utility form
|
||||
dmapGlobals.num_entities = 0;
|
||||
c_numMapPatches = 0;
|
||||
c_areaportals = 0;
|
||||
|
||||
size = dmapGlobals.dmapFile->GetNumEntities() * sizeof( dmapGlobals.uEntities[0] );
|
||||
dmapGlobals.uEntities = (uEntity_t *)Mem_Alloc( size );
|
||||
memset( dmapGlobals.uEntities, 0, size );
|
||||
|
||||
// allocate a very large temporary brush for building
|
||||
// the brushes as they are loaded
|
||||
buildBrush = AllocBrush( MAX_BUILD_SIDES );
|
||||
|
||||
for ( i = 0 ; i < dmapGlobals.dmapFile->GetNumEntities() ; i++ ) {
|
||||
ProcessMapEntity( dmapGlobals.dmapFile->GetEntity(i) );
|
||||
}
|
||||
|
||||
CreateMapLights( dmapGlobals.dmapFile );
|
||||
|
||||
brushes = 0;
|
||||
triSurfs = 0;
|
||||
|
||||
mapBounds.Clear();
|
||||
for ( prim = dmapGlobals.uEntities[0].primitives ; prim ; prim = prim->next ) {
|
||||
if ( prim->brush ) {
|
||||
brushes++;
|
||||
mapBounds.AddBounds( prim->brush->bounds );
|
||||
} else if ( prim->tris ) {
|
||||
triSurfs++;
|
||||
}
|
||||
}
|
||||
|
||||
common->Printf( "%5i total world brushes\n", brushes );
|
||||
common->Printf( "%5i total world triSurfs\n", triSurfs );
|
||||
common->Printf( "%5i patches\n", c_numMapPatches );
|
||||
common->Printf( "%5i entities\n", dmapGlobals.num_entities );
|
||||
common->Printf( "%5i planes\n", dmapGlobals.mapPlanes.Num() );
|
||||
common->Printf( "%5i areaportals\n", c_areaportals );
|
||||
common->Printf( "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", mapBounds[0][0], mapBounds[0][1],mapBounds[0][2],
|
||||
mapBounds[1][0], mapBounds[1][1], mapBounds[1][2] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FreeOptimizeGroupList
|
||||
================
|
||||
*/
|
||||
void FreeOptimizeGroupList( optimizeGroup_t *groups ) {
|
||||
optimizeGroup_t *next;
|
||||
|
||||
for ( ; groups ; groups = next ) {
|
||||
next = groups->nextGroup;
|
||||
FreeTriList( groups->triList );
|
||||
Mem_Free( groups );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FreeDMapFile
|
||||
================
|
||||
*/
|
||||
void FreeDMapFile( void ) {
|
||||
int i, j;
|
||||
|
||||
FreeBrush( buildBrush );
|
||||
buildBrush = NULL;
|
||||
|
||||
// free the entities and brushes
|
||||
for ( i = 0 ; i < dmapGlobals.num_entities ; i++ ) {
|
||||
uEntity_t *ent;
|
||||
primitive_t *prim, *nextPrim;
|
||||
|
||||
ent = &dmapGlobals.uEntities[i];
|
||||
|
||||
FreeTree( ent->tree );
|
||||
|
||||
// free primitives
|
||||
for ( prim = ent->primitives ; prim ; prim = nextPrim ) {
|
||||
nextPrim = prim->next;
|
||||
if ( prim->brush ) {
|
||||
FreeBrush( prim->brush );
|
||||
}
|
||||
if ( prim->tris ) {
|
||||
FreeTriList( prim->tris );
|
||||
}
|
||||
Mem_Free( prim );
|
||||
}
|
||||
|
||||
// free area surfaces
|
||||
if ( ent->areas ) {
|
||||
for ( j = 0 ; j < ent->numAreas ; j++ ) {
|
||||
uArea_t *area;
|
||||
|
||||
area = &ent->areas[j];
|
||||
FreeOptimizeGroupList( area->groups );
|
||||
|
||||
}
|
||||
Mem_Free( ent->areas );
|
||||
}
|
||||
}
|
||||
|
||||
Mem_Free( dmapGlobals.uEntities );
|
||||
|
||||
dmapGlobals.num_entities = 0;
|
||||
|
||||
// free the map lights
|
||||
for ( i = 0; i < dmapGlobals.mapLights.Num(); i++ ) {
|
||||
R_FreeLightDefDerivedData( &dmapGlobals.mapLights[i]->def );
|
||||
}
|
||||
dmapGlobals.mapLights.DeleteContents( true );
|
||||
}
|
||||
1990
neo/tools/compilers/dmap/optimize.cpp
Normal file
1990
neo/tools/compilers/dmap/optimize.cpp
Normal file
File diff suppressed because it is too large
Load Diff
84
neo/tools/compilers/dmap/optimize_gcc.cpp
Normal file
84
neo/tools/compilers/dmap/optimize_gcc.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
/*
|
||||
crazy gcc 3.3.5 optimization bug
|
||||
happens even at -O1
|
||||
if you remove the 'return NULL;' after Error(), it only happens at -O3 / release
|
||||
see dmap.gcc.zip test map and .proc outputs
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
extern idBounds optBounds;
|
||||
|
||||
#define MAX_OPT_VERTEXES 0x10000
|
||||
extern int numOptVerts;
|
||||
extern optVertex_t optVerts[MAX_OPT_VERTEXES];
|
||||
|
||||
/*
|
||||
================
|
||||
FindOptVertex
|
||||
================
|
||||
*/
|
||||
optVertex_t *FindOptVertex( idDrawVert *v, optimizeGroup_t *opt ) {
|
||||
int i;
|
||||
float x, y;
|
||||
optVertex_t *vert;
|
||||
|
||||
// deal with everything strictly as 2D
|
||||
x = v->xyz * opt->axis[0];
|
||||
y = v->xyz * opt->axis[1];
|
||||
|
||||
// should we match based on the t-junction fixing hash verts?
|
||||
for ( i = 0 ; i < numOptVerts ; i++ ) {
|
||||
if ( optVerts[i].pv[0] == x && optVerts[i].pv[1] == y ) {
|
||||
return &optVerts[i];
|
||||
}
|
||||
}
|
||||
|
||||
if ( numOptVerts >= MAX_OPT_VERTEXES ) {
|
||||
common->Error( "MAX_OPT_VERTEXES" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
numOptVerts++;
|
||||
|
||||
vert = &optVerts[i];
|
||||
memset( vert, 0, sizeof( *vert ) );
|
||||
vert->v = *v;
|
||||
vert->pv[0] = x;
|
||||
vert->pv[1] = y;
|
||||
vert->pv[2] = 0;
|
||||
|
||||
optBounds.AddPoint( vert->pv );
|
||||
|
||||
return vert;
|
||||
}
|
||||
682
neo/tools/compilers/dmap/output.cpp
Normal file
682
neo/tools/compilers/dmap/output.cpp
Normal file
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
//=================================================================================
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
should we try and snap values very close to 0.5, 0.25, 0.125, etc?
|
||||
|
||||
do we write out normals, or just a "smooth shade" flag?
|
||||
resolved: normals. otherwise adjacent facet shaded surfaces get their
|
||||
vertexes merged, and they would have to be split apart before drawing
|
||||
|
||||
do we save out "wings" for shadow silhouette info?
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
static idFile *procFile;
|
||||
|
||||
#define AREANUM_DIFFERENT -2
|
||||
/*
|
||||
=============
|
||||
PruneNodes_r
|
||||
|
||||
Any nodes that have all children with the same
|
||||
area can be combined into a single leaf node
|
||||
|
||||
Returns the area number of all children, or
|
||||
AREANUM_DIFFERENT if not the same.
|
||||
=============
|
||||
*/
|
||||
int PruneNodes_r( node_t *node ) {
|
||||
int a1, a2;
|
||||
|
||||
if ( node->planenum == PLANENUM_LEAF ) {
|
||||
return node->area;
|
||||
}
|
||||
|
||||
a1 = PruneNodes_r( node->children[0] );
|
||||
a2 = PruneNodes_r( node->children[1] );
|
||||
|
||||
if ( a1 != a2 || a1 == AREANUM_DIFFERENT ) {
|
||||
return AREANUM_DIFFERENT;
|
||||
}
|
||||
|
||||
// free all the nodes below this point
|
||||
FreeTreePortals_r( node->children[0] );
|
||||
FreeTreePortals_r( node->children[1] );
|
||||
FreeTree_r( node->children[0] );
|
||||
FreeTree_r( node->children[1] );
|
||||
|
||||
// change this node to a leaf
|
||||
node->planenum = PLANENUM_LEAF;
|
||||
node->area = a1;
|
||||
|
||||
return a1;
|
||||
}
|
||||
|
||||
static void WriteFloat( idFile *f, float v )
|
||||
{
|
||||
if ( idMath::Fabs(v - idMath::Rint(v)) < 0.001 ) {
|
||||
f->WriteFloatString( "%i ", (int)idMath::Rint(v) );
|
||||
}
|
||||
else {
|
||||
f->WriteFloatString( "%f ", v );
|
||||
}
|
||||
}
|
||||
|
||||
void Write1DMatrix( idFile *f, int x, float *m ) {
|
||||
int i;
|
||||
|
||||
f->WriteFloatString( "( " );
|
||||
|
||||
for ( i = 0; i < x; i++ ) {
|
||||
WriteFloat( f, m[i] );
|
||||
}
|
||||
|
||||
f->WriteFloatString( ") " );
|
||||
}
|
||||
|
||||
static int CountUniqueShaders( optimizeGroup_t *groups ) {
|
||||
optimizeGroup_t *a, *b;
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
|
||||
for ( a = groups ; a ; a = a->nextGroup ) {
|
||||
if ( !a->triList ) { // ignore groups with no tris
|
||||
continue;
|
||||
}
|
||||
for ( b = groups ; b != a ; b = b->nextGroup ) {
|
||||
if ( !b->triList ) {
|
||||
continue;
|
||||
}
|
||||
if ( a->material != b->material ) {
|
||||
continue;
|
||||
}
|
||||
if ( a->mergeGroup != b->mergeGroup ) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ( a == b ) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
MatchVert
|
||||
==============
|
||||
*/
|
||||
#define XYZ_EPSILON 0.01
|
||||
#define ST_EPSILON 0.001
|
||||
#define COSINE_EPSILON 0.999
|
||||
|
||||
static bool MatchVert( const idDrawVert *a, const idDrawVert *b ) {
|
||||
if ( idMath::Fabs( a->xyz[0] - b->xyz[0] ) > XYZ_EPSILON ) {
|
||||
return false;
|
||||
}
|
||||
if ( idMath::Fabs( a->xyz[1] - b->xyz[1] ) > XYZ_EPSILON ) {
|
||||
return false;
|
||||
}
|
||||
if ( idMath::Fabs( a->xyz[2] - b->xyz[2] ) > XYZ_EPSILON ) {
|
||||
return false;
|
||||
}
|
||||
if ( idMath::Fabs( a->st[0] - b->st[0] ) > ST_EPSILON ) {
|
||||
return false;
|
||||
}
|
||||
if ( idMath::Fabs( a->st[1] - b->st[1] ) > ST_EPSILON ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the normal is 0 (smoothed normals), consider it a match
|
||||
if ( a->normal[0] == 0 && a->normal[1] == 0 && a->normal[2] == 0
|
||||
&& b->normal[0] == 0 && b->normal[1] == 0 && b->normal[2] == 0 ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise do a dot-product cosine check
|
||||
if ( DotProduct( a->normal, b->normal ) < COSINE_EPSILON ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
ShareMapTriVerts
|
||||
|
||||
Converts independent triangles to shared vertex triangles
|
||||
====================
|
||||
*/
|
||||
srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris ) {
|
||||
const mapTri_t *step;
|
||||
int count;
|
||||
int i, j;
|
||||
int numVerts;
|
||||
int numIndexes;
|
||||
srfTriangles_t *uTri;
|
||||
|
||||
// unique the vertexes
|
||||
count = CountTriList( tris );
|
||||
|
||||
uTri = R_AllocStaticTriSurf();
|
||||
R_AllocStaticTriSurfVerts( uTri, count * 3 );
|
||||
R_AllocStaticTriSurfIndexes( uTri, count * 3 );
|
||||
|
||||
numVerts = 0;
|
||||
numIndexes = 0;
|
||||
|
||||
for ( step = tris ; step ; step = step->next ) {
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
const idDrawVert *dv;
|
||||
|
||||
dv = &step->v[i];
|
||||
|
||||
// search for a match
|
||||
for ( j = 0 ; j < numVerts ; j++ ) {
|
||||
if ( MatchVert( &uTri->verts[j], dv ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j == numVerts ) {
|
||||
numVerts++;
|
||||
uTri->verts[j].xyz = dv->xyz;
|
||||
uTri->verts[j].normal = dv->normal;
|
||||
uTri->verts[j].st[0] = dv->st[0];
|
||||
uTri->verts[j].st[1] = dv->st[1];
|
||||
}
|
||||
|
||||
uTri->indexes[numIndexes++] = j;
|
||||
}
|
||||
}
|
||||
|
||||
uTri->numVerts = numVerts;
|
||||
uTri->numIndexes = numIndexes;
|
||||
|
||||
return uTri;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CleanupUTriangles
|
||||
==================
|
||||
*/
|
||||
static void CleanupUTriangles( srfTriangles_t *tri ) {
|
||||
// perform cleanup operations
|
||||
|
||||
R_RangeCheckIndexes( tri );
|
||||
R_CreateSilIndexes( tri );
|
||||
// R_RemoveDuplicatedTriangles( tri ); // this may remove valid overlapped transparent triangles
|
||||
R_RemoveDegenerateTriangles( tri );
|
||||
// R_RemoveUnusedVerts( tri );
|
||||
|
||||
R_FreeStaticTriSurfSilIndexes( tri );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
WriteUTriangles
|
||||
|
||||
Writes text verts and indexes to procfile
|
||||
====================
|
||||
*/
|
||||
static void WriteUTriangles( const srfTriangles_t *uTris ) {
|
||||
int col;
|
||||
int i;
|
||||
|
||||
// emit this chain
|
||||
procFile->WriteFloatString( "/* numVerts = */ %i /* numIndexes = */ %i\n",
|
||||
uTris->numVerts, uTris->numIndexes );
|
||||
|
||||
// verts
|
||||
col = 0;
|
||||
for ( i = 0 ; i < uTris->numVerts ; i++ ) {
|
||||
float vec[8];
|
||||
const idDrawVert *dv;
|
||||
|
||||
dv = &uTris->verts[i];
|
||||
|
||||
vec[0] = dv->xyz[0];
|
||||
vec[1] = dv->xyz[1];
|
||||
vec[2] = dv->xyz[2];
|
||||
vec[3] = dv->st[0];
|
||||
vec[4] = dv->st[1];
|
||||
vec[5] = dv->normal[0];
|
||||
vec[6] = dv->normal[1];
|
||||
vec[7] = dv->normal[2];
|
||||
Write1DMatrix( procFile, 8, vec );
|
||||
|
||||
if ( ++col == 3 ) {
|
||||
col = 0;
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
}
|
||||
if ( col != 0 ) {
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
|
||||
// indexes
|
||||
col = 0;
|
||||
for ( i = 0 ; i < uTris->numIndexes ; i++ ) {
|
||||
procFile->WriteFloatString( "%i ", uTris->indexes[i] );
|
||||
|
||||
if ( ++col == 18 ) {
|
||||
col = 0;
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
}
|
||||
if ( col != 0 ) {
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
WriteShadowTriangles
|
||||
|
||||
Writes text verts and indexes to procfile
|
||||
====================
|
||||
*/
|
||||
static void WriteShadowTriangles( const srfTriangles_t *tri ) {
|
||||
int col;
|
||||
int i;
|
||||
|
||||
// emit this chain
|
||||
procFile->WriteFloatString( "/* numVerts = */ %i /* noCaps = */ %i /* noFrontCaps = */ %i /* numIndexes = */ %i /* planeBits = */ %i\n",
|
||||
tri->numVerts, tri->numShadowIndexesNoCaps, tri->numShadowIndexesNoFrontCaps, tri->numIndexes, tri->shadowCapPlaneBits );
|
||||
|
||||
// verts
|
||||
col = 0;
|
||||
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
||||
Write1DMatrix( procFile, 3, &tri->shadowVertexes[i].xyz[0] );
|
||||
|
||||
if ( ++col == 5 ) {
|
||||
col = 0;
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
}
|
||||
if ( col != 0 ) {
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
|
||||
// indexes
|
||||
col = 0;
|
||||
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
|
||||
procFile->WriteFloatString( "%i ", tri->indexes[i] );
|
||||
|
||||
if ( ++col == 18 ) {
|
||||
col = 0;
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
}
|
||||
if ( col != 0 ) {
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=======================
|
||||
GroupsAreSurfaceCompatible
|
||||
|
||||
Planes, texcoords, and groupLights can differ,
|
||||
but the material and mergegroup must match
|
||||
=======================
|
||||
*/
|
||||
static bool GroupsAreSurfaceCompatible( const optimizeGroup_t *a, const optimizeGroup_t *b ) {
|
||||
if ( a->material != b->material ) {
|
||||
return false;
|
||||
}
|
||||
if ( a->mergeGroup != b->mergeGroup ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
WriteOutputSurfaces
|
||||
====================
|
||||
*/
|
||||
static void WriteOutputSurfaces( int entityNum, int areaNum ) {
|
||||
mapTri_t *ambient, *copy;
|
||||
int surfaceNum;
|
||||
int numSurfaces;
|
||||
idMapEntity *entity;
|
||||
uArea_t *area;
|
||||
optimizeGroup_t *group, *groupStep;
|
||||
int i; // , j;
|
||||
// int col;
|
||||
srfTriangles_t *uTri;
|
||||
// mapTri_t *tri;
|
||||
typedef struct interactionTris_s {
|
||||
struct interactionTris_s *next;
|
||||
mapTri_t *triList;
|
||||
mapLight_t *light;
|
||||
} interactionTris_t;
|
||||
|
||||
interactionTris_t *interactions, *checkInter; //, *nextInter;
|
||||
|
||||
|
||||
area = &dmapGlobals.uEntities[entityNum].areas[areaNum];
|
||||
entity = dmapGlobals.uEntities[entityNum].mapEntity;
|
||||
|
||||
numSurfaces = CountUniqueShaders( area->groups );
|
||||
|
||||
|
||||
if ( entityNum == 0 ) {
|
||||
procFile->WriteFloatString( "model { /* name = */ \"_area%i\" /* numSurfaces = */ %i\n\n",
|
||||
areaNum, numSurfaces );
|
||||
} else {
|
||||
const char *name;
|
||||
|
||||
entity->epairs.GetString( "name", "", &name );
|
||||
if ( !name[0] ) {
|
||||
common->Error( "Entity %i has surfaces, but no name key", entityNum );
|
||||
}
|
||||
procFile->WriteFloatString( "model { /* name = */ \"%s\" /* numSurfaces = */ %i\n\n",
|
||||
name, numSurfaces );
|
||||
}
|
||||
|
||||
surfaceNum = 0;
|
||||
for ( group = area->groups ; group ; group = group->nextGroup ) {
|
||||
if ( group->surfaceEmited ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// combine all groups compatible with this one
|
||||
// usually several optimizeGroup_t can be combined into a single
|
||||
// surface, even though they couldn't be merged together to save
|
||||
// vertexes because they had different planes, texture coordinates, or lights.
|
||||
// Different mergeGroups will stay in separate surfaces.
|
||||
ambient = NULL;
|
||||
|
||||
// each light that illuminates any of the groups in the surface will
|
||||
// get its own list of indexes out of the original surface
|
||||
interactions = NULL;
|
||||
|
||||
for ( groupStep = group ; groupStep ; groupStep = groupStep->nextGroup ) {
|
||||
if ( groupStep->surfaceEmited ) {
|
||||
continue;
|
||||
}
|
||||
if ( !GroupsAreSurfaceCompatible( group, groupStep ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// copy it out to the ambient list
|
||||
copy = CopyTriList( groupStep->triList );
|
||||
ambient = MergeTriLists( ambient, copy );
|
||||
groupStep->surfaceEmited = true;
|
||||
|
||||
// duplicate it into an interaction for each groupLight
|
||||
for ( i = 0 ; i < groupStep->numGroupLights ; i++ ) {
|
||||
for ( checkInter = interactions ; checkInter ; checkInter = checkInter->next ) {
|
||||
if ( checkInter->light == groupStep->groupLights[i] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !checkInter ) {
|
||||
// create a new interaction
|
||||
checkInter = (interactionTris_t *)Mem_ClearedAlloc( sizeof( *checkInter ) );
|
||||
checkInter->light = groupStep->groupLights[i];
|
||||
checkInter->next = interactions;
|
||||
interactions = checkInter;
|
||||
}
|
||||
copy = CopyTriList( groupStep->triList );
|
||||
checkInter->triList = MergeTriLists( checkInter->triList, copy );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !ambient ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( surfaceNum >= numSurfaces ) {
|
||||
common->Error( "WriteOutputSurfaces: surfaceNum >= numSurfaces" );
|
||||
}
|
||||
|
||||
procFile->WriteFloatString( "/* surface %i */ { ", surfaceNum );
|
||||
surfaceNum++;
|
||||
procFile->WriteFloatString( "\"%s\" ", ambient->material->GetName() );
|
||||
|
||||
uTri = ShareMapTriVerts( ambient );
|
||||
FreeTriList( ambient );
|
||||
|
||||
CleanupUTriangles( uTri );
|
||||
WriteUTriangles( uTri );
|
||||
R_FreeStaticTriSurf( uTri );
|
||||
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
}
|
||||
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
WriteNode_r
|
||||
|
||||
===============
|
||||
*/
|
||||
static void WriteNode_r( node_t *node ) {
|
||||
int child[2];
|
||||
int i;
|
||||
idPlane *plane;
|
||||
|
||||
if ( node->planenum == PLANENUM_LEAF ) {
|
||||
// we shouldn't get here unless the entire world
|
||||
// was a single leaf
|
||||
procFile->WriteFloatString( "/* node 0 */ ( 0 0 0 0 ) -1 -1\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
if ( node->children[i]->planenum == PLANENUM_LEAF ) {
|
||||
child[i] = -1 - node->children[i]->area;
|
||||
} else {
|
||||
child[i] = node->children[i]->nodeNumber;
|
||||
}
|
||||
}
|
||||
|
||||
plane = &dmapGlobals.mapPlanes[node->planenum];
|
||||
|
||||
procFile->WriteFloatString( "/* node %i */ ", node->nodeNumber );
|
||||
Write1DMatrix( procFile, 4, plane->ToFloatPtr() );
|
||||
procFile->WriteFloatString( "%i %i\n", child[0], child[1] );
|
||||
|
||||
if ( child[0] > 0 ) {
|
||||
WriteNode_r( node->children[0] );
|
||||
}
|
||||
if ( child[1] > 0 ) {
|
||||
WriteNode_r( node->children[1] );
|
||||
}
|
||||
}
|
||||
|
||||
static int NumberNodes_r( node_t *node, int nextNumber ) {
|
||||
if ( node->planenum == PLANENUM_LEAF ) {
|
||||
return nextNumber;
|
||||
}
|
||||
node->nodeNumber = nextNumber;
|
||||
nextNumber++;
|
||||
nextNumber = NumberNodes_r( node->children[0], nextNumber );
|
||||
nextNumber = NumberNodes_r( node->children[1], nextNumber );
|
||||
|
||||
return nextNumber;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
WriteOutputNodes
|
||||
====================
|
||||
*/
|
||||
static void WriteOutputNodes( node_t *node ) {
|
||||
int numNodes;
|
||||
|
||||
// prune unneeded nodes and count
|
||||
PruneNodes_r( node );
|
||||
numNodes = NumberNodes_r( node, 0 );
|
||||
|
||||
// output
|
||||
procFile->WriteFloatString( "nodes { /* numNodes = */ %i\n\n", numNodes );
|
||||
procFile->WriteFloatString( "/* node format is: ( planeVector ) positiveChild negativeChild */\n" );
|
||||
procFile->WriteFloatString( "/* a child number of 0 is an opaque, solid area */\n" );
|
||||
procFile->WriteFloatString( "/* negative child numbers are areas: (-1-child) */\n" );
|
||||
|
||||
WriteNode_r( node );
|
||||
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
WriteOutputPortals
|
||||
====================
|
||||
*/
|
||||
static void WriteOutputPortals( uEntity_t *e ) {
|
||||
int i, j;
|
||||
interAreaPortal_t *iap;
|
||||
idWinding *w;
|
||||
|
||||
procFile->WriteFloatString( "interAreaPortals { /* numAreas = */ %i /* numIAP = */ %i\n\n",
|
||||
e->numAreas, numInterAreaPortals );
|
||||
procFile->WriteFloatString( "/* interAreaPortal format is: numPoints positiveSideArea negativeSideArea ( point) ... */\n" );
|
||||
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
||||
iap = &interAreaPortals[i];
|
||||
w = iap->side->winding;
|
||||
procFile->WriteFloatString("/* iap %i */ %i %i %i ", i, w->GetNumPoints(), iap->area0, iap->area1 );
|
||||
for ( j = 0 ; j < w->GetNumPoints() ; j++ ) {
|
||||
Write1DMatrix( procFile, 3, (*w)[j].ToFloatPtr() );
|
||||
}
|
||||
procFile->WriteFloatString("\n" );
|
||||
}
|
||||
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
WriteOutputEntity
|
||||
====================
|
||||
*/
|
||||
static void WriteOutputEntity( int entityNum ) {
|
||||
int i;
|
||||
uEntity_t *e;
|
||||
|
||||
e = &dmapGlobals.uEntities[entityNum];
|
||||
|
||||
if ( entityNum != 0 ) {
|
||||
// entities may have enclosed, empty areas that we don't need to write out
|
||||
if ( e->numAreas > 1 ) {
|
||||
e->numAreas = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < e->numAreas ; i++ ) {
|
||||
WriteOutputSurfaces( entityNum, i );
|
||||
}
|
||||
|
||||
// we will completely skip the portals and nodes if it is a single area
|
||||
if ( entityNum == 0 && e->numAreas > 1 ) {
|
||||
// output the area portals
|
||||
WriteOutputPortals( e );
|
||||
|
||||
// output the nodes
|
||||
WriteOutputNodes( e->tree->headnode );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
WriteOutputFile
|
||||
====================
|
||||
*/
|
||||
void WriteOutputFile( void ) {
|
||||
int i;
|
||||
uEntity_t *entity;
|
||||
idStr qpath;
|
||||
|
||||
// write the file
|
||||
common->Printf( "----- WriteOutputFile -----\n" );
|
||||
|
||||
sprintf( qpath, "%s." PROC_FILE_EXT, dmapGlobals.mapFileBase );
|
||||
|
||||
common->Printf( "writing %s\n", qpath.c_str() );
|
||||
// _D3XP used fs_cdpath
|
||||
procFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" );
|
||||
if ( !procFile ) {
|
||||
common->Error( "Error opening %s", qpath.c_str() );
|
||||
}
|
||||
|
||||
procFile->WriteFloatString( "%s\n\n", PROC_FILE_ID );
|
||||
|
||||
// write the entity models and information, writing entities first
|
||||
for ( i=dmapGlobals.num_entities - 1 ; i >= 0 ; i-- ) {
|
||||
entity = &dmapGlobals.uEntities[i];
|
||||
|
||||
if ( !entity->primitives ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WriteOutputEntity( i );
|
||||
}
|
||||
|
||||
// write the shadow volumes
|
||||
for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) {
|
||||
mapLight_t *light = dmapGlobals.mapLights[i];
|
||||
if ( !light->shadowTris ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
procFile->WriteFloatString( "shadowModel { /* name = */ \"_prelight_%s\"\n\n", light->name );
|
||||
WriteShadowTriangles( light->shadowTris );
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
|
||||
R_FreeStaticTriSurf( light->shadowTris );
|
||||
light->shadowTris = NULL;
|
||||
}
|
||||
|
||||
fileSystem->CloseFile( procFile );
|
||||
}
|
||||
999
neo/tools/compilers/dmap/portals.cpp
Normal file
999
neo/tools/compilers/dmap/portals.cpp
Normal file
@@ -0,0 +1,999 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
|
||||
interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS];
|
||||
int numInterAreaPortals;
|
||||
|
||||
|
||||
int c_active_portals;
|
||||
int c_peak_portals;
|
||||
|
||||
/*
|
||||
===========
|
||||
AllocPortal
|
||||
===========
|
||||
*/
|
||||
uPortal_t *AllocPortal (void)
|
||||
{
|
||||
uPortal_t *p;
|
||||
|
||||
c_active_portals++;
|
||||
if (c_active_portals > c_peak_portals)
|
||||
c_peak_portals = c_active_portals;
|
||||
|
||||
p = (uPortal_t *)Mem_Alloc (sizeof(uPortal_t ));
|
||||
memset (p, 0, sizeof(uPortal_t ));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void FreePortal (uPortal_t *p)
|
||||
{
|
||||
if (p->winding)
|
||||
delete p->winding;
|
||||
c_active_portals--;
|
||||
Mem_Free (p);
|
||||
}
|
||||
|
||||
//==============================================================
|
||||
|
||||
/*
|
||||
=============
|
||||
Portal_Passable
|
||||
|
||||
Returns true if the portal has non-opaque leafs on both sides
|
||||
=============
|
||||
*/
|
||||
static bool Portal_Passable( uPortal_t *p ) {
|
||||
if (!p->onnode) {
|
||||
return false; // to global outsideleaf
|
||||
}
|
||||
|
||||
if (p->nodes[0]->planenum != PLANENUM_LEAF
|
||||
|| p->nodes[1]->planenum != PLANENUM_LEAF) {
|
||||
common->Error( "Portal_EntityFlood: not a leaf");
|
||||
}
|
||||
|
||||
if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
int c_tinyportals;
|
||||
|
||||
/*
|
||||
=============
|
||||
AddPortalToNodes
|
||||
=============
|
||||
*/
|
||||
void AddPortalToNodes (uPortal_t *p, node_t *front, node_t *back) {
|
||||
if (p->nodes[0] || p->nodes[1]) {
|
||||
common->Error( "AddPortalToNode: allready included");
|
||||
}
|
||||
|
||||
p->nodes[0] = front;
|
||||
p->next[0] = front->portals;
|
||||
front->portals = p;
|
||||
|
||||
p->nodes[1] = back;
|
||||
p->next[1] = back->portals;
|
||||
back->portals = p;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
RemovePortalFromNode
|
||||
=============
|
||||
*/
|
||||
void RemovePortalFromNode (uPortal_t *portal, node_t *l)
|
||||
{
|
||||
uPortal_t **pp, *t;
|
||||
|
||||
// remove reference to the current portal
|
||||
pp = &l->portals;
|
||||
while (1)
|
||||
{
|
||||
t = *pp;
|
||||
if (!t)
|
||||
common->Error( "RemovePortalFromNode: portal not in leaf");
|
||||
|
||||
if ( t == portal )
|
||||
break;
|
||||
|
||||
if (t->nodes[0] == l)
|
||||
pp = &t->next[0];
|
||||
else if (t->nodes[1] == l)
|
||||
pp = &t->next[1];
|
||||
else
|
||||
common->Error( "RemovePortalFromNode: portal not bounding leaf");
|
||||
}
|
||||
|
||||
if ( portal->nodes[0] == l ) {
|
||||
*pp = portal->next[0];
|
||||
portal->nodes[0] = NULL;
|
||||
} else if ( portal->nodes[1] == l ) {
|
||||
*pp = portal->next[1];
|
||||
portal->nodes[1] = NULL;
|
||||
} else {
|
||||
common->Error( "RemovePortalFromNode: mislinked" );
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void PrintPortal (uPortal_t *p)
|
||||
{
|
||||
int i;
|
||||
idWinding *w;
|
||||
|
||||
w = p->winding;
|
||||
for ( i = 0; i < w->GetNumPoints(); i++ )
|
||||
common->Printf("(%5.0f,%5.0f,%5.0f)\n",(*w)[i][0], (*w)[i][1], (*w)[i][2]);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
MakeHeadnodePortals
|
||||
|
||||
The created portals will face the global outside_node
|
||||
================
|
||||
*/
|
||||
#define SIDESPACE 8
|
||||
static void MakeHeadnodePortals( tree_t *tree ) {
|
||||
idBounds bounds;
|
||||
int i, j, n;
|
||||
uPortal_t *p, *portals[6];
|
||||
idPlane bplanes[6], *pl;
|
||||
node_t *node;
|
||||
|
||||
node = tree->headnode;
|
||||
|
||||
tree->outside_node.planenum = PLANENUM_LEAF;
|
||||
tree->outside_node.brushlist = NULL;
|
||||
tree->outside_node.portals = NULL;
|
||||
tree->outside_node.opaque = false;
|
||||
|
||||
// if no nodes, don't go any farther
|
||||
if ( node->planenum == PLANENUM_LEAF ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// pad with some space so there will never be null volume leafs
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
bounds[0][i] = tree->bounds[0][i] - SIDESPACE;
|
||||
bounds[1][i] = tree->bounds[1][i] + SIDESPACE;
|
||||
if ( bounds[0][i] >= bounds[1][i] ) {
|
||||
common->Error( "Backwards tree volume" );
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
for (j=0 ; j<2 ; j++) {
|
||||
n = j*3 + i;
|
||||
|
||||
p = AllocPortal ();
|
||||
portals[n] = p;
|
||||
|
||||
pl = &bplanes[n];
|
||||
memset (pl, 0, sizeof(*pl));
|
||||
if (j) {
|
||||
(*pl)[i] = -1;
|
||||
(*pl)[3] = bounds[j][i];
|
||||
} else {
|
||||
(*pl)[i] = 1;
|
||||
(*pl)[3] = -bounds[j][i];
|
||||
}
|
||||
p->plane = *pl;
|
||||
p->winding = new idWinding( *pl );
|
||||
AddPortalToNodes (p, node, &tree->outside_node);
|
||||
}
|
||||
}
|
||||
|
||||
// clip the basewindings by all the other planes
|
||||
for (i=0 ; i<6 ; i++) {
|
||||
for (j=0 ; j<6 ; j++) {
|
||||
if (j == i) {
|
||||
continue;
|
||||
}
|
||||
portals[i]->winding = portals[i]->winding->Clip( bplanes[j], ON_EPSILON );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
BaseWindingForNode
|
||||
================
|
||||
*/
|
||||
#define BASE_WINDING_EPSILON 0.001f
|
||||
#define SPLIT_WINDING_EPSILON 0.001f
|
||||
|
||||
idWinding *BaseWindingForNode (node_t *node) {
|
||||
idWinding *w;
|
||||
node_t *n;
|
||||
|
||||
w = new idWinding( dmapGlobals.mapPlanes[node->planenum] );
|
||||
|
||||
// clip by all the parents
|
||||
for ( n = node->parent ; n && w ; ) {
|
||||
idPlane &plane = dmapGlobals.mapPlanes[n->planenum];
|
||||
|
||||
if ( n->children[0] == node ) {
|
||||
// take front
|
||||
w = w->Clip( plane, BASE_WINDING_EPSILON );
|
||||
} else {
|
||||
// take back
|
||||
idPlane back = -plane;
|
||||
w = w->Clip( back, BASE_WINDING_EPSILON );
|
||||
}
|
||||
node = n;
|
||||
n = n->parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
MakeNodePortal
|
||||
|
||||
create the new portal by taking the full plane winding for the cutting plane
|
||||
and clipping it by all of parents of this node
|
||||
==================
|
||||
*/
|
||||
static void MakeNodePortal( node_t *node ) {
|
||||
uPortal_t *new_portal, *p;
|
||||
idWinding *w;
|
||||
idVec3 normal;
|
||||
int side;
|
||||
|
||||
w = BaseWindingForNode (node);
|
||||
|
||||
// clip the portal by all the other portals in the node
|
||||
for (p = node->portals ; p && w; p = p->next[side])
|
||||
{
|
||||
idPlane plane;
|
||||
|
||||
if (p->nodes[0] == node)
|
||||
{
|
||||
side = 0;
|
||||
plane = p->plane;
|
||||
}
|
||||
else if (p->nodes[1] == node)
|
||||
{
|
||||
side = 1;
|
||||
plane = -p->plane;
|
||||
}
|
||||
else {
|
||||
common->Error( "CutNodePortals_r: mislinked portal");
|
||||
side = 0; // quiet a compiler warning
|
||||
}
|
||||
|
||||
w = w->Clip( plane, CLIP_EPSILON );
|
||||
}
|
||||
|
||||
if (!w)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( w->IsTiny() )
|
||||
{
|
||||
c_tinyportals++;
|
||||
delete w;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
new_portal = AllocPortal ();
|
||||
new_portal->plane = dmapGlobals.mapPlanes[node->planenum];
|
||||
new_portal->onnode = node;
|
||||
new_portal->winding = w;
|
||||
AddPortalToNodes (new_portal, node->children[0], node->children[1]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
SplitNodePortals
|
||||
|
||||
Move or split the portals that bound node so that the node's
|
||||
children have portals instead of node.
|
||||
==============
|
||||
*/
|
||||
static void SplitNodePortals( node_t *node ) {
|
||||
uPortal_t *p, *next_portal, *new_portal;
|
||||
node_t *f, *b, *other_node;
|
||||
int side;
|
||||
idPlane *plane;
|
||||
idWinding *frontwinding, *backwinding;
|
||||
|
||||
plane = &dmapGlobals.mapPlanes[node->planenum];
|
||||
f = node->children[0];
|
||||
b = node->children[1];
|
||||
|
||||
for ( p = node->portals ; p ; p = next_portal ) {
|
||||
if (p->nodes[0] == node ) {
|
||||
side = 0;
|
||||
} else if ( p->nodes[1] == node ) {
|
||||
side = 1;
|
||||
} else {
|
||||
common->Error( "SplitNodePortals: mislinked portal" );
|
||||
side = 0; // quiet a compiler warning
|
||||
}
|
||||
next_portal = p->next[side];
|
||||
|
||||
other_node = p->nodes[!side];
|
||||
RemovePortalFromNode (p, p->nodes[0]);
|
||||
RemovePortalFromNode (p, p->nodes[1]);
|
||||
|
||||
//
|
||||
// cut the portal into two portals, one on each side of the cut plane
|
||||
//
|
||||
p->winding->Split( *plane, SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
|
||||
|
||||
if ( frontwinding && frontwinding->IsTiny() )
|
||||
{
|
||||
delete frontwinding;
|
||||
frontwinding = NULL;
|
||||
c_tinyportals++;
|
||||
}
|
||||
|
||||
if ( backwinding && backwinding->IsTiny() )
|
||||
{
|
||||
delete backwinding;
|
||||
backwinding = NULL;
|
||||
c_tinyportals++;
|
||||
}
|
||||
|
||||
if ( !frontwinding && !backwinding )
|
||||
{ // tiny windings on both sides
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!frontwinding)
|
||||
{
|
||||
delete backwinding;
|
||||
if (side == 0)
|
||||
AddPortalToNodes (p, b, other_node);
|
||||
else
|
||||
AddPortalToNodes (p, other_node, b);
|
||||
continue;
|
||||
}
|
||||
if (!backwinding)
|
||||
{
|
||||
delete frontwinding;
|
||||
if (side == 0)
|
||||
AddPortalToNodes (p, f, other_node);
|
||||
else
|
||||
AddPortalToNodes (p, other_node, f);
|
||||
continue;
|
||||
}
|
||||
|
||||
// the winding is split
|
||||
new_portal = AllocPortal ();
|
||||
*new_portal = *p;
|
||||
new_portal->winding = backwinding;
|
||||
delete p->winding;
|
||||
p->winding = frontwinding;
|
||||
|
||||
if (side == 0)
|
||||
{
|
||||
AddPortalToNodes (p, f, other_node);
|
||||
AddPortalToNodes (new_portal, b, other_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddPortalToNodes (p, other_node, f);
|
||||
AddPortalToNodes (new_portal, other_node, b);
|
||||
}
|
||||
}
|
||||
|
||||
node->portals = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CalcNodeBounds
|
||||
================
|
||||
*/
|
||||
void CalcNodeBounds (node_t *node)
|
||||
{
|
||||
uPortal_t *p;
|
||||
int s;
|
||||
int i;
|
||||
|
||||
// calc mins/maxs for both leafs and nodes
|
||||
node->bounds.Clear();
|
||||
for (p = node->portals ; p ; p = p->next[s]) {
|
||||
s = (p->nodes[1] == node);
|
||||
for ( i = 0; i < p->winding->GetNumPoints(); i++ ) {
|
||||
node->bounds.AddPoint( (*p->winding)[i].ToVec3() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
MakeTreePortals_r
|
||||
==================
|
||||
*/
|
||||
void MakeTreePortals_r (node_t *node)
|
||||
{
|
||||
int i;
|
||||
|
||||
CalcNodeBounds( node );
|
||||
|
||||
if ( node->bounds[0][0] >= node->bounds[1][0]) {
|
||||
common->Warning( "node without a volume" );
|
||||
}
|
||||
|
||||
for ( i = 0; i < 3; i++ ) {
|
||||
if ( node->bounds[0][i] < MIN_WORLD_COORD || node->bounds[1][i] > MAX_WORLD_COORD ) {
|
||||
common->Warning( "node with unbounded volume");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( node->planenum == PLANENUM_LEAF ) {
|
||||
return;
|
||||
}
|
||||
|
||||
MakeNodePortal (node);
|
||||
SplitNodePortals (node);
|
||||
|
||||
MakeTreePortals_r (node->children[0]);
|
||||
MakeTreePortals_r (node->children[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
MakeTreePortals
|
||||
==================
|
||||
*/
|
||||
void MakeTreePortals (tree_t *tree)
|
||||
{
|
||||
common->Printf( "----- MakeTreePortals -----\n");
|
||||
MakeHeadnodePortals (tree);
|
||||
MakeTreePortals_r (tree->headnode);
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================
|
||||
|
||||
FLOOD ENTITIES
|
||||
|
||||
=========================================================
|
||||
*/
|
||||
|
||||
int c_floodedleafs;
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodPortals_r
|
||||
=============
|
||||
*/
|
||||
void FloodPortals_r (node_t *node, int dist) {
|
||||
uPortal_t *p;
|
||||
int s;
|
||||
|
||||
if ( node->occupied ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->opaque ) {
|
||||
return;
|
||||
}
|
||||
|
||||
c_floodedleafs++;
|
||||
node->occupied = dist;
|
||||
|
||||
for (p=node->portals ; p ; p = p->next[s]) {
|
||||
s = (p->nodes[1] == node);
|
||||
FloodPortals_r (p->nodes[!s], dist+1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
PlaceOccupant
|
||||
=============
|
||||
*/
|
||||
bool PlaceOccupant( node_t *headnode, idVec3 origin, uEntity_t *occupant ) {
|
||||
node_t *node;
|
||||
float d;
|
||||
idPlane *plane;
|
||||
|
||||
// find the leaf to start in
|
||||
node = headnode;
|
||||
while ( node->planenum != PLANENUM_LEAF ) {
|
||||
plane = &dmapGlobals.mapPlanes[node->planenum];
|
||||
d = plane->Distance( origin );
|
||||
if ( d >= 0.0f ) {
|
||||
node = node->children[0];
|
||||
} else {
|
||||
node = node->children[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ( node->opaque ) {
|
||||
return false;
|
||||
}
|
||||
node->occupant = occupant;
|
||||
|
||||
FloodPortals_r (node, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodEntities
|
||||
|
||||
Marks all nodes that can be reached by entites
|
||||
=============
|
||||
*/
|
||||
bool FloodEntities( tree_t *tree ) {
|
||||
int i;
|
||||
idVec3 origin;
|
||||
const char *cl;
|
||||
bool inside;
|
||||
node_t *headnode;
|
||||
|
||||
headnode = tree->headnode;
|
||||
common->Printf ("--- FloodEntities ---\n");
|
||||
inside = false;
|
||||
tree->outside_node.occupied = 0;
|
||||
|
||||
c_floodedleafs = 0;
|
||||
bool errorShown = false;
|
||||
for (i=1 ; i<dmapGlobals.num_entities ; i++) {
|
||||
idMapEntity *mapEnt;
|
||||
|
||||
mapEnt = dmapGlobals.uEntities[i].mapEntity;
|
||||
if ( !mapEnt->epairs.GetVector( "origin", "", origin) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// any entity can have "noFlood" set to skip it
|
||||
if ( mapEnt->epairs.GetString( "noFlood", "", &cl ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mapEnt->epairs.GetString( "classname", "", &cl );
|
||||
|
||||
if ( !strcmp( cl, "light" ) ) {
|
||||
const char *v;
|
||||
|
||||
// don't place lights that have a light_start field, because they can still
|
||||
// be valid if their origin is outside the world
|
||||
mapEnt->epairs.GetString( "light_start", "", &v);
|
||||
if ( v[0] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't place fog lights, because they often
|
||||
// have origins outside the light
|
||||
mapEnt->epairs.GetString( "texture", "", &v);
|
||||
if ( v[0] ) {
|
||||
const idMaterial *mat = declManager->FindMaterial( v );
|
||||
if ( mat->IsFogLight() ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PlaceOccupant (headnode, origin, &dmapGlobals.uEntities[i])) {
|
||||
inside = true;
|
||||
}
|
||||
|
||||
if (tree->outside_node.occupied && !errorShown) {
|
||||
errorShown = true;
|
||||
common->Printf("Leak on entity # %d\n", i);
|
||||
const char *p;
|
||||
|
||||
mapEnt->epairs.GetString( "classname", "", &p);
|
||||
common->Printf("Entity classname was: %s\n", p);
|
||||
mapEnt->epairs.GetString( "name", "", &p);
|
||||
common->Printf("Entity name was: %s\n", p);
|
||||
idVec3 origin;
|
||||
if ( mapEnt->epairs.GetVector( "origin", "", origin)) {
|
||||
common->Printf("Entity origin is: %f %f %f\n\n\n", origin.x, origin.y, origin.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
common->Printf("%5i flooded leafs\n", c_floodedleafs );
|
||||
|
||||
if (!inside)
|
||||
{
|
||||
common->Printf ("no entities in open -- no filling\n");
|
||||
}
|
||||
else if (tree->outside_node.occupied)
|
||||
{
|
||||
common->Printf ("entity reached from outside -- no filling\n");
|
||||
}
|
||||
|
||||
return (bool)(inside && !tree->outside_node.occupied);
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================
|
||||
|
||||
FLOOD AREAS
|
||||
|
||||
=========================================================
|
||||
*/
|
||||
|
||||
static int c_areas;
|
||||
static int c_areaFloods;
|
||||
|
||||
/*
|
||||
=================
|
||||
FindSideForPortal
|
||||
=================
|
||||
*/
|
||||
static side_t *FindSideForPortal( uPortal_t *p ) {
|
||||
int i, j, k;
|
||||
node_t *node;
|
||||
uBrush_t *b, *orig;
|
||||
side_t *s, *s2;
|
||||
|
||||
// scan both bordering nodes brush lists for a portal brush
|
||||
// that shares the plane
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
node = p->nodes[i];
|
||||
for ( b = node->brushlist ; b ; b = b->next ) {
|
||||
if ( !( b->contents & CONTENTS_AREAPORTAL ) ) {
|
||||
continue;
|
||||
}
|
||||
orig = b->original;
|
||||
for ( j = 0 ; j < orig->numsides ; j++ ) {
|
||||
s = orig->sides + j;
|
||||
if ( !s->visibleHull ) {
|
||||
continue;
|
||||
}
|
||||
if ( !( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( ( s->planenum & ~1 ) != ( p->onnode->planenum & ~1 ) ) {
|
||||
continue;
|
||||
}
|
||||
// remove the visible hull from any other portal sides of this portal brush
|
||||
for ( k = 0; k < orig->numsides; k++ ) {
|
||||
if ( k == j ) {
|
||||
continue;
|
||||
}
|
||||
s2 = orig->sides + k;
|
||||
if ( s2->visibleHull == NULL ) {
|
||||
continue;
|
||||
}
|
||||
if ( !( s2->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
|
||||
continue;
|
||||
}
|
||||
common->Warning( "brush has multiple area portal sides at %s", s2->visibleHull->GetCenter().ToString() );
|
||||
delete s2->visibleHull;
|
||||
s2->visibleHull = NULL;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodAreas_r
|
||||
=============
|
||||
*/
|
||||
void FloodAreas_r (node_t *node)
|
||||
{
|
||||
uPortal_t *p;
|
||||
int s;
|
||||
|
||||
if ( node->area != -1 ) {
|
||||
return; // allready got it
|
||||
}
|
||||
if ( node->opaque ) {
|
||||
return;
|
||||
}
|
||||
|
||||
c_areaFloods++;
|
||||
node->area = c_areas;
|
||||
|
||||
for ( p=node->portals ; p ; p = p->next[s] ) {
|
||||
node_t *other;
|
||||
|
||||
s = (p->nodes[1] == node);
|
||||
other = p->nodes[!s];
|
||||
|
||||
if ( !Portal_Passable(p) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// can't flood through an area portal
|
||||
if ( FindSideForPortal( p ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FloodAreas_r( other );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FindAreas_r
|
||||
|
||||
Just decend the tree, and for each node that hasn't had an
|
||||
area set, flood fill out from there
|
||||
=============
|
||||
*/
|
||||
void FindAreas_r( node_t *node ) {
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
FindAreas_r (node->children[0]);
|
||||
FindAreas_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->opaque ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->area != -1 ) {
|
||||
return; // allready got it
|
||||
}
|
||||
|
||||
c_areaFloods = 0;
|
||||
FloodAreas_r (node);
|
||||
common->Printf( "area %i has %i leafs\n", c_areas, c_areaFloods );
|
||||
c_areas++;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
CheckAreas_r
|
||||
============
|
||||
*/
|
||||
void CheckAreas_r( node_t *node ) {
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
CheckAreas_r (node->children[0]);
|
||||
CheckAreas_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
if ( !node->opaque && node->area < 0 ) {
|
||||
common->Error( "CheckAreas_r: area = %i", node->area );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
ClearAreas_r
|
||||
|
||||
Set all the areas to -1 before filling
|
||||
============
|
||||
*/
|
||||
void ClearAreas_r( node_t *node ) {
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
ClearAreas_r (node->children[0]);
|
||||
ClearAreas_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
node->area = -1;
|
||||
}
|
||||
|
||||
//=============================================================
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
FindInterAreaPortals_r
|
||||
|
||||
=================
|
||||
*/
|
||||
static void FindInterAreaPortals_r( node_t *node ) {
|
||||
uPortal_t *p;
|
||||
int s;
|
||||
int i;
|
||||
idWinding *w;
|
||||
interAreaPortal_t *iap;
|
||||
side_t *side;
|
||||
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
FindInterAreaPortals_r( node->children[0] );
|
||||
FindInterAreaPortals_r( node->children[1] );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->opaque ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( p=node->portals ; p ; p = p->next[s] ) {
|
||||
node_t *other;
|
||||
|
||||
s = (p->nodes[1] == node);
|
||||
other = p->nodes[!s];
|
||||
|
||||
if ( other->opaque ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only report areas going from lower number to higher number
|
||||
// so we don't report the portal twice
|
||||
if ( other->area <= node->area ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
side = FindSideForPortal( p );
|
||||
// w = p->winding;
|
||||
if ( !side ) {
|
||||
common->Warning( "FindSideForPortal failed at %s", p->winding->GetCenter().ToString() );
|
||||
continue;
|
||||
}
|
||||
w = side->visibleHull;
|
||||
if ( !w ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// see if we have created this portal before
|
||||
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
||||
iap = &interAreaPortals[i];
|
||||
|
||||
if ( side == iap->side &&
|
||||
( ( p->nodes[0]->area == iap->area0 && p->nodes[1]->area == iap->area1 )
|
||||
|| ( p->nodes[1]->area == iap->area0 && p->nodes[0]->area == iap->area1 ) ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i != numInterAreaPortals ) {
|
||||
continue; // already emited
|
||||
}
|
||||
|
||||
iap = &interAreaPortals[numInterAreaPortals];
|
||||
numInterAreaPortals++;
|
||||
if ( side->planenum == p->onnode->planenum ) {
|
||||
iap->area0 = p->nodes[0]->area;
|
||||
iap->area1 = p->nodes[1]->area;
|
||||
} else {
|
||||
iap->area0 = p->nodes[1]->area;
|
||||
iap->area1 = p->nodes[0]->area;
|
||||
}
|
||||
iap->side = side;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FloodAreas
|
||||
|
||||
Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
|
||||
Sets e->areas.numAreas
|
||||
=============
|
||||
*/
|
||||
void FloodAreas( uEntity_t *e ) {
|
||||
common->Printf ("--- FloodAreas ---\n");
|
||||
|
||||
// set all areas to -1
|
||||
ClearAreas_r( e->tree->headnode );
|
||||
|
||||
// flood fill from non-opaque areas
|
||||
c_areas = 0;
|
||||
FindAreas_r( e->tree->headnode );
|
||||
|
||||
common->Printf ("%5i areas\n", c_areas);
|
||||
e->numAreas = c_areas;
|
||||
|
||||
// make sure we got all of them
|
||||
CheckAreas_r( e->tree->headnode );
|
||||
|
||||
// identify all portals between areas if this is the world
|
||||
if ( e == &dmapGlobals.uEntities[0] ) {
|
||||
numInterAreaPortals = 0;
|
||||
FindInterAreaPortals_r( e->tree->headnode );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
======================================================
|
||||
|
||||
FILL OUTSIDE
|
||||
|
||||
======================================================
|
||||
*/
|
||||
|
||||
static int c_outside;
|
||||
static int c_inside;
|
||||
static int c_solid;
|
||||
|
||||
void FillOutside_r (node_t *node)
|
||||
{
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FillOutside_r (node->children[0]);
|
||||
FillOutside_r (node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// anything not reachable by an entity
|
||||
// can be filled away
|
||||
if (!node->occupied) {
|
||||
if ( !node->opaque ) {
|
||||
c_outside++;
|
||||
node->opaque = true;
|
||||
} else {
|
||||
c_solid++;
|
||||
}
|
||||
} else {
|
||||
c_inside++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FillOutside
|
||||
|
||||
Fill (set node->opaque = true) all nodes that can't be reached by entities
|
||||
=============
|
||||
*/
|
||||
void FillOutside( uEntity_t *e ) {
|
||||
c_outside = 0;
|
||||
c_inside = 0;
|
||||
c_solid = 0;
|
||||
common->Printf ("--- FillOutside ---\n");
|
||||
FillOutside_r( e->tree->headnode );
|
||||
common->Printf ("%5i solid leafs\n", c_solid);
|
||||
common->Printf ("%5i leafs filled\n", c_outside);
|
||||
common->Printf ("%5i inside leafs\n", c_inside);
|
||||
}
|
||||
1276
neo/tools/compilers/dmap/shadowopt3.cpp
Normal file
1276
neo/tools/compilers/dmap/shadowopt3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
663
neo/tools/compilers/dmap/tritjunction.cpp
Normal file
663
neo/tools/compilers/dmap/tritjunction.cpp
Normal file
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
/*
|
||||
|
||||
T junction fixing never creates more xyz points, but
|
||||
new vertexes will be created when different surfaces
|
||||
cause a fix
|
||||
|
||||
The vertex cleaning accomplishes two goals: removing extranious low order
|
||||
bits to avoid numbers like 1.000001233, and grouping nearby vertexes
|
||||
together. Straight truncation accomplishes the first foal, but two vertexes
|
||||
only a tiny epsilon apart could still be spread to different snap points.
|
||||
To avoid this, we allow the merge test to group points together that
|
||||
snapped to neighboring integer coordinates.
|
||||
|
||||
Snaping verts can drag some triangles backwards or collapse them to points,
|
||||
which will cause them to be removed.
|
||||
|
||||
|
||||
When snapping to ints, a point can move a maximum of sqrt(3)/2 distance
|
||||
Two points that were an epsilon apart can then become sqrt(3) apart
|
||||
|
||||
A case that causes recursive overflow with point to triangle fixing:
|
||||
|
||||
A
|
||||
C D
|
||||
B
|
||||
|
||||
Triangle ABC tests against point D and splits into triangles ADC and DBC
|
||||
Triangle DBC then tests against point A again and splits into ABC and ADB
|
||||
infinite recursive loop
|
||||
|
||||
|
||||
For a given source triangle
|
||||
init the no-check list to hold the three triangle hashVerts
|
||||
|
||||
recursiveFixTriAgainstHash
|
||||
|
||||
recursiveFixTriAgainstHashVert_r
|
||||
if hashVert is on the no-check list
|
||||
exit
|
||||
if the hashVert should split the triangle
|
||||
add to the no-check list
|
||||
recursiveFixTriAgainstHash(a)
|
||||
recursiveFixTriAgainstHash(b)
|
||||
|
||||
*/
|
||||
|
||||
#define SNAP_FRACTIONS 32
|
||||
//#define SNAP_FRACTIONS 8
|
||||
//#define SNAP_FRACTIONS 1
|
||||
|
||||
#define VERTEX_EPSILON ( 1.0 / SNAP_FRACTIONS )
|
||||
|
||||
#define COLINEAR_EPSILON ( 1.8 * VERTEX_EPSILON )
|
||||
|
||||
#define HASH_BINS 16
|
||||
|
||||
typedef struct hashVert_s {
|
||||
struct hashVert_s *next;
|
||||
idVec3 v;
|
||||
int iv[3];
|
||||
} hashVert_t;
|
||||
|
||||
static idBounds hashBounds;
|
||||
static idVec3 hashScale;
|
||||
static hashVert_t *hashVerts[HASH_BINS][HASH_BINS][HASH_BINS];
|
||||
static int numHashVerts, numTotalVerts;
|
||||
static int hashIntMins[3], hashIntScale[3];
|
||||
|
||||
/*
|
||||
===============
|
||||
GetHashVert
|
||||
|
||||
Also modifies the original vert to the snapped value
|
||||
===============
|
||||
*/
|
||||
struct hashVert_s *GetHashVert( idVec3 &v ) {
|
||||
int iv[3];
|
||||
int block[3];
|
||||
int i;
|
||||
hashVert_t *hv;
|
||||
|
||||
numTotalVerts++;
|
||||
|
||||
// snap the vert to integral values
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
iv[i] = floor( ( v[i] + 0.5/SNAP_FRACTIONS ) * SNAP_FRACTIONS );
|
||||
block[i] = ( iv[i] - hashIntMins[i] ) / hashIntScale[i];
|
||||
if ( block[i] < 0 ) {
|
||||
block[i] = 0;
|
||||
} else if ( block[i] >= HASH_BINS ) {
|
||||
block[i] = HASH_BINS - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// see if a vertex near enough already exists
|
||||
// this could still fail to find a near neighbor right at the hash block boundary
|
||||
for ( hv = hashVerts[block[0]][block[1]][block[2]] ; hv ; hv = hv->next ) {
|
||||
#if 0
|
||||
if ( hv->iv[0] == iv[0] && hv->iv[1] == iv[1] && hv->iv[2] == iv[2] ) {
|
||||
VectorCopy( hv->v, v );
|
||||
return hv;
|
||||
}
|
||||
#else
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
int d;
|
||||
d = hv->iv[i] - iv[i];
|
||||
if ( d < -1 || d > 1 ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( i == 3 ) {
|
||||
VectorCopy( hv->v, v );
|
||||
return hv;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// create a new one
|
||||
hv = (hashVert_t *)Mem_Alloc( sizeof( *hv ) );
|
||||
|
||||
hv->next = hashVerts[block[0]][block[1]][block[2]];
|
||||
hashVerts[block[0]][block[1]][block[2]] = hv;
|
||||
|
||||
hv->iv[0] = iv[0];
|
||||
hv->iv[1] = iv[1];
|
||||
hv->iv[2] = iv[2];
|
||||
|
||||
hv->v[0] = (float)iv[0] / SNAP_FRACTIONS;
|
||||
hv->v[1] = (float)iv[1] / SNAP_FRACTIONS;
|
||||
hv->v[2] = (float)iv[2] / SNAP_FRACTIONS;
|
||||
|
||||
VectorCopy( hv->v, v );
|
||||
|
||||
numHashVerts++;
|
||||
|
||||
return hv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
HashBlocksForTri
|
||||
|
||||
Returns an inclusive bounding box of hash
|
||||
bins that should hold the triangle
|
||||
==================
|
||||
*/
|
||||
static void HashBlocksForTri( const mapTri_t *tri, int blocks[2][3] ) {
|
||||
idBounds bounds;
|
||||
int i;
|
||||
|
||||
bounds.Clear();
|
||||
bounds.AddPoint( tri->v[0].xyz );
|
||||
bounds.AddPoint( tri->v[1].xyz );
|
||||
bounds.AddPoint( tri->v[2].xyz );
|
||||
|
||||
// add a 1.0 slop margin on each side
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
blocks[0][i] = ( bounds[0][i] - 1.0 - hashBounds[0][i] ) / hashScale[i];
|
||||
if ( blocks[0][i] < 0 ) {
|
||||
blocks[0][i] = 0;
|
||||
} else if ( blocks[0][i] >= HASH_BINS ) {
|
||||
blocks[0][i] = HASH_BINS - 1;
|
||||
}
|
||||
|
||||
blocks[1][i] = ( bounds[1][i] + 1.0 - hashBounds[0][i] ) / hashScale[i];
|
||||
if ( blocks[1][i] < 0 ) {
|
||||
blocks[1][i] = 0;
|
||||
} else if ( blocks[1][i] >= HASH_BINS ) {
|
||||
blocks[1][i] = HASH_BINS - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
HashTriangles
|
||||
|
||||
Removes triangles that are degenerated or flipped backwards
|
||||
=================
|
||||
*/
|
||||
void HashTriangles( optimizeGroup_t *groupList ) {
|
||||
mapTri_t *a;
|
||||
int vert;
|
||||
int i;
|
||||
optimizeGroup_t *group;
|
||||
|
||||
// clear the hash tables
|
||||
memset( hashVerts, 0, sizeof( hashVerts ) );
|
||||
|
||||
numHashVerts = 0;
|
||||
numTotalVerts = 0;
|
||||
|
||||
// bound all the triangles to determine the bucket size
|
||||
hashBounds.Clear();
|
||||
for ( group = groupList ; group ; group = group->nextGroup ) {
|
||||
for ( a = group->triList ; a ; a = a->next ) {
|
||||
hashBounds.AddPoint( a->v[0].xyz );
|
||||
hashBounds.AddPoint( a->v[1].xyz );
|
||||
hashBounds.AddPoint( a->v[2].xyz );
|
||||
}
|
||||
}
|
||||
|
||||
// spread the bounds so it will never have a zero size
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
|
||||
hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
|
||||
hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
|
||||
|
||||
hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
|
||||
hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
|
||||
if ( hashIntScale[i] < 1 ) {
|
||||
hashIntScale[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// add all the points to the hash buckets
|
||||
for ( group = groupList ; group ; group = group->nextGroup ) {
|
||||
// don't create tjunctions against discrete surfaces (blood decals, etc)
|
||||
if ( group->material != NULL && group->material->IsDiscrete() ) {
|
||||
continue;
|
||||
}
|
||||
for ( a = group->triList ; a ; a = a->next ) {
|
||||
for ( vert = 0 ; vert < 3 ; vert++ ) {
|
||||
a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FreeTJunctionHash
|
||||
|
||||
The optimizer may add some more crossing verts
|
||||
after t junction processing
|
||||
=================
|
||||
*/
|
||||
void FreeTJunctionHash( void ) {
|
||||
int i, j, k;
|
||||
hashVert_t *hv, *next;
|
||||
|
||||
for ( i = 0 ; i < HASH_BINS ; i++ ) {
|
||||
for ( j = 0 ; j < HASH_BINS ; j++ ) {
|
||||
for ( k = 0 ; k < HASH_BINS ; k++ ) {
|
||||
for ( hv = hashVerts[i][j][k] ; hv ; hv = next ) {
|
||||
next = hv->next;
|
||||
Mem_Free( hv );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
memset( hashVerts, 0, sizeof( hashVerts ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
FixTriangleAgainstHashVert
|
||||
|
||||
Returns a list of two new mapTri if the hashVert is
|
||||
on an edge of the given mapTri, otherwise returns NULL.
|
||||
==================
|
||||
*/
|
||||
static mapTri_t *FixTriangleAgainstHashVert( const mapTri_t *a, const hashVert_t *hv ) {
|
||||
int i;
|
||||
const idDrawVert *v1, *v2, *v3;
|
||||
idDrawVert split;
|
||||
idVec3 dir;
|
||||
float len;
|
||||
float frac;
|
||||
mapTri_t *new1, *new2;
|
||||
idVec3 temp;
|
||||
float d, off;
|
||||
const idVec3 *v;
|
||||
idPlane plane1, plane2;
|
||||
|
||||
v = &hv->v;
|
||||
|
||||
// if the triangle already has this hashVert as a vert,
|
||||
// it can't be split by it
|
||||
if ( a->hashVert[0] == hv || a->hashVert[1] == hv || a->hashVert[2] == hv ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// we probably should find the edge that the vertex is closest to.
|
||||
// it is possible to be < 1 unit away from multiple
|
||||
// edges, but we only want to split by one of them
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
v1 = &a->v[i];
|
||||
v2 = &a->v[(i+1)%3];
|
||||
v3 = &a->v[(i+2)%3];
|
||||
VectorSubtract( v2->xyz, v1->xyz, dir );
|
||||
len = dir.Normalize();
|
||||
|
||||
// if it is close to one of the edge vertexes, skip it
|
||||
VectorSubtract( *v, v1->xyz, temp );
|
||||
d = DotProduct( temp, dir );
|
||||
if ( d <= 0 || d >= len ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure it is on the line
|
||||
VectorMA( v1->xyz, d, dir, temp );
|
||||
VectorSubtract( temp, *v, temp );
|
||||
off = temp.Length();
|
||||
if ( off <= -COLINEAR_EPSILON || off >= COLINEAR_EPSILON ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// take the x/y/z from the splitter,
|
||||
// but interpolate everything else from the original tri
|
||||
VectorCopy( *v, split.xyz );
|
||||
frac = d / len;
|
||||
split.st[0] = v1->st[0] + frac * ( v2->st[0] - v1->st[0] );
|
||||
split.st[1] = v1->st[1] + frac * ( v2->st[1] - v1->st[1] );
|
||||
split.normal[0] = v1->normal[0] + frac * ( v2->normal[0] - v1->normal[0] );
|
||||
split.normal[1] = v1->normal[1] + frac * ( v2->normal[1] - v1->normal[1] );
|
||||
split.normal[2] = v1->normal[2] + frac * ( v2->normal[2] - v1->normal[2] );
|
||||
split.normal.Normalize();
|
||||
|
||||
// split the tri
|
||||
new1 = CopyMapTri( a );
|
||||
new1->v[(i+1)%3] = split;
|
||||
new1->hashVert[(i+1)%3] = hv;
|
||||
new1->next = NULL;
|
||||
|
||||
new2 = CopyMapTri( a );
|
||||
new2->v[i] = split;
|
||||
new2->hashVert[i] = hv;
|
||||
new2->next = new1;
|
||||
|
||||
plane1.FromPoints( new1->hashVert[0]->v, new1->hashVert[1]->v, new1->hashVert[2]->v );
|
||||
plane2.FromPoints( new2->hashVert[0]->v, new2->hashVert[1]->v, new2->hashVert[2]->v );
|
||||
|
||||
d = DotProduct( plane1, plane2 );
|
||||
|
||||
// if the two split triangle's normals don't face the same way,
|
||||
// it should not be split
|
||||
if ( d <= 0 ) {
|
||||
FreeTriList( new2 );
|
||||
continue;
|
||||
}
|
||||
|
||||
return new2;
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
FixTriangleAgainstHash
|
||||
|
||||
Potentially splits a triangle into a list of triangles based on tjunctions
|
||||
==================
|
||||
*/
|
||||
static mapTri_t *FixTriangleAgainstHash( const mapTri_t *tri ) {
|
||||
mapTri_t *fixed;
|
||||
mapTri_t *a;
|
||||
mapTri_t *test, *next;
|
||||
int blocks[2][3];
|
||||
int i, j, k;
|
||||
hashVert_t *hv;
|
||||
|
||||
// if this triangle is degenerate after point snapping,
|
||||
// do nothing (this shouldn't happen, because they should
|
||||
// be removed as they are hashed)
|
||||
if ( tri->hashVert[0] == tri->hashVert[1]
|
||||
|| tri->hashVert[0] == tri->hashVert[2]
|
||||
|| tri->hashVert[1] == tri->hashVert[2] ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fixed = CopyMapTri( tri );
|
||||
fixed->next = NULL;
|
||||
|
||||
HashBlocksForTri( tri, blocks );
|
||||
for ( i = blocks[0][0] ; i <= blocks[1][0] ; i++ ) {
|
||||
for ( j = blocks[0][1] ; j <= blocks[1][1] ; j++ ) {
|
||||
for ( k = blocks[0][2] ; k <= blocks[1][2] ; k++ ) {
|
||||
for ( hv = hashVerts[i][j][k] ; hv ; hv = hv->next ) {
|
||||
// fix all triangles in the list against this point
|
||||
test = fixed;
|
||||
fixed = NULL;
|
||||
for ( ; test ; test = next ) {
|
||||
next = test->next;
|
||||
a = FixTriangleAgainstHashVert( test, hv );
|
||||
if ( a ) {
|
||||
// cut into two triangles
|
||||
a->next->next = fixed;
|
||||
fixed = a;
|
||||
FreeTri( test );
|
||||
} else {
|
||||
test->next = fixed;
|
||||
fixed = test;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fixed;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CountGroupListTris
|
||||
==================
|
||||
*/
|
||||
int CountGroupListTris( const optimizeGroup_t *groupList ) {
|
||||
int c;
|
||||
|
||||
c = 0;
|
||||
for ( ; groupList ; groupList = groupList->nextGroup ) {
|
||||
c += CountTriList( groupList->triList );
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FixAreaGroupsTjunctions
|
||||
==================
|
||||
*/
|
||||
void FixAreaGroupsTjunctions( optimizeGroup_t *groupList ) {
|
||||
const mapTri_t *tri;
|
||||
mapTri_t *newList;
|
||||
mapTri_t *fixed;
|
||||
int startCount, endCount;
|
||||
optimizeGroup_t *group;
|
||||
|
||||
if ( dmapGlobals.noTJunc ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !groupList ) {
|
||||
return;
|
||||
}
|
||||
|
||||
startCount = CountGroupListTris( groupList );
|
||||
|
||||
if ( dmapGlobals.verbose ) {
|
||||
common->Printf( "----- FixAreaGroupsTjunctions -----\n" );
|
||||
common->Printf( "%6i triangles in\n", startCount );
|
||||
}
|
||||
|
||||
HashTriangles( groupList );
|
||||
|
||||
for ( group = groupList ; group ; group = group->nextGroup ) {
|
||||
// don't touch discrete surfaces
|
||||
if ( group->material != NULL && group->material->IsDiscrete() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newList = NULL;
|
||||
for ( tri = group->triList ; tri ; tri = tri->next ) {
|
||||
fixed = FixTriangleAgainstHash( tri );
|
||||
newList = MergeTriLists( newList, fixed );
|
||||
}
|
||||
FreeTriList( group->triList );
|
||||
group->triList = newList;
|
||||
}
|
||||
|
||||
endCount = CountGroupListTris( groupList );
|
||||
if ( dmapGlobals.verbose ) {
|
||||
common->Printf( "%6i triangles out\n", endCount );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
FixEntityTjunctions
|
||||
==================
|
||||
*/
|
||||
void FixEntityTjunctions( uEntity_t *e ) {
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < e->numAreas ; i++ ) {
|
||||
FixAreaGroupsTjunctions( e->areas[i].groups );
|
||||
FreeTJunctionHash();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
FixGlobalTjunctions
|
||||
==================
|
||||
*/
|
||||
void FixGlobalTjunctions( uEntity_t *e ) {
|
||||
mapTri_t *a;
|
||||
int vert;
|
||||
int i;
|
||||
optimizeGroup_t *group;
|
||||
int areaNum;
|
||||
|
||||
common->Printf( "----- FixGlobalTjunctions -----\n" );
|
||||
|
||||
// clear the hash tables
|
||||
memset( hashVerts, 0, sizeof( hashVerts ) );
|
||||
|
||||
numHashVerts = 0;
|
||||
numTotalVerts = 0;
|
||||
|
||||
// bound all the triangles to determine the bucket size
|
||||
hashBounds.Clear();
|
||||
for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
|
||||
for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
|
||||
for ( a = group->triList ; a ; a = a->next ) {
|
||||
hashBounds.AddPoint( a->v[0].xyz );
|
||||
hashBounds.AddPoint( a->v[1].xyz );
|
||||
hashBounds.AddPoint( a->v[2].xyz );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// spread the bounds so it will never have a zero size
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
|
||||
hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
|
||||
hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
|
||||
|
||||
hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
|
||||
hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
|
||||
if ( hashIntScale[i] < 1 ) {
|
||||
hashIntScale[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// add all the points to the hash buckets
|
||||
for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
|
||||
for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
|
||||
// don't touch discrete surfaces
|
||||
if ( group->material != NULL && group->material->IsDiscrete() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( a = group->triList ; a ; a = a->next ) {
|
||||
for ( vert = 0 ; vert < 3 ; vert++ ) {
|
||||
a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add all the func_static model vertexes to the hash buckets
|
||||
// optionally inline some of the func_static models
|
||||
if ( dmapGlobals.entityNum == 0 ) {
|
||||
for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) {
|
||||
uEntity_t *entity = &dmapGlobals.uEntities[eNum];
|
||||
const char *className = entity->mapEntity->epairs.GetString( "classname" );
|
||||
if ( idStr::Icmp( className, "func_static" ) ) {
|
||||
continue;
|
||||
}
|
||||
const char *modelName = entity->mapEntity->epairs.GetString( "model" );
|
||||
if ( !modelName ) {
|
||||
continue;
|
||||
}
|
||||
if ( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idRenderModel *model = renderModelManager->FindModel( modelName );
|
||||
|
||||
// common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
|
||||
|
||||
idMat3 axis;
|
||||
// get the rotation matrix in either full form, or single angle form
|
||||
if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
|
||||
float angle = entity->mapEntity->epairs.GetFloat( "angle" );
|
||||
if ( angle != 0.0f ) {
|
||||
axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
|
||||
} else {
|
||||
axis.Identity();
|
||||
}
|
||||
}
|
||||
|
||||
idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" );
|
||||
|
||||
for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
|
||||
const modelSurface_t *surface = model->Surface( i );
|
||||
const srfTriangles_t *tri = surface->geometry;
|
||||
|
||||
mapTri_t mapTri;
|
||||
memset( &mapTri, 0, sizeof( mapTri ) );
|
||||
mapTri.material = surface->shader;
|
||||
// don't let discretes (autosprites, etc) merge together
|
||||
if ( mapTri.material->IsDiscrete() ) {
|
||||
mapTri.mergeGroup = (void *)surface;
|
||||
}
|
||||
for ( int j = 0 ; j < tri->numVerts ; j += 3 ) {
|
||||
idVec3 v = tri->verts[j].xyz * axis + origin;
|
||||
GetHashVert( v );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// now fix each area
|
||||
for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
|
||||
for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
|
||||
// don't touch discrete surfaces
|
||||
if ( group->material != NULL && group->material->IsDiscrete() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mapTri_t *newList = NULL;
|
||||
for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) {
|
||||
mapTri_t *fixed = FixTriangleAgainstHash( tri );
|
||||
newList = MergeTriLists( newList, fixed );
|
||||
}
|
||||
FreeTriList( group->triList );
|
||||
group->triList = newList;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// done
|
||||
FreeTJunctionHash();
|
||||
}
|
||||
387
neo/tools/compilers/dmap/tritools.cpp
Normal file
387
neo/tools/compilers/dmap/tritools.cpp
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
/*
|
||||
|
||||
All triangle list functions should behave reasonably with NULL lists.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
===============
|
||||
AllocTri
|
||||
===============
|
||||
*/
|
||||
mapTri_t *AllocTri( void ) {
|
||||
mapTri_t *tri;
|
||||
|
||||
tri = (mapTri_t *)Mem_Alloc( sizeof( *tri ) );
|
||||
memset( tri, 0, sizeof( *tri ) );
|
||||
return tri;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
FreeTri
|
||||
===============
|
||||
*/
|
||||
void FreeTri( mapTri_t *tri ) {
|
||||
Mem_Free( tri );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
MergeTriLists
|
||||
|
||||
This does not copy any tris, it just relinks them
|
||||
===============
|
||||
*/
|
||||
mapTri_t *MergeTriLists( mapTri_t *a, mapTri_t *b ) {
|
||||
mapTri_t **prev;
|
||||
|
||||
prev = &a;
|
||||
while ( *prev ) {
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
|
||||
*prev = b;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
FreeTriList
|
||||
===============
|
||||
*/
|
||||
void FreeTriList( mapTri_t *a ) {
|
||||
mapTri_t *next;
|
||||
|
||||
for ( ; a ; a = next ) {
|
||||
next = a->next;
|
||||
Mem_Free( a );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CopyTriList
|
||||
===============
|
||||
*/
|
||||
mapTri_t *CopyTriList( const mapTri_t *a ) {
|
||||
mapTri_t *testList;
|
||||
const mapTri_t *tri;
|
||||
|
||||
testList = NULL;
|
||||
for ( tri = a ; tri ; tri = tri->next ) {
|
||||
mapTri_t *copy;
|
||||
|
||||
copy = CopyMapTri( tri );
|
||||
copy ->next = testList;
|
||||
testList = copy;
|
||||
}
|
||||
|
||||
return testList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
CountTriList
|
||||
=============
|
||||
*/
|
||||
int CountTriList( const mapTri_t *tri ) {
|
||||
int c;
|
||||
|
||||
c = 0;
|
||||
while ( tri ) {
|
||||
c++;
|
||||
tri = tri->next;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
CopyMapTri
|
||||
===============
|
||||
*/
|
||||
mapTri_t *CopyMapTri( const mapTri_t *tri ) {
|
||||
mapTri_t *t;
|
||||
|
||||
t = (mapTri_t *)Mem_Alloc( sizeof( *t ) );
|
||||
*t = *tri;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
MapTriArea
|
||||
===============
|
||||
*/
|
||||
float MapTriArea( const mapTri_t *tri ) {
|
||||
return idWinding::TriangleArea( tri->v[0].xyz, tri->v[1].xyz, tri->v[2].xyz );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
RemoveBadTris
|
||||
|
||||
Return a new list with any zero or negative area triangles removed
|
||||
===============
|
||||
*/
|
||||
mapTri_t *RemoveBadTris( const mapTri_t *list ) {
|
||||
mapTri_t *newList;
|
||||
mapTri_t *copy;
|
||||
const mapTri_t *tri;
|
||||
|
||||
newList = NULL;
|
||||
|
||||
for ( tri = list ; tri ; tri = tri->next ) {
|
||||
if ( MapTriArea( tri ) > 0 ) {
|
||||
copy = CopyMapTri( tri );
|
||||
copy->next = newList;
|
||||
newList = copy;
|
||||
}
|
||||
}
|
||||
|
||||
return newList;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
BoundTriList
|
||||
================
|
||||
*/
|
||||
void BoundTriList( const mapTri_t *list, idBounds &b ) {
|
||||
b.Clear();
|
||||
for ( ; list ; list = list->next ) {
|
||||
b.AddPoint( list->v[0].xyz );
|
||||
b.AddPoint( list->v[1].xyz );
|
||||
b.AddPoint( list->v[2].xyz );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
DrawTri
|
||||
================
|
||||
*/
|
||||
void DrawTri( const mapTri_t *tri ) {
|
||||
idWinding w;
|
||||
|
||||
w.SetNumPoints( 3 );
|
||||
VectorCopy( tri->v[0].xyz, w[0] );
|
||||
VectorCopy( tri->v[1].xyz, w[1] );
|
||||
VectorCopy( tri->v[2].xyz, w[2] );
|
||||
DrawWinding( &w );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
FlipTriList
|
||||
|
||||
Swaps the vertex order
|
||||
================
|
||||
*/
|
||||
void FlipTriList( mapTri_t *tris ) {
|
||||
mapTri_t *tri;
|
||||
|
||||
for ( tri = tris ; tri ; tri = tri->next ) {
|
||||
idDrawVert v;
|
||||
const struct hashVert_s *hv;
|
||||
struct optVertex_s *ov;
|
||||
|
||||
v = tri->v[0];
|
||||
tri->v[0] = tri->v[2];
|
||||
tri->v[2] = v;
|
||||
|
||||
hv = tri->hashVert[0];
|
||||
tri->hashVert[0] = tri->hashVert[2];
|
||||
tri->hashVert[2] = hv;
|
||||
|
||||
ov = tri->optVert[0];
|
||||
tri->optVert[0] = tri->optVert[2];
|
||||
tri->optVert[2] = ov;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WindingForTri
|
||||
================
|
||||
*/
|
||||
idWinding *WindingForTri( const mapTri_t *tri ) {
|
||||
idWinding *w;
|
||||
|
||||
w = new idWinding( 3 );
|
||||
w->SetNumPoints( 3 );
|
||||
VectorCopy( tri->v[0].xyz, (*w)[0] );
|
||||
VectorCopy( tri->v[1].xyz, (*w)[1] );
|
||||
VectorCopy( tri->v[2].xyz, (*w)[2] );
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
TriVertsFromOriginal
|
||||
|
||||
Regenerate the texcoords and colors on a fragmented tri from the plane equations
|
||||
================
|
||||
*/
|
||||
void TriVertsFromOriginal( mapTri_t *tri, const mapTri_t *original ) {
|
||||
int i, j;
|
||||
float denom;
|
||||
|
||||
denom = idWinding::TriangleArea( original->v[0].xyz, original->v[1].xyz, original->v[2].xyz );
|
||||
if ( denom == 0 ) {
|
||||
return; // original was degenerate, so it doesn't matter
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
float a, b, c;
|
||||
|
||||
// find the barycentric coordinates
|
||||
a = idWinding::TriangleArea( tri->v[i].xyz, original->v[1].xyz, original->v[2].xyz ) / denom;
|
||||
b = idWinding::TriangleArea( tri->v[i].xyz, original->v[2].xyz, original->v[0].xyz ) / denom;
|
||||
c = idWinding::TriangleArea( tri->v[i].xyz, original->v[0].xyz, original->v[1].xyz ) / denom;
|
||||
|
||||
// regenerate the interpolated values
|
||||
tri->v[i].st[0] = a * original->v[0].st[0]
|
||||
+ b * original->v[1].st[0] + c * original->v[2].st[0];
|
||||
tri->v[i].st[1] = a * original->v[0].st[1]
|
||||
+ b * original->v[1].st[1] + c * original->v[2].st[1];
|
||||
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
tri->v[i].normal[j] = a * original->v[0].normal[j]
|
||||
+ b * original->v[1].normal[j] + c * original->v[2].normal[j];
|
||||
}
|
||||
tri->v[i].normal.Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WindingToTriList
|
||||
|
||||
Generates a new list of triangles with proper texcoords from a winding
|
||||
created by clipping the originalTri
|
||||
|
||||
OriginalTri can be NULL if you don't care about texCoords
|
||||
================
|
||||
*/
|
||||
mapTri_t *WindingToTriList( const idWinding *w, const mapTri_t *originalTri ) {
|
||||
mapTri_t *tri;
|
||||
mapTri_t *triList;
|
||||
int i, j;
|
||||
const idVec3 *vec;
|
||||
|
||||
if ( !w ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
triList = NULL;
|
||||
for ( i = 2 ; i < w->GetNumPoints() ; i++ ) {
|
||||
tri = AllocTri();
|
||||
if ( !originalTri ) {
|
||||
memset( tri, 0, sizeof( *tri ) );
|
||||
} else {
|
||||
*tri = *originalTri;
|
||||
}
|
||||
tri->next = triList;
|
||||
triList = tri;
|
||||
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
if ( j == 0 ) {
|
||||
vec = &((*w)[0]).ToVec3();
|
||||
} else if ( j == 1 ) {
|
||||
vec = &((*w)[i-1]).ToVec3();
|
||||
} else {
|
||||
vec = &((*w)[i]).ToVec3();
|
||||
}
|
||||
|
||||
VectorCopy( *vec, tri->v[j].xyz );
|
||||
}
|
||||
if ( originalTri ) {
|
||||
TriVertsFromOriginal( tri, originalTri );
|
||||
}
|
||||
}
|
||||
|
||||
return triList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
ClipTriList
|
||||
==================
|
||||
*/
|
||||
void ClipTriList( const mapTri_t *list, const idPlane &plane, float epsilon,
|
||||
mapTri_t **front, mapTri_t **back ) {
|
||||
const mapTri_t *tri;
|
||||
mapTri_t *newList;
|
||||
idWinding *w, *frontW, *backW;
|
||||
|
||||
*front = NULL;
|
||||
*back = NULL;
|
||||
|
||||
for ( tri = list ; tri ; tri = tri->next ) {
|
||||
w = WindingForTri( tri );
|
||||
w->Split( plane, epsilon, &frontW, &backW );
|
||||
|
||||
newList = WindingToTriList( frontW, tri );
|
||||
*front = MergeTriLists( *front, newList );
|
||||
|
||||
newList = WindingToTriList( backW, tri );
|
||||
*back = MergeTriLists( *back, newList );
|
||||
|
||||
delete w;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PlaneForTri
|
||||
==================
|
||||
*/
|
||||
void PlaneForTri( const mapTri_t *tri, idPlane &plane ) {
|
||||
plane.FromPoints( tri->v[0].xyz, tri->v[1].xyz, tri->v[2].xyz );
|
||||
}
|
||||
709
neo/tools/compilers/dmap/ubrush.cpp
Normal file
709
neo/tools/compilers/dmap/ubrush.cpp
Normal file
@@ -0,0 +1,709 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "dmap.h"
|
||||
|
||||
int c_active_brushes;
|
||||
|
||||
int c_nodes;
|
||||
|
||||
// if a brush just barely pokes onto the other side,
|
||||
// let it slide by without chopping
|
||||
#define PLANESIDE_EPSILON 0.001
|
||||
//0.1
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CountBrushList
|
||||
================
|
||||
*/
|
||||
int CountBrushList (uBrush_t *brushes)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = 0;
|
||||
for ( ; brushes ; brushes = brushes->next)
|
||||
c++;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
int BrushSizeForSides( int numsides ) {
|
||||
int c;
|
||||
|
||||
// allocate a structure with a variable number of sides at the end
|
||||
// c = (int)&(((uBrush_t *)0)->sides[numsides]); // bounds checker complains about this
|
||||
c = sizeof( uBrush_t ) + sizeof( side_t ) * (numsides - 6);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
AllocBrush
|
||||
================
|
||||
*/
|
||||
uBrush_t *AllocBrush (int numsides)
|
||||
{
|
||||
uBrush_t *bb;
|
||||
int c;
|
||||
|
||||
c = BrushSizeForSides( numsides );
|
||||
|
||||
bb = (uBrush_t *)Mem_Alloc(c);
|
||||
memset (bb, 0, c);
|
||||
c_active_brushes++;
|
||||
return bb;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FreeBrush
|
||||
================
|
||||
*/
|
||||
void FreeBrush (uBrush_t *brushes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0 ; i < brushes->numsides ; i++ ) {
|
||||
if ( brushes->sides[i].winding ) {
|
||||
delete brushes->sides[i].winding;
|
||||
}
|
||||
if ( brushes->sides[i].visibleHull ) {
|
||||
delete brushes->sides[i].visibleHull;
|
||||
}
|
||||
}
|
||||
Mem_Free (brushes);
|
||||
c_active_brushes--;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
FreeBrushList
|
||||
================
|
||||
*/
|
||||
void FreeBrushList (uBrush_t *brushes)
|
||||
{
|
||||
uBrush_t *next;
|
||||
|
||||
for ( ; brushes ; brushes = next)
|
||||
{
|
||||
next = brushes->next;
|
||||
|
||||
FreeBrush (brushes);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CopyBrush
|
||||
|
||||
Duplicates the brush, the sides, and the windings
|
||||
==================
|
||||
*/
|
||||
uBrush_t *CopyBrush (uBrush_t *brush)
|
||||
{
|
||||
uBrush_t *newbrush;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
size = BrushSizeForSides( brush->numsides );
|
||||
|
||||
newbrush = AllocBrush (brush->numsides);
|
||||
memcpy (newbrush, brush, size);
|
||||
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
if (brush->sides[i].winding)
|
||||
newbrush->sides[i].winding = brush->sides[i].winding->Copy();
|
||||
}
|
||||
|
||||
return newbrush;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
DrawBrushList
|
||||
================
|
||||
*/
|
||||
void DrawBrushList (uBrush_t *brush)
|
||||
{
|
||||
int i;
|
||||
side_t *s;
|
||||
|
||||
GLS_BeginScene ();
|
||||
for ( ; brush ; brush=brush->next)
|
||||
{
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
s = &brush->sides[i];
|
||||
if (!s->winding)
|
||||
continue;
|
||||
GLS_Winding (s->winding, 0);
|
||||
}
|
||||
}
|
||||
GLS_EndScene ();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
PrintBrush
|
||||
=============
|
||||
*/
|
||||
void PrintBrush (uBrush_t *brush)
|
||||
{
|
||||
int i;
|
||||
|
||||
common->Printf( "brush: %p\n", brush );
|
||||
for ( i=0;i<brush->numsides ; i++ ) {
|
||||
brush->sides[i].winding->Print();
|
||||
common->Printf ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BoundBrush
|
||||
|
||||
Sets the mins/maxs based on the windings
|
||||
returns false if the brush doesn't enclose a valid volume
|
||||
==================
|
||||
*/
|
||||
bool BoundBrush (uBrush_t *brush) {
|
||||
int i, j;
|
||||
idWinding *w;
|
||||
|
||||
brush->bounds.Clear();
|
||||
for ( i = 0; i < brush->numsides; i++ ) {
|
||||
w = brush->sides[i].winding;
|
||||
if (!w)
|
||||
continue;
|
||||
for ( j = 0; j < w->GetNumPoints(); j++ )
|
||||
brush->bounds.AddPoint( (*w)[j].ToVec3() );
|
||||
}
|
||||
|
||||
for ( i = 0; i < 3; i++ ) {
|
||||
if (brush->bounds[0][i] < MIN_WORLD_COORD || brush->bounds[1][i] > MAX_WORLD_COORD
|
||||
|| brush->bounds[0][i] >= brush->bounds[1][i] ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CreateBrushWindings
|
||||
|
||||
makes basewindigs for sides and mins / maxs for the brush
|
||||
returns false if the brush doesn't enclose a valid volume
|
||||
==================
|
||||
*/
|
||||
bool CreateBrushWindings (uBrush_t *brush) {
|
||||
int i, j;
|
||||
idWinding *w;
|
||||
idPlane *plane;
|
||||
side_t *side;
|
||||
|
||||
for ( i = 0; i < brush->numsides; i++ ) {
|
||||
side = &brush->sides[i];
|
||||
plane = &dmapGlobals.mapPlanes[side->planenum];
|
||||
w = new idWinding( *plane );
|
||||
for ( j = 0; j < brush->numsides && w; j++ ) {
|
||||
if ( i == j ) {
|
||||
continue;
|
||||
}
|
||||
if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) ) {
|
||||
continue; // back side clipaway
|
||||
}
|
||||
plane = &dmapGlobals.mapPlanes[brush->sides[j].planenum^1];
|
||||
w = w->Clip( *plane, 0 );//CLIP_EPSILON);
|
||||
}
|
||||
if ( side->winding ) {
|
||||
delete side->winding;
|
||||
}
|
||||
side->winding = w;
|
||||
}
|
||||
|
||||
return BoundBrush( brush );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BrushFromBounds
|
||||
|
||||
Creates a new axial brush
|
||||
==================
|
||||
*/
|
||||
uBrush_t *BrushFromBounds( const idBounds &bounds ) {
|
||||
uBrush_t *b;
|
||||
int i;
|
||||
idPlane plane;
|
||||
|
||||
b = AllocBrush (6);
|
||||
b->numsides = 6;
|
||||
for (i=0 ; i<3 ; i++) {
|
||||
plane[0] = plane[1] = plane[2] = 0;
|
||||
plane[i] = 1;
|
||||
plane[3] = -bounds[1][i];
|
||||
b->sides[i].planenum = FindFloatPlane( plane );
|
||||
|
||||
plane[i] = -1;
|
||||
plane[3] = bounds[0][i];
|
||||
b->sides[3+i].planenum = FindFloatPlane( plane );
|
||||
}
|
||||
|
||||
CreateBrushWindings (b);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BrushVolume
|
||||
|
||||
==================
|
||||
*/
|
||||
float BrushVolume (uBrush_t *brush) {
|
||||
int i;
|
||||
idWinding *w;
|
||||
idVec3 corner;
|
||||
float d, area, volume;
|
||||
idPlane *plane;
|
||||
|
||||
if (!brush)
|
||||
return 0;
|
||||
|
||||
// grab the first valid point as the corner
|
||||
|
||||
w = NULL;
|
||||
for ( i = 0; i < brush->numsides; i++ ) {
|
||||
w = brush->sides[i].winding;
|
||||
if (w)
|
||||
break;
|
||||
}
|
||||
if (!w) {
|
||||
return 0;
|
||||
}
|
||||
VectorCopy ( (*w)[0], corner);
|
||||
|
||||
// make tetrahedrons to all other faces
|
||||
|
||||
volume = 0;
|
||||
for ( ; i < brush->numsides; i++ )
|
||||
{
|
||||
w = brush->sides[i].winding;
|
||||
if (!w)
|
||||
continue;
|
||||
plane = &dmapGlobals.mapPlanes[brush->sides[i].planenum];
|
||||
d = -plane->Distance( corner );
|
||||
area = w->GetArea();
|
||||
volume += d * area;
|
||||
}
|
||||
|
||||
volume /= 3;
|
||||
return volume;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteBspBrushMap
|
||||
|
||||
FIXME: use new brush format
|
||||
==================
|
||||
*/
|
||||
void WriteBspBrushMap( const char *name, uBrush_t *list ) {
|
||||
idFile * f;
|
||||
side_t * s;
|
||||
int i;
|
||||
idWinding * w;
|
||||
|
||||
common->Printf ("writing %s\n", name);
|
||||
f = fileSystem->OpenFileWrite( name );
|
||||
|
||||
if ( !f ) {
|
||||
common->Error( "Can't write %s\b", name);
|
||||
}
|
||||
|
||||
f->Printf( "{\n\"classname\" \"worldspawn\"\n" );
|
||||
|
||||
for ( ; list ; list=list->next )
|
||||
{
|
||||
f->Printf( "{\n" );
|
||||
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
|
||||
{
|
||||
w = new idWinding( dmapGlobals.mapPlanes[s->planenum] );
|
||||
|
||||
f->Printf ("( %i %i %i ) ", (int)(*w)[0][0], (int)(*w)[0][1], (int)(*w)[0][2]);
|
||||
f->Printf ("( %i %i %i ) ", (int)(*w)[1][0], (int)(*w)[1][1], (int)(*w)[1][2]);
|
||||
f->Printf ("( %i %i %i ) ", (int)(*w)[2][0], (int)(*w)[2][1], (int)(*w)[2][2]);
|
||||
|
||||
f->Printf ("notexture 0 0 0 1 1\n" );
|
||||
delete w;
|
||||
}
|
||||
f->Printf ("}\n");
|
||||
}
|
||||
f->Printf ("}\n");
|
||||
|
||||
fileSystem->CloseFile(f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
|
||||
/*
|
||||
====================
|
||||
FilterBrushIntoTree_r
|
||||
|
||||
====================
|
||||
*/
|
||||
int FilterBrushIntoTree_r( uBrush_t *b, node_t *node ) {
|
||||
uBrush_t *front, *back;
|
||||
int c;
|
||||
|
||||
if ( !b ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// add it to the leaf list
|
||||
if ( node->planenum == PLANENUM_LEAF ) {
|
||||
b->next = node->brushlist;
|
||||
node->brushlist = b;
|
||||
|
||||
// classify the leaf by the structural brush
|
||||
if ( b->opaque ) {
|
||||
node->opaque = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// split it by the node plane
|
||||
SplitBrush ( b, node->planenum, &front, &back );
|
||||
FreeBrush( b );
|
||||
|
||||
c = 0;
|
||||
c += FilterBrushIntoTree_r( front, node->children[0] );
|
||||
c += FilterBrushIntoTree_r( back, node->children[1] );
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=====================
|
||||
FilterBrushesIntoTree
|
||||
|
||||
Mark the leafs as opaque and areaportals and put brush
|
||||
fragments in each leaf so portal surfaces can be matched
|
||||
to materials
|
||||
=====================
|
||||
*/
|
||||
void FilterBrushesIntoTree( uEntity_t *e ) {
|
||||
primitive_t *prim;
|
||||
uBrush_t *b, *newb;
|
||||
int r;
|
||||
int c_unique, c_clusters;
|
||||
|
||||
common->Printf( "----- FilterBrushesIntoTree -----\n");
|
||||
|
||||
c_unique = 0;
|
||||
c_clusters = 0;
|
||||
for ( prim = e->primitives ; prim ; prim = prim->next ) {
|
||||
b = prim->brush;
|
||||
if ( !b ) {
|
||||
continue;
|
||||
}
|
||||
c_unique++;
|
||||
newb = CopyBrush( b );
|
||||
r = FilterBrushIntoTree_r( newb, e->tree->headnode );
|
||||
c_clusters += r;
|
||||
}
|
||||
|
||||
common->Printf( "%5i total brushes\n", c_unique );
|
||||
common->Printf( "%5i cluster references\n", c_clusters );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
AllocTree
|
||||
================
|
||||
*/
|
||||
tree_t *AllocTree (void)
|
||||
{
|
||||
tree_t *tree;
|
||||
|
||||
tree = (tree_t *)Mem_Alloc(sizeof(*tree));
|
||||
memset (tree, 0, sizeof(*tree));
|
||||
tree->bounds.Clear();
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
AllocNode
|
||||
================
|
||||
*/
|
||||
node_t *AllocNode (void)
|
||||
{
|
||||
node_t *node;
|
||||
|
||||
node = (node_t *)Mem_Alloc(sizeof(*node));
|
||||
memset (node, 0, sizeof(*node));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
BrushMostlyOnSide
|
||||
|
||||
==================
|
||||
*/
|
||||
int BrushMostlyOnSide (uBrush_t *brush, idPlane &plane) {
|
||||
int i, j;
|
||||
idWinding *w;
|
||||
float d, max;
|
||||
int side;
|
||||
|
||||
max = 0;
|
||||
side = PSIDE_FRONT;
|
||||
for ( i = 0; i < brush->numsides; i++ ) {
|
||||
w = brush->sides[i].winding;
|
||||
if (!w)
|
||||
continue;
|
||||
for ( j = 0; j < w->GetNumPoints(); j++ )
|
||||
{
|
||||
d = plane.Distance( (*w)[j].ToVec3() );
|
||||
if (d > max)
|
||||
{
|
||||
max = d;
|
||||
side = PSIDE_FRONT;
|
||||
}
|
||||
if (-d > max)
|
||||
{
|
||||
max = -d;
|
||||
side = PSIDE_BACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return side;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SplitBrush
|
||||
|
||||
Generates two new brushes, leaving the original
|
||||
unchanged
|
||||
================
|
||||
*/
|
||||
void SplitBrush (uBrush_t *brush, int planenum, uBrush_t **front, uBrush_t **back) {
|
||||
uBrush_t *b[2];
|
||||
int i, j;
|
||||
idWinding *w, *cw[2], *midwinding;
|
||||
side_t *s, *cs;
|
||||
float d, d_front, d_back;
|
||||
|
||||
*front = *back = NULL;
|
||||
idPlane &plane = dmapGlobals.mapPlanes[planenum];
|
||||
|
||||
// check all points
|
||||
d_front = d_back = 0;
|
||||
for ( i = 0; i < brush->numsides; i++ )
|
||||
{
|
||||
w = brush->sides[i].winding;
|
||||
if (!w) {
|
||||
continue;
|
||||
}
|
||||
for ( j = 0; j < w->GetNumPoints(); j++ ) {
|
||||
d = plane.Distance( (*w)[j].ToVec3() );
|
||||
if (d > 0 && d > d_front)
|
||||
d_front = d;
|
||||
if (d < 0 && d < d_back)
|
||||
d_back = d;
|
||||
}
|
||||
}
|
||||
if (d_front < 0.1) // PLANESIDE_EPSILON)
|
||||
{ // only on back
|
||||
*back = CopyBrush( brush );
|
||||
return;
|
||||
}
|
||||
if (d_back > -0.1) // PLANESIDE_EPSILON)
|
||||
{ // only on front
|
||||
*front = CopyBrush( brush );
|
||||
return;
|
||||
}
|
||||
|
||||
// create a new winding from the split plane
|
||||
|
||||
w = new idWinding( plane );
|
||||
for ( i = 0; i < brush->numsides && w; i++ ) {
|
||||
idPlane &plane2 = dmapGlobals.mapPlanes[brush->sides[i].planenum ^ 1];
|
||||
w = w->Clip( plane2, 0 ); // PLANESIDE_EPSILON);
|
||||
}
|
||||
|
||||
if ( !w || w->IsTiny() ) {
|
||||
// the brush isn't really split
|
||||
int side;
|
||||
|
||||
side = BrushMostlyOnSide( brush, plane );
|
||||
if (side == PSIDE_FRONT)
|
||||
*front = CopyBrush (brush);
|
||||
if (side == PSIDE_BACK)
|
||||
*back = CopyBrush (brush);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( w->IsHuge() ) {
|
||||
common->Printf ("WARNING: huge winding\n");
|
||||
}
|
||||
|
||||
midwinding = w;
|
||||
|
||||
// split it for real
|
||||
|
||||
for ( i = 0; i < 2; i++ ) {
|
||||
b[i] = AllocBrush (brush->numsides+1);
|
||||
memcpy( b[i], brush, sizeof( uBrush_t ) - sizeof( brush->sides ) );
|
||||
b[i]->numsides = 0;
|
||||
b[i]->next = NULL;
|
||||
b[i]->original = brush->original;
|
||||
}
|
||||
|
||||
// split all the current windings
|
||||
|
||||
for ( i = 0; i < brush->numsides; i++ ) {
|
||||
s = &brush->sides[i];
|
||||
w = s->winding;
|
||||
if (!w)
|
||||
continue;
|
||||
w->Split( plane, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] );
|
||||
for ( j = 0; j < 2; j++ ) {
|
||||
if ( !cw[j] ) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
if ( cw[j]->IsTiny() )
|
||||
{
|
||||
delete cw[j];
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
cs = &b[j]->sides[b[j]->numsides];
|
||||
b[j]->numsides++;
|
||||
*cs = *s;
|
||||
cs->winding = cw[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// see if we have valid polygons on both sides
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
if ( !BoundBrush (b[i]) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( b[i]->numsides < 3 )
|
||||
{
|
||||
FreeBrush (b[i]);
|
||||
b[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(b[0] && b[1]) )
|
||||
{
|
||||
if (!b[0] && !b[1])
|
||||
common->Printf ("split removed brush\n");
|
||||
else
|
||||
common->Printf ("split not on both sides\n");
|
||||
if (b[0])
|
||||
{
|
||||
FreeBrush (b[0]);
|
||||
*front = CopyBrush (brush);
|
||||
}
|
||||
if (b[1])
|
||||
{
|
||||
FreeBrush (b[1]);
|
||||
*back = CopyBrush (brush);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// add the midwinding to both sides
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
cs = &b[i]->sides[b[i]->numsides];
|
||||
b[i]->numsides++;
|
||||
|
||||
cs->planenum = planenum^i^1;
|
||||
cs->material = NULL;
|
||||
if (i==0)
|
||||
cs->winding = midwinding->Copy();
|
||||
else
|
||||
cs->winding = midwinding;
|
||||
}
|
||||
|
||||
{
|
||||
float v1;
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
v1 = BrushVolume (b[i]);
|
||||
if (v1 < 1.0)
|
||||
{
|
||||
FreeBrush (b[i]);
|
||||
b[i] = NULL;
|
||||
// common->Printf ("tiny volume after clip\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*front = b[0];
|
||||
*back = b[1];
|
||||
}
|
||||
1046
neo/tools/compilers/dmap/usurface.cpp
Normal file
1046
neo/tools/compilers/dmap/usurface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1641
neo/tools/compilers/renderbump/renderbump.cpp
Normal file
1641
neo/tools/compilers/renderbump/renderbump.cpp
Normal file
File diff suppressed because it is too large
Load Diff
105
neo/tools/compilers/roqvq/NSBitmapImageRep.cpp
Normal file
105
neo/tools/compilers/roqvq/NSBitmapImageRep.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "roq.h"
|
||||
|
||||
void R_LoadImage( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp, bool makePowerOf2 );
|
||||
|
||||
NSBitmapImageRep::NSBitmapImageRep( void ) {
|
||||
bmap = NULL;
|
||||
width = 0;
|
||||
height = 0;
|
||||
timestamp = 0;
|
||||
}
|
||||
|
||||
NSBitmapImageRep::NSBitmapImageRep( const char *filename ) {
|
||||
|
||||
R_LoadImage( filename, &bmap, &width, &height, ×tamp, false );
|
||||
if (!width || !height) {
|
||||
common->FatalError( "roqvq: unable to load image %s\n", filename );
|
||||
}
|
||||
}
|
||||
|
||||
NSBitmapImageRep::NSBitmapImageRep( int wide, int high ) {
|
||||
bmap = (byte *)Mem_ClearedAlloc( wide * high * 4 );
|
||||
width = wide;
|
||||
height = high;
|
||||
}
|
||||
|
||||
void R_StaticFree( void *data );
|
||||
|
||||
NSBitmapImageRep::~NSBitmapImageRep() {
|
||||
R_StaticFree( bmap );
|
||||
bmap = NULL;
|
||||
}
|
||||
|
||||
int NSBitmapImageRep::samplesPerPixel( void ) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
int NSBitmapImageRep::pixelsWide( void ) {
|
||||
return width;
|
||||
}
|
||||
|
||||
int NSBitmapImageRep::pixelsHigh( void ) {
|
||||
return height;
|
||||
}
|
||||
|
||||
byte * NSBitmapImageRep::bitmapData( void ) {
|
||||
return bmap;
|
||||
}
|
||||
|
||||
bool NSBitmapImageRep::hasAlpha( void ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NSBitmapImageRep::isPlanar( void ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NSBitmapImageRep &NSBitmapImageRep::operator=( const NSBitmapImageRep &a ) {
|
||||
|
||||
// check for assignment to self
|
||||
if ( this == &a ) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (bmap) {
|
||||
Mem_Free(bmap);
|
||||
}
|
||||
bmap = (byte *)Mem_Alloc( a.width * a.height * 4 );
|
||||
memcpy( bmap, a.bmap, a.width * a.height * 4 );
|
||||
width = a.width;
|
||||
height = a.height;
|
||||
timestamp = a.timestamp;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
1696
neo/tools/compilers/roqvq/codec.cpp
Normal file
1696
neo/tools/compilers/roqvq/codec.cpp
Normal file
File diff suppressed because it is too large
Load Diff
108
neo/tools/compilers/roqvq/codec.h
Normal file
108
neo/tools/compilers/roqvq/codec.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#ifndef __codec_h__
|
||||
#define __codec_h__
|
||||
|
||||
#define MAXERRORMAX 200
|
||||
#define IPSIZE int
|
||||
const float MOTION_MIN = 1.0f;
|
||||
const float MIN_SNR = 3.0f;
|
||||
|
||||
#define FULLFRAME 0
|
||||
#define JUSTMOTION 1
|
||||
|
||||
#define VQDATA double
|
||||
|
||||
#include "gdefs.h"
|
||||
#include "roq.h"
|
||||
#include "quaddefs.h"
|
||||
|
||||
class codec {
|
||||
public:
|
||||
codec();
|
||||
~codec();
|
||||
|
||||
void SparseEncode( void );
|
||||
void EncodeNothing( void );
|
||||
void IRGBtab(void);
|
||||
void InitImages(void);
|
||||
void QuadX( int startX, int startY, int quadSize);
|
||||
void InitQStatus();
|
||||
float Snr(byte *old, byte *bnew, int size);
|
||||
void FvqData( byte *bitmap, int size, int realx, int realy, quadcel *pquad, bool clamp);
|
||||
void GetData( unsigned char *iData, int qSize, int startX, int startY, NSBitmapImageRep *bitmap);
|
||||
int ComputeMotionBlock( byte *old, byte *bnew, int size);
|
||||
void VqData8( byte *cel, quadcel *pquad);
|
||||
void VqData4( byte *cel, quadcel *pquad);
|
||||
void VqData2( byte *cel, quadcel *pquad);
|
||||
int MotMeanY(void);
|
||||
int MotMeanX(void);
|
||||
void SetPreviousImage( const char*filename, NSBitmapImageRep *timage );
|
||||
int BestCodeword( unsigned char *tempvector, int dimension, VQDATA **codebook );
|
||||
private:
|
||||
|
||||
void VQ( const int numEntries, const int dimension, const unsigned char *vectors, float *snr, VQDATA **codebook, const bool optimize );
|
||||
void Sort( float *list, int *intIndex, int numElements );
|
||||
void Segment( int *alist, float *flist, int numElements, float rmse);
|
||||
void LowestQuad( quadcel*qtemp, int* status, float* snr, int bweigh);
|
||||
void MakePreviousImage( quadcel *pquad );
|
||||
float GetCurrentRMSE( quadcel *pquad );
|
||||
int GetCurrentQuadOutputSize( quadcel *pquad );
|
||||
int AddQuad( quadcel *pquad, int lownum );
|
||||
|
||||
NSBitmapImageRep *image;
|
||||
NSBitmapImageRep *newImage;
|
||||
NSBitmapImageRep *previousImage[2]; // the ones in video ram and offscreen ram
|
||||
int numQuadCels;
|
||||
int whichFrame;
|
||||
int slop;
|
||||
bool detail;
|
||||
int onQuad;
|
||||
int initRGBtab;
|
||||
quadcel *qStatus;
|
||||
int dxMean;
|
||||
int dyMean;
|
||||
int codebooksize;
|
||||
int index2[256];
|
||||
int overAmount;
|
||||
int pixelsWide;
|
||||
int pixelsHigh;
|
||||
int codebookmade;
|
||||
bool used2[256];
|
||||
bool used4[256];
|
||||
int dimension2;
|
||||
int dimension4;
|
||||
|
||||
byte luty[256];
|
||||
byte *luti;
|
||||
VQDATA **codebook2;
|
||||
VQDATA **codebook4;
|
||||
|
||||
};
|
||||
|
||||
#endif // __codec_h__
|
||||
68
neo/tools/compilers/roqvq/gdefs.h
Normal file
68
neo/tools/compilers/roqvq/gdefs.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#ifndef __gdefs_h__
|
||||
#define __gdefs_h__
|
||||
|
||||
/*==================*
|
||||
* TYPE DEFINITIONS *
|
||||
*==================*/
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned short word;
|
||||
#pragma once
|
||||
|
||||
#define dabs(a) (((a)<0) ? -(a) : (a))
|
||||
#define CLAMP(v,l,h) ((v)<(l) ? (l) : (v)>(h) ? (h) : v)
|
||||
#define xswap(a,b) { a^=b; b^=a; a^=b; }
|
||||
#define lum(a) ( 0.2990*(a>>16) + 0.5870*((a>>8)&0xff) + 0.1140*(a&0xff) )
|
||||
#define gsign(a) ((a) < 0 ? -1 : 1)
|
||||
#define mnint(a) ((a) < 0 ? (int)(a - 0.5) : (int)(a + 0.5))
|
||||
#define mmax(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define mmin(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define RGBDIST( src0, src1 ) ( ((src0[0]-src1[0])*(src0[0]-src1[0])) + \
|
||||
((src0[1]-src1[1])*(src0[1]-src1[1])) + \
|
||||
((src0[2]-src1[2])*(src0[2]-src1[2])) )
|
||||
|
||||
#define RGBADIST( src0, src1 ) ( ((src0[0]-src1[0])*(src0[0]-src1[0])) + \
|
||||
((src0[1]-src1[1])*(src0[1]-src1[1])) + \
|
||||
((src0[2]-src1[2])*(src0[2]-src1[2])) + \
|
||||
((src0[3]-src1[3])*(src0[3]-src1[3])) )
|
||||
|
||||
|
||||
#define RMULT 0.2990f // use these for televisions
|
||||
#define GMULT 0.5870f
|
||||
#define BMULT 0.1140f
|
||||
|
||||
#define RIEMULT -0.16874f
|
||||
#define RQEMULT 0.50000f
|
||||
#define GIEMULT -0.33126f
|
||||
#define GQEMULT -0.41869f
|
||||
#define BIEMULT 0.50000f
|
||||
#define BQEMULT -0.08131f
|
||||
|
||||
#endif // gdefs
|
||||
131
neo/tools/compilers/roqvq/quaddefs.h
Normal file
131
neo/tools/compilers/roqvq/quaddefs.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#ifndef __quaddefs_h__
|
||||
#define __quaddefs_h__
|
||||
|
||||
#pragma once
|
||||
|
||||
#define DEP 0
|
||||
#define FCC 1
|
||||
#define CCC 2
|
||||
#define SLD 3
|
||||
#define PAT 4
|
||||
#define MOT 5
|
||||
#define DEAD 6
|
||||
|
||||
#define COLA 0
|
||||
#define COLB 1
|
||||
#define COLC 2
|
||||
#define COLS 3
|
||||
#define COLPATA 4
|
||||
#define COLPATB 5
|
||||
#define COLPATS 6
|
||||
#define GENERATION 7
|
||||
|
||||
#define CCCBITMAP 0
|
||||
#define FCCDOMAIN 1
|
||||
#define PATNUMBER 2
|
||||
#define PATNUMBE2 3
|
||||
#define PATNUMBE3 4
|
||||
#define PATNUMBE4 5
|
||||
#define PATNUMBE5 6
|
||||
|
||||
#define MAXSIZE 16
|
||||
#define MINSIZE 4
|
||||
|
||||
#define RoQ_ID 0x1084
|
||||
#define RoQ_QUAD 0x1000
|
||||
#define RoQ_PUZZLE_QUAD 0x1003
|
||||
#define RoQ_QUAD_HANG 0x1013
|
||||
#define RoQ_QUAD_SMALL 0x1010
|
||||
#define RoQ_QUAD_INFO 0x1001
|
||||
#define RoQ_QUAD_VQ 0x1011
|
||||
#define RoQ_QUAD_JPEG 0x1012
|
||||
#define RoQ_QUAD_CODEBOOK 0x1002
|
||||
|
||||
typedef struct {
|
||||
byte size; // 32, 16, 8, or 4
|
||||
word xat; // where is it at on the screen
|
||||
word yat; //
|
||||
} shortQuadCel;
|
||||
|
||||
typedef struct {
|
||||
byte size; // 32, 16, 8, or 4
|
||||
word xat; // where is it at on the screen
|
||||
word yat; //
|
||||
|
||||
float cccsnr; // ccc bitmap snr to actual image
|
||||
float fccsnr; // fcc bitmap snr to actual image
|
||||
float motsnr; // delta snr to previous image
|
||||
float sldsnr; // solid color snr
|
||||
float patsnr;
|
||||
float dctsnr;
|
||||
float rsnr; // what's the current snr
|
||||
|
||||
unsigned int cola; // color a for ccc
|
||||
unsigned int colb; // color b for ccc
|
||||
unsigned int colc; // color b for ccc
|
||||
unsigned int sldcol; // sold color
|
||||
unsigned int colpata;
|
||||
unsigned int colpatb;
|
||||
unsigned int colpats;
|
||||
unsigned int bitmap; // ccc bitmap
|
||||
|
||||
word domain; // where to copy from for fcc
|
||||
word patten[5]; // which pattern
|
||||
|
||||
int status;
|
||||
bool mark;
|
||||
float snr[DEAD+1]; // snrssss
|
||||
} quadcel;
|
||||
|
||||
typedef struct {
|
||||
float snr[DEAD+1]; // snrssss
|
||||
unsigned int cols[8];
|
||||
unsigned int bitmaps[7]; // ccc bitmap
|
||||
} dataQuadCel;
|
||||
|
||||
typedef struct {
|
||||
float normal;
|
||||
unsigned short int index;
|
||||
} norm;
|
||||
|
||||
typedef struct {
|
||||
unsigned char dtlMap[256];
|
||||
int r[4];
|
||||
int g[4];
|
||||
int b[4];
|
||||
int a[4];
|
||||
float ymean;
|
||||
} dtlCel;
|
||||
|
||||
typedef struct {
|
||||
byte r,g,b,a;
|
||||
} pPixel;
|
||||
|
||||
#endif // quaddef
|
||||
855
neo/tools/compilers/roqvq/roq.cpp
Normal file
855
neo/tools/compilers/roqvq/roq.cpp
Normal file
@@ -0,0 +1,855 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "roq.h"
|
||||
#include "codec.h"
|
||||
|
||||
roq *theRoQ; // current roq file
|
||||
|
||||
roq::roq( void )
|
||||
{
|
||||
image = 0;
|
||||
quietMode = false;
|
||||
encoder = 0;
|
||||
previousSize = 0;
|
||||
lastFrame = false;
|
||||
dataStuff=false;
|
||||
}
|
||||
|
||||
roq::~roq( void )
|
||||
{
|
||||
if (image) delete image;
|
||||
if (encoder) delete encoder;
|
||||
return;
|
||||
}
|
||||
|
||||
void roq::EncodeQuietly( bool which )
|
||||
{
|
||||
quietMode = which;
|
||||
}
|
||||
|
||||
bool roq::IsQuiet( void )
|
||||
{
|
||||
return quietMode;
|
||||
}
|
||||
|
||||
bool roq::IsLastFrame( void )
|
||||
{
|
||||
return lastFrame;
|
||||
}
|
||||
|
||||
bool roq::Scaleable( void )
|
||||
{
|
||||
return paramFile->IsScaleable();
|
||||
}
|
||||
|
||||
bool roq::ParamNoAlpha( void )
|
||||
{
|
||||
return paramFile->NoAlpha();
|
||||
}
|
||||
|
||||
bool roq::MakingVideo( void )
|
||||
{
|
||||
return true; //paramFile->timecode];
|
||||
}
|
||||
|
||||
bool roq::SearchType( void )
|
||||
{
|
||||
return paramFile->SearchType();
|
||||
}
|
||||
|
||||
bool roq::HasSound( void )
|
||||
{
|
||||
return paramFile->HasSound();
|
||||
}
|
||||
|
||||
int roq::PreviousFrameSize( void )
|
||||
{
|
||||
return previousSize;
|
||||
}
|
||||
|
||||
int roq::FirstFrameSize( void )
|
||||
{
|
||||
return paramFile->FirstFrameSize();
|
||||
}
|
||||
|
||||
int roq::NormalFrameSize( void )
|
||||
{
|
||||
return paramFile->NormalFrameSize();
|
||||
}
|
||||
|
||||
const char * roq::CurrentFilename( void )
|
||||
{
|
||||
return currentFile.c_str();
|
||||
}
|
||||
|
||||
void roq::EncodeStream( const char *paramInputFile )
|
||||
{
|
||||
int onFrame;
|
||||
idStr f0, f1, f2;
|
||||
int morestuff;
|
||||
|
||||
onFrame = 1;
|
||||
|
||||
encoder = new codec;
|
||||
paramFile = new roqParam;
|
||||
paramFile->numInputFiles = 0;
|
||||
|
||||
paramFile->InitFromFile( paramInputFile );
|
||||
|
||||
if (!paramFile->NumberOfFrames()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InitRoQFile( paramFile->outputFilename);
|
||||
|
||||
numberOfFrames = paramFile->NumberOfFrames();
|
||||
|
||||
if (paramFile->NoAlpha()==true) common->Printf("encodeStream: eluding alpha\n");
|
||||
|
||||
f0 = "";
|
||||
f1 = paramFile->GetNextImageFilename();
|
||||
if (( paramFile->MoreFrames() == true )) {
|
||||
f2 = paramFile->GetNextImageFilename();
|
||||
}
|
||||
morestuff = numberOfFrames;
|
||||
|
||||
while( morestuff ) {
|
||||
LoadAndDisplayImage( f1 );
|
||||
|
||||
if (onFrame==1) {
|
||||
encoder->SparseEncode();
|
||||
// WriteLossless();
|
||||
} else {
|
||||
if (!strcmp( f0, f1 ) && strcmp( f1, f2) ) {
|
||||
WriteHangFrame();
|
||||
} else {
|
||||
encoder->SparseEncode();
|
||||
}
|
||||
}
|
||||
|
||||
onFrame++;
|
||||
f0 = f1;
|
||||
f1 = f2;
|
||||
if (paramFile->MoreFrames() == true) {
|
||||
f2 = paramFile->GetNextImageFilename();
|
||||
}
|
||||
morestuff--;
|
||||
session->UpdateScreen();
|
||||
}
|
||||
|
||||
// if (numberOfFrames != 1) {
|
||||
// if (image->hasAlpha() && paramFile->NoAlpha()==false) {
|
||||
// lastFrame = true;
|
||||
// encoder->SparseEncode();
|
||||
// } else {
|
||||
// WriteLossless();
|
||||
// }
|
||||
// }
|
||||
CloseRoQFile();
|
||||
}
|
||||
|
||||
void roq::Write16Word( word *aWord, idFile *stream )
|
||||
{
|
||||
byte a, b;
|
||||
|
||||
a = *aWord & 0xff;
|
||||
b = *aWord >> 8;
|
||||
|
||||
stream->Write( &a, 1 );
|
||||
stream->Write( &b, 1 );
|
||||
}
|
||||
|
||||
void roq::Write32Word( unsigned int *aWord, idFile *stream )
|
||||
{
|
||||
byte a, b, c, d;
|
||||
|
||||
a = *aWord & 0xff;
|
||||
b = (*aWord >> 8) & 0xff;
|
||||
c = (*aWord >> 16) & 0xff;
|
||||
d = (*aWord >> 24) & 0xff;
|
||||
|
||||
stream->Write( &a, 1 );
|
||||
stream->Write( &b, 1 );
|
||||
stream->Write( &c, 1 );
|
||||
stream->Write( &d, 1 );
|
||||
}
|
||||
|
||||
int roq::SizeFile( idFile *ftosize )
|
||||
{
|
||||
return ftosize->Length();
|
||||
}
|
||||
|
||||
/* Expanded data destination object for stdio output */
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
|
||||
byte* outfile; /* target stream */
|
||||
int size;
|
||||
} my_destination_mgr;
|
||||
|
||||
typedef my_destination_mgr * my_dest_ptr;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize destination --- called by jpeg_start_compress
|
||||
* before any data is actually written.
|
||||
*/
|
||||
|
||||
void roq::JPEGInitDestination (j_compress_ptr cinfo) {
|
||||
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
|
||||
|
||||
dest->pub.next_output_byte = dest->outfile;
|
||||
dest->pub.free_in_buffer = dest->size;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Empty the output buffer --- called whenever buffer fills up.
|
||||
*
|
||||
* In typical applications, this should write the entire output buffer
|
||||
* (ignoring the current state of next_output_byte & free_in_buffer),
|
||||
* reset the pointer & count to the start of the buffer, and return true
|
||||
* indicating that the buffer has been dumped.
|
||||
*
|
||||
* In applications that need to be able to suspend compression due to output
|
||||
* overrun, a FALSE return indicates that the buffer cannot be emptied now.
|
||||
* In this situation, the compressor will return to its caller (possibly with
|
||||
* an indication that it has not accepted all the supplied scanlines). The
|
||||
* application should resume compression after it has made more room in the
|
||||
* output buffer. Note that there are substantial restrictions on the use of
|
||||
* suspension --- see the documentation.
|
||||
*
|
||||
* When suspending, the compressor will back up to a convenient restart point
|
||||
* (typically the start of the current MCU). next_output_byte & free_in_buffer
|
||||
* indicate where the restart point will be if the current call returns FALSE.
|
||||
* Data beyond this point will be regenerated after resumption, so do not
|
||||
* write it out when emptying the buffer externally.
|
||||
*/
|
||||
|
||||
boolean roq::JPEGEmptyOutputBuffer (j_compress_ptr cinfo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compression initialization.
|
||||
* Before calling this, all parameters and a data destination must be set up.
|
||||
*
|
||||
* We require a write_all_tables parameter as a failsafe check when writing
|
||||
* multiple datastreams from the same compression object. Since prior runs
|
||||
* will have left all the tables marked sent_table=true, a subsequent run
|
||||
* would emit an abbreviated stream (no tables) by default. This may be what
|
||||
* is wanted, but for safety's sake it should not be the default behavior:
|
||||
* programmers should have to make a deliberate choice to emit abbreviated
|
||||
* images. Therefore the documentation and examples should encourage people
|
||||
* to pass write_all_tables=true; then it will take active thought to do the
|
||||
* wrong thing.
|
||||
*/
|
||||
|
||||
void roq::JPEGStartCompress (j_compress_ptr cinfo, bool write_all_tables) {
|
||||
if (cinfo->global_state != CSTATE_START)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
|
||||
if (write_all_tables)
|
||||
jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */
|
||||
|
||||
/* (Re)initialize error mgr and destination modules */
|
||||
(*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);
|
||||
(*cinfo->dest->init_destination) (cinfo);
|
||||
/* Perform master selection of active modules */
|
||||
jinit_compress_master(cinfo);
|
||||
/* Set up for the first pass */
|
||||
(*cinfo->master->prepare_for_pass) (cinfo);
|
||||
/* Ready for application to drive first pass through jpeg_write_scanlines
|
||||
* or jpeg_write_raw_data.
|
||||
*/
|
||||
cinfo->next_scanline = 0;
|
||||
cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write some scanlines of data to the JPEG compressor.
|
||||
*
|
||||
* The return value will be the number of lines actually written.
|
||||
* This should be less than the supplied num_lines only in case that
|
||||
* the data destination module has requested suspension of the compressor,
|
||||
* or if more than image_height scanlines are passed in.
|
||||
*
|
||||
* Note: we warn about excess calls to jpeg_write_scanlines() since
|
||||
* this likely signals an application programmer error. However,
|
||||
* excess scanlines passed in the last valid call are *silently* ignored,
|
||||
* so that the application need not adjust num_lines for end-of-image
|
||||
* when using a multiple-scanline buffer.
|
||||
*/
|
||||
|
||||
JDIMENSION roq::JPEGWriteScanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines) {
|
||||
JDIMENSION row_ctr, rows_left;
|
||||
|
||||
if (cinfo->global_state != CSTATE_SCANNING)
|
||||
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
|
||||
if (cinfo->next_scanline >= cinfo->image_height)
|
||||
WARNMS(cinfo, JWRN_TOO_MUCH_DATA);
|
||||
|
||||
/* Call progress monitor hook if present */
|
||||
if (cinfo->progress != NULL) {
|
||||
cinfo->progress->pass_counter = (long) cinfo->next_scanline;
|
||||
cinfo->progress->pass_limit = (long) cinfo->image_height;
|
||||
(*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo);
|
||||
}
|
||||
|
||||
/* Give master control module another chance if this is first call to
|
||||
* jpeg_write_scanlines. This lets output of the frame/scan headers be
|
||||
* delayed so that application can write COM, etc, markers between
|
||||
* jpeg_start_compress and jpeg_write_scanlines.
|
||||
*/
|
||||
if (cinfo->master->call_pass_startup)
|
||||
(*cinfo->master->pass_startup) (cinfo);
|
||||
|
||||
/* Ignore any extra scanlines at bottom of image. */
|
||||
rows_left = cinfo->image_height - cinfo->next_scanline;
|
||||
if (num_lines > rows_left)
|
||||
num_lines = rows_left;
|
||||
|
||||
row_ctr = 0;
|
||||
(*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines);
|
||||
cinfo->next_scanline += row_ctr;
|
||||
return row_ctr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminate destination --- called by jpeg_finish_compress
|
||||
* after all data has been written. Usually needs to flush buffer.
|
||||
*
|
||||
* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
|
||||
* application must deal with any cleanup that should happen even
|
||||
* for error exit.
|
||||
*/
|
||||
|
||||
static int hackSize;
|
||||
|
||||
void roq::JPEGTermDestination (j_compress_ptr cinfo) {
|
||||
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
|
||||
size_t datacount = dest->size - dest->pub.free_in_buffer;
|
||||
hackSize = datacount;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for output to a stdio stream.
|
||||
* The caller must have already opened the stream, and is responsible
|
||||
* for closing it after finishing compression.
|
||||
*/
|
||||
|
||||
void roq::JPEGDest (j_compress_ptr cinfo, byte* outfile, int size) {
|
||||
my_dest_ptr dest;
|
||||
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||
* This makes it dangerous to use this manager and a different destination
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if (cinfo->dest == NULL) { /* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)
|
||||
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
||||
sizeof(my_destination_mgr));
|
||||
}
|
||||
|
||||
dest = (my_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = JPEGInitDestination;
|
||||
dest->pub.empty_output_buffer = JPEGEmptyOutputBuffer;
|
||||
dest->pub.term_destination = JPEGTermDestination;
|
||||
dest->outfile = outfile;
|
||||
dest->size = size;
|
||||
}
|
||||
|
||||
void roq::WriteLossless( void ) {
|
||||
|
||||
word direct;
|
||||
uint directdw;
|
||||
|
||||
if (!dataStuff) {
|
||||
InitRoQPatterns();
|
||||
dataStuff=true;
|
||||
}
|
||||
direct = RoQ_QUAD_JPEG;
|
||||
Write16Word( &direct, RoQFile);
|
||||
|
||||
/* This struct contains the JPEG compression parameters and pointers to
|
||||
* working space (which is allocated as needed by the JPEG library).
|
||||
* It is possible to have several such structures, representing multiple
|
||||
* compression/decompression processes, in existence at once. We refer
|
||||
* to any one struct (and its associated working data) as a "JPEG object".
|
||||
*/
|
||||
struct jpeg_compress_struct cinfo;
|
||||
/* This struct represents a JPEG error handler. It is declared separately
|
||||
* because applications often want to supply a specialized error handler
|
||||
* (see the second half of this file for an example). But here we just
|
||||
* take the easy way out and use the standard error handler, which will
|
||||
* print a message on stderr and call exit() if compression fails.
|
||||
* Note that this struct must live as long as the main JPEG parameter
|
||||
* struct, to avoid dangling-pointer problems.
|
||||
*/
|
||||
struct jpeg_error_mgr jerr;
|
||||
/* More stuff */
|
||||
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
|
||||
int row_stride; /* physical row width in image buffer */
|
||||
byte *out;
|
||||
|
||||
/* Step 1: allocate and initialize JPEG compression object */
|
||||
|
||||
/* We have to set up the error handler first, in case the initialization
|
||||
* step fails. (Unlikely, but it could happen if you are out of memory.)
|
||||
* This routine fills in the contents of struct jerr, and returns jerr's
|
||||
* address which we place into the link field in cinfo.
|
||||
*/
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
/* Now we can initialize the JPEG compression object. */
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
/* Step 2: specify data destination (eg, a file) */
|
||||
/* Note: steps 2 and 3 can be done in either order. */
|
||||
|
||||
/* Here we use the library-supplied code to send compressed data to a
|
||||
* stdio stream. You can also write your own code to do something else.
|
||||
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
|
||||
* requires it in order to write binary files.
|
||||
*/
|
||||
out = (byte *)Mem_Alloc(image->pixelsWide()*image->pixelsHigh()*4);
|
||||
JPEGDest(&cinfo, out, image->pixelsWide()*image->pixelsHigh()*4);
|
||||
|
||||
/* Step 3: set parameters for compression */
|
||||
|
||||
/* First we supply a description of the input image.
|
||||
* Four fields of the cinfo struct must be filled in:
|
||||
*/
|
||||
cinfo.image_width = image->pixelsWide(); /* image width and height, in pixels */
|
||||
cinfo.image_height = image->pixelsHigh();
|
||||
cinfo.input_components = 4; /* # of color components per pixel */
|
||||
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
|
||||
/* Now use the library's routine to set default compression parameters.
|
||||
* (You must set at least cinfo.in_color_space before calling this,
|
||||
* since the defaults depend on the source color space.)
|
||||
*/
|
||||
jpeg_set_defaults(&cinfo);
|
||||
/* Now you can set any non-default parameters you wish to.
|
||||
* Here we just illustrate the use of quality (quantization table) scaling:
|
||||
*/
|
||||
jpeg_set_quality(&cinfo, paramFile->JpegQuality(), true /* limit to baseline-JPEG values */);
|
||||
|
||||
/* Step 4: Start compressor */
|
||||
|
||||
/* true ensures that we will write a complete interchange-JPEG file.
|
||||
* Pass true unless you are very sure of what you're doing.
|
||||
*/
|
||||
JPEGStartCompress(&cinfo, true);
|
||||
|
||||
/* Step 5: while (scan lines remain to be written) */
|
||||
/* jpeg_write_scanlines(...); */
|
||||
|
||||
/* Here we use the library's state variable cinfo.next_scanline as the
|
||||
* loop counter, so that we don't have to keep track ourselves.
|
||||
* To keep things simple, we pass one scanline per call; you can pass
|
||||
* more if you wish, though.
|
||||
*/
|
||||
row_stride = image->pixelsWide() * 4; /* JSAMPLEs per row in image_buffer */
|
||||
|
||||
byte *pixbuf = image->bitmapData();
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
/* jpeg_write_scanlines expects an array of pointers to scanlines.
|
||||
* Here the array is only one element long, but you could pass
|
||||
* more than one scanline at a time if that's more convenient.
|
||||
*/
|
||||
row_pointer[0] = &pixbuf[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
|
||||
(void) JPEGWriteScanlines(&cinfo, row_pointer, 1);
|
||||
}
|
||||
|
||||
/* Step 6: Finish compression */
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
/* After finish_compress, we can close the output file. */
|
||||
|
||||
directdw = hackSize;
|
||||
common->Printf("writeLossless: writing %d bytes to RoQ_QUAD_JPEG\n", hackSize);
|
||||
Write32Word( &directdw, RoQFile );
|
||||
direct = 0; // flags
|
||||
Write16Word( &direct, RoQFile );
|
||||
|
||||
RoQFile->Write( out, hackSize );
|
||||
Mem_Free(out);
|
||||
|
||||
/* Step 7: release JPEG compression object */
|
||||
|
||||
/* This is an important step since it will release a good deal of memory. */
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
/* And we're done! */
|
||||
encoder->SetPreviousImage( "first frame", image );
|
||||
}
|
||||
|
||||
void roq::InitRoQFile( const char *RoQFilename )
|
||||
{
|
||||
word i;
|
||||
static int finit = 0;
|
||||
|
||||
if (!finit) {
|
||||
finit++;
|
||||
common->Printf("initRoQFile: %s\n", RoQFilename);
|
||||
RoQFile = fileSystem->OpenFileWrite( RoQFilename );
|
||||
// chmod(RoQFilename, S_IREAD|S_IWRITE|S_ISUID|S_ISGID|0070|0007 );
|
||||
if ( !RoQFile ) {
|
||||
common->Error("Unable to open output file %s.\n", RoQFilename);
|
||||
}
|
||||
|
||||
i = RoQ_ID;
|
||||
Write16Word( &i, RoQFile );
|
||||
|
||||
i = 0xffff;
|
||||
Write16Word( &i, RoQFile );
|
||||
Write16Word( &i, RoQFile );
|
||||
|
||||
// to retain exact file format write out 32 for new roq's
|
||||
// on loading this will be noted and converted to 1000 / 30
|
||||
// as with any new sound dump avi demos we need to playback
|
||||
// at the speed the sound engine dumps the audio
|
||||
i = 30; // framerate
|
||||
Write16Word( &i, RoQFile );
|
||||
}
|
||||
roqOutfile = RoQFilename;
|
||||
}
|
||||
|
||||
void roq::InitRoQPatterns( void )
|
||||
{
|
||||
uint j;
|
||||
word direct;
|
||||
|
||||
direct = RoQ_QUAD_INFO;
|
||||
Write16Word( &direct, RoQFile );
|
||||
|
||||
j = 8;
|
||||
|
||||
Write32Word( &j, RoQFile );
|
||||
common->Printf("initRoQPatterns: outputting %d bytes to RoQ_INFO\n", j);
|
||||
direct = image->hasAlpha();
|
||||
if (ParamNoAlpha() == true) direct = 0;
|
||||
|
||||
Write16Word( &direct, RoQFile );
|
||||
|
||||
direct = image->pixelsWide();
|
||||
Write16Word( &direct, RoQFile );
|
||||
direct = image->pixelsHigh();
|
||||
Write16Word( &direct, RoQFile );
|
||||
direct = 8;
|
||||
Write16Word( &direct, RoQFile );
|
||||
direct = 4;
|
||||
Write16Word( &direct, RoQFile );
|
||||
}
|
||||
|
||||
void roq::CloseRoQFile( void )
|
||||
{
|
||||
common->Printf("closeRoQFile: closing RoQ file\n");
|
||||
fileSystem->CloseFile( RoQFile );
|
||||
}
|
||||
|
||||
void roq::WriteHangFrame( void )
|
||||
{
|
||||
uint j;
|
||||
word direct;
|
||||
common->Printf("*******************************************************************\n");
|
||||
direct = RoQ_QUAD_HANG;
|
||||
Write16Word( &direct, RoQFile);
|
||||
j = 0;
|
||||
Write32Word( &j, RoQFile);
|
||||
direct = 0;
|
||||
Write16Word( &direct, RoQFile);
|
||||
}
|
||||
|
||||
void roq::WriteCodeBookToStream( byte *codebook, int csize, word cflags )
|
||||
{
|
||||
uint j;
|
||||
word direct;
|
||||
|
||||
if (!csize) {
|
||||
common->Printf("writeCodeBook: false VQ DATA!!!!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
direct = RoQ_QUAD_CODEBOOK;
|
||||
|
||||
Write16Word( &direct, RoQFile);
|
||||
|
||||
j = csize;
|
||||
|
||||
Write32Word( &j, RoQFile);
|
||||
common->Printf("writeCodeBook: outputting %d bytes to RoQ_QUAD_CODEBOOK\n", j);
|
||||
|
||||
direct = cflags;
|
||||
Write16Word( &direct, RoQFile);
|
||||
|
||||
RoQFile->Write( codebook, j );
|
||||
}
|
||||
|
||||
void roq::WriteCodeBook( byte *codebook )
|
||||
{
|
||||
memcpy( codes, codebook, 4096 );
|
||||
}
|
||||
|
||||
void roq::WriteFrame( quadcel *pquad )
|
||||
{
|
||||
word action, direct;
|
||||
int onCCC, onAction, i, code;
|
||||
uint j;
|
||||
byte *cccList;
|
||||
bool *use2, *use4;
|
||||
int dx,dy,dxMean,dyMean,index2[256],index4[256], dimension;
|
||||
|
||||
cccList = (byte *)Mem_Alloc( numQuadCels * 8); // maximum length
|
||||
use2 = (bool *)Mem_Alloc(256*sizeof(bool));
|
||||
use4 = (bool *)Mem_Alloc(256*sizeof(bool));
|
||||
|
||||
for(i=0;i<256;i++) {
|
||||
use2[i] = false;
|
||||
use4[i] = false;
|
||||
}
|
||||
|
||||
action = 0;
|
||||
j = onAction = 0;
|
||||
onCCC = 2; // onAction going to go at zero
|
||||
|
||||
dxMean = encoder->MotMeanX();
|
||||
dyMean = encoder->MotMeanY();
|
||||
|
||||
if (image->hasAlpha()) dimension = 10; else dimension = 6;
|
||||
|
||||
for (i=0; i<numQuadCels; i++) {
|
||||
if ( pquad[i].size && pquad[i].size < 16 ) {
|
||||
switch( pquad[i].status ) {
|
||||
case SLD:
|
||||
use4[pquad[i].patten[0]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+0]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+1]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+2]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+3]] = true;
|
||||
break;
|
||||
case PAT:
|
||||
use4[pquad[i].patten[0]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+0]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+1]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+2]] = true;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+3]] = true;
|
||||
break;
|
||||
case CCC:
|
||||
use2[pquad[i].patten[1]] = true;
|
||||
use2[pquad[i].patten[2]] = true;
|
||||
use2[pquad[i].patten[3]] = true;
|
||||
use2[pquad[i].patten[4]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataStuff) {
|
||||
dataStuff=true;
|
||||
InitRoQPatterns();
|
||||
if (image->hasAlpha()) i = 3584; else i = 2560;
|
||||
WriteCodeBookToStream( codes, i, 0 );
|
||||
for(i=0;i<256;i++) {
|
||||
index2[i] = i;
|
||||
index4[i] = i;
|
||||
}
|
||||
} else {
|
||||
j = 0;
|
||||
for(i=0;i<256;i++) {
|
||||
if (use2[i]) {
|
||||
index2[i] = j;
|
||||
for(dx=0;dx<dimension;dx++) cccList[j*dimension+dx] = codes[i*dimension+dx];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
code = j*dimension;
|
||||
direct = j;
|
||||
common->Printf("writeFrame: really used %d 2x2 cels\n", j);
|
||||
j = 0;
|
||||
for(i=0;i<256;i++) {
|
||||
if (use4[i]) {
|
||||
index4[i] = j;
|
||||
for(dx=0;dx<4;dx++) cccList[j*4+code+dx] = index2[codes[i*4+(dimension*256)+dx]];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
code += j*4;
|
||||
direct = (direct<<8) + j;
|
||||
common->Printf("writeFrame: really used %d 4x4 cels\n", j);
|
||||
if (image->hasAlpha()) i = 3584; else i = 2560;
|
||||
if ( code == i || j == 256) {
|
||||
WriteCodeBookToStream( codes, i, 0 );
|
||||
} else {
|
||||
WriteCodeBookToStream( cccList, code, direct );
|
||||
}
|
||||
}
|
||||
|
||||
action = 0;
|
||||
j = onAction = 0;
|
||||
|
||||
for (i=0; i<numQuadCels; i++) {
|
||||
if ( pquad[i].size && pquad[i].size < 16 ) {
|
||||
code = -1;
|
||||
switch( pquad[i].status ) {
|
||||
case DEP:
|
||||
code = 3;
|
||||
break;
|
||||
case SLD:
|
||||
code = 2;
|
||||
cccList[onCCC++] = index4[pquad[i].patten[0]];
|
||||
break;
|
||||
case MOT:
|
||||
code = 0;
|
||||
break;
|
||||
case FCC:
|
||||
code = 1;
|
||||
dx = ((pquad[i].domain >> 8 )) - 128 - dxMean + 8;
|
||||
dy = ((pquad[i].domain & 0xff)) - 128 - dyMean + 8;
|
||||
if (dx>15 || dx<0 || dy>15 || dy<0 ) {
|
||||
common->Error("writeFrame: FCC error %d,%d mean %d,%d at %d,%d,%d rmse %f\n", dx,dy, dxMean, dyMean,pquad[i].xat,pquad[i].yat,pquad[i].size, pquad[i].snr[FCC] );
|
||||
}
|
||||
cccList[onCCC++] = (dx<<4)+dy;
|
||||
break;
|
||||
case PAT:
|
||||
code = 2;
|
||||
cccList[onCCC++] = index4[pquad[i].patten[0]];
|
||||
break;
|
||||
case CCC:
|
||||
code = 3;
|
||||
cccList[onCCC++] = index2[pquad[i].patten[1]];
|
||||
cccList[onCCC++] = index2[pquad[i].patten[2]];
|
||||
cccList[onCCC++] = index2[pquad[i].patten[3]];
|
||||
cccList[onCCC++] = index2[pquad[i].patten[4]];
|
||||
break;
|
||||
case DEAD:
|
||||
common->Error("dead cels in picture\n");
|
||||
break;
|
||||
}
|
||||
if (code == -1) {
|
||||
common->Error( "writeFrame: an error occurred writing the frame\n");
|
||||
}
|
||||
|
||||
action = (action<<2)|code;
|
||||
j++;
|
||||
if (j == 8) {
|
||||
j = 0;
|
||||
cccList[onAction+0] = (action & 0xff);
|
||||
cccList[onAction+1] = ((action >> 8) & 0xff);
|
||||
onAction = onCCC;
|
||||
onCCC += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (j) {
|
||||
action <<= ((8-j)*2);
|
||||
cccList[onAction+0] = (action & 0xff);
|
||||
cccList[onAction+1] = ((action >> 8) & 0xff);
|
||||
}
|
||||
|
||||
direct = RoQ_QUAD_VQ;
|
||||
|
||||
Write16Word( &direct, RoQFile);
|
||||
|
||||
j = onCCC;
|
||||
Write32Word( &j, RoQFile);
|
||||
|
||||
direct = dyMean;
|
||||
direct &= 0xff;
|
||||
direct += (dxMean<<8); // flags
|
||||
|
||||
Write16Word( &direct, RoQFile);
|
||||
|
||||
common->Printf("writeFrame: outputting %d bytes to RoQ_QUAD_VQ\n", j);
|
||||
|
||||
previousSize = j;
|
||||
|
||||
RoQFile->Write( cccList, onCCC );
|
||||
|
||||
Mem_Free( cccList );
|
||||
Mem_Free( use2 );
|
||||
Mem_Free( use4 );
|
||||
}
|
||||
|
||||
//
|
||||
// load a frame, create a window (if neccesary) and display the frame
|
||||
//
|
||||
void roq::LoadAndDisplayImage( const char * filename )
|
||||
{
|
||||
if (image) delete image;
|
||||
|
||||
common->Printf("loadAndDisplayImage: %s\n", filename);
|
||||
|
||||
currentFile = filename;
|
||||
|
||||
image = new NSBitmapImageRep( filename );
|
||||
|
||||
numQuadCels = ((image->pixelsWide() & 0xfff0)*(image->pixelsHigh() & 0xfff0))/(MINSIZE*MINSIZE);
|
||||
numQuadCels += numQuadCels/4 + numQuadCels/16;
|
||||
|
||||
// if (paramFile->deltaFrames] == true && cleared == false && [image isPlanar] == false) {
|
||||
// cleared = true;
|
||||
// imageData = [image data];
|
||||
// memset( imageData, 0, image->pixelsWide()*image->pixelsHigh()*[image samplesPerPixel]);
|
||||
// }
|
||||
|
||||
if (!quietMode) common->Printf("loadAndDisplayImage: %dx%d\n", image->pixelsWide(), image->pixelsHigh());
|
||||
}
|
||||
|
||||
void roq::MarkQuadx( int xat, int yat, int size, float cerror, int choice ) {
|
||||
}
|
||||
|
||||
NSBitmapImageRep* roq::CurrentImage( void )
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
int roq::NumberOfFrames( void ) {
|
||||
return numberOfFrames;
|
||||
}
|
||||
|
||||
void RoQFileEncode_f( const idCmdArgs &args ) {
|
||||
if ( args.Argc() != 2 ) {
|
||||
common->Printf( "Usage: roq <paramfile>\n" );
|
||||
return;
|
||||
}
|
||||
theRoQ = new roq;
|
||||
int startMsec = Sys_Milliseconds();
|
||||
theRoQ->EncodeStream( args.Argv( 1 ) );
|
||||
int stopMsec = Sys_Milliseconds();
|
||||
common->Printf( "total encoding time: %i second\n", ( stopMsec - startMsec ) / 1000 );
|
||||
|
||||
}
|
||||
135
neo/tools/compilers/roqvq/roq.h
Normal file
135
neo/tools/compilers/roqvq/roq.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#ifndef __roq_h__
|
||||
#define __roq_h__
|
||||
|
||||
#include "gdefs.h"
|
||||
#include "roqParam.h"
|
||||
#include "quaddefs.h"
|
||||
#define JPEG_INTERNALS
|
||||
extern "C" {
|
||||
#include "../../../renderer/jpeg-6/jpeglib.h"
|
||||
}
|
||||
#pragma once
|
||||
|
||||
class codec;
|
||||
class roqParam;
|
||||
|
||||
class NSBitmapImageRep {
|
||||
public:
|
||||
|
||||
NSBitmapImageRep( void );
|
||||
NSBitmapImageRep( const char *filename );
|
||||
NSBitmapImageRep( int wide, int high );
|
||||
~NSBitmapImageRep();
|
||||
|
||||
NSBitmapImageRep & operator=( const NSBitmapImageRep &a );
|
||||
|
||||
int samplesPerPixel( void );
|
||||
int pixelsWide( void );
|
||||
int pixelsHigh( void );
|
||||
byte * bitmapData( void );
|
||||
bool hasAlpha( void );
|
||||
bool isPlanar( void );
|
||||
|
||||
private:
|
||||
|
||||
byte * bmap;
|
||||
int width;
|
||||
int height;
|
||||
ID_TIME_T timestamp;
|
||||
|
||||
};
|
||||
|
||||
class roq {
|
||||
public:
|
||||
roq();
|
||||
~roq();
|
||||
|
||||
void WriteLossless( void );
|
||||
void LoadAndDisplayImage( const char *filename );
|
||||
void CloseRoQFile( bool which );
|
||||
void InitRoQFile( const char *roqFilename );
|
||||
void InitRoQPatterns( void );
|
||||
void EncodeStream( const char *paramInputFile );
|
||||
void EncodeQuietly( bool which );
|
||||
bool IsQuiet( void );
|
||||
bool IsLastFrame( void );
|
||||
NSBitmapImageRep * CurrentImage( void );
|
||||
void MarkQuadx( int xat, int yat, int size, float cerror, int choice );
|
||||
void WritePuzzleFrame( quadcel *pquad );
|
||||
void WriteFrame( quadcel *pquad );
|
||||
void WriteCodeBook( byte *codebook );
|
||||
void WwriteCodeBookToStream( byte *codes, int csize, word cflags );
|
||||
int PreviousFrameSize( void );
|
||||
bool MakingVideo( void );
|
||||
bool ParamNoAlpha( void );
|
||||
bool SearchType( void );
|
||||
bool HasSound( void );
|
||||
const char * CurrentFilename( void );
|
||||
int NormalFrameSize( void );
|
||||
int FirstFrameSize( void );
|
||||
bool Scaleable( void );
|
||||
void WriteHangFrame( void );
|
||||
int NumberOfFrames( void );
|
||||
private:
|
||||
void Write16Word( word *aWord, idFile *stream );
|
||||
void Write32Word( unsigned int *aWord, idFile *stream );
|
||||
int SizeFile( idFile *ftosize );
|
||||
void CloseRoQFile( void );
|
||||
void WriteCodeBookToStream( byte *codebook, int csize, word cflags );
|
||||
|
||||
static void JPEGInitDestination( j_compress_ptr cinfo );
|
||||
static boolean JPEGEmptyOutputBuffer( j_compress_ptr cinfo );
|
||||
static void JPEGTermDestination( j_compress_ptr cinfo );
|
||||
|
||||
void JPEGStartCompress( j_compress_ptr cinfo, bool write_all_tables );
|
||||
JDIMENSION JPEGWriteScanlines( j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines );
|
||||
void JPEGDest( j_compress_ptr cinfo, byte* outfile, int size );
|
||||
void JPEGSave( char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer );
|
||||
|
||||
codec * encoder;
|
||||
roqParam * paramFile;
|
||||
|
||||
idFile * RoQFile;
|
||||
NSBitmapImageRep * image;
|
||||
int numQuadCels;
|
||||
bool quietMode;
|
||||
bool lastFrame;
|
||||
idStr roqOutfile;
|
||||
idStr currentFile;
|
||||
int numberOfFrames;
|
||||
int previousSize;
|
||||
byte codes[4096];
|
||||
bool dataStuff;
|
||||
|
||||
};
|
||||
|
||||
extern roq *theRoQ; // current roq
|
||||
|
||||
#endif /* !__roq_h__ */
|
||||
933
neo/tools/compilers/roqvq/roq.m
Normal file
933
neo/tools/compilers/roqvq/roq.m
Normal file
@@ -0,0 +1,933 @@
|
||||
|
||||
#import "roq.h"
|
||||
#import "codec.h"
|
||||
|
||||
#ifdef __MACOS__
|
||||
|
||||
blah
|
||||
|
||||
#endif
|
||||
|
||||
@implementation roq
|
||||
|
||||
|
||||
- init
|
||||
{
|
||||
cWindow = eWindow = sWindow = 0;
|
||||
image = 0;
|
||||
quietMode = NO;
|
||||
encoder = 0;
|
||||
previousSize = 0;
|
||||
lastFrame = NO;
|
||||
codes = malloc( 4*1024 );
|
||||
dataStuff=NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
free( codes );
|
||||
|
||||
if (image) [image dealloc];
|
||||
if (encoder) [encoder dealloc];
|
||||
return;
|
||||
}
|
||||
|
||||
- encodeQuietly:(BOOL)which
|
||||
{
|
||||
quietMode = which;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isQuiet
|
||||
{
|
||||
return quietMode;
|
||||
}
|
||||
|
||||
- (BOOL)isLastFrame
|
||||
{
|
||||
return lastFrame;
|
||||
}
|
||||
|
||||
- (BOOL)scaleable
|
||||
{
|
||||
return [paramFileId isScaleable];
|
||||
}
|
||||
|
||||
- (BOOL)paramNoAlpha
|
||||
{
|
||||
return [paramFileId noAlpha];
|
||||
}
|
||||
|
||||
- (BOOL)makingVideo
|
||||
{
|
||||
return YES; //[paramFileId timecode];
|
||||
}
|
||||
|
||||
- (BOOL)searchType
|
||||
{
|
||||
return [paramFileId searchType];
|
||||
}
|
||||
|
||||
- (BOOL)hasSound
|
||||
{
|
||||
return [paramFileId hasSound];
|
||||
}
|
||||
|
||||
- (int)previousFrameSize
|
||||
{
|
||||
return previousSize;
|
||||
}
|
||||
|
||||
-(int)firstFrameSize
|
||||
{
|
||||
return [paramFileId firstFrameSize];
|
||||
}
|
||||
|
||||
-(int)normalFrameSize
|
||||
{
|
||||
return [paramFileId normalFrameSize];
|
||||
}
|
||||
|
||||
- (char *)currentFilename
|
||||
{
|
||||
return currentFile;
|
||||
}
|
||||
|
||||
-encodeStream: (id)paramInputFile
|
||||
{
|
||||
int onFrame;
|
||||
char f0[MAXPATHLEN], f1[MAXPATHLEN], f2[MAXPATHLEN];
|
||||
int morestuff;
|
||||
|
||||
onFrame = 1;
|
||||
|
||||
encoder = [[codec alloc] init: self];
|
||||
numberOfFrames = [paramInputFile numberOfFrames];
|
||||
paramFileId = paramInputFile;
|
||||
|
||||
if ([paramInputFile noAlpha]==YES) printf("encodeStream: eluding alpha\n");
|
||||
|
||||
f0[0] = 0;
|
||||
strcpy( f1, [paramInputFile getNextImageFilename]);
|
||||
if (( [paramInputFile moreFrames] == YES )) strcpy( f2, [paramInputFile getNextImageFilename]);
|
||||
morestuff = numberOfFrames;
|
||||
|
||||
while( morestuff ) {
|
||||
[self loadAndDisplayImage: f1];
|
||||
|
||||
if (onFrame==1 && ([image hadAlpha]==NO || [paramInputFile noAlpha]==YES) && ![self makingVideo] && ![self scaleable]) {
|
||||
[encoder sparseEncode: self];
|
||||
// [self writeLossless];
|
||||
} else {
|
||||
if (!strcmp( f0, f1 ) && strcmp( f1, f2) ) {
|
||||
[self writeHangFrame];
|
||||
} else {
|
||||
[encoder sparseEncode: self];
|
||||
}
|
||||
}
|
||||
|
||||
onFrame++;
|
||||
strcpy( f0, f1 );
|
||||
strcpy( f1, f2 );
|
||||
if ([paramInputFile moreFrames] == YES) strcpy( f2, [paramInputFile getNextImageFilename]);
|
||||
morestuff--;
|
||||
}
|
||||
|
||||
if (numberOfFrames != 1) {
|
||||
if ([image hadAlpha] && [paramInputFile noAlpha]==NO) {
|
||||
lastFrame = YES;
|
||||
[encoder sparseEncode: self];
|
||||
} else {
|
||||
[self writeLossless];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- write16Word:(word *)aWord to:(FILE *)stream
|
||||
{
|
||||
byte a, b;
|
||||
|
||||
a = *aWord & 0xff;
|
||||
b = *aWord >> 8;
|
||||
|
||||
fputc( a, stream );
|
||||
fputc( b, stream );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- write32Word:( unsigned int *)aWord to:(FILE *)stream
|
||||
{
|
||||
byte a, b, c, d;
|
||||
|
||||
a = *aWord & 0xff;
|
||||
b = (*aWord >> 8) & 0xff;
|
||||
c = (*aWord >> 16) & 0xff;
|
||||
d = (*aWord >> 24) & 0xff;
|
||||
|
||||
fputc( a, stream );
|
||||
fputc( b, stream );
|
||||
fputc( c, stream );
|
||||
fputc( d, stream );
|
||||
return self;
|
||||
}
|
||||
|
||||
-(int)sizeFile:(FILE *)ftosize;
|
||||
{
|
||||
long int fat, fend;
|
||||
fat = ftell(ftosize);
|
||||
fseek( ftosize, 0, SEEK_END );
|
||||
fend = ftell(ftosize);
|
||||
fseek( ftosize, fat, SEEK_SET);
|
||||
return (fend);
|
||||
}
|
||||
|
||||
- convertPlanertoPacked
|
||||
{
|
||||
byte *iPlane[5], *newdata, *olddata;
|
||||
int x,y,index, sample, pixelsWide, pixelsHigh;
|
||||
TByteBitmapImageRep *newImage;
|
||||
|
||||
pixelsWide = [image pixelsWide];
|
||||
pixelsHigh = [image pixelsHigh];
|
||||
|
||||
printf("convertPlanertoPacked: converting\n");
|
||||
|
||||
newImage = [[TByteBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
|
||||
pixelsWide: pixelsWide
|
||||
pixelsHigh: pixelsHigh
|
||||
bitsPerSample: 8
|
||||
samplesPerPixel: 4
|
||||
hasAlpha: YES
|
||||
isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bytesPerRow: 0
|
||||
bitsPerPixel: 0];
|
||||
|
||||
newdata = [newImage bitmapData];
|
||||
index = 0;
|
||||
|
||||
if ([image isPlanar]) {
|
||||
[image getBitmapDataPlanes: iPlane];
|
||||
for(y=0;y<pixelsHigh;y++) {
|
||||
for(x=0;x<pixelsWide;x++) {
|
||||
newdata[index++] = iPlane[0][y*pixelsWide+x];
|
||||
newdata[index++] = iPlane[1][y*pixelsWide+x];
|
||||
newdata[index++] = iPlane[2][y*pixelsWide+x];
|
||||
if ([image hasAlpha]) {
|
||||
newdata[index++] = iPlane[3][y*pixelsWide+x];
|
||||
} else {
|
||||
newdata[index++] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sample = 0;
|
||||
olddata = [image bitmapData];
|
||||
for(y=0;y<pixelsHigh;y++) {
|
||||
for(x=0;x<pixelsWide;x++) {
|
||||
newdata[index++] = olddata[sample++];
|
||||
newdata[index++] = olddata[sample++];
|
||||
newdata[index++] = olddata[sample++];
|
||||
if ([image hasAlpha]) {
|
||||
newdata[index++] = olddata[sample++];
|
||||
} else {
|
||||
newdata[index++] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[image dealloc];
|
||||
image = newImage;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- writeLossless
|
||||
{
|
||||
word direct;
|
||||
unsigned int j;
|
||||
char tempFile[MAXPATHLEN];
|
||||
FILE *ftemp;
|
||||
byte *buffer;
|
||||
int res, mess;
|
||||
|
||||
[self convertPlanertoPacked];
|
||||
if (!dataStuff) {
|
||||
[self initRoQPatterns];
|
||||
dataStuff=YES;
|
||||
}
|
||||
direct = RoQ_QUAD_JPEG;
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
sprintf(tempFile, "%s.jpg",[paramFileId roqTempFilename]);
|
||||
|
||||
[image writeJFIF:tempFile quality: [paramFileId jpegQuality]];
|
||||
|
||||
ftemp = fopen(tempFile, "rb");
|
||||
if (!ftemp) { fprintf(stderr, "Could not open temp file\n"); exit(1); }
|
||||
j = [self sizeFile: ftemp];
|
||||
printf("writeLossless: writing %d bytes to RoQ_QUAD_JPEG\n", j);
|
||||
[self write32Word: &j to: RoQFile];
|
||||
direct = 0; // flags
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
buffer = malloc( 16384 );
|
||||
do {
|
||||
res = fread( buffer, 1, 16384, ftemp);
|
||||
mess = fwrite( buffer, 1, res, RoQFile );
|
||||
if (res != mess) { fprintf(stderr, "Could not write to output stream\n"); exit(1); }
|
||||
} while ( res == 16384 );
|
||||
free( buffer );
|
||||
fclose(ftemp);
|
||||
[encoder setPreviousImage: tempFile from: image parent: self];
|
||||
remove( tempFile );
|
||||
fflush( RoQFile );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- initRoQFile:(const char *)RoQFilename
|
||||
{
|
||||
word i;
|
||||
static int finit = 0;
|
||||
|
||||
if (!finit) {
|
||||
finit++;
|
||||
printf("initRoQFile: %s\n", RoQFilename);
|
||||
RoQFile = fopen( RoQFilename, "w" );
|
||||
// chmod(RoQFilename, S_IREAD|S_IWRITE|S_ISUID|S_ISGID|0070|0007 );
|
||||
if (!RoQFile) {
|
||||
fprintf(stderr,"Unable to open output file %s.\n", RoQFilename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i = RoQ_ID;
|
||||
[self write16Word: &i to: RoQFile];
|
||||
|
||||
i = 0xffff;
|
||||
[self write16Word: &i to: RoQFile];
|
||||
[self write16Word: &i to: RoQFile];
|
||||
|
||||
i = 24; // framerate
|
||||
[self write16Word: &i to: RoQFile];
|
||||
fflush( RoQFile );
|
||||
}
|
||||
strcpy( roqOutfile, RoQFilename );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- initRoQPatterns
|
||||
{
|
||||
int j;
|
||||
word direct;
|
||||
|
||||
direct = RoQ_QUAD_INFO;
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
j = 8;
|
||||
|
||||
[self write32Word: &j to: RoQFile];
|
||||
printf("initRoQPatterns: outputting %d bytes to RoQ_INFO\n", j);
|
||||
direct = [image hadAlpha];
|
||||
if ([self paramNoAlpha] == YES) direct = 0;
|
||||
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
direct = [image pixelsWide];
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
direct = [image pixelsHigh];
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
direct = 8;
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
direct = 4;
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
fflush( RoQFile );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- closeRoQFile
|
||||
{
|
||||
fflush( RoQFile );
|
||||
printf("closeRoQFile: closing RoQ file\n");
|
||||
fclose( RoQFile );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- writeHangFrame
|
||||
{
|
||||
int j;
|
||||
word direct;
|
||||
printf("*******************************************************************\n");
|
||||
direct = RoQ_QUAD_HANG;
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
j = 0;
|
||||
[self write32Word: &j to: RoQFile];
|
||||
direct = 0;
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
return self;
|
||||
}
|
||||
|
||||
- writeCodeBookToStream: (byte *)codebook size: (int)csize flags: (word)cflags
|
||||
{
|
||||
int j;
|
||||
word direct;
|
||||
|
||||
if (!csize) {
|
||||
printf("writeCodeBook: NO VQ DATA!!!!\n");
|
||||
return self;
|
||||
}
|
||||
|
||||
direct = RoQ_QUAD_CODEBOOK;
|
||||
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
j = csize;
|
||||
|
||||
[self write32Word: &j to: RoQFile];
|
||||
printf("writeCodeBook: outputting %d bytes to RoQ_QUAD_CODEBOOK\n", j);
|
||||
|
||||
direct = cflags;
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
fwrite( codebook, j, 1, RoQFile);
|
||||
|
||||
fflush( RoQFile );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- writeCodeBook: (byte *)codebook
|
||||
{
|
||||
memcpy( codes, codebook, 4096 );
|
||||
return self;
|
||||
}
|
||||
|
||||
- writeFrame:(quadcel *)pquad
|
||||
{
|
||||
word action, direct;
|
||||
int onCCC, onAction, i, j, code;
|
||||
byte *cccList;
|
||||
BOOL *use2, *use4;
|
||||
int dx,dy,dxMean,dyMean,index2[256],index4[256], dimension;
|
||||
|
||||
cccList = malloc( numQuadCels * 8); // maximum length
|
||||
use2 = malloc(256*sizeof(BOOL));
|
||||
use4 = malloc(256*sizeof(BOOL));
|
||||
|
||||
for(i=0;i<256;i++) {
|
||||
use2[i] = NO;
|
||||
use4[i] = NO;
|
||||
}
|
||||
|
||||
action = 0;
|
||||
j = onAction = 0;
|
||||
onCCC = 2; // onAction going to go at zero
|
||||
|
||||
dxMean = [encoder motMeanX];
|
||||
dyMean = [encoder motMeanY];
|
||||
|
||||
if ([image hadAlpha]) dimension = 10; else dimension = 6;
|
||||
|
||||
for (i=0; i<numQuadCels; i++) {
|
||||
if ( pquad[i].size && pquad[i].size < 16 ) {
|
||||
switch( pquad[i].status ) {
|
||||
case SLD:
|
||||
use4[pquad[i].patten[0]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+0]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+1]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+2]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+3]] = YES;
|
||||
break;
|
||||
case PAT:
|
||||
use4[pquad[i].patten[0]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+0]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+1]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+2]] = YES;
|
||||
use2[codes[dimension*256+(pquad[i].patten[0]*4)+3]] = YES;
|
||||
break;
|
||||
case CCC:
|
||||
use2[pquad[i].patten[1]] = YES;
|
||||
use2[pquad[i].patten[2]] = YES;
|
||||
use2[pquad[i].patten[3]] = YES;
|
||||
use2[pquad[i].patten[4]] = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataStuff) {
|
||||
dataStuff=YES;
|
||||
[self initRoQPatterns];
|
||||
if ([image hadAlpha]) i = 3584; else i = 2560;
|
||||
[self writeCodeBookToStream: codes size: i flags: 0];
|
||||
for(i=0;i<256;i++) {
|
||||
index2[i] = i;
|
||||
index4[i] = i;
|
||||
}
|
||||
} else {
|
||||
j = 0;
|
||||
for(i=0;i<256;i++) {
|
||||
if (use2[i]) {
|
||||
index2[i] = j;
|
||||
for(dx=0;dx<dimension;dx++) cccList[j*dimension+dx] = codes[i*dimension+dx];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
code = j*dimension;
|
||||
direct = j;
|
||||
printf("writeFrame: really used %d 2x2 cels\n", j);
|
||||
j = 0;
|
||||
for(i=0;i<256;i++) {
|
||||
if (use4[i]) {
|
||||
index4[i] = j;
|
||||
for(dx=0;dx<4;dx++) cccList[j*4+code+dx] = index2[codes[i*4+(dimension*256)+dx]];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
code += j*4;
|
||||
direct = (direct<<8) + j;
|
||||
printf("writeFrame: really used %d 4x4 cels\n", j);
|
||||
if ([image hadAlpha]) i = 3584; else i = 2560;
|
||||
if ( code == i || j == 256) {
|
||||
[self writeCodeBookToStream: codes size: i flags: 0];
|
||||
} else {
|
||||
[self writeCodeBookToStream: cccList size: code flags: direct];
|
||||
}
|
||||
}
|
||||
|
||||
action = 0;
|
||||
j = onAction = 0;
|
||||
|
||||
for (i=0; i<numQuadCels; i++) {
|
||||
if ( pquad[i].size && pquad[i].size < 16 ) {
|
||||
code = -1;
|
||||
switch( pquad[i].status ) {
|
||||
case DEP:
|
||||
code = 3;
|
||||
break;
|
||||
case SLD:
|
||||
code = 2;
|
||||
cccList[onCCC++] = index4[pquad[i].patten[0]];
|
||||
break;
|
||||
case MOT:
|
||||
code = 0;
|
||||
break;
|
||||
case FCC:
|
||||
code = 1;
|
||||
dx = ((pquad[i].domain >> 8 )) - 128 - dxMean + 8;
|
||||
dy = ((pquad[i].domain & 0xff)) - 128 - dyMean + 8;
|
||||
if (dx>15 || dx<0 || dy>15 || dy<0 ) {
|
||||
printf("writeFrame: FCC error %d,%d mean %d,%d at %d,%d,%d rmse %f\n", dx,dy, dxMean, dyMean,pquad[i].xat,pquad[i].yat,pquad[i].size, pquad[i].snr[FCC] );
|
||||
exit(1);
|
||||
}
|
||||
cccList[onCCC++] = (dx<<4)+dy;
|
||||
break;
|
||||
case PAT:
|
||||
code = 2;
|
||||
cccList[onCCC++] = index4[pquad[i].patten[0]];
|
||||
break;
|
||||
case CCC:
|
||||
code = 3;
|
||||
cccList[onCCC++] = index2[pquad[i].patten[1]];
|
||||
cccList[onCCC++] = index2[pquad[i].patten[2]];
|
||||
cccList[onCCC++] = index2[pquad[i].patten[3]];
|
||||
cccList[onCCC++] = index2[pquad[i].patten[4]];
|
||||
break;
|
||||
case DEAD:
|
||||
fprintf(stderr,"dead cels in picture\n");
|
||||
break;
|
||||
}
|
||||
if (code == -1) {
|
||||
fprintf(stderr, "writeFrame: an error occurred writing the frame\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
action = (action<<2)|code;
|
||||
j++;
|
||||
if (j == 8) {
|
||||
j = 0;
|
||||
cccList[onAction+0] = (action & 0xff);
|
||||
cccList[onAction+1] = ((action >> 8) & 0xff);
|
||||
onAction = onCCC;
|
||||
onCCC += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (j) {
|
||||
action <<= ((8-j)*2);
|
||||
cccList[onAction+0] = (action & 0xff);
|
||||
cccList[onAction+1] = ((action >> 8) & 0xff);
|
||||
}
|
||||
|
||||
direct = RoQ_QUAD_VQ;
|
||||
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
j = onCCC;
|
||||
[self write32Word: &j to: RoQFile];
|
||||
|
||||
direct = dyMean;
|
||||
direct &= 0xff;
|
||||
direct += (dxMean<<8); // flags
|
||||
|
||||
[self write16Word: &direct to: RoQFile];
|
||||
|
||||
printf("writeFrame: outputting %d bytes to RoQ_QUAD_VQ\n", j);
|
||||
|
||||
previousSize = j;
|
||||
|
||||
fwrite( cccList, onCCC, 1, RoQFile );
|
||||
|
||||
fflush( RoQFile );
|
||||
|
||||
free( cccList );
|
||||
free( use2 );
|
||||
free( use4 );
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- writePuzzleFrame:(quadcel *)pquad
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (TByteBitmapImageRep *)scaleImage: (TByteBitmapImageRep *)toscale
|
||||
{
|
||||
int newx, newy,x,y,s,linesize;
|
||||
TByteBitmapImageRep *newImage;
|
||||
unsigned char *i0, *i1;
|
||||
int newv;
|
||||
|
||||
newx = 288;
|
||||
|
||||
if ([toscale pixelsHigh] == 160) {
|
||||
newy = 320;
|
||||
} else {
|
||||
newy = [toscale pixelsHigh];
|
||||
}
|
||||
newImage = [[TByteBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
|
||||
pixelsWide: newx
|
||||
pixelsHigh: newy
|
||||
bitsPerSample: 8
|
||||
samplesPerPixel: 4
|
||||
hasAlpha: YES
|
||||
isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bytesPerRow: 0
|
||||
bitsPerPixel: 0];
|
||||
|
||||
i0 = [toscale bitmapData];
|
||||
i1 = [newImage bitmapData];
|
||||
linesize = [toscale pixelsWide]*4;
|
||||
|
||||
for(y=0; y<newy; y++) {
|
||||
for(x=0; x<320; x++) {
|
||||
if (x>=16 && x<304) {
|
||||
for(s=0;s<4;s++) {
|
||||
if ([toscale pixelsHigh] == 160) {
|
||||
newv = i0[(x*2+0)*4+(y>>1)*linesize+s];
|
||||
newv += i0[(x*2+1)*4+(y>>1)*linesize+s];
|
||||
newv += i0[(x*2+0)*4+((y>>1)+1)*linesize+s];
|
||||
newv += i0[(x*2+1)*4+((y>>1)+1)*linesize+s];
|
||||
newv = newv/4;
|
||||
} else {
|
||||
newv = i0[(x*2+0)*4+y*linesize+s];
|
||||
newv += i0[(x*2+1)*4+y*linesize+s];
|
||||
newv = newv/2;
|
||||
}
|
||||
i1[(x-16)*4+y*(288*4)+s] = newv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (newImage);
|
||||
}
|
||||
|
||||
//
|
||||
// load a frame, create a window (if neccesary) and display the frame
|
||||
//
|
||||
- loadAndDisplayImage: (const char *) filename
|
||||
{
|
||||
NSRect cRect;
|
||||
char *secondFilename,firstFilename[MAXPATHLEN+1];
|
||||
id image1;
|
||||
NSSize newSize;
|
||||
// unsigned char *imageData;
|
||||
// static BOOL cleared = NO;
|
||||
|
||||
if (image) [image dealloc];
|
||||
|
||||
printf("loadAndDisplayImage: %s\n", filename);
|
||||
|
||||
strcpy( currentFile, filename );
|
||||
|
||||
image = [TByteBitmapImageRep alloc];
|
||||
if (!(secondFilename= strchr(filename,'\n'))) { //one filename, no compositing
|
||||
[image initFromFile: filename ];
|
||||
}
|
||||
else {
|
||||
strncpy(firstFilename,filename,secondFilename-filename);
|
||||
firstFilename[secondFilename-filename]='\0';
|
||||
secondFilename++;//point past the \n
|
||||
image1 = [[TByteBitmapImageRep alloc] initFromFile:firstFilename];
|
||||
[image initFromFile: secondFilename ];//result will be in here
|
||||
//image is the composite of those two...
|
||||
[self composite:image1 to:image];
|
||||
}
|
||||
//
|
||||
// wolf stuff
|
||||
//
|
||||
newSize.width = 256;
|
||||
newSize.height = 256;
|
||||
|
||||
[image setSize: newSize];
|
||||
|
||||
if ([paramFileId output3DO] == YES) {
|
||||
image1 = [self scaleImage: image];
|
||||
[image dealloc];
|
||||
image = image1;
|
||||
}
|
||||
|
||||
numQuadCels = (([image pixelsWide] & 0xfff0)*([image pixelsHigh] & 0xfff0))/(MINSIZE*MINSIZE);
|
||||
numQuadCels += numQuadCels/4 + numQuadCels/16;
|
||||
|
||||
// if ([paramFileId deltaFrames] == YES && cleared == NO && [image isPlanar] == NO) {
|
||||
// cleared = YES;
|
||||
// imageData = [image data];
|
||||
// memset( imageData, 0, [image pixelsWide]*[image pixelsHigh]*[image samplesPerPixel]);
|
||||
// }
|
||||
|
||||
if (!quietMode) printf("loadAndDisplayImage: %dx%d\n", [image pixelsWide], [image pixelsHigh]);
|
||||
|
||||
if (!quietMode) {
|
||||
if (!cWindow) {
|
||||
cRect.origin.x = 8.0 * 48.0;
|
||||
cRect.origin.y = ([image pixelsHigh]+80);
|
||||
cRect.size.width = [image pixelsWide];
|
||||
cRect.size.height = [image pixelsHigh];
|
||||
cWindow = [[NSWindow alloc] initWithContentRect:cRect styleMask:NSTitledWindowMask
|
||||
backing:NSBackingStoreBuffered defer:NO];
|
||||
cRect.origin.x = cRect.origin.y = 0.0;
|
||||
// [[cWindow contentView] setClipping:NO];
|
||||
[cWindow setTitle: @"current frame"];
|
||||
[cWindow makeKeyAndOrderFront: [cWindow contentView]];
|
||||
[cWindow display];
|
||||
}
|
||||
|
||||
|
||||
cRect = [[cWindow contentView] bounds];
|
||||
[[cWindow contentView] lockFocus];
|
||||
[image drawInRect: cRect];
|
||||
[[cWindow contentView] unlockFocus];
|
||||
[cWindow flushWindow];
|
||||
|
||||
if (!eWindow) {
|
||||
cRect.origin.x = 8.0 * 48.0 - ([image pixelsWide]>>1) - 4;
|
||||
cRect.origin.y = ([image pixelsHigh]+80);
|
||||
cRect.size.width = [image pixelsWide] >> 1;
|
||||
cRect.size.height = [image pixelsHigh] >> 1;
|
||||
eWindow = [[NSWindow alloc] initWithContentRect:cRect styleMask:NSTitledWindowMask
|
||||
backing:NSBackingStoreBuffered defer:NO];
|
||||
cRect.origin.x = cRect.origin.y = 0.0;
|
||||
// [[eWindow contentView] setClipping:NO];
|
||||
[eWindow setTitle: @"cel error"];
|
||||
[eWindow makeKeyAndOrderFront: [eWindow contentView]];
|
||||
[eWindow display];
|
||||
}
|
||||
|
||||
if (!sWindow) {
|
||||
cRect.origin.x = 8.0 * 48.0 - ([image pixelsWide]>>1) - 4;
|
||||
cRect.origin.y = ([image pixelsHigh]+80) + (([image pixelsHigh]+48)>>1);
|
||||
cRect.size.width = [image pixelsWide] >> 1;
|
||||
cRect.size.height = [image pixelsHigh] >> 1;
|
||||
sWindow = [[NSWindow alloc] initWithContentRect: cRect styleMask:NSTitledWindowMask
|
||||
backing:NSBackingStoreBuffered defer:NO];
|
||||
cRect.origin.x = cRect.origin.y = 0.0;
|
||||
// [[eWindow contentView] setClipping:NO];
|
||||
[sWindow setTitle: @"quadtree map"];
|
||||
[sWindow makeKeyAndOrderFront: [sWindow contentView]];
|
||||
[sWindow display];
|
||||
}
|
||||
|
||||
cRect = [[sWindow contentView] bounds];
|
||||
[[sWindow contentView] lockFocus];
|
||||
[image drawInRect: cRect];
|
||||
[[sWindow contentView] unlockFocus];
|
||||
[sWindow flushWindow];
|
||||
|
||||
cRect = [[eWindow contentView] bounds];
|
||||
[[eWindow contentView] lockFocus];
|
||||
[image drawInRect: cRect];
|
||||
[[eWindow contentView] unlockFocus];
|
||||
[eWindow flushWindow];
|
||||
|
||||
if (!errImage) {
|
||||
errImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL
|
||||
pixelsWide: 1
|
||||
pixelsHigh: 1
|
||||
bitsPerSample: 8
|
||||
samplesPerPixel: 3
|
||||
hasAlpha: NO
|
||||
isPlanar: NO
|
||||
colorSpaceName: NSCalibratedRGBColorSpace
|
||||
bytesPerRow: 0
|
||||
bitsPerPixel: 0];
|
||||
}
|
||||
|
||||
// NSPing();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- markQuadx: (int)xat quady: (int)yat quads: (int)size error: (float)cerror type: (int)choice
|
||||
{
|
||||
NSRect cRect;
|
||||
byte *err;
|
||||
static int ywasat = -1;
|
||||
if (!quietMode) {
|
||||
cRect.origin.x = (xat)>>1;
|
||||
cRect.origin.y = ([image pixelsHigh] - yat - size)>>1;
|
||||
cRect.size.width = cRect.size.height = (size)>>1;
|
||||
|
||||
if (size < 1) {
|
||||
[[sWindow contentView] lockFocus];
|
||||
if (size == 8 && choice == 1) {
|
||||
PSsetgray(NSWhite);
|
||||
} else if (size == 8 && choice == 3) {
|
||||
PSsetgray(NSLightGray);
|
||||
} else if (size == 4) {
|
||||
PSsetgray(NSDarkGray);
|
||||
} else if (size == 2) {
|
||||
PSsetgray(NSBlack);
|
||||
}
|
||||
NSFrameRectWithWidth(cRect,0.0);
|
||||
[[sWindow contentView] unlockFocus];
|
||||
if (!(ywasat & 31)) {
|
||||
[sWindow flushWindow];
|
||||
}
|
||||
}
|
||||
|
||||
err = [errImage bitmapData];
|
||||
err[0] = err[1] = err[2] = 0;
|
||||
if ( cerror > 31 ) cerror = 31;
|
||||
if (choice & 1) err[0] = (int)cerror*8;
|
||||
if (choice & 2) err[1] = (int)cerror*8;
|
||||
if (choice & 4) err[2] = (int)cerror*8;
|
||||
|
||||
[[eWindow contentView] lockFocus];
|
||||
[errImage drawInRect: cRect];
|
||||
[[eWindow contentView] unlockFocus];
|
||||
if (!(ywasat & 31)) {
|
||||
[eWindow flushWindow];
|
||||
}
|
||||
ywasat++;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSBitmapImageRep*)currentImage
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
- (id)errorWindow
|
||||
{
|
||||
return eWindow;
|
||||
}
|
||||
|
||||
- (id)scaleWindow
|
||||
{
|
||||
return sWindow;
|
||||
}
|
||||
- (int)numberOfFrames {
|
||||
return numberOfFrames;
|
||||
}
|
||||
|
||||
- composite:(NSBitmapImageRep *)source to: (NSBitmapImageRep *)destination
|
||||
{
|
||||
unsigned short value;
|
||||
int x,y,bpp,inc,pixelsWide,pixelsHigh,yoff,pixel;
|
||||
byte *iPlane[5], *dPlane[5];
|
||||
|
||||
bpp = [source samplesPerPixel];
|
||||
pixelsWide = [source pixelsWide];
|
||||
pixelsHigh = [source pixelsHigh];
|
||||
|
||||
if ([source isPlanar]) {
|
||||
[source getBitmapDataPlanes: iPlane];
|
||||
[destination getBitmapDataPlanes: dPlane];
|
||||
for(y=0;y<pixelsHigh;y++) {
|
||||
yoff = y*pixelsWide;
|
||||
for(x=0;x<pixelsWide;x++) {
|
||||
if ([destination hasAlpha]) {
|
||||
value = dPlane[3][yoff+x];
|
||||
} else {
|
||||
value = 255;
|
||||
}
|
||||
if (value == 0) {
|
||||
dPlane[0][yoff+x] = iPlane[0][yoff+x];
|
||||
dPlane[1][yoff+x] = iPlane[1][yoff+x];
|
||||
dPlane[2][yoff+x] = iPlane[2][yoff+x];
|
||||
dPlane[3][yoff+x] = iPlane[3][yoff+x];
|
||||
} else if (value != 255) {
|
||||
pixel = ((iPlane[0][yoff+x]*(255-value))/255) + ((dPlane[0][yoff+x]*value)/255);
|
||||
dPlane[0][yoff+x] = pixel;
|
||||
pixel = ((iPlane[1][yoff+x]*(255-value))/255) + ((dPlane[1][yoff+x]*value)/255);
|
||||
dPlane[1][yoff+x] = pixel;
|
||||
pixel = ((iPlane[2][yoff+x]*(255-value))/255) + ((dPlane[2][yoff+x]*value)/255);
|
||||
dPlane[2][yoff+x] = pixel;
|
||||
if ([destination hasAlpha]) {
|
||||
if (iPlane[3][yoff+x]>dPlane[3][yoff+x]) dPlane[3][yoff+x] = iPlane[3][yoff+x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iPlane[0] = [source bitmapData];
|
||||
dPlane[0] = [destination bitmapData];
|
||||
for(y=0;y<pixelsHigh;y++) {
|
||||
yoff = y*pixelsWide*bpp;
|
||||
for(x=0;x<pixelsWide;x++) {
|
||||
inc = x*bpp;
|
||||
if ([destination hasAlpha]) {
|
||||
value = dPlane[0][yoff+inc+3];
|
||||
} else {
|
||||
value = 255;
|
||||
}
|
||||
if (value == 0) {
|
||||
dPlane[0][yoff+inc+0] = iPlane[0][yoff+inc+0];
|
||||
dPlane[0][yoff+inc+1] = iPlane[0][yoff+inc+1];
|
||||
dPlane[0][yoff+inc+2] = iPlane[0][yoff+inc+2];
|
||||
dPlane[0][yoff+inc+3] = iPlane[0][yoff+inc+3];
|
||||
} else if (value != 255) {
|
||||
pixel = ((iPlane[0][yoff+inc+0]*(255-value))/255) + ((dPlane[0][yoff+inc+0]*value)/255);
|
||||
dPlane[0][yoff+inc+0] = pixel;
|
||||
pixel = ((iPlane[0][yoff+inc+1]*(255-value))/255) + ((dPlane[0][yoff+inc+1]*value)/255);
|
||||
dPlane[0][yoff+inc+1] = pixel;
|
||||
pixel = ((iPlane[0][yoff+inc+2]*(255-value))/255) + ((dPlane[0][yoff+inc+2]*value)/255);
|
||||
dPlane[0][yoff+inc+2] = pixel;
|
||||
if ([destination hasAlpha]) {
|
||||
if (iPlane[0][yoff+inc+3]>dPlane[0][yoff+inc+3]) dPlane[0][yoff+inc+3] = iPlane[0][yoff+inc+3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
|
||||
}
|
||||
@end
|
||||
635
neo/tools/compilers/roqvq/roqParam.cpp
Normal file
635
neo/tools/compilers/roqvq/roqParam.cpp
Normal file
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#include "../../../idlib/precompiled.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "roqParam.h"
|
||||
|
||||
//
|
||||
// read a parameter file in (true I bloddy well had to do this again) (and yet again to c++)
|
||||
//
|
||||
|
||||
int parseRange(const char *rangeStr,int field, int skipnum[], int startnum[], int endnum[],int numfiles[],bool padding[],int numpadding[] );
|
||||
int parseTimecodeRange(const char *rangeStr,int field, int skipnum[], int startnum[], int endnum[],int numfiles[],bool padding[],int numpadding[] );
|
||||
|
||||
void roqParam::InitFromFile( const char *fileName )
|
||||
{
|
||||
idParser *src;
|
||||
idToken token;
|
||||
int i, readarg;
|
||||
|
||||
|
||||
src = new idParser( fileName, LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES );
|
||||
if ( !src->IsLoaded() ) {
|
||||
delete src;
|
||||
common->Printf("Error: can't open param file %s\n", fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
common->Printf("initFromFile: %s\n", fileName);
|
||||
|
||||
fullSearch = false;
|
||||
scaleDown = false;
|
||||
encodeVideo = false;
|
||||
addPath = false;
|
||||
screenShots = false;
|
||||
startPalette = false;
|
||||
endPalette = false;
|
||||
fixedPalette = false;
|
||||
keyColor = false;
|
||||
justDelta = false;
|
||||
useTimecodeForRange = false;
|
||||
onFrame = 0;
|
||||
numInputFiles = 0;
|
||||
currentPath[0] = '\0';
|
||||
make3DO = false;
|
||||
makeVectors = false;
|
||||
justDeltaFlag = false;
|
||||
noAlphaAtAll = false;
|
||||
twentyFourToThirty = false;
|
||||
hasSound = false;
|
||||
isScaleable = false;
|
||||
firstframesize = 56*1024;
|
||||
normalframesize = 20000;
|
||||
jpegDefault = 85;
|
||||
|
||||
realnum = 0;
|
||||
while( 1 ) {
|
||||
if ( !src->ReadToken( &token ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
readarg = 0;
|
||||
// input dir
|
||||
if (token.Icmp( "input_dir") == 0) {
|
||||
src->ReadToken( &token );
|
||||
addPath = true;
|
||||
currentPath = token;
|
||||
// common->Printf(" + input directory is %s\n", currentPath );
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// input dir
|
||||
if (token.Icmp( "scale_down") == 0) {
|
||||
scaleDown = true;
|
||||
// common->Printf(" + scaling down input\n" );
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// full search
|
||||
if (token.Icmp( "fullsearch") == 0) {
|
||||
normalframesize += normalframesize/2;
|
||||
fullSearch = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// scaleable
|
||||
if (token.Icmp( "scaleable") == 0) {
|
||||
isScaleable = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// input dir
|
||||
if (token.Icmp( "no_alpha") == 0) {
|
||||
noAlphaAtAll = true;
|
||||
// common->Printf(" + scaling down input\n" );
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
if (token.Icmp( "24_fps_in_30_fps_out") == 0) {
|
||||
twentyFourToThirty = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// video in
|
||||
if (token.Icmp( "video_in") == 0) {
|
||||
encodeVideo = true;
|
||||
// common->Printf(" + Using the video port as input\n");
|
||||
continue;
|
||||
}
|
||||
//timecode range
|
||||
if (token.Icmp( "timecode") == 0) {
|
||||
useTimecodeForRange = true;
|
||||
firstframesize = 12*1024;
|
||||
normalframesize = 4500;
|
||||
// common->Printf(" + Using timecode as range\n");
|
||||
continue;
|
||||
}
|
||||
// soundfile for making a .RnR
|
||||
if (token.Icmp( "sound") == 0) {
|
||||
src->ReadToken( &token );
|
||||
soundfile = token;
|
||||
hasSound = true;
|
||||
// common->Printf(" + Using timecode as range\n");
|
||||
continue;
|
||||
}
|
||||
// soundfile for making a .RnR
|
||||
if (token.Icmp( "has_sound") == 0) {
|
||||
hasSound = true;
|
||||
continue;
|
||||
}
|
||||
// outfile
|
||||
if (token.Icmp( "filename") == 0) {
|
||||
src->ReadToken( &token );
|
||||
outputFilename = token;
|
||||
i = strlen(outputFilename);
|
||||
// common->Printf(" + output file is %s\n", outputFilename );
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// starting palette
|
||||
if (token.Icmp( "start_palette") == 0) {
|
||||
src->ReadToken( &token );
|
||||
sprintf(startPal, "/LocalLibrary/vdxPalettes/%s", token.c_str());
|
||||
// common->Error(" + starting palette is %s\n", startPal );
|
||||
startPalette = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// ending palette
|
||||
if (token.Icmp( "end_palette") == 0) {
|
||||
src->ReadToken( &token );
|
||||
sprintf(endPal, "/LocalLibrary/vdxPalettes/%s", token.c_str());
|
||||
// common->Printf(" + ending palette is %s\n", endPal );
|
||||
endPalette = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// fixed palette
|
||||
if (token.Icmp( "fixed_palette") == 0) {
|
||||
src->ReadToken( &token );
|
||||
sprintf(startPal, "/LocalLibrary/vdxPalettes/%s", token.c_str());
|
||||
// common->Printf(" + fixed palette is %s\n", startPal );
|
||||
fixedPalette = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// these are screen shots
|
||||
if (token.Icmp( "screenshot") == 0) {
|
||||
// common->Printf(" + shooting screen shots\n" );
|
||||
screenShots = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// key_color r g b
|
||||
if (token.Icmp( "key_color") == 0) {
|
||||
keyR = src->ParseInt();
|
||||
keyG = src->ParseInt();
|
||||
keyB = src->ParseInt();
|
||||
keyColor = true;
|
||||
// common->Printf(" + key color is %03d %03d %03d\n", keyR, keyG, keyB );
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// only want deltas
|
||||
if (token.Icmp( "just_delta") == 0) {
|
||||
// common->Printf(" + outputting deltas in the night\n" );
|
||||
// justDelta = true;
|
||||
// justDeltaFlag = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// doing 3DO
|
||||
if (token.Icmp( "3DO") == 0) {
|
||||
make3DO = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// makes codebook vector tables
|
||||
if (token.Icmp( "codebook") == 0) {
|
||||
makeVectors = true;
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// set first frame size
|
||||
if (token.Icmp( "firstframesize") == 0) {
|
||||
firstframesize = src->ParseInt();
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// set normal frame size
|
||||
if (token.Icmp( "normalframesize") == 0) {
|
||||
normalframesize = src->ParseInt();
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
// set normal frame size
|
||||
if (token.Icmp( "stillframequality") == 0) {
|
||||
jpegDefault = src->ParseInt();
|
||||
readarg++;
|
||||
continue;
|
||||
}
|
||||
if (token.Icmp( "input") == 0) {
|
||||
int num_files = 255;
|
||||
|
||||
range = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
padding = (bool *)Mem_ClearedAlloc( num_files * sizeof(bool) );
|
||||
padding2 = (bool *)Mem_ClearedAlloc( num_files * sizeof(bool) );
|
||||
skipnum = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
skipnum2 = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
startnum = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
startnum2 = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
endnum = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
endnum2 = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
numpadding = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
numpadding2 = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
numfiles = (int *)Mem_ClearedAlloc( num_files * sizeof(int) );
|
||||
idStr empty;
|
||||
file.AssureSize( num_files, empty );
|
||||
file.AssureSize( num_files, empty );
|
||||
|
||||
field = 0;
|
||||
realnum = 0;
|
||||
do {
|
||||
src->ReadToken(&token);
|
||||
if ( token.Icmp( "end_input") != 0 ) {
|
||||
idStr arg1, arg2, arg3;
|
||||
|
||||
file[field] = token;
|
||||
while (src->ReadTokenOnLine( &token ) && token.Icmp( "[" ) ) {
|
||||
file[field].Append( token );
|
||||
}
|
||||
|
||||
arg1 = token;
|
||||
while (src->ReadTokenOnLine( &token ) && token.Icmp( "[" ) ) {
|
||||
arg1 += token;
|
||||
}
|
||||
|
||||
arg2 = token;
|
||||
while (src->ReadTokenOnLine( &token ) && token.Icmp( "[" ) ) {
|
||||
arg2 += token;
|
||||
}
|
||||
|
||||
arg3 = token;
|
||||
while (src->ReadTokenOnLine( &token ) && token.Icmp( "[" ) ) {
|
||||
arg3 += token;
|
||||
}
|
||||
|
||||
if ( arg1[0] != '[' ) {
|
||||
// common->Printf(" + reading %s\n", file[field] );
|
||||
range[field] = 0;
|
||||
numfiles[field] = 1;
|
||||
realnum++;
|
||||
}
|
||||
else {
|
||||
if ( arg1[0] == '[' )
|
||||
{
|
||||
range[field] = 1;
|
||||
if (useTimecodeForRange) {
|
||||
realnum += parseTimecodeRange( arg1, field, skipnum, startnum, endnum, numfiles, padding, numpadding);
|
||||
// common->Printf(" + reading %s from %d to %d\n", file[field], startnum[field], endnum[field]);
|
||||
}
|
||||
else {
|
||||
realnum += parseRange( arg1, field, skipnum, startnum, endnum, numfiles, padding, numpadding);
|
||||
// common->Printf(" + reading %s from %d to %d\n", file[field], startnum[field], endnum[field]);
|
||||
}
|
||||
}
|
||||
else if (( arg1[0] != '[' ) && ( arg2[0] == '[') && ( arg3[0] =='[')) { //a double ranger...
|
||||
int files1,files2;
|
||||
|
||||
file2[field] = arg1;
|
||||
range[field] = 2;
|
||||
files1 = parseRange(arg2, field, skipnum, startnum, endnum, numfiles, padding, numpadding);
|
||||
// common->Printf(" + reading %s from %d to %d\n", file[field], startnum[field], endnum[field]);
|
||||
files2 = parseRange(arg3, field, skipnum2, startnum2, endnum2, numfiles, padding2, numpadding2);
|
||||
// common->Printf(" + reading %s from %d to %d\n", file2[field], startnum2[field], endnum2[field]);
|
||||
if (files1 != files2) {
|
||||
common->Error( "You had %d files for %s and %d for %s!", files1, arg1.c_str(), files2, arg2.c_str() );
|
||||
}
|
||||
else {
|
||||
realnum += files1;//not both, they are parallel
|
||||
}
|
||||
}
|
||||
else {
|
||||
common->Error("Error: invalid range on open (%s %s %s)\n", arg1.c_str(), arg2.c_str(), arg3.c_str() );
|
||||
}
|
||||
}
|
||||
field++;
|
||||
}
|
||||
} while (token.Icmp( "end_input"));
|
||||
}
|
||||
}
|
||||
|
||||
if (TwentyFourToThirty()) realnum = realnum+(realnum>>2);
|
||||
numInputFiles = realnum;
|
||||
common->Printf(" + reading a total of %d frames in %s\n", numInputFiles, currentPath.c_str() );
|
||||
delete src;
|
||||
}
|
||||
|
||||
void roqParam::GetNthInputFileName( idStr &fileName, int n ) {
|
||||
int i, myfield, index,hrs,mins,secs,frs;
|
||||
char tempfile[33], left[256], right[256], *strp;
|
||||
if ( n > realnum ) n = realnum;
|
||||
// overcome starting at zero by ++ing and then --ing.
|
||||
if (TwentyFourToThirty()) { n++; n = (n/5) * 4 + (n % 5); n--; }
|
||||
|
||||
i = 0;
|
||||
myfield = 0;
|
||||
|
||||
while (i <= n) {
|
||||
i += numfiles[myfield++];
|
||||
}
|
||||
myfield--;
|
||||
i -= numfiles[myfield];
|
||||
|
||||
if ( range[myfield] == 1 ) {
|
||||
|
||||
strcpy( left, file[myfield] );
|
||||
strp = strstr( left, "*" );
|
||||
*strp++ = 0;
|
||||
sprintf(right, "%s", strp);
|
||||
|
||||
if ( startnum[myfield] <= endnum[myfield] ) {
|
||||
index = startnum[myfield] + ((n-i)*skipnum[myfield]);
|
||||
} else {
|
||||
index = startnum[myfield] - ((n-i)*skipnum[myfield]);
|
||||
}
|
||||
|
||||
if ( padding[myfield] == true ) {
|
||||
if (useTimecodeForRange) {
|
||||
hrs = index/(30*60*60) ;
|
||||
mins = (index/(30*60)) %60;
|
||||
secs = (index/(30)) % 60;
|
||||
frs = index % 30;
|
||||
sprintf(fileName,"%s%.02d%.02d/%.02d%.02d%.02d%.02d%s",left,hrs,mins,hrs,mins,secs,frs,right);
|
||||
}
|
||||
else {
|
||||
sprintf(tempfile, "%032d", index );
|
||||
sprintf(fileName, "%s%s%s", left, &tempfile[ 32-numpadding[myfield] ], right );
|
||||
}
|
||||
} else {
|
||||
if (useTimecodeForRange) {
|
||||
hrs = index/(30*60*60) ;
|
||||
mins = (index/(30*60)) %60;
|
||||
secs = (index/(30)) % 60;
|
||||
frs = index % 30;
|
||||
sprintf(fileName,"%s%.02d%.02d/%.02d%.02d%.02d%.02d%s",left,hrs,mins,hrs,mins,secs,frs,right);
|
||||
}
|
||||
else {
|
||||
sprintf(fileName, "%s%d%s", left, index, right );
|
||||
}
|
||||
}
|
||||
} else if ( range[myfield] == 2 ) {
|
||||
|
||||
strcpy( left, file[myfield] );
|
||||
strp = strstr( left, "*" );
|
||||
*strp++ = 0;
|
||||
sprintf(right, "%s", strp);
|
||||
|
||||
if ( startnum[myfield] <= endnum[myfield] ) {
|
||||
index = startnum[myfield] + ((n-i)*skipnum[myfield]);
|
||||
} else {
|
||||
index = startnum[myfield] - ((n-i)*skipnum[myfield]);
|
||||
}
|
||||
|
||||
if ( padding[myfield] == true ) {
|
||||
sprintf(tempfile, "%032d", index );
|
||||
sprintf(fileName, "%s%s%s", left, &tempfile[ 32-numpadding[myfield] ], right );
|
||||
} else {
|
||||
sprintf(fileName, "%s%d%s", left, index, right );
|
||||
}
|
||||
|
||||
strcpy( left, file2[myfield] );
|
||||
strp = strstr( left, "*" );
|
||||
*strp++ = 0;
|
||||
sprintf(right, "%s", strp);
|
||||
|
||||
if ( startnum2[myfield] <= endnum2[myfield] ) {
|
||||
index = startnum2[myfield] + ((n-i)*skipnum2[myfield]);
|
||||
} else {
|
||||
index = startnum2[myfield] - ((n-i)*skipnum2[myfield]);
|
||||
}
|
||||
|
||||
if ( padding2[myfield] == true ) {
|
||||
sprintf(tempfile, "%032d", index );
|
||||
fileName += va( "\n%s%s%s", left, &tempfile[ 32-numpadding2[myfield] ], right );
|
||||
} else {
|
||||
fileName += va( "\n%s%d%s", left, index, right );
|
||||
}
|
||||
} else {
|
||||
fileName = file[myfield];
|
||||
}
|
||||
}
|
||||
|
||||
const char* roqParam::GetNextImageFilename( void ) {
|
||||
idStr tempBuffer;
|
||||
int i;
|
||||
int len;
|
||||
|
||||
GetNthInputFileName( tempBuffer, onFrame++);
|
||||
if ( justDeltaFlag == true ) {
|
||||
onFrame--;
|
||||
justDeltaFlag = false;
|
||||
}
|
||||
|
||||
if ( addPath == true ) {
|
||||
currentFile = currentPath + "/" + tempBuffer;
|
||||
} else {
|
||||
currentFile = tempBuffer;
|
||||
}
|
||||
len = currentFile.Length();
|
||||
for(i=0;i<len;i++) {
|
||||
if (currentFile[i] == '^') {
|
||||
currentFile[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return currentFile.c_str();
|
||||
}
|
||||
|
||||
const char* roqParam::RoqFilename( void ) {
|
||||
return outputFilename.c_str();
|
||||
}
|
||||
|
||||
const char* roqParam::SoundFilename( void ) {
|
||||
return soundfile.c_str();
|
||||
}
|
||||
|
||||
const char* roqParam::RoqTempFilename( void ) {
|
||||
int i, j, len;
|
||||
|
||||
j = 0;
|
||||
len = outputFilename.Length();
|
||||
for(i=0; i<len; i++ )
|
||||
if ( outputFilename[i] == '/' ) j = i;
|
||||
|
||||
sprintf(tempFilename, "/%s.temp", &outputFilename[j+1] );
|
||||
|
||||
return tempFilename.c_str();
|
||||
}
|
||||
|
||||
bool roqParam::Timecode( void ) {
|
||||
return useTimecodeForRange;
|
||||
}
|
||||
|
||||
bool roqParam::OutputVectors( void ) {
|
||||
return makeVectors;
|
||||
}
|
||||
|
||||
bool roqParam::HasSound( void ) {
|
||||
return hasSound;
|
||||
}
|
||||
|
||||
bool roqParam::DeltaFrames( void ) {
|
||||
return justDelta;
|
||||
}
|
||||
|
||||
bool roqParam::NoAlpha( void ) {
|
||||
return noAlphaAtAll;
|
||||
}
|
||||
|
||||
bool roqParam::SearchType( void ) {
|
||||
return fullSearch;
|
||||
}
|
||||
|
||||
bool roqParam::MoreFrames( void ) {
|
||||
if (onFrame < numInputFiles) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool roqParam::TwentyFourToThirty( void ) {
|
||||
return twentyFourToThirty;
|
||||
}
|
||||
|
||||
int roqParam::NumberOfFrames( void ) {
|
||||
return numInputFiles;
|
||||
}
|
||||
|
||||
int roqParam::FirstFrameSize( void ) {
|
||||
return firstframesize;
|
||||
}
|
||||
|
||||
int roqParam::NormalFrameSize( void ) {
|
||||
return normalframesize;
|
||||
}
|
||||
|
||||
bool roqParam::IsScaleable( void ) {
|
||||
return isScaleable;
|
||||
}
|
||||
|
||||
int roqParam::JpegQuality( void ) {
|
||||
return jpegDefault;
|
||||
}
|
||||
|
||||
int parseRange(const char *rangeStr,int field, int skipnum[], int startnum[], int endnum[],int numfiles[],bool padding[],int numpadding[] ) {
|
||||
char start[64], end[64], skip[64];
|
||||
char *stptr, *enptr, *skptr;
|
||||
int i,realnum;
|
||||
|
||||
i = 1;
|
||||
realnum = 0;
|
||||
stptr = start;
|
||||
enptr = end;
|
||||
skptr = skip;
|
||||
do {
|
||||
*stptr++ = rangeStr[i++];
|
||||
} while ( rangeStr[i] >= '0' && rangeStr[i] <= '9' );
|
||||
*stptr = '\0';
|
||||
if ( rangeStr[i++] != '-' ) {
|
||||
common->Error("Error: invalid range on middle \n");
|
||||
}
|
||||
do {
|
||||
*enptr++ = rangeStr[i++];
|
||||
} while ( rangeStr[i] >= '0' && rangeStr[i] <= '9' );
|
||||
*enptr = '\0';
|
||||
if ( rangeStr[i] != ']' ) {
|
||||
if ( rangeStr[i++] != '+' ) {
|
||||
common->Error("Error: invalid range on close\n");
|
||||
}
|
||||
do {
|
||||
*skptr++ = rangeStr[i++];
|
||||
} while ( rangeStr[i] >= '0' && rangeStr[i] <= '9' );
|
||||
*skptr = '\0';
|
||||
skipnum[field] = atoi( skip );
|
||||
} else {
|
||||
skipnum[field] = 1;
|
||||
}
|
||||
startnum[field] = atoi( start );
|
||||
endnum[field] = atoi( end );
|
||||
numfiles[field] = (abs( startnum[field] - endnum[field] ) / skipnum[field]) + 1;
|
||||
realnum += numfiles[field];
|
||||
if ( start[0] == '0' && start[1] != '\0' ) {
|
||||
padding[field] = true;
|
||||
numpadding[field] = strlen( start );
|
||||
} else {
|
||||
padding[field] = false;
|
||||
}
|
||||
return realnum;
|
||||
}
|
||||
|
||||
int parseTimecodeRange(const char *rangeStr,int field, int skipnum[], int startnum[], int endnum[],int numfiles[],bool padding[],int numpadding[] )
|
||||
{
|
||||
char start[64], end[64], skip[64];
|
||||
char *stptr, *enptr, *skptr;
|
||||
int i,realnum,hrs,mins,secs,frs;
|
||||
|
||||
i = 1;//skip the '['
|
||||
realnum = 0;
|
||||
stptr = start;
|
||||
enptr = end;
|
||||
skptr = skip;
|
||||
do {
|
||||
*stptr++ = rangeStr[i++];
|
||||
} while ( rangeStr[i] >= '0' && rangeStr[i] <= '9' );
|
||||
*stptr = '\0';
|
||||
if ( rangeStr[i++] != '-' ) {
|
||||
common->Error("Error: invalid range on middle \n");
|
||||
}
|
||||
do {
|
||||
*enptr++ = rangeStr[i++];
|
||||
} while ( rangeStr[i] >= '0' && rangeStr[i] <= '9' );
|
||||
*enptr = '\0';
|
||||
if ( rangeStr[i] != ']' ) {
|
||||
if ( rangeStr[i++] != '+' ) {
|
||||
common->Error("Error: invalid range on close\n");
|
||||
}
|
||||
do {
|
||||
*skptr++ = rangeStr[i++];
|
||||
} while ( rangeStr[i] >= '0' && rangeStr[i] <= '9' );
|
||||
*skptr = '\0';
|
||||
skipnum[field] = atoi( skip );
|
||||
} else {
|
||||
skipnum[field] = 1;
|
||||
}
|
||||
sscanf(start,"%2d%2d%2d%2d",&hrs,&mins,&secs,&frs);
|
||||
startnum[field] = hrs*30*60*60 + mins *60*30 + secs*30 +frs;
|
||||
sscanf(end,"%2d%2d%2d%2d",&hrs,&mins,&secs,&frs);
|
||||
endnum[field] = hrs*30*60*60 + mins *60*30 + secs*30 +frs;
|
||||
numfiles[field] = (abs( startnum[field] - endnum[field] ) / skipnum[field]) + 1;
|
||||
realnum += numfiles[field];
|
||||
if ( start[0] == '0' && start[1] != '\0' ) {
|
||||
padding[field] = true;
|
||||
numpadding[field] = strlen( start );
|
||||
} else {
|
||||
padding[field] = false;
|
||||
}
|
||||
return realnum;
|
||||
}
|
||||
105
neo/tools/compilers/roqvq/roqParam.h
Normal file
105
neo/tools/compilers/roqvq/roqParam.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
Doom 3 GPL Source Code
|
||||
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
||||
|
||||
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
|
||||
|
||||
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Doom 3 Source Code is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
||||
|
||||
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
||||
|
||||
===========================================================================
|
||||
*/
|
||||
#ifndef __roqParam_h__
|
||||
#define __roqParam_h__
|
||||
|
||||
#include "gdefs.h"
|
||||
#pragma once
|
||||
|
||||
class roqParam
|
||||
{
|
||||
public:
|
||||
|
||||
const char* RoqFilename( void );
|
||||
const char* RoqTempFilename( void );
|
||||
const char* GetNextImageFilename( void );
|
||||
const char* SoundFilename( void );
|
||||
void InitFromFile( const char *fileName );
|
||||
void GetNthInputFileName( idStr &fileName, int n);
|
||||
bool MoreFrames( void );
|
||||
bool OutputVectors( void );
|
||||
bool Timecode( void );
|
||||
bool DeltaFrames( void );
|
||||
bool NoAlpha( void );
|
||||
bool SearchType( void );
|
||||
bool TwentyFourToThirty( void );
|
||||
bool HasSound( void );
|
||||
int NumberOfFrames( void );
|
||||
int NormalFrameSize( void );
|
||||
int FirstFrameSize( void );
|
||||
int JpegQuality( void );
|
||||
bool IsScaleable( void );
|
||||
|
||||
idStr outputFilename;
|
||||
int numInputFiles;
|
||||
private:
|
||||
int *range;
|
||||
bool *padding, *padding2;
|
||||
idStrList file;
|
||||
idStrList file2;
|
||||
idStr soundfile;
|
||||
idStr currentPath;
|
||||
idStr tempFilename;
|
||||
idStr startPal;
|
||||
idStr endPal;
|
||||
idStr currentFile;
|
||||
int *skipnum, *skipnum2;
|
||||
int *startnum, *startnum2;
|
||||
int *endnum, *endnum2;
|
||||
int *numpadding, *numpadding2;
|
||||
int *numfiles;
|
||||
byte keyR, keyG, keyB;
|
||||
int field;
|
||||
int realnum;
|
||||
int onFrame;
|
||||
int firstframesize;
|
||||
int normalframesize;
|
||||
int jpegDefault;
|
||||
|
||||
bool scaleDown;
|
||||
bool twentyFourToThirty;
|
||||
bool encodeVideo;
|
||||
bool useTimecodeForRange;
|
||||
bool addPath;
|
||||
bool screenShots;
|
||||
bool startPalette;
|
||||
bool endPalette;
|
||||
bool fixedPalette;
|
||||
bool keyColor;
|
||||
bool justDelta;
|
||||
bool make3DO;
|
||||
bool makeVectors;
|
||||
bool justDeltaFlag;
|
||||
bool noAlphaAtAll;
|
||||
bool fullSearch;
|
||||
bool hasSound;
|
||||
bool isScaleable;
|
||||
};
|
||||
|
||||
|
||||
#endif // roqParam
|
||||
Reference in New Issue
Block a user