| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program 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.
- **
- ** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
- ***********************************************************************************************
- * *
- * Project Name : wwphys *
- * *
- * $Archive:: /Commando/Code/wwphys/lightsolve.cpp $*
- * *
- * Original Author:: Greg Hjelstrom *
- * *
- * $Author:: Patrick $*
- * *
- * $Modtime:: 3/28/02 11:53a $*
- * *
- * $Revision:: 3 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "lightsolve.h"
- #include "phys.h"
- #include "staticphys.h"
- #include "rendobj.h"
- #include "mesh.h"
- #include "dx8renderer.h"
- #include "simplevec.h"
- #include "vp.h"
- #include "lightphys.h"
- #include "light.h"
- #include "lightsolvecontext.h"
- #include "lightsolveprogress.h"
- #include "renegadeterrainpatch.h"
- /**
- ** VertexSolveClass
- ** This class does the job of generated a vertex solve for a mesh.
- */
- class VertexSolveClass
- {
- public:
- VertexSolveClass(void) {}
-
- void Light_Mesh(LightSolveContextClass & context,MeshClass * mesh, NonRefPhysListClass & lights);
- void Light_Terrain(LightSolveContextClass & context,RenegadeTerrainPatchClass * patch, NonRefPhysListClass & lights);
- protected:
- void Grow_Arrays(int new_vcount);
- void Add_Light_To_Vertex(LightSolveContextClass & context,int idx,LightClass * light_obj);
- SimpleVecClass<Vector3> Position;
- SimpleVecClass<Vector3> Normal;
- SimpleVecClass<Vector4> MeshAmbient;
- SimpleVecClass<Vector4> MeshDiffuse;
- SimpleVecClass<Vector4> AmbientSolve;
- SimpleVecClass<Vector4> DiffuseSolve;
- SimpleVecClass<Vector4> Solve;
- };
- static VertexSolveClass TheVertexSolver;
- void VertexSolveClass::Light_Mesh(LightSolveContextClass & context,MeshClass * mesh, NonRefPhysListClass & lights)
- {
- int vi;
- context.Get_Progress().Set_Current_Mesh_Name(mesh->Get_Name());
- context.Get_Progress().Set_Current_Mesh_Vertex_Count(mesh->Peek_Model()->Get_Vertex_Count());
- /*
- ** Get the model and initialize our arrays
- */
- MeshModelClass * model = mesh->Peek_Model();
- int vcount = model->Get_Vertex_Count();
- Grow_Arrays(vcount);
- /*
- ** Transform the positions and normals into world space
- */
- Matrix3D tm = mesh->Get_Transform();
- const Vector3 * src_verts = model->Get_Vertex_Array();
- const Vector3 * src_norms = model->Get_Vertex_Normal_Array();
- VectorProcessorClass::Transform(&(Position[0]), src_verts, tm, vcount);
- tm.Set_Translation(Vector3(0,0,0));
- VectorProcessorClass::Transform(&(Normal[0]), src_norms, tm, vcount);
- /*
- ** Fill the mesh ambient and diffuse arrays with the per vertex material
- ** color (either from the material itself or the vertex color array)
- **
- ** LIGHT SOLVE GENERATION RULES:
- ** - for each vertex
- ** - for ambient and diffuse
- ** - if this vertex is supposed to use a color array in any pass copy the color from the array
- ** - else copy the material setting from pass0
- **
- ** LIGHT SOLVE INSTALL RULES:
- ** - for each pass
- ** - for each vertex material
- ** - if this material has any emissive component, leave the vmtl alone
- ** - else, point the emissive source to the new light solve color array,
- ** make the diffuse and ambient use the material settings.
- */
- unsigned * dcg = NULL;
- for (int pi=0; pi<model->Get_Pass_Count(); pi++) {
- if (model->Get_DCG_Array(pi) != NULL) {
- dcg = model->Get_DCG_Array(pi);
- }
- }
-
- for (vi=0; vi<model->Get_Vertex_Count(); vi++) {
- bool use_array = false;
- for (int pi=0; pi<model->Get_Pass_Count(); pi++) {
- VertexMaterialClass * pass_mtl = model->Peek_Material(vi,pi);
- if ( (pass_mtl != NULL) &&
- (dcg != NULL) &&
- (pass_mtl->Get_Diffuse_Color_Source() == VertexMaterialClass::COLOR1) )
- {
- use_array = true;
- }
- }
- /*
- ** Set up the ambient color for this vertex
- */
- VertexMaterialClass * vmtl = model->Peek_Material(vi,0);
- if (use_array) {
- MeshAmbient[vi] = DX8Wrapper::Convert_Color(dcg[vi]);
- } else if (vmtl != NULL) {
- Vector3 ambient;
- vmtl->Get_Ambient(&ambient);
- MeshAmbient[vi] = Vector4(ambient.X,ambient.Y,ambient.Z,1.0f);
- } else {
- MeshAmbient[vi].Set(1,1,1,1);
- }
- /*
- ** Set up the diffuse color for this vertex
- */
- if (use_array) {
- MeshDiffuse[vi] = DX8Wrapper::Convert_Color(dcg[vi]);
- } else if (vmtl != NULL) {
- Vector3 diffuse;
- vmtl->Get_Diffuse(&diffuse);
- MeshDiffuse[vi] = Vector4(diffuse.X,diffuse.Y,diffuse.Z,1.0f);
- } else {
- MeshDiffuse[vi].Set(1,1,1,1);
- }
- }
- /*
- ** Compute the light solve
- */
- LightClass * sun = PhysicsSceneClass::Get_Instance()->Get_Sun_Light();
- Vector3 scene_ambient = PhysicsSceneClass::Get_Instance()->Get_Ambient_Light();
- int callback_counter = 0;
- for (vi=0; vi<model->Get_Vertex_Count(); vi++) {
- AmbientSolve[vi].X = scene_ambient.X;
- AmbientSolve[vi].Y = scene_ambient.Y;
- AmbientSolve[vi].Z = scene_ambient.Z;
- AmbientSolve[vi].W = 1.0f;
-
- DiffuseSolve[vi].X = 0.0f;
- DiffuseSolve[vi].Y = 0.0f;
- DiffuseSolve[vi].Z = 0.0f;
- DiffuseSolve[vi].W = MeshDiffuse[vi].W;
- /*
- ** Sun
- */
- Add_Light_To_Vertex(context,vi,sun);
- /*
- ** Other lights
- */
- NonRefPhysListIterator it(&lights);
- while (!it.Is_Done()) {
-
- LightPhysClass * light = it.Peek_Obj()->As_LightPhysClass();
- if ((light) && (light->Peek_Model()) && (light->Peek_Model()->Class_ID() == RenderObjClass::CLASSID_LIGHT)) {
- LightClass * light_obj = (LightClass*)light->Peek_Model();
- Add_Light_To_Vertex(context,vi,light_obj);
- }
- it.Next();
- }
- context.Get_Progress().Set_Current_Vertex(vi);
- callback_counter++;
- if (callback_counter > 100) {
- callback_counter = 0;
- context.Update_Observer();
- }
- }
- VectorProcessorClass::Clamp(&(AmbientSolve[0]),&(AmbientSolve[0]), 0.0f, 1.0f,vcount);
- VectorProcessorClass::Clamp(&(DiffuseSolve[0]),&(DiffuseSolve[0]), 0.0f, 1.0f,vcount);
- /*
- ** Modulate the accumulated light by the material properties
- */
- for (vi=0; vi<vcount; vi++) {
- Solve[vi].X = AmbientSolve[vi].X * MeshAmbient[vi].X + DiffuseSolve[vi].X * MeshDiffuse[vi].X;
- Solve[vi].Y = AmbientSolve[vi].Y * MeshAmbient[vi].Y + DiffuseSolve[vi].Y * MeshDiffuse[vi].Y;
- Solve[vi].Z = AmbientSolve[vi].Z * MeshAmbient[vi].Z + DiffuseSolve[vi].Z * MeshDiffuse[vi].Z;
- Solve[vi].W = DiffuseSolve[vi].W;
- }
- VectorProcessorClass::Clamp(&(Solve[0]),&(Solve[0]), 0.0f, 1.0f,vcount);
- mesh->Install_User_Lighting_Array(&(Solve[0]));
- context.Update_Observer();
- REF_PTR_RELEASE(sun);
- }
- static Vector3 _offset (0, 0, 0);
- void VertexSolveClass::Light_Terrain(LightSolveContextClass & context,RenegadeTerrainPatchClass * patch, NonRefPhysListClass & lights)
- {
- int vi;
- context.Get_Progress().Set_Current_Mesh_Name(patch->Get_Name());
- context.Get_Progress().Set_Current_Mesh_Vertex_Count(patch->Get_Vertex_Count());
- /*
- ** Get the model and initialize our arrays
- */
- int vcount = patch->Get_Vertex_Count();
- Grow_Arrays(vcount);
- /*
- ** Transform the positions and normals into world space
- */
- Matrix3D tm = patch->Get_Transform();
- const Vector3 * src_verts = patch->Get_Vertex_Array();
- const Vector3 * src_norms = patch->Get_Vertex_Normal_Array();
- VectorProcessorClass::Transform(&(Position[0]), src_verts, tm, vcount);
- tm.Set_Translation(Vector3(0,0,0));
- VectorProcessorClass::Transform(&(Normal[0]), src_norms, tm, vcount);
- /*
- ** For heightfield terrains, the mesh ambient and diffuse colors are white
- */
- for (int index = 0; index < vcount; index ++) {
- MeshAmbient[index].Set (1.0F, 1.0F, 1.0F, 1.0F);
- MeshDiffuse[index].Set (1.0F, 1.0F, 1.0F, 1.0F);
- }
- /*
- ** Compute the light solve
- */
- LightClass * sun = PhysicsSceneClass::Get_Instance()->Get_Sun_Light();
- Vector3 scene_ambient = PhysicsSceneClass::Get_Instance()->Get_Ambient_Light();
- int callback_counter = 0;
- for (vi = 0; vi < vcount; vi ++) {
- AmbientSolve[vi].X = scene_ambient.X;
- AmbientSolve[vi].Y = scene_ambient.Y;
- AmbientSolve[vi].Z = scene_ambient.Z;
- AmbientSolve[vi].W = 1.0f;
-
- DiffuseSolve[vi].X = 0.0f;
- DiffuseSolve[vi].Y = 0.0f;
- DiffuseSolve[vi].Z = 0.0f;
- DiffuseSolve[vi].W = MeshDiffuse[vi].W;
- /*
- ** Sun
- */
- Add_Light_To_Vertex(context,vi,sun);
- /*
- ** Other lights
- */
- NonRefPhysListIterator it(&lights);
- while (!it.Is_Done()) {
-
- LightPhysClass * light = it.Peek_Obj()->As_LightPhysClass();
- if ((light) && (light->Peek_Model()) && (light->Peek_Model()->Class_ID() == RenderObjClass::CLASSID_LIGHT)) {
- LightClass * light_obj = (LightClass*)light->Peek_Model();
- Add_Light_To_Vertex(context,vi,light_obj);
- }
- it.Next();
- }
- context.Get_Progress().Set_Current_Vertex(vi);
- callback_counter++;
- if (callback_counter > 100) {
- callback_counter = 0;
- context.Update_Observer();
- }
- }
- VectorProcessorClass::Clamp(&(AmbientSolve[0]),&(AmbientSolve[0]), 0.0f, 1.0f,vcount);
- VectorProcessorClass::Clamp(&(DiffuseSolve[0]),&(DiffuseSolve[0]), 0.0f, 1.0f,vcount);
- /*
- ** Modulate the accumulated light by the material properties
- */
- for (vi=0; vi<vcount; vi++) {
- Solve[vi].X = AmbientSolve[vi].X * MeshAmbient[vi].X + DiffuseSolve[vi].X * MeshDiffuse[vi].X;
- Solve[vi].Y = AmbientSolve[vi].Y * MeshAmbient[vi].Y + DiffuseSolve[vi].Y * MeshDiffuse[vi].Y;
- Solve[vi].Z = AmbientSolve[vi].Z * MeshAmbient[vi].Z + DiffuseSolve[vi].Z * MeshDiffuse[vi].Z;
- Solve[vi].W = DiffuseSolve[vi].W;
- }
- VectorProcessorClass::Clamp(&(Solve[0]),&(Solve[0]), 0.0f, 1.0f,vcount);
- /*
- ** Pass the resultant vertex colors onto the patch
- */
- for (vi=0; vi<vcount; vi++) {
- patch->Set_Vertex_Color (vi, Vector3 (Solve[vi].X, Solve[vi].Y, Solve[vi].Z));
- }
- //
- // Let the patch know that it is now prelit
- //
- patch->Set_Is_Prelit (true);
- return ;
- }
- void VertexSolveClass::Add_Light_To_Vertex(LightSolveContextClass & context,int vi,LightClass * light_obj)
- {
- if (light_obj->Is_Within_Attenuation_Radius(Position[vi])) {
-
- // cast ray...
- bool is_occluded = false;
- #pragma message("need a collision group for light occlusion here...")
- if (context.Is_Occlusion_Enabled()) {
- CastResultStruct res;
- Vector3 p0 = Position[vi];
- Vector3 p1 = light_obj->Get_Position();
- if (light_obj->Get_Type() == LightClass::DIRECTIONAL) {
- Vector3 dir = -(light_obj->Get_Transform().Get_Z_Vector());
- p1 = p0 + dir * light_obj->Get_Attenuation_Range();
- }
- const float MOVE_AMOUNT = 0.25f; // can't be occluded closer than this
- Vector3 delta = p1-p0;
- delta.Normalize();
- p0 += MOVE_AMOUNT * delta;
- p0 += _offset;
- p1 += _offset;
- LineSegClass ray(p0,p1);
- PhysRayCollisionTestClass raytest(ray,&res,0,COLLISION_TYPE_PROJECTILE);
- raytest.CheckDynamicObjs = false;
- PhysicsSceneClass::Get_Instance()->Cast_Ray(raytest);
- if (res.Fraction < 1.0f) {
- is_occluded = true;
- }
- }
- if (!is_occluded) {
- Vector3 ambient;
- Vector3 diffuse;
- light_obj->Compute_Lighting(Position[vi],Normal[vi],&ambient,&diffuse);
- AmbientSolve[vi] += Vector4(ambient.X,ambient.Y,ambient.Z,0.0f);
- DiffuseSolve[vi] += Vector4(diffuse.X,diffuse.Y,diffuse.Z,0.0f);
- }
- }
- }
- void VertexSolveClass::Grow_Arrays(int vcount)
- {
- Position.Uninitialised_Grow(vcount);
- Normal.Uninitialised_Grow(vcount);
- AmbientSolve.Uninitialised_Grow(vcount);
- DiffuseSolve.Uninitialised_Grow(vcount);
- Solve.Uninitialised_Grow(vcount);
- MeshAmbient.Uninitialised_Grow(vcount);
- MeshDiffuse.Uninitialised_Grow(vcount);
- }
- /*****************************************************************************
- **
- ** LightSolveClass Implementation
- **
- *****************************************************************************/
- /*
- Global Solve:
- - start with StaticObjList
- - pass list to cull function
- - cull function gens new list with objects that will not be lit removed
- - pass to solve function which accepts a list
- - iterate list, pass each to solve function for an object
- List Solve:
- - pass list to cull function
- - cull function gens new list with objects that will not be lit removed
- - pass to solve function which accepts a list
- - iterate list, pass each to solve function for an object
- */
- void LightSolveClass::Generate_Static_Light_Solve(LightSolveContextClass & context)
- {
- RefPhysListIterator it = PhysicsSceneClass::Get_Instance()->Get_Static_Object_Iterator();
-
- /*
- ** Build a list of the objects to be solved
- */
- RefPhysListClass solve_list;
- while (!it.Is_Done()) {
- StaticPhysClass * obj = it.Peek_Obj()->As_StaticPhysClass();
- if ((obj != NULL) && (Does_Obj_Get_Static_Light_Solve(obj))) {
- solve_list.Add(obj);
- }
- it.Next();
- }
- /*
- ** Pass the list to the solve function
- */
- Compute_Solve(context,solve_list);
- }
- void LightSolveClass::Generate_Static_Light_Solve(LightSolveContextClass & context,RefPhysListClass & input_list)
- {
- RefPhysListClass solve_list;
- /*
- ** Cull out objects which should not get a lighting solve
- */
- RefPhysListIterator it(&input_list);
- while (!it.Is_Done()) {
- StaticPhysClass * obj = it.Peek_Obj()->As_StaticPhysClass();
- if ((obj != NULL) && (Does_Obj_Get_Static_Light_Solve(obj))) {
- solve_list.Add(obj);
- }
- it.Next();
- }
- /*
- ** Pass the list to the solve function
- */
- Compute_Solve(context,solve_list);
- }
- void LightSolveClass::Compute_Solve(LightSolveContextClass & context,RefPhysListClass & obj_list)
- {
- RefPhysListIterator it(&obj_list);
- /*
- ** Count the objects for the progress display
- */
- int count = 0;
- while (!it.Is_Done()) {
- count++;
- it.Next();
- }
- context.Get_Progress().Set_Object_Count(count);
-
- /*
- ** Generate a light solve for each static object
- */
- it.First();
- while (!it.Is_Done() && !context.Get_Progress().Is_Cancel_Requested()) {
- StaticPhysClass * obj = it.Peek_Obj()->As_StaticPhysClass();
- if (obj != NULL) {
- Compute_Solve(context,obj);
- context.Get_Progress().Increment_Processed_Object_Count();
- }
- it.Next();
- }
- /*
- ** Re-gen all VB's, IB's, etc
- */
- TheDX8MeshRenderer.Invalidate();
- }
- void LightSolveClass::Compute_Solve(LightSolveContextClass & context,StaticPhysClass * obj)
- {
- WWASSERT(obj != NULL);
- if (context.Get_Progress().Get_Object_Count() == 0) {
- context.Get_Progress().Set_Object_Count(1);
- }
- /*
- ** Generate a light solve for each mesh in this object
- */
- if (obj->Is_Model_Pre_Lit() == false) {
- NonRefPhysListClass light_list;
- PhysicsSceneClass::Get_Instance()->Collect_Lights(obj->Get_Cull_Box(),true,false,&light_list);
- Compute_Solve(context,obj->Peek_Model(),light_list);
- }
- }
- void LightSolveClass::Compute_Solve(LightSolveContextClass & context,RenderObjClass * obj,NonRefPhysListClass & light_list)
- {
- /*
- ** Mark this render object as containing a static lighting solve
- */
- obj->Set_Has_User_Lighting(true);
- /*
- ** Recurse through all sub-objects
- */
- WWASSERT(obj != NULL);
- for (int i=0; i<obj->Get_Num_Sub_Objects(); i++) {
- RenderObjClass * sub_obj = obj->Get_Sub_Object(i);
- if (sub_obj != NULL) {
- Compute_Solve(context,sub_obj,light_list);
- REF_PTR_RELEASE(sub_obj);
- }
- }
- /*
- ** For all mesh objects, generate a vertex lighting solution.
- */
- if (obj->Class_ID() == RenderObjClass::CLASSID_MESH) {
- MeshClass * mesh = (MeshClass*)obj;
- WWDEBUG_SAY(("Generating Solve for Mesh: %s\r\n",mesh->Get_Name()));
- TheVertexSolver.Light_Mesh(context,mesh,light_list);
- } else if (obj->Class_ID() == RenderObjClass::CLASSID_RENEGADE_TERRAIN) {
- TheVertexSolver.Light_Terrain(context,(RenegadeTerrainPatchClass *)obj,light_list);
- }
- }
- bool LightSolveClass::Does_Obj_Get_Static_Light_Solve(StaticPhysClass * obj)
- {
- if (obj->Is_Model_Pre_Lit()) {
- return false;
- }
- return Does_Model_Get_Static_Light_Solve(obj->Peek_Model());
- }
- bool LightSolveClass::Does_Model_Get_Static_Light_Solve(RenderObjClass * model)
- {
- if (model == NULL) {
- return false;
- }
- if (!model->Is_Not_Hidden_At_All()) {
- return false;
- }
- if (model->Get_Num_Sub_Objects() > 0) {
- bool any_sub_objs_visible = false;
- for (int i=0; i<model->Get_Num_Sub_Objects(); i++) {
- RenderObjClass * sub_obj = model->Get_Sub_Object(i);
- if (sub_obj != NULL) {
- if (sub_obj->Is_Not_Hidden_At_All()) {
- any_sub_objs_visible = true;
- }
- REF_PTR_RELEASE(sub_obj);
- }
- }
- if (any_sub_objs_visible == false) {
- return false;
- }
- }
- // TODO: check if the entire model is emissive???
- return true;
- }
|