// Created by ivan_the_B // #include "TrailGenerator.h" #include "Game_local.h" #include "renderer/ModelManager.h" //uncomment those to enable debug //#define _DEBUG_CUSTOM_GEOM //#define _DEBUG_TRAIL static const dword trailGenerator_vertexColor = PackColor( idVec4( 1,1,1,1 ) ); static const char *trailGenerator_SnapshotName = "_TrailGenerator_Snapshot_"; //Note: all the trail models use this name. It's not used so it's ok. static const int TRAIL_ID_INVALID_UNIQUE = 0; /* =============================================================================== idTrailManager =============================================================================== */ /* ================ idTrailManager::idTrailManager ================ */ idTrailManager::idTrailManager( void ) { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::idTrailManager\n"); #endif initialized = false; } /* ================ idTrailManager::Init ================ */ void idTrailManager::Init( void ) { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::Init\n"); #endif Shutdown(); memset( trails, 0, sizeof( trails ) ); memset( uniqueIds, TRAIL_ID_INVALID_UNIQUE, sizeof( uniqueIds ) ); lastUniqueId = TRAIL_ID_INVALID_UNIQUE; minFreePos = 0; //first slot will be empty here maxAllocPos = -1; //no one initialized = true; } /* ================ idTrailManager::~idTrailManager ================ */ idTrailManager::~idTrailManager( void ) { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::~idTrailManager\n"); #endif if(!initialized) return; for ( int i = 0; i < MAX_TRAILS; i++ ) { if( trails[ i ] ){ delete trails[ i ]; trails[ i ] = NULL; } } } /* ================ idTrailManager::Shutdown ================ */ void idTrailManager::Shutdown( void ) { if( !initialized ) return; #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::Shutdown\n"); #endif //deallocate all the trails that are still in memory for ( int i = 0; i < MAX_TRAILS; i++ ) { if( trails[ i ] ){ gameLocal.Warning("Someone created a trail (uid: %d), but never removed it!\nTrailManager will now take care to free the memory...but check your code!", trails[ i ]->GetUniqueId() ); delete trails[ i ]; trails[ i ] = NULL; } } initialized = false; } /* ================ idTrailManager::Save ================ */ void idTrailManager::Save( idSaveGame *savefile ) const { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::Save\n"); #endif savefile->WriteBool( initialized ); savefile->WriteInt( lastUniqueId ); savefile->WriteInt( minFreePos ); savefile->WriteInt( maxAllocPos ); for ( int i = 0; i <= maxAllocPos; i++ ) { //only stuff before 'maxAllocPos' #ifdef _DEBUG_TRAIL gameLocal.Printf("saving int %d\n", uniqueIds[ i ]); #endif savefile->WriteInt( uniqueIds[ i ] ); if( uniqueIds[ i ] != TRAIL_ID_INVALID_UNIQUE ){ trails[ i ]->Save( savefile ); } } } /* ================ idTrailManager::Restore ================ */ void idTrailManager::Restore( idRestoreGame *savefile ) { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::Restore\n"); #endif savefile->ReadBool( initialized ); savefile->ReadInt( lastUniqueId ); savefile->ReadInt( minFreePos ); savefile->ReadInt( maxAllocPos ); //load the ones before 'maxAllocPos' for( int i = 0; i <= maxAllocPos; i++ ) { //only stuff before 'maxAllocPos' savefile->ReadInt( uniqueIds[ i ] ); if( uniqueIds[ i ] != TRAIL_ID_INVALID_UNIQUE ){ //it was there. NOTE: same local pos! trails[i] = new idTrailGenerator; trails[i]->Restore( savefile ); #ifdef _DEBUG_TRAIL //test only if( trails[ i ]->GetLocalPos() != i ){ gameLocal.Error("localPos != array position! (%d)", i); } gameLocal.Printf("restored: - localPos: %d, uniId: %d\n", trails[ i ]->GetLocalPos(), trails[ i ]->GetUniqueId() ); #endif }else{ #ifdef _DEBUG_TRAIL gameLocal.Printf("restored: NULL at pos %d\n", i); #endif trails[i] = NULL; } } //null the others for( int i = maxAllocPos+1; i < MAX_TRAILS; i++ ) { uniqueIds[ i ] = TRAIL_ID_INVALID_UNIQUE; trails[i] = NULL; } } /* ================ idTrailManager::FindTrailByUniqueId ================ */ int idTrailManager::GetSafeUniqueId( idTrailGenerator* trailGen ){ return ( trailGen ? trailGen->GetUniqueId() : TRAIL_ID_INVALID_UNIQUE ); } /* ================ idTrailManager::FindTrailByUniqueId ================ */ idTrailGenerator* idTrailManager::FindTrailByLocalPos( int pos ) { //fast if( pos < 0 || pos >= MAX_TRAILS ){ return NULL; } return trails[ pos ]; } /* ================ idTrailManager::FindTrailByUniqueId ================ */ idTrailGenerator* idTrailManager::FindTrailByUniqueId( int id ) { //slow, but ensure it's not another trail at the same pos #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::FindTrailById...\n"); #endif if( id == TRAIL_ID_INVALID_UNIQUE ){ return NULL; } for ( int i = 0; i < MAX_TRAILS; i++ ) { if( trails[ i ] && trails[ i ]->GetUniqueId() == id ){ #ifdef _DEBUG_TRAIL gameLocal.Printf("...found at pos: %d\n", i); #endif return trails[ i ]; } } #ifdef _DEBUG_TRAIL gameLocal.Printf("...not found\n"); #endif return NULL; } /* ================ idTrailManager::Think ================ */ void idTrailManager::Think( void ) { for ( int i = 0; i <= maxAllocPos; i++ ) { //don't even check beyond 'maxAllocPos' because they all are null if( trails[ i ] && trails[ i ]->fading ){ //->IsFading() trails[ i ]->Fade(); } } } /* ================ idTrailManager::GetTrailGen ================ */ idTrailGenerator* idTrailManager::NewTrailGen( void ) { int i; #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::NewTrailGen\n"); #endif if( minFreePos == MAX_TRAILS ){ gameLocal.Error("No free slots for another trail!"); return NULL; } idTrailGenerator* myGen = new idTrailGenerator; //set an unique id lastUniqueId++; myGen->Init( lastUniqueId, minFreePos ); //add it to list trails[ minFreePos ] = myGen; uniqueIds[ minFreePos ] = lastUniqueId; #ifdef _DEBUG_TRAIL gameLocal.Printf("Added trail 'uid: %d' at pos: %d\n", lastUniqueId, minFreePos ); #endif //-- upd max pos allocated -- if( minFreePos > maxAllocPos ){ //note: minFreePos is were it was placed maxAllocPos = minFreePos; #ifdef _DEBUG_TRAIL gameLocal.Printf("new maxAllocPos: %d\n", maxAllocPos); #endif } //-- upd min pos free -- (we are sure no one is free before 'minFreePos') for ( i = minFreePos+1; i < MAX_TRAILS; i++ ) { //from here (well, next one) to max if( !trails[ i ] ){ break; } //null } minFreePos = i; // MAX_TRAILS if full array #ifdef _DEBUG_TRAIL gameLocal.Printf("new minFreePos: %d\n", minFreePos); if( minFreePos == MAX_TRAILS ){ gameLocal.Warning("This was the last free slot for a trail!"); } #endif return myGen; } /* ================ idTrailManager::RemoveTrailGen ================ */ void idTrailManager::RemoveTrailGen( idTrailGenerator* trailGen ) { int i; #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailManager::RemoveTrailGen\n"); #endif if( !trailGen ){ return; } //delete int localPos = trailGen->GetLocalPos(); delete trails[ localPos ]; trails[ localPos ] = NULL; //clear id uniqueIds[ localPos ] = TRAIL_ID_INVALID_UNIQUE; //-- upd min pos free -- if( localPos < minFreePos ){ //if this one is now the first NULL minFreePos = localPos; #ifdef _DEBUG_TRAIL gameLocal.Printf("new minFreePos: %d\n", minFreePos); #endif } //-- upd max pos allocated -- if( localPos == maxAllocPos ){ //if this one was the last allocated... go back until we find the first used slot for ( i = maxAllocPos-1; i >= 0; i-- ) { //from prev one to 0 if( trails[ i ] ){ break; } //used } maxAllocPos = i; // -1 if empty array #ifdef _DEBUG_TRAIL gameLocal.Printf("new maxAllocPos: %d\n", maxAllocPos); #endif } #ifdef _DEBUG_TRAIL gameLocal.Printf("Removed trail at pos: %d\n", minFreePos ); #endif } /* =============================================================================== idTrailGenerator =============================================================================== */ /* ================ idTrailGenerator::idTrailGenerator ================ */ idTrailGenerator::idTrailGenerator( void ) { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailGenerator::idTrailGenerator\n"); #endif initialized = false; //new uniqueId = TRAIL_ID_INVALID_UNIQUE; localPos = MAX_TRAILS; //thinkFlags = 0; } /* ================ idTrailGenerator::Init ================ */ void idTrailGenerator::Init( int uniId, int locPos ) { //gameLocal.Printf("idTrailGenerator::Init\n"); if( initialized ){ gameLocal.Error("Cannot initialize the same trail twice"); } uniqueId = uniId; //used for restoring references! localPos = locPos; //used for callback! It's faster but cannot identify it univocally //thinkFlags = 0; //settings enabled = false; fading = false; fadingTime = 0; fadingEndTime = 0; fadingColorStep.Zero(); maxPoints = 0; material = NULL; trailDef = NULL; trailDefName = ""; points.SetGranularity( 2 ); //use 2 because we always add/remove couple of points. //rendering memset( &renderEntity, 0, sizeof( renderEntity ) ); renderEntity.entityNum = localPos; renderEntity.bounds.Clear(); renderEntity.axis = mat3_identity; renderEntity.shaderParms[ SHADERPARM_RED ] = 1; renderEntity.shaderParms[ SHADERPARM_GREEN ] = 1; renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1; renderEntity.shaderParms[3] = 1; renderEntity.hModel = renderModelManager->AllocModel(); renderEntity.hModel->InitEmpty( trailGenerator_SnapshotName ); renderEntity.noShadow = 1; renderEntity.callback = idTrailGenerator::ModelCallback; renderEntity.bounds.AddPoint( idVec3(-100000, -100000, -100000) ); // huge bounds, so it will be present in every world area renderEntity.bounds.AddPoint( idVec3( 100000, 100000, 100000) ); // add to renderer list //renderEntityHandle = gameRenderWorld->AddEntityDef( &renderEntity ); renderEntityHandle = -1; //don't add it to renderworld until it's disabled lastRenderEntityUpdate = -1; modelChanged = false; initialized = true; } //always call this after color/flags change on renderEntity void idTrailGenerator::UpdateVisuals( void ) { // add to refresh list if ( renderEntityHandle == -1 ) { renderEntityHandle = gameRenderWorld->AddEntityDef( &renderEntity ); } else { gameRenderWorld->UpdateEntityDef( renderEntityHandle, &renderEntity ); //save colors and other flags on renderEntity } } //remove it from RenderWorld when disabled void idTrailGenerator::FreeEntityDef( void ) { if ( renderEntityHandle != -1 ) { gameRenderWorld->FreeEntityDef( renderEntityHandle ); renderEntityHandle = -1; } } /* ================ idTrailGenerator::~idTrailGenerator ================ */ idTrailGenerator::~idTrailGenerator( void ) { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailGenerator::~idTrailGenerator\n"); #endif if(!initialized){ return; } // make sure the render entity is freed before the model is freed if ( renderEntityHandle != -1 ) { gameRenderWorld->FreeEntityDef( renderEntityHandle ); renderEntityHandle = -1; } if ( renderEntity.hModel != NULL ) { renderModelManager->FreeModel( renderEntity.hModel ); renderEntity.hModel = NULL; } } /* ================ idTrailGenerator::Save ================ */ void idTrailGenerator::Save( idSaveGame *savefile ) const { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailGenerator::Save\n"); #endif //global savefile->WriteInt( uniqueId ); savefile->WriteInt( localPos ); savefile->WriteBool( initialized ); // settings savefile->WriteBool( enabled ); savefile->WriteBool( fading ); savefile->WriteVec3( fadingColorStep ); savefile->WriteInt( fadingTime ); savefile->WriteInt( fadingEndTime ); savefile->WriteInt( maxPoints ); savefile->WriteMaterial( material ); //trail def //trailDef is NOT saved, but can be retrieved from trailDefName savefile->WriteString( trailDefName ); //points are NOT saved!! The trail will be be empty. //idList points; //rendering savefile->WriteRenderEntity( renderEntity ); savefile->WriteInt( renderEntityHandle ); savefile->WriteInt( lastRenderEntityUpdate ); savefile->WriteBool( modelChanged ); } /* ================ idTrailGenerator::Restore ================ */ void idTrailGenerator::Restore( idRestoreGame *savefile ) { #ifdef _DEBUG_TRAIL gameLocal.Printf("idTrailGenerator::Restore\n"); #endif //global savefile->ReadInt( uniqueId ); //used for restoring references! savefile->ReadInt( localPos ); savefile->ReadBool( initialized ); // settings savefile->ReadBool( enabled ); savefile->ReadBool( fading ); savefile->ReadVec3( fadingColorStep ); savefile->ReadInt( fadingTime ); savefile->ReadInt( fadingEndTime ); savefile->ReadInt( maxPoints ); savefile->ReadMaterial( material ); //trail def savefile->ReadString( trailDefName ); if ( trailDefName.Length() ) { trailDef = gameLocal.FindEntityDefDict( trailDefName ); } else { trailDef = NULL; //gameLocal.Warning("trailDef not found!"); } //points are NOT saved!! points.Clear(); points.SetGranularity( 2 ); //rendering savefile->ReadRenderEntity( renderEntity ); savefile->ReadInt( renderEntityHandle ); savefile->ReadInt( lastRenderEntityUpdate ); savefile->ReadBool( modelChanged ); //fix - reallocate the model ( this was created on init ) renderEntity.entityNum = localPos; //used for callback! renderEntity.hModel = renderModelManager->AllocModel(); renderEntity.hModel->InitEmpty( trailGenerator_SnapshotName ); renderEntity.callback = idTrailGenerator::ModelCallback; renderEntity.noShadow = true; renderEntity.noSelfShadow = true; renderEntity.noDynamicInteractions = false; /* // restore must retrieve renderEntityHandle from the renderer if ( renderEntityHandle != -1 ) { renderEntityHandle = gameRenderWorld->AddEntityDef( &renderEntity ); } */ if ( renderEntityHandle != -1 ) { renderEntityHandle = gameRenderWorld->AddEntityDef( &renderEntity ); } } /* ================ idTrailGenerator::RestartTrail ================ */ void idTrailGenerator::RestartTrail( void ){ idVec3 color; #ifdef _DEBUG_TRAIL gameLocal.Printf("RestartTrail %s\n", trailDefName.c_str() ); #endif if( trailDef ){ //restore the old color trailDef->GetVector( "color", "1 1 1", color ); renderEntity.shaderParms[ SHADERPARM_RED ] = color[ 0 ]; renderEntity.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ]; renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ]; }else{ gameLocal.Warning("Trying to restart a trail without a def!\n"); return; } //state enabled = true; //weapon will upd the position fading = false; modelChanged = true; //upd renderer UpdateVisuals(); //make sure it's in the world and updated } /* ================ idTrailGenerator::StartTrail ================ */ void idTrailGenerator::StartTrail( const char *newTrailDefName ){ const char *materialName; idVec3 color; const idDict *newTrailDef; #ifdef _DEBUG_TRAIL gameLocal.Printf("StartTrail %s\n", newTrailDefName ); #endif newTrailDef = gameLocal.FindEntityDefDict( newTrailDefName, false ); if ( !newTrailDef ) { gameLocal.Error( "Unknown def '%s'", newTrailDefName ); } //NOTE: this works, but we assume that if it's the same def, RestartTrail() will be called instead. if( trailDef && newTrailDef && (newTrailDef == trailDef) ){ //gameLocal.Warning("Started a new Trail with the same def! use RestartTrail() instead to save performance!"); RestartTrail(); return; } //remove the points //points.Clear(); //we can keep the old ones so, if we already are active the trail goes on. //fadingTime newTrailDef->GetInt( "fadeTime", "200", fadingTime ); if( fadingTime < 0 ) fadingTime = 0; //maxPoints newTrailDef->GetInt( "numFrames", "10", maxPoints ); maxPoints = ( maxPoints < 1 ) ? 4 : ( maxPoints*2+2 ); //1frame -> 4points, 2frame -> 6points, ... //color newTrailDef->GetVector( "color", "1 1 1", color ); renderEntity.shaderParms[ SHADERPARM_RED ] = color[ 0 ]; renderEntity.shaderParms[ SHADERPARM_GREEN ] = color[ 1 ]; renderEntity.shaderParms[ SHADERPARM_BLUE ] = color[ 2 ]; //material materialName = newTrailDef->GetString( "mtr_surface", "" ); material = declManager->FindMaterial( materialName ); //remember the new trail def trailDef = newTrailDef; trailDefName = newTrailDefName; //state enabled = true; //weapon will upd the position fading = false; modelChanged = true; //upd renderer UpdateVisuals(); //make sure it's in the world and updated } /* ================ idTrailGenerator::FadeTrail ================ */ void idTrailGenerator::FadeTrail( void ){ idVec3 colorNow; if( fadingTime <= 0 ){ StopTrail(); return; } //color colorNow[ 0 ] = renderEntity.shaderParms[ SHADERPARM_RED ]; colorNow[ 1 ] = renderEntity.shaderParms[ SHADERPARM_GREEN ]; colorNow[ 2 ] = renderEntity.shaderParms[ SHADERPARM_BLUE ]; //color step for each frame fadingColorStep = ( vec3_zero - colorNow)*USERCMD_MSEC/fadingTime; //time fadingEndTime = gameLocal.time + fadingTime; //state enabled = true; //weapon will upd the position fading = true; UpdateVisuals(); //make sure it's in the world and updated } /* ================ idTrailGenerator::StopTrail ================ */ void idTrailGenerator::StopTrail( void ){ if(!enabled) return; //remove points points.Clear(); //state enabled = false; fading = false; modelChanged = true; //upd renderer FreeEntityDef(); //remove from world } /* ================ idTrailGenerator::AddNewPoints ================ */ void idTrailGenerator::AddNewPoints( const idVec3 &newPosL, const idVec3 &newPosH ) { if( !enabled ){ gameLocal.Warning("Cannot add points to a disabled trail!"); return; } /* //NOTE: this is commented out because we assume that sword is moving while trail is active. if( points.Num() >= 2 ){ if( points[0].Compare( newPosL ) && points[1].Compare( newPosH ) ){ return; //if points did not change don't upd anything } } */ //remove oldest points while( points.Num() >= maxPoints ){ //example: maxPoints 6 and we have 6 -> remove 2 so we can add the new ones points.RemoveIndex(0); points.RemoveIndex(0); } points.Append( newPosL ); points.Append( newPosH ); modelChanged = true; //upd renderer } /* ================ idTrailGenerator::RemoveOldestPoints ================ */ void idTrailGenerator::RemoveOldestPoints( void ) { if( !enabled ){ gameLocal.Warning("Cannot remove points to a disabled trail!"); return; } /* //NOTE: this is commented out because we assume that sword is moving while trail is active. if( points.Num() >= 2 ){ if( points[0].Compare( newPosL ) && points[1].Compare( newPosH ) ){ return; //if points did not change don't upd anything } } */ //remove oldest points if( points.Num() >= 2 ){ points.RemoveIndex(0); points.RemoveIndex(0); modelChanged = true; //upd renderer } } /* ================ idTrailGenerator::UpdateRenderEntity ================ */ bool idTrailGenerator::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const { int i, j, numTris, numPoints; float fitpct, fitoffset; srfTriangles_t *tris; modelSurface_t surface; idDrawVert *v; idPlane plane; idMat3 tangents; idFixedWinding winding; //TODO: this is big...promote it to class member to save perf? // this may be triggered by a model trace or other non-view related source, // to which we should look like an empty model if ( !renderView ) { return false; } // regenerate only if modelChanged. Only one per frame. //was: if ( !modelChanged || lastRenderEntityUpdate == gameLocal.time ) { if ( lastRenderEntityUpdate == gameLocal.time ) { //moved the "modelChanged" check in callback to save perf. return false; } lastRenderEntityUpdate = gameLocal.time; modelChanged = false; //gameLocal.Printf("UpdateRenderEntity at time: %d\n", lastRenderEntityUpdate); //we need at least 4 points + even number numPoints = points.Num(); if( numPoints < 4 ){ //|| (numPoints % 2 != 0) //number is always even thanks to creation policy. //clean the model and return. renderEntity->hModel->InitEmpty( trailGenerator_SnapshotName ); return true; } //calculate number of tris and materialOffset-per-coupleOfTris pct numTris = numPoints - 2; //examples: 4p -> 2t, 6p -> 4t, 8p ->6t fitpct = 1.0f / ( numTris /2.0f ); // numTris/2 is the number of steps. 4p -> 2t -> 1step, 6p -> 4t -> 2steps, 8p ->6t -> 3steps // FIXME: re-use model surfaces? <-- we cannot do that: we would need to remove the oldest piece and shift the texture. renderEntity->hModel->InitEmpty( trailGenerator_SnapshotName ); #ifdef _DEBUG_CUSTOM_GEOM //debug idBounds bbox; bbox.Zero(); bbox.ExpandSelf(1); idVec4 color; color[3] = 1; //alpha for( i = 0; i < numPoints; i++ ){ if( i % 2 == 0){ color[0] = gameLocal.random.RandomFloat(); color[1] = gameLocal.random.RandomFloat(); color[2] = gameLocal.random.RandomFloat(); } gameRenderWorld->DebugBounds( color, bbox, points[i], 20000); } #endif // allocate triangle surfaces for the fractures and decals tris = renderEntity->hModel->AllocSurfaceTriangles( numTris * 3, material->ShouldCreateBackSides() ? numTris * 6 : numTris * 3 ); //packedColor = PackColor( idVec4( 1,1,1,1 ) ); /* gameLocal.Printf("color found: %f, %f, %f\n", renderEntity->shaderParms[ SHADERPARM_RED ], renderEntity->shaderParms[ SHADERPARM_GREEN ], renderEntity->shaderParms[ SHADERPARM_BLUE ] ); if( &(this->renderEntity) == renderEntity ){ gameLocal.Printf("Stesso renderEntity!\n"); } */ //gameLocal.Printf("iteration starts...\n "); fitoffset = 1.0f; for( i = 0; i < (numPoints - 3); i += 2 ){ //gameLocal.Printf(",%d",i); //re-use the same winding every time winding.Clear(); //points closer to the sword winding.AddPoint( idVec5( points[i], idVec2(0, fitoffset )) ); //Lower point winding.AddPoint( idVec5( points[i+1], idVec2(1, fitoffset)) ); //Higher point //gameLocal.Printf("fitoffset: %f, fitoffset-fitpct: %f\n", fitoffset, (fitoffset-fitpct) ); fitoffset -= fitpct; //decrease the texture offset so that the texture is correctly fit if( fitoffset < 0.0f ){ fitoffset = 0.0f; } //solve approximation issues //previous points winding.AddPoint( idVec5( points[i+3], idVec2(1, fitoffset)) ); //Higher point winding.AddPoint( idVec5( points[i+2], idVec2(0, fitoffset)) ); //Lower point //get info from windings winding.GetPlane( plane ); tangents = ( plane.Normal() ).ToMat3(); //was: ( plane.Normal() * axis ).ToMat3(); //create tris given points in winding. Usually we have 4 points -> 2tris for ( j = 2; j < winding.GetNumPoints(); j++ ) { //first vertex is always placed at the first point v = &tris->verts[tris->numVerts++]; v->Clear(); v->xyz = winding[0].ToVec3(); v->st[0] = winding[0].s; v->st[1] = winding[0].t; v->normal = tangents[0]; v->tangents[0] = tangents[1]; v->tangents[1] = tangents[2]; v->SetColor( trailGenerator_vertexColor ); //second vertex v = &tris->verts[tris->numVerts++]; v->Clear(); v->xyz = winding[j-1].ToVec3(); v->st[0] = winding[j-1].s; v->st[1] = winding[j-1].t; v->normal = tangents[0]; v->tangents[0] = tangents[1]; v->tangents[1] = tangents[2]; v->SetColor( trailGenerator_vertexColor ); //third vertex v = &tris->verts[tris->numVerts++]; v->Clear(); v->xyz = winding[j].ToVec3(); v->st[0] = winding[j].s; v->st[1] = winding[j].t; v->normal = tangents[0]; v->tangents[0] = tangents[1]; v->tangents[1] = tangents[2]; v->SetColor( trailGenerator_vertexColor ); //set the index for each tris tris->indexes[tris->numIndexes++] = tris->numVerts - 3; tris->indexes[tris->numIndexes++] = tris->numVerts - 2; tris->indexes[tris->numIndexes++] = tris->numVerts - 1; if ( material->ShouldCreateBackSides() ) { tris->indexes[tris->numIndexes++] = tris->numVerts - 2; tris->indexes[tris->numIndexes++] = tris->numVerts - 3; tris->indexes[tris->numIndexes++] = tris->numVerts - 1; } } } tris->tangentsCalculated = true; SIMDProcessor->MinMax( tris->bounds[0], tris->bounds[1], tris->verts, tris->numVerts ); memset( &surface, 0, sizeof( surface ) ); surface.shader = material; surface.id = 0; surface.geometry = tris; renderEntity->hModel->AddSurface( surface ); return true; } /* ================ idTrailGenerator::ModelCallback ================ */ bool idTrailGenerator::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) { //gameLocal.Printf("idTrailGenerator::ModelCallback\n"); const idTrailGenerator *trailGen; trailGen = gameLocal.trailsManager->trails[ renderEntity->entityNum ]; if ( !trailGen ) { gameLocal.Error( "idTrailGenerator::ModelCallback: callback with NULL trailGen" ); } if ( !trailGen->modelChanged ){ return false; } return trailGen->UpdateRenderEntity( renderEntity, renderView ); } /* ================ idTrailGenerator::Think ================ */ void idTrailGenerator::Fade( void ) { //TrailGenerator thinks before all the other entities. //gameLocal.Printf("idTrailGenerator::Fade\n"); //if ( fading ) { if( fadingEndTime > gameLocal.time ){ //gameLocal.Printf("fading...\n"); renderEntity.shaderParms[ SHADERPARM_RED ] += fadingColorStep[ 0 ]; renderEntity.shaderParms[ SHADERPARM_GREEN ] += fadingColorStep[ 1 ]; renderEntity.shaderParms[ SHADERPARM_BLUE ] += fadingColorStep[ 2 ]; UpdateVisuals(); //make sure it's in the world and updated }else{ StopTrail(); } //} }