hello world

This commit is contained in:
Timothee 'TTimo' Besset
2011-11-22 15:28:15 -06:00
commit fb1609f554
2155 changed files with 1017022 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View 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;
}

View 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 );
}

View 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 );
}

View 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__ */

View 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 );
}

View 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;
}

View 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__ */

File diff suppressed because it is too large Load Diff

View 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__ */

View 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;
}

View 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__ */

View 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__ */

View 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;
}

View 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;
}

View 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;
}

View 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__ */

File diff suppressed because it is too large Load Diff

View 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__ */

File diff suppressed because it is too large Load Diff

View 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__ */

View 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__ */

View 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();
}

View 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 );

View 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;
}

View 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

View 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 );
}

View 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);
}

View 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 );
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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 );
}

View 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);
}

File diff suppressed because it is too large Load Diff

View 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();
}

View 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 );
}

View 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];
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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, &timestamp, 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;
}

File diff suppressed because it is too large Load Diff

View 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__

View 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

View 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

View 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 );
}

View 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__ */

View 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

View 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;
}

View 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