mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
964 lines
24 KiB
C++
964 lines
24 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
IceTech GPL Source Code
|
|
Copyright (C) 2026 Justin Marshall
|
|
|
|
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
|
|
|
|
IceTech 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.
|
|
|
|
IceTech 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 IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the IceTech 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 IceTech 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 Justin Marshall, justinmarshall20@gmail.com
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "tr_local.h"
|
|
#include "../models/Model_local.h"
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::FreeWorld
|
|
================
|
|
*/
|
|
void idRenderWorldLocal::FreeWorld() {
|
|
int i;
|
|
|
|
// this will free all the lightDefs and entityDefs
|
|
FreeDefs();
|
|
|
|
// clear the dxr models.
|
|
glRaytracingClearScene(dxrWorldId);
|
|
for (i = 0; i < worldDXRmodels.Num(); i++) {
|
|
glRaytracingDeleteMesh(worldDXRmodels[i].dxrBottomAcel);
|
|
}
|
|
worldDXRmodels.Clear();
|
|
|
|
// free all the portals and check light/model references
|
|
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
|
portalArea_t *area;
|
|
portal_t *portal, *nextPortal;
|
|
|
|
area = &portalAreas[i];
|
|
for ( portal = area->portals ; portal ; portal = nextPortal ) {
|
|
nextPortal = portal->next;
|
|
delete portal->w;
|
|
R_StaticFree( portal );
|
|
}
|
|
|
|
// there shouldn't be any remaining lightRefs or entityRefs
|
|
if ( area->lightRefs.areaNext != &area->lightRefs ) {
|
|
common->Error( "FreeWorld: unexpected remaining lightRefs" );
|
|
}
|
|
if ( area->entityRefs.areaNext != &area->entityRefs ) {
|
|
common->Error( "FreeWorld: unexpected remaining entityRefs" );
|
|
}
|
|
}
|
|
|
|
if ( portalAreas ) {
|
|
R_StaticFree( portalAreas );
|
|
portalAreas = NULL;
|
|
numPortalAreas = 0;
|
|
R_StaticFree( areaScreenRect );
|
|
areaScreenRect = NULL;
|
|
}
|
|
|
|
if ( doublePortals ) {
|
|
R_StaticFree( doublePortals );
|
|
doublePortals = NULL;
|
|
numInterAreaPortals = 0;
|
|
}
|
|
|
|
if ( areaNodes ) {
|
|
R_StaticFree( areaNodes );
|
|
areaNodes = NULL;
|
|
}
|
|
|
|
// free all the inline idRenderModels
|
|
for ( i = 0 ; i < localModels.Num() ; i++ ) {
|
|
renderModelManager->RemoveModel( localModels[i] );
|
|
delete localModels[i];
|
|
}
|
|
localModels.Clear();
|
|
worldDecals.Clear();
|
|
|
|
areaReferenceAllocator.Shutdown();
|
|
//interactionAllocator.Shutdown();
|
|
areaNumRefAllocator.Shutdown();
|
|
|
|
mapName = "<FREED>";
|
|
}
|
|
|
|
static void R_NormalizeDecalBasis(renderWorldDecal_t& decal) {
|
|
if (decal.normal.LengthSqr() < 0.0001f) {
|
|
decal.normal.Set(0.0f, 0.0f, 1.0f);
|
|
}
|
|
decal.normal.Normalize();
|
|
|
|
decal.axisU -= decal.normal * (decal.axisU * decal.normal);
|
|
if (decal.axisU.LengthSqr() < 0.0001f) {
|
|
decal.axisU = decal.normal.Cross(idVec3(0.0f, 0.0f, 1.0f));
|
|
if (decal.axisU.LengthSqr() < 0.0001f) {
|
|
decal.axisU = decal.normal.Cross(idVec3(0.0f, 1.0f, 0.0f));
|
|
}
|
|
}
|
|
decal.axisU.Normalize();
|
|
decal.axisV = decal.normal.Cross(decal.axisU);
|
|
decal.axisV.Normalize();
|
|
if (decal.size <= 0.0f) {
|
|
decal.size = 64.0f;
|
|
}
|
|
const float minProjectorDepth = decal.size * 2.0f;
|
|
if (decal.depth <= 0.0f) {
|
|
decal.depth = minProjectorDepth;
|
|
}
|
|
else if (decal.depth < minProjectorDepth) {
|
|
decal.depth = minProjectorDepth;
|
|
}
|
|
if (decal.opacity <= 0.0f) {
|
|
decal.opacity = 1.0f;
|
|
}
|
|
}
|
|
|
|
void idRenderWorldLocal::ClearWorldDecals() {
|
|
worldDecals.Clear();
|
|
}
|
|
|
|
int idRenderWorldLocal::AddWorldDecal(const renderWorldDecal_t& inDecal) {
|
|
renderWorldDecal_t decal = inDecal;
|
|
R_NormalizeDecalBasis(decal);
|
|
return worldDecals.Append(decal);
|
|
}
|
|
|
|
bool idRenderWorldLocal::UpdateWorldDecal(int decalIndex, const renderWorldDecal_t& inDecal) {
|
|
if (decalIndex < 0 || decalIndex >= worldDecals.Num()) {
|
|
return false;
|
|
}
|
|
renderWorldDecal_t decal = inDecal;
|
|
R_NormalizeDecalBasis(decal);
|
|
worldDecals[decalIndex] = decal;
|
|
return true;
|
|
}
|
|
|
|
void idRenderWorldLocal::RemoveWorldDecal(int decalIndex) {
|
|
if (decalIndex >= 0 && decalIndex < worldDecals.Num()) {
|
|
worldDecals.RemoveIndex(decalIndex);
|
|
}
|
|
}
|
|
|
|
int idRenderWorldLocal::NumWorldDecals() const {
|
|
return worldDecals.Num();
|
|
}
|
|
|
|
const renderWorldDecal_t* idRenderWorldLocal::GetWorldDecal(int decalIndex) const {
|
|
if (decalIndex < 0 || decalIndex >= worldDecals.Num()) {
|
|
return NULL;
|
|
}
|
|
return &worldDecals[decalIndex];
|
|
}
|
|
|
|
void idRenderWorldLocal::SubmitWorldDecalsToScreenSpace() const {
|
|
glScreenSpaceDecal_t gpuDecals[64];
|
|
const int count = min(worldDecals.Num(), 64);
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
const renderWorldDecal_t& src = worldDecals[i];
|
|
glScreenSpaceDecal_t& dst = gpuDecals[i];
|
|
memset(&dst, 0, sizeof(dst));
|
|
dst.origin.x = src.origin.x;
|
|
dst.origin.y = src.origin.y;
|
|
dst.origin.z = src.origin.z;
|
|
dst.normal.x = src.normal.x;
|
|
dst.normal.y = src.normal.y;
|
|
dst.normal.z = src.normal.z;
|
|
dst.axisU.x = src.axisU.x;
|
|
dst.axisU.y = src.axisU.y;
|
|
dst.axisU.z = src.axisU.z;
|
|
dst.axisV.x = src.axisV.x;
|
|
dst.axisV.y = src.axisV.y;
|
|
dst.axisV.z = src.axisV.z;
|
|
dst.halfSize = src.size * 0.5f;
|
|
const float minProjectorDepth = src.size * 2.0f;
|
|
dst.depth = (src.depth > minProjectorDepth) ? src.depth : minProjectorDepth;
|
|
dst.opacity = src.opacity;
|
|
dst.color[0] = src.color.x;
|
|
dst.color[1] = src.color.y;
|
|
dst.color[2] = src.color.z;
|
|
dst.color[3] = src.color.w;
|
|
dst.textureId = 0;
|
|
dst.blendMode = 0;
|
|
|
|
const idMaterial* mat = declManager->FindMaterial(src.material.c_str());
|
|
idImage* image = mat ? mat->GetEditorImage() : NULL;
|
|
if (mat && mat->GetNumStages() > 0) {
|
|
const shaderStage_t* stage = mat->GetStage(0);
|
|
const int srcBlend = stage->drawStateBits & GLS_SRCBLEND_BITS;
|
|
const int dstBlend = stage->drawStateBits & GLS_DSTBLEND_BITS;
|
|
if (srcBlend == GLS_SRCBLEND_ZERO && dstBlend == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR) {
|
|
dst.blendMode = 1;
|
|
}
|
|
}
|
|
#ifndef QUAKE4
|
|
if (mat && src.color.x == 1.0f && src.color.y == 1.0f && src.color.z == 1.0f && src.color.w == 1.0f) {
|
|
decalInfo_t decalInfo = mat->GetDecalInfo();
|
|
dst.color[0] *= decalInfo.start[0];
|
|
dst.color[1] *= decalInfo.start[1];
|
|
dst.color[2] *= decalInfo.start[2];
|
|
dst.color[3] *= decalInfo.start[3];
|
|
}
|
|
#endif
|
|
if (image) {
|
|
image->Bind();
|
|
dst.textureId = image->texnum;
|
|
}
|
|
}
|
|
|
|
glSetScreenSpaceDecalsQD3D12(gpuDecals, count);
|
|
glDrawScreenSpaceDecalsQD3D12();
|
|
}
|
|
|
|
static idStr R_WorldDecalFileName(const char* mapName) {
|
|
idStr filename = mapName ? mapName : "";
|
|
filename.StripFileExtension();
|
|
filename.SetFileExtension(".stamp");
|
|
return filename;
|
|
}
|
|
|
|
void idRenderWorldLocal::LoadWorldDecals() {
|
|
ClearWorldDecals();
|
|
|
|
idStr filename = R_WorldDecalFileName(mapName.c_str());
|
|
const bool osPath = strstr(filename.c_str(), ":") != NULL;
|
|
idLexer src(LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE | LEXFL_ALLOWPATHNAMES);
|
|
src.LoadFile(filename.c_str(), osPath);
|
|
if (!src.IsLoaded()) {
|
|
return;
|
|
}
|
|
|
|
idToken token;
|
|
if (!src.ReadToken(&token) || token.Icmp("worldDecals")) {
|
|
return;
|
|
}
|
|
|
|
src.ReadToken(&token);
|
|
if (token != "{") {
|
|
src.ExpectTokenString("{");
|
|
}
|
|
|
|
while (src.ReadToken(&token)) {
|
|
if (token == "}") {
|
|
break;
|
|
}
|
|
if (token.Icmp("decal")) {
|
|
src.Error("idRenderWorldLocal::LoadWorldDecals: bad token \"%s\"", token.c_str());
|
|
break;
|
|
}
|
|
|
|
renderWorldDecal_t decal;
|
|
decal.material = "textures/decals/default";
|
|
decal.origin.Zero();
|
|
decal.normal.Set(0.0f, 0.0f, 1.0f);
|
|
decal.axisU.Set(1.0f, 0.0f, 0.0f);
|
|
decal.axisV.Set(0.0f, 1.0f, 0.0f);
|
|
decal.size = 64.0f;
|
|
decal.depth = 8.0f;
|
|
decal.opacity = 1.0f;
|
|
decal.color.Set(0.05f, 0.05f, 0.05f, 1.0f);
|
|
|
|
src.ExpectTokenString("{");
|
|
while (src.ReadToken(&token)) {
|
|
if (token == "}") {
|
|
break;
|
|
}
|
|
if (!token.Icmp("material")) {
|
|
src.ExpectAnyToken(&token);
|
|
decal.material = token;
|
|
} else if (!token.Icmp("origin")) {
|
|
src.Parse1DMatrix(3, decal.origin.ToFloatPtr());
|
|
} else if (!token.Icmp("normal")) {
|
|
src.Parse1DMatrix(3, decal.normal.ToFloatPtr());
|
|
} else if (!token.Icmp("axisU")) {
|
|
src.Parse1DMatrix(3, decal.axisU.ToFloatPtr());
|
|
} else if (!token.Icmp("axisV")) {
|
|
src.Parse1DMatrix(3, decal.axisV.ToFloatPtr());
|
|
} else if (!token.Icmp("size")) {
|
|
decal.size = src.ParseFloat();
|
|
} else if (!token.Icmp("depth")) {
|
|
decal.depth = src.ParseFloat();
|
|
} else if (!token.Icmp("opacity")) {
|
|
decal.opacity = src.ParseFloat();
|
|
} else if (!token.Icmp("color")) {
|
|
src.Parse1DMatrix(4, decal.color.ToFloatPtr());
|
|
} else {
|
|
src.Error("idRenderWorldLocal::LoadWorldDecals: bad decal key \"%s\"", token.c_str());
|
|
}
|
|
}
|
|
AddWorldDecal(decal);
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::TouchWorldModels
|
|
================
|
|
*/
|
|
void idRenderWorldLocal::TouchWorldModels( void ) {
|
|
int i;
|
|
|
|
for ( i = 0 ; i < localModels.Num() ; i++ ) {
|
|
renderModelManager->CheckModel( localModels[i]->Name() );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::ParseModel
|
|
================
|
|
*/
|
|
idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
|
|
idRenderModel *model;
|
|
idToken token;
|
|
int i, j;
|
|
srfTriangles_t *tri;
|
|
modelSurface_t surf;
|
|
|
|
src->ExpectTokenString( "{" );
|
|
|
|
// parse the name
|
|
src->ExpectAnyToken( &token );
|
|
|
|
model = renderModelManager->AllocModel();
|
|
model->InitEmpty( token );
|
|
|
|
int numSurfaces = src->ParseInt();
|
|
if ( numSurfaces < 0 ) {
|
|
src->Error( "R_ParseModel: bad numSurfaces" );
|
|
}
|
|
#ifdef QUAKE4
|
|
// jmarshall - quake 4 proc format
|
|
if (!src->PeekTokenString("{") && !src->PeekTokenString("}")) {
|
|
int sky = src->ParseInt();
|
|
}
|
|
// jmarshall end
|
|
#endif
|
|
|
|
for ( i = 0 ; i < numSurfaces ; i++ ) {
|
|
src->ExpectTokenString( "{" );
|
|
|
|
src->ExpectAnyToken( &token );
|
|
|
|
surf.shader = declManager->FindMaterial( token );
|
|
|
|
((idMaterial*)surf.shader)->AddReference();
|
|
|
|
tri = R_AllocStaticTriSurf();
|
|
surf.geometry = tri;
|
|
|
|
tri->numVerts = src->ParseInt();
|
|
tri->numIndexes = src->ParseInt();
|
|
|
|
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
|
|
for ( j = 0 ; j < tri->numVerts ; j++ ) {
|
|
#ifdef QUAKE4
|
|
// jmarshall - quake 4 proc format
|
|
float vec[12];
|
|
const int numFloats = src->Parse1DMatrixOpenEnded( 12, vec );
|
|
if ( numFloats != 8 && numFloats != 12 ) {
|
|
src->Error( "R_ParseModel: bad vertex read" );
|
|
}
|
|
|
|
tri->verts[j].xyz[0] = vec[0];
|
|
tri->verts[j].xyz[1] = vec[1];
|
|
tri->verts[j].xyz[2] = vec[2];
|
|
tri->verts[j].st[0] = vec[3];
|
|
tri->verts[j].st[1] = vec[4];
|
|
tri->verts[j].normal[0] = vec[5];
|
|
tri->verts[j].normal[1] = vec[6];
|
|
tri->verts[j].normal[2] = vec[7];
|
|
|
|
if ( numFloats == 12 ) {
|
|
tri->verts[j].color[0] = idMath::Ftob( vec[8] );
|
|
tri->verts[j].color[1] = idMath::Ftob( vec[9] );
|
|
tri->verts[j].color[2] = idMath::Ftob( vec[10] );
|
|
tri->verts[j].color[3] = idMath::Ftob( vec[11] );
|
|
} else {
|
|
tri->verts[j].color[0] = 0;
|
|
tri->verts[j].color[1] = 0;
|
|
tri->verts[j].color[2] = 0;
|
|
tri->verts[j].color[3] = 255;
|
|
}
|
|
// jmarshall end
|
|
#else
|
|
float vec[8];
|
|
|
|
src->Parse1DMatrix( 8, vec );
|
|
|
|
tri->verts[j].xyz[0] = vec[0];
|
|
tri->verts[j].xyz[1] = vec[1];
|
|
tri->verts[j].xyz[2] = vec[2];
|
|
tri->verts[j].st[0] = vec[3];
|
|
tri->verts[j].st[1] = vec[4];
|
|
tri->verts[j].normal[0] = vec[5];
|
|
tri->verts[j].normal[1] = vec[6];
|
|
tri->verts[j].normal[2] = vec[7];
|
|
#endif
|
|
}
|
|
|
|
R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
|
|
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
|
|
tri->indexes[j] = src->ParseInt();
|
|
}
|
|
src->ExpectTokenString( "}" );
|
|
|
|
// add the completed surface to the model
|
|
model->AddSurface( surf );
|
|
|
|
if (!surf.shader->IsSky() && surf.shader->IsLitMaterial() && surf.shader->SurfaceCastsShadow())
|
|
{
|
|
idRenderModelStatic* modelStatic = (idRenderModelStatic*)model;
|
|
dxrWorldModel_t dxrModel;
|
|
dxrModel.tri = tri;
|
|
modelStatic->UpdateDXR(dxrModel.dxrBottomAcel, model->NumSurfaces() - 1);
|
|
glUpdateTopLevelAceelStructure(dxrWorldId, dxrModel.dxrBottomAcel, NULL, dxrModel.topAccelStruct);
|
|
worldDXRmodels.Append(dxrModel);
|
|
}
|
|
}
|
|
|
|
src->ExpectTokenString( "}" );
|
|
|
|
model->FinishSurfaces();
|
|
|
|
return model;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::ParseShadowModel
|
|
================
|
|
*/
|
|
idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) {
|
|
idRenderModel *model;
|
|
idToken token;
|
|
int j;
|
|
srfTriangles_t *tri;
|
|
modelSurface_t surf;
|
|
|
|
src->ExpectTokenString( "{" );
|
|
|
|
// parse the name
|
|
src->ExpectAnyToken( &token );
|
|
|
|
model = renderModelManager->AllocModel();
|
|
model->InitEmpty( token );
|
|
|
|
surf.shader = tr.defaultMaterial;
|
|
|
|
tri = R_AllocStaticTriSurf();
|
|
surf.geometry = tri;
|
|
|
|
tri->numVerts = src->ParseInt();
|
|
tri->numShadowIndexesNoCaps = src->ParseInt();
|
|
tri->numShadowIndexesNoFrontCaps = src->ParseInt();
|
|
tri->numIndexes = src->ParseInt();
|
|
tri->shadowCapPlaneBits = src->ParseInt();
|
|
|
|
tri->bounds.Clear();
|
|
for ( j = 0 ; j < tri->numVerts ; j++ ) {
|
|
float vec[8];
|
|
|
|
src->Parse1DMatrix( 3, vec );
|
|
|
|
tri->bounds.AddPoint( idVec3( vec[0], vec[1], vec[2] ) );
|
|
}
|
|
|
|
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
|
|
src->ParseInt();
|
|
}
|
|
|
|
// add the completed surface to the model
|
|
model->AddSurface( surf );
|
|
|
|
src->ExpectTokenString( "}" );
|
|
|
|
// we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc.
|
|
// model->FinishSurfaces();
|
|
|
|
return model;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::SetupAreaRefs
|
|
================
|
|
*/
|
|
void idRenderWorldLocal::SetupAreaRefs() {
|
|
int i;
|
|
|
|
connectedAreaNum = 0;
|
|
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
|
portalAreas[i].areaNum = i;
|
|
portalAreas[i].lightRefs.areaNext =
|
|
portalAreas[i].lightRefs.areaPrev =
|
|
&portalAreas[i].lightRefs;
|
|
portalAreas[i].entityRefs.areaNext =
|
|
portalAreas[i].entityRefs.areaPrev =
|
|
&portalAreas[i].entityRefs;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::ParseInterAreaPortals
|
|
================
|
|
*/
|
|
void idRenderWorldLocal::ParseInterAreaPortals( idLexer *src ) {
|
|
int i, j;
|
|
|
|
src->ExpectTokenString( "{" );
|
|
|
|
numPortalAreas = src->ParseInt();
|
|
if ( numPortalAreas < 0 ) {
|
|
src->Error( "R_ParseInterAreaPortals: bad numPortalAreas" );
|
|
return;
|
|
}
|
|
portalAreas = (portalArea_t *)R_ClearedStaticAlloc( numPortalAreas * sizeof( portalAreas[0] ) );
|
|
areaScreenRect = (idScreenRect *) R_ClearedStaticAlloc( numPortalAreas * sizeof( idScreenRect ) );
|
|
|
|
// set the doubly linked lists
|
|
SetupAreaRefs();
|
|
|
|
numInterAreaPortals = src->ParseInt();
|
|
if ( numInterAreaPortals < 0 ) {
|
|
src->Error( "R_ParseInterAreaPortals: bad numInterAreaPortals" );
|
|
return;
|
|
}
|
|
|
|
doublePortals = (doublePortal_t *)R_ClearedStaticAlloc( numInterAreaPortals *
|
|
sizeof( doublePortals [0] ) );
|
|
|
|
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
|
int numPoints, a1, a2;
|
|
idWinding *w;
|
|
portal_t *p;
|
|
|
|
numPoints = src->ParseInt();
|
|
a1 = src->ParseInt();
|
|
a2 = src->ParseInt();
|
|
|
|
w = new idWinding( numPoints );
|
|
w->SetNumPoints( numPoints );
|
|
for ( j = 0 ; j < numPoints ; j++ ) {
|
|
src->Parse1DMatrix( 3, (*w)[j].ToFloatPtr() );
|
|
// no texture coordinates
|
|
(*w)[j][3] = 0;
|
|
(*w)[j][4] = 0;
|
|
}
|
|
|
|
// add the portal to a1
|
|
p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
|
|
p->intoArea = a2;
|
|
p->doublePortal = &doublePortals[i];
|
|
p->w = w;
|
|
p->w->GetPlane( p->plane );
|
|
|
|
p->next = portalAreas[a1].portals;
|
|
portalAreas[a1].portals = p;
|
|
|
|
doublePortals[i].portals[0] = p;
|
|
|
|
// reverse it for a2
|
|
p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
|
|
p->intoArea = a1;
|
|
p->doublePortal = &doublePortals[i];
|
|
p->w = w->Reverse();
|
|
p->w->GetPlane( p->plane );
|
|
|
|
p->next = portalAreas[a2].portals;
|
|
portalAreas[a2].portals = p;
|
|
|
|
doublePortals[i].portals[1] = p;
|
|
}
|
|
|
|
src->ExpectTokenString( "}" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::ParseNodes
|
|
================
|
|
*/
|
|
void idRenderWorldLocal::ParseNodes( idLexer *src ) {
|
|
int i;
|
|
|
|
src->ExpectTokenString( "{" );
|
|
|
|
numAreaNodes = src->ParseInt();
|
|
if ( numAreaNodes < 0 ) {
|
|
src->Error( "R_ParseNodes: bad numAreaNodes" );
|
|
}
|
|
areaNodes = (areaNode_t *)R_ClearedStaticAlloc( numAreaNodes * sizeof( areaNodes[0] ) );
|
|
|
|
for ( i = 0 ; i < numAreaNodes ; i++ ) {
|
|
areaNode_t *node;
|
|
|
|
node = &areaNodes[i];
|
|
|
|
src->Parse1DMatrix( 4, node->plane.ToFloatPtr() );
|
|
node->children[0] = src->ParseInt();
|
|
node->children[1] = src->ParseInt();
|
|
}
|
|
|
|
src->ExpectTokenString( "}" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
idRenderWorldLocal::CommonChildrenArea_r
|
|
================
|
|
*/
|
|
int idRenderWorldLocal::CommonChildrenArea_r( areaNode_t *node ) {
|
|
int nums[2];
|
|
|
|
for ( int i = 0 ; i < 2 ; i++ ) {
|
|
if ( node->children[i] <= 0 ) {
|
|
nums[i] = -1 - node->children[i];
|
|
} else {
|
|
nums[i] = CommonChildrenArea_r( &areaNodes[ node->children[i] ] );
|
|
}
|
|
}
|
|
|
|
// solid nodes will match any area
|
|
if ( nums[0] == AREANUM_SOLID ) {
|
|
nums[0] = nums[1];
|
|
}
|
|
if ( nums[1] == AREANUM_SOLID ) {
|
|
nums[1] = nums[0];
|
|
}
|
|
|
|
int common;
|
|
if ( nums[0] == nums[1] ) {
|
|
common = nums[0];
|
|
} else {
|
|
common = CHILDREN_HAVE_MULTIPLE_AREAS;
|
|
}
|
|
|
|
node->commonChildrenArea = common;
|
|
|
|
return common;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderWorldLocal::ClearWorld
|
|
|
|
Sets up for a single area world
|
|
=================
|
|
*/
|
|
void idRenderWorldLocal::ClearWorld() {
|
|
numPortalAreas = 1;
|
|
portalAreas = (portalArea_t *)R_ClearedStaticAlloc( sizeof( portalAreas[0] ) );
|
|
areaScreenRect = (idScreenRect *) R_ClearedStaticAlloc( sizeof( idScreenRect ) );
|
|
|
|
SetupAreaRefs();
|
|
|
|
// even though we only have a single area, create a node
|
|
// that has both children pointing at it so we don't need to
|
|
//
|
|
areaNodes = (areaNode_t *)R_ClearedStaticAlloc( sizeof( areaNodes[0] ) );
|
|
areaNodes[0].plane[3] = 1;
|
|
areaNodes[0].children[0] = -1;
|
|
areaNodes[0].children[1] = -1;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderWorldLocal::FreeDefs
|
|
|
|
dump all the interactions
|
|
=================
|
|
*/
|
|
void idRenderWorldLocal::FreeDefs() {
|
|
int i;
|
|
|
|
// free all lightDefs
|
|
for ( i = 0 ; i < lightDefs.Num() ; i++ ) {
|
|
idRenderLightLocal *light;
|
|
|
|
light = lightDefs[i];
|
|
if ( light && light->world == this ) {
|
|
FreeLightDef( i );
|
|
lightDefs[i] = NULL;
|
|
}
|
|
}
|
|
|
|
// free all entityDefs
|
|
for ( i = 0 ; i < entityDefs.Num() ; i++ ) {
|
|
idRenderEntityLocal *mod;
|
|
|
|
mod = entityDefs[i];
|
|
if ( mod && mod->world == this ) {
|
|
FreeEntityDef( i );
|
|
entityDefs[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderWorldLocal::InitFromMap
|
|
|
|
A NULL or empty name will make a world without a map model, which
|
|
is still useful for displaying a bare model
|
|
=================
|
|
*/
|
|
bool idRenderWorldLocal::InitFromMap( const char *name ) {
|
|
idLexer * src;
|
|
idToken token;
|
|
idStr filename;
|
|
idRenderModel * lastModel;
|
|
|
|
// if this is an empty world, initialize manually
|
|
if ( !name || !name[0] ) {
|
|
FreeWorld();
|
|
mapName.Clear();
|
|
ClearWorld();
|
|
return true;
|
|
}
|
|
|
|
// load it
|
|
filename = name;
|
|
filename.SetFileExtension( PROC_FILE_EXT );
|
|
|
|
// if we are reloading the same map, check the timestamp
|
|
// and try to skip all the work
|
|
ID_TIME_T currentTimeStamp;
|
|
fileSystem->ReadFile( filename, NULL, ¤tTimeStamp );
|
|
|
|
if ( name == mapName ) {
|
|
if ( currentTimeStamp != FILE_NOT_FOUND_TIMESTAMP && currentTimeStamp == mapTimeStamp ) {
|
|
common->Printf( "idRenderWorldLocal::InitFromMap: retaining existing map\n" );
|
|
FreeDefs();
|
|
TouchWorldModels();
|
|
LoadWorldDecals();
|
|
AddWorldModelEntities();
|
|
ClearPortalStates();
|
|
return true;
|
|
}
|
|
common->Printf( "idRenderWorldLocal::InitFromMap: timestamp has changed, reloading.\n" );
|
|
}
|
|
|
|
FreeWorld();
|
|
|
|
src = new idLexer( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
|
|
if ( !src->IsLoaded() ) {
|
|
common->Printf( "idRenderWorldLocal::InitFromMap: %s not found\n", filename.c_str() );
|
|
ClearWorld();
|
|
return false;
|
|
}
|
|
|
|
|
|
mapName = name;
|
|
mapTimeStamp = currentTimeStamp;
|
|
LoadWorldDecals();
|
|
|
|
// if we are writing a demo, archive the load command
|
|
if ( session->writeDemo ) {
|
|
WriteLoadMap();
|
|
}
|
|
|
|
if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) {
|
|
common->Printf( "idRenderWorldLocal::InitFromMap: bad id '%s' instead of '%s'\n", token.c_str(), PROC_FILE_ID );
|
|
delete src;
|
|
return false;
|
|
}
|
|
#ifdef QUAKE4
|
|
// jmarshall: quake 4 proc format
|
|
if (!src->ReadToken(&token)) {
|
|
common->Printf("idRenderWorldLocal::InitFromMap: bad version '%s' instead of '%s'\n", token.c_str(), PROC_FILEVERSION);
|
|
delete src;
|
|
return false;
|
|
}
|
|
|
|
// Map CRC, we aren't going to use it.
|
|
src->ReadToken(&token);
|
|
// jmarshall end
|
|
#endif
|
|
// parse the file
|
|
while ( 1 ) {
|
|
if ( !src->ReadToken( &token ) ) {
|
|
break;
|
|
}
|
|
|
|
if ( token == "model" ) {
|
|
lastModel = ParseModel( src );
|
|
|
|
// add it to the model manager list
|
|
renderModelManager->AddModel( lastModel );
|
|
|
|
// save it in the list to free when clearing this map
|
|
localModels.Append( lastModel );
|
|
continue;
|
|
}
|
|
|
|
if ( token == "shadowModel" ) {
|
|
lastModel = ParseShadowModel( src );
|
|
|
|
// add it to the model manager list
|
|
renderModelManager->AddModel( lastModel );
|
|
|
|
// save it in the list to free when clearing this map
|
|
localModels.Append( lastModel );
|
|
continue;
|
|
}
|
|
|
|
if ( token == "interAreaPortals" ) {
|
|
ParseInterAreaPortals( src );
|
|
continue;
|
|
}
|
|
|
|
if ( token == "nodes" ) {
|
|
ParseNodes( src );
|
|
continue;
|
|
}
|
|
|
|
src->Error( "idRenderWorldLocal::InitFromMap: bad token \"%s\"", token.c_str() );
|
|
}
|
|
|
|
delete src;
|
|
|
|
// if it was a trivial map without any areas, create a single area
|
|
if ( !numPortalAreas ) {
|
|
ClearWorld();
|
|
}
|
|
|
|
// find the points where we can early-our of reference pushing into the BSP tree
|
|
CommonChildrenArea_r( &areaNodes[0] );
|
|
|
|
AddWorldModelEntities();
|
|
ClearPortalStates();
|
|
|
|
// done!
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idRenderWorldLocal::ClearPortalStates
|
|
=====================
|
|
*/
|
|
void idRenderWorldLocal::ClearPortalStates() {
|
|
int i, j;
|
|
|
|
// all portals start off open
|
|
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
|
doublePortals[i].blockingBits = PS_BLOCK_NONE;
|
|
}
|
|
|
|
// flood fill all area connections
|
|
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
|
for ( j = 0 ; j < NUM_PORTAL_ATTRIBUTES ; j++ ) {
|
|
connectedAreaNum++;
|
|
FloodConnectedAreas( &portalAreas[i], j );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
idRenderWorldLocal::AddWorldModelEntities
|
|
=====================
|
|
*/
|
|
void idRenderWorldLocal::AddWorldModelEntities() {
|
|
int i;
|
|
|
|
// add the world model for each portal area
|
|
// we can't just call AddEntityDef, because that would place the references
|
|
// based on the bounding box, rather than explicitly into the correct area
|
|
for ( i = 0 ; i < numPortalAreas ; i++ ) {
|
|
idRenderEntityLocal *def;
|
|
int index;
|
|
|
|
def = new idRenderEntityLocal;
|
|
|
|
// try and reuse a free spot
|
|
index = entityDefs.FindNull();
|
|
if ( index == -1 ) {
|
|
index = entityDefs.Append(def);
|
|
} else {
|
|
entityDefs[index] = def;
|
|
}
|
|
|
|
def->index = index;
|
|
def->world = this;
|
|
|
|
def->parms.hModel = renderModelManager->FindModel( va("_area%i", i ) );
|
|
if ( def->parms.hModel->IsDefaultModel() || !def->parms.hModel->IsStaticWorldModel() ) {
|
|
common->Error( "idRenderWorldLocal::InitFromMap: bad area model lookup" );
|
|
}
|
|
|
|
idRenderModel *hModel = def->parms.hModel;
|
|
|
|
for ( int j = 0; j < hModel->NumSurfaces(); j++ ) {
|
|
const modelSurface_t *surf = hModel->Surface( j );
|
|
|
|
if ( surf->shader->GetName() == idStr( "textures/smf/portal_sky" ) ) {
|
|
def->needsPortalSky = true;
|
|
}
|
|
}
|
|
|
|
def->referenceBounds = def->parms.hModel->Bounds();
|
|
|
|
def->parms.axis[0][0] = 1;
|
|
def->parms.axis[1][1] = 1;
|
|
def->parms.axis[2][2] = 1;
|
|
|
|
R_AxisToModelMatrix( def->parms.axis, def->parms.origin, def->modelMatrix );
|
|
|
|
// in case an explicit shader is used on the world, we don't
|
|
// want it to have a 0 alpha or color
|
|
def->parms.shaderParms[0] =
|
|
def->parms.shaderParms[1] =
|
|
def->parms.shaderParms[2] =
|
|
def->parms.shaderParms[3] = 1;
|
|
|
|
AddEntityRefToArea( def, &portalAreas[i] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CheckAreaForPortalSky
|
|
=====================
|
|
*/
|
|
bool idRenderWorldLocal::CheckAreaForPortalSky( int areaNum ) {
|
|
areaReference_t *ref;
|
|
|
|
assert( areaNum >= 0 && areaNum < numPortalAreas );
|
|
|
|
for ( ref = portalAreas[areaNum].entityRefs.areaNext; ref->entity; ref = ref->areaNext ) {
|
|
assert( ref->area == &portalAreas[areaNum] );
|
|
|
|
if ( ref->entity && ref->entity->needsPortalSky ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|