/* ** 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 . */ /*********************************************************************************************** *** 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/pscene_projectors.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Greg_h $* * * * $Modtime:: 1/11/02 3:12p $* * * * $Revision:: 50 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "pscene.h" #include "colmathaabox.h" #include "rinfo.h" #include "staticaabtreecull.h" #include "dynamicaabtreecull.h" #include "physgridcull.h" #include "lightcull.h" #include "staticanimphys.h" #include "assetmgr.h" #include "refcount.h" #include "camera.h" #include "quat.h" #include "win.h" #include "vertmaterial.h" #include "wwprofile.h" #include "texture.h" #include "dx8wrapper.h" #include "pot.h" #include "materialeffect.h" #include "wwmemlog.h" #include "targa.h" #include "bitmaphandler.h" #include "dx8caps.h" #include "vertmaterial.h" #define DEBUG_SHADOW_RENDERING 0 #if (DEBUG_SHADOW_RENDERING) #define SHADOW_WINDOW_STYLE WS_POPUP | WS_CAPTION | WS_BORDER | WS_VISIBLE #else #define SHADOW_WINDOW_STYLE WS_POPUP | WS_CAPTION | WS_BORDER #endif const int SHADOW_CLIP_FAR = 500; const int STATIC_PROJECTOR_RESOLUTION = 256; //128; const float STATIC_SHADOW_INTENSITY = 0.6f; const float MIN_STATIC_SHADOW_COS_HALF_THETA = cos(DEG_TO_RADF(10.0f)/2.0f); // angle to allow shadow tex re-use const int DEFAULT_MAX_DYNAMIC_SHADOWS = 6; const int DEFAULT_DYNAMIC_SHADOW_RESOLUTION = 256; /** ** StaticShadowTexMgrClass ** This object simply manages the list of unique shadow textures being used by the ** static shadow projectors. For example, all instances of the same tree model ** re-use the same shadow texture... */ class StaticShadowTexMgrClass { public: StaticShadowTexMgrClass(void); virtual ~StaticShadowTexMgrClass(void); void Reset(void); TextureClass * Peek_Shadow_Texture(uint32 obj_type_id,const Quaternion & orientation); void Add_Shadow_Texture(uint32 obj_type_id,const Quaternion & orientation,TextureClass * tex); void Remove_Shadow_Texture(TextureClass * tex); private: class ShadowTexClass { public: ShadowTexClass(void); ShadowTexClass(uint32 obj_type_id,const Quaternion & orientation,TextureClass * tex); ShadowTexClass(const ShadowTexClass & that); ~ShadowTexClass(void); const ShadowTexClass & operator = (const ShadowTexClass &); bool operator == (const ShadowTexClass &) { return false; } bool operator != (const ShadowTexClass & that) { return true; } uint32 ObjectTypeID; // use the Definition ID to uniquely identify object types Quaternion ObjectOrientation; // orientation of the object when this shadow was generated TextureClass * Texture; // texture }; DynamicVectorClass ShadowTextures; }; /** ** DynamicShadowTexMgrClass ** This class manages a pool of render target textures which are shared by the currently ** active shadow projectors. */ class DynamicShadowTexMgrClass { public: DynamicShadowTexMgrClass(void); virtual ~DynamicShadowTexMgrClass(void); // void Reset(); // Jani: Disabling reset. Re-allocating render targets if the device is out of // free texture memory causes problems on at least TNT2. void Set_Max_Simultaneous_Shadows(unsigned int max); unsigned int Get_Max_Simultaneous_Shadows(void); void Set_Shadow_Resolution(unsigned int size); unsigned int Get_Shadow_Resolution(void); void Per_Frame_Reset(void); void Assign_Render_Target_Texture(TexProjectClass * tex_proj); private: TextureClass * Allocate_Render_Target_Texture(void); unsigned int CurShadow; unsigned int TextureResolution; SimpleVecClass ShadowTextures; }; /* ** ** Instantiate the Shadow Texture Managers. ** */ static StaticShadowTexMgrClass _StaticShadowTexMgr; static DynamicShadowTexMgrClass _DynamicShadowTexMgr; static TextureClass* Create_Projector_Render_Target(unsigned w,unsigned h) { WW3DFormat format=WW3D_FORMAT_UNKNOWN; // Try if 8 or 16 bit render target formats would be supported if (DX8Wrapper::Get_Current_Caps()->Support_Render_To_Texture_Format(WW3D_FORMAT_R3G3B2)) format=WW3D_FORMAT_R3G3B2; else if (DX8Wrapper::Get_Current_Caps()->Support_Render_To_Texture_Format(WW3D_FORMAT_R5G6B5)) format=WW3D_FORMAT_R5G6B5; else if (DX8Wrapper::Get_Current_Caps()->Support_Render_To_Texture_Format(WW3D_FORMAT_A4R4G4B4)) format=WW3D_FORMAT_A4R4G4B4; else if (DX8Wrapper::Get_Current_Caps()->Support_Render_To_Texture_Format(WW3D_FORMAT_X1R5G5B5)) format=WW3D_FORMAT_X1R5G5B5; TextureClass* texture = DX8Wrapper::Create_Render_Target(w,h,format); if (texture) return texture; // As a last resort, try creating with unknown format (which means using the current display resolution) if (format!=WW3D_FORMAT_UNKNOWN) { format=WW3D_FORMAT_UNKNOWN; return DX8Wrapper::Create_Render_Target(w,h,format); } return NULL; } /************************************************************************************ ** ** ShadowTexClass Implemenation ** ************************************************************************************/ StaticShadowTexMgrClass::ShadowTexClass::ShadowTexClass(void) : ObjectTypeID(0), ObjectOrientation(1), Texture(NULL) { } StaticShadowTexMgrClass::ShadowTexClass::ShadowTexClass ( uint32 obj_type_id, const Quaternion & orientation, TextureClass * tex ) : ObjectTypeID(obj_type_id), ObjectOrientation(orientation), Texture(NULL) { REF_PTR_SET(Texture,tex); } StaticShadowTexMgrClass::ShadowTexClass::ShadowTexClass(const ShadowTexClass & that) : ObjectTypeID(0), ObjectOrientation(1), Texture(NULL) { *this = that; } const StaticShadowTexMgrClass::ShadowTexClass & StaticShadowTexMgrClass::ShadowTexClass::operator = (const ShadowTexClass & that) { ObjectTypeID = that.ObjectTypeID; ObjectOrientation = that.ObjectOrientation; REF_PTR_SET(Texture,that.Texture); return *this; } StaticShadowTexMgrClass::ShadowTexClass::~ShadowTexClass(void) { REF_PTR_RELEASE(Texture); } /************************************************************************************ ** ** StaticShadowTexMgrClass Implemenation ** ************************************************************************************/ StaticShadowTexMgrClass::StaticShadowTexMgrClass(void) { } StaticShadowTexMgrClass::~StaticShadowTexMgrClass(void) { } void StaticShadowTexMgrClass::Reset(void) { ShadowTextures.Delete_All(); } TextureClass * StaticShadowTexMgrClass::Peek_Shadow_Texture ( uint32 obj_type_id, const Quaternion & orientation ) { for (int i=0; i MIN_STATIC_SHADOW_COS_HALF_THETA)) { return ShadowTextures[i].Texture; } } return NULL; } void StaticShadowTexMgrClass::Add_Shadow_Texture ( uint32 obj_type_id, const Quaternion & orientation, TextureClass * tex ) { WWASSERT(Peek_Shadow_Texture(obj_type_id,orientation) == NULL); ShadowTexClass record(obj_type_id,orientation,tex); ShadowTextures.Add(record); } void StaticShadowTexMgrClass::Remove_Shadow_Texture ( TextureClass * tex ) { for (int i=0; iShadowTextures.Length()) curlen=ShadowTextures.Length(); for (i=0; i 256) { oksize = 256; } if (oksize < 16) { oksize = 16; } if (oksize != TextureResolution) { int i; for (i=0; iSet_Render_Target(ShadowTextures[CurShadow]); CurShadow++; } } TextureClass * DynamicShadowTexMgrClass::Allocate_Render_Target_Texture(void) { TextureClass * texture=Create_Projector_Render_Target(TextureResolution,TextureResolution); if (texture != NULL) { SET_REF_OWNER(texture); texture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); texture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); } return texture; } /************************************************************************************ ** ** PhysicsSceneClass Texture Projection Code ** ************************************************************************************/ void PhysicsSceneClass::Release_Projector_Resources(void) { if (ShadowRenderContext != NULL) { delete ShadowRenderContext; ShadowRenderContext=NULL; } REF_PTR_RELEASE(ShadowMaterialPass); REF_PTR_RELEASE(ShadowCamera); REF_PTR_RELEASE(ShadowBlobTexture); _StaticShadowTexMgr.Reset(); // _DynamicShadowTexMgr.Reset(); } void PhysicsSceneClass::Set_Shadow_Resolution(unsigned int res) { _DynamicShadowTexMgr.Set_Shadow_Resolution(res); } unsigned int PhysicsSceneClass::Get_Shadow_Resolution(void) { return _DynamicShadowTexMgr.Get_Shadow_Resolution(); } void PhysicsSceneClass::Set_Max_Simultaneous_Shadows(unsigned int count) { _DynamicShadowTexMgr.Set_Max_Simultaneous_Shadows(count); } unsigned int PhysicsSceneClass::Get_Max_Simultaneous_Shadows(void) { return _DynamicShadowTexMgr.Get_Max_Simultaneous_Shadows(); } SpecialRenderInfoClass * PhysicsSceneClass::Get_Shadow_Render_Context(int width,int height) { if (ShadowRenderContext == NULL) { /* ** Create a camera for shadow rendering to use */ if (ShadowCamera == NULL) { ShadowCamera = NEW_REF(CameraClass,()); ShadowCamera->Set_Clip_Planes(0.2f,SHADOW_CLIP_FAR); ShadowCamera->Set_View_Plane(DEG_TO_RAD(90.0f),DEG_TO_RAD(90.0f)); ShadowCamera->Set_Viewport(Vector2(0,0),Vector2(1,1)); } /* ** Create the render context */ ShadowRenderContext = new SpecialRenderInfoClass(*ShadowCamera,SpecialRenderInfoClass::RENDER_SHADOW); } return ShadowRenderContext; } MaterialPassClass * PhysicsSceneClass::Get_Shadow_Material_Pass(void) { if (ShadowMaterialPass == NULL) { VertexMaterialClass * vmtl = NEW_REF(VertexMaterialClass,()); vmtl->Set_Ambient(0,0,0); vmtl->Set_Diffuse(0,0,0); vmtl->Set_Specular(0,0,0); vmtl->Set_Emissive(0,0,0); vmtl->Set_Lighting(true); ShaderClass shader = ShaderClass::_PresetOpaqueShader; shader.Set_Depth_Compare(ShaderClass::PASS_ALWAYS); shader.Set_Depth_Mask(ShaderClass::DEPTH_WRITE_DISABLE); shader.Set_Texturing(ShaderClass::TEXTURING_DISABLE); ShadowMaterialPass = NEW_REF(MaterialPassClass,()); ShadowMaterialPass->Set_Material(vmtl); ShadowMaterialPass->Set_Shader(shader); ShadowMaterialPass->Enable_On_Translucent_Meshes(false); REF_PTR_RELEASE(vmtl); } ShadowMaterialPass->Add_Ref(); return ShadowMaterialPass; } void PhysicsSceneClass::Enable_Static_Projectors(bool onoff) { StaticProjectorsEnabled = onoff; } bool PhysicsSceneClass::Are_Static_Projectors_Enabled(void) { return StaticProjectorsEnabled; } void PhysicsSceneClass::Enable_Dynamic_Projectors(bool onoff) { DynamicProjectorsEnabled = onoff; } bool PhysicsSceneClass::Are_Dynamic_Projectors_Enabled(void) { return DynamicProjectorsEnabled; } void PhysicsSceneClass::Set_Shadow_Mode(ShadowEnum shadow_mode) { if (((int)shadow_mode >= 0) && ((int)shadow_mode < SHADOW_MODE_COUNT)) { if (ShadowMode!=shadow_mode) { ShadowMode = shadow_mode; switch (ShadowMode) { default: case SHADOW_MODE_NONE: // no shadows at all case SHADOW_MODE_BLOBS: // projected blob shadows Set_Max_Simultaneous_Shadows(0); break; case SHADOW_MODE_BLOBS_PLUS: // projected blobs with main character having a rendered shadow Set_Max_Simultaneous_Shadows(1); break; case SHADOW_MODE_HARDWARE: // use render-to-texture hardware Set_Max_Simultaneous_Shadows(4); break; } } } } PhysicsSceneClass::ShadowEnum PhysicsSceneClass::Get_Shadow_Mode(void) { return ShadowMode; } void PhysicsSceneClass::Set_Shadow_Attenuation(float atten_start_distance,float atten_end_distance) { if (atten_start_distance < 0.0f) { atten_start_distance = 0.0f; } if (atten_end_distance < atten_start_distance) { atten_end_distance = atten_start_distance; } ShadowAttenStart = atten_start_distance; ShadowAttenEnd = atten_end_distance; } void PhysicsSceneClass::Get_Shadow_Attenuation(float * set_atten_start,float * set_atten_end) { if (set_atten_start != NULL) { *set_atten_start = ShadowAttenStart; } if (set_atten_end != NULL) { *set_atten_end = ShadowAttenEnd; } } void PhysicsSceneClass::Set_Shadow_Normal_Intensity(float normal_intensity) { if (normal_intensity < 0.0f) { normal_intensity = 0.0f; } if (normal_intensity > 1.0f) { normal_intensity = 1.0f; } ShadowNormalIntensity = normal_intensity; } float PhysicsSceneClass::Get_Shadow_Normal_Intensity(void) { return ShadowNormalIntensity; } void PhysicsSceneClass::Add_Static_Texture_Projector(TexProjectClass * newprojector) { WWASSERT(newprojector); WWASSERT(!StaticProjectorList.Is_In_List(newprojector)); StaticProjectorList.Add(newprojector); StaticProjectorCullingSystem->Add_Object(newprojector); } void PhysicsSceneClass::Remove_Static_Texture_Projector(TexProjectClass * projector) { WWASSERT(projector); if (projector->Get_Culling_System() == StaticProjectorCullingSystem) { WWASSERT(StaticProjectorList.Is_In_List(projector)); StaticProjectorList.Remove(projector); StaticProjectorCullingSystem->Remove_Object(projector); } } void PhysicsSceneClass::Add_Dynamic_Texture_Projector(TexProjectClass * newprojector) { WWASSERT(newprojector); WWASSERT(!DynamicProjectorList.Is_In_List(newprojector)); DynamicProjectorList.Add(newprojector); DynamicProjectorCullingSystem->Add_Object(newprojector); } void PhysicsSceneClass::Remove_Dynamic_Texture_Projector(TexProjectClass * projector) { WWASSERT(projector); if (projector->Get_Culling_System() == DynamicProjectorCullingSystem) { WWASSERT(DynamicProjectorList.Is_In_List(projector)); DynamicProjectorList.Remove(projector); DynamicProjectorCullingSystem->Remove_Object(projector); } } void PhysicsSceneClass::Remove_Texture_Projector(TexProjectClass * projector) { WWASSERT(projector); if (DynamicProjectorList.Is_In_List(projector)) { DynamicProjectorCullingSystem->Remove_Object(projector); DynamicProjectorList.Remove(projector); } else if (StaticProjectorList.Is_In_List(projector)) { StaticProjectorCullingSystem->Remove_Object(projector); StaticProjectorList.Remove(projector); } } bool PhysicsSceneClass::Contains(TexProjectClass * projector) { return (DynamicProjectorList.Is_In_List(projector) || StaticProjectorList.Is_In_List(projector)); } float PhysicsSceneClass::Compute_Projector_Attenuation(TexProjectClass * dynamic_projector,const Vector3 & view_pos,const Vector3 & view_dir) { Vector3 r; Vector3::Subtract(dynamic_projector->Get_Bounding_Volume().Center,view_pos,&r); float dist = Vector3::Dot_Product(r,view_dir); if (dist > ShadowAttenEnd) { return 0.0f; } if (dist < ShadowAttenStart) { return 1.0f; } return 1.0f - (dist - ShadowAttenStart) / (ShadowAttenEnd - ShadowAttenStart); } void PhysicsSceneClass::Apply_Projectors ( const CameraClass & camera ) { WWPROFILE("pscene::Apply_Projectors"); Vector3 view_pos; Vector3 view_dir; camera.Get_Transform().Get_Translation(&view_pos); camera.Get_Transform().Get_Z_Vector(&view_dir); view_dir = -view_dir; if (StaticProjectorsEnabled) { /* ** collect the visible static projectors */ StaticProjectorCullingSystem->Reset_Collection(); StaticProjectorCullingSystem->Collect_Objects(camera.Get_Frustum()); TexProjectClass * static_projector = StaticProjectorCullingSystem->Get_First_Collected_Object(); while (static_projector != NULL) { /* ** only keep considering this projector if its intensity is above ZERO */ if (!static_projector->Is_Intensity_Zero()) { /* ** attenuate the projector with distance from the camera (if needed) */ float attenuation = 1.0f; if (static_projector->Is_Attenuation_Enabled()) { attenuation = Compute_Projector_Attenuation(static_projector,view_pos,view_dir); static_projector->Set_Attenuation(attenuation); } /* ** if the projector is completely attenuated, don't process it */ if (attenuation > 0.0f) { if (ProjectorDebugDisplayEnabled) { DEBUG_RENDER_OBBOX(static_projector->Get_Bounding_Volume(),Vector3(0,1,0),0.25f); } Apply_Projector_To_Objects(static_projector,camera); } } static_projector = StaticProjectorCullingSystem->Get_Next_Collected_Object(static_projector); } } /* ** Build a list of the dynamic shadow textures that need to be rendered */ TexProjListClass rt_projector_list; unsigned int count = 0; if (DynamicProjectorsEnabled) { /* ** Collect the visible dynamic projectors */ DynamicProjectorCullingSystem->Reset_Collection(); DynamicProjectorCullingSystem->Collect_Objects(camera.Get_Frustum()); TexProjectClass * dynamic_projector = DynamicProjectorCullingSystem->Get_First_Collected_Object(); while (dynamic_projector != NULL) { /* ** only keep considering this projector if its intensity is above ZERO */ if (!dynamic_projector->Is_Intensity_Zero()) { /* ** attenuate the projector with distance from the camera (if needed) */ float attenuation = 1.0f; if (dynamic_projector->Is_Attenuation_Enabled()) { attenuation = Compute_Projector_Attenuation(dynamic_projector,view_pos,view_dir); dynamic_projector->Set_Attenuation(attenuation); } /* ** if the projector is completely attenuated, don't process it */ if (attenuation > 0.0f) { if (ProjectorDebugDisplayEnabled) { DEBUG_RENDER_OBBOX(dynamic_projector->Get_Bounding_Volume(),Vector3(1,0,0),0.25f); } /* ** If this projector needs to recompute its texture, add it to the list, ** otherwise set it up now. */ if (dynamic_projector->Needs_Render_Target()) { rt_projector_list.Add(dynamic_projector); count++; } else { Apply_Projector_To_Objects(dynamic_projector,camera); } } } dynamic_projector = DynamicProjectorCullingSystem->Get_Next_Collected_Object(dynamic_projector); } } /* ** Reject texture projectors until we are below the maximum allowed simultaneous shadows */ Vector3 cam_pos; camera.Get_Transform().Get_Translation(&cam_pos); TexProjListIterator it(&rt_projector_list); while (count > _DynamicShadowTexMgr.Get_Max_Simultaneous_Shadows()) { /* ** Find the projector farthest from the camera */ it.First(); TexProjectClass * farthest_shadow = it.Peek_Obj(); float farthest_dist = (farthest_shadow->Get_Bounding_Volume().Center - cam_pos).Length2(); it.Next(); while (!it.Is_Done()) { float dist = (it.Peek_Obj()->Get_Bounding_Volume().Center - cam_pos).Length2(); if (dist > farthest_dist) { farthest_dist = dist; farthest_shadow = it.Peek_Obj(); } it.Next(); } /* ** Remove it from the list and disable it. */ rt_projector_list.Remove(farthest_shadow); farthest_shadow->Set_Render_Target(NULL); count--; } /* ** Process the most "important" active dynamic shadows */ _DynamicShadowTexMgr.Per_Frame_Reset(); it.First(); while (!it.Is_Done()) { TexProjectClass * projector = it.Peek_Obj(); _DynamicShadowTexMgr.Assign_Render_Target_Texture(projector); if (projector->Peek_Render_Target() != NULL) { Apply_Projector_To_Objects(projector,camera); } it.Next(); } DX8Wrapper::Set_Render_Target((IDirect3DSurface8 *)NULL); } void PhysicsSceneClass::Apply_Projector_To_Objects ( TexProjectClass * tex_proj, const CameraClass & camera ) { bool projector_update_needed = false; // Needed if just one mesh uses it SimpleEffectClass * effect = NEW_REF(SimpleEffectClass,(tex_proj->Peek_Material_Pass())); effect->Enable_Auto_Remove(true); /* ** collect the static objects intersecting the projector's volume */ if (tex_proj->Is_Affect_Static_Objects_Enabled()) { StaticCullingSystem->Reset_Collection(); StaticCullingSystem->Collect_Objects(tex_proj->Get_Bounding_Volume()); StaticPhysClass * static_obj = (StaticPhysClass *)StaticCullingSystem->Get_First_Collected_Object(); while (static_obj) { /* ** for each static object, if it also in our visible list, add this projector to it ** since the visible static objects are split into two lists we check both. */ if (VisibleStaticObjectList.Contains(static_obj) || VisibleWSMeshList.Is_In_List(static_obj)) { // check if our volume actually intersects the mesh! if (static_obj->Intersects(tex_proj->Get_Bounding_Volume())) { static_obj->Add_Effect_To_Me(effect); projector_update_needed = true; } } static_obj = (StaticPhysClass *)StaticCullingSystem->Get_Next_Collected_Object(static_obj); } } /* ** collect the dynamic objects intersecting the projector's volume */ if (tex_proj->Is_Affect_Dynamic_Objects_Enabled()) { DynamicCullingSystem->Reset_Collection(); DynamicCullingSystem->Collect_Objects(tex_proj->Get_Bounding_Volume()); PhysClass * dyn_obj = DynamicCullingSystem->Get_First_Collected_Object(); while (dyn_obj) { /* ** for each dynamic object, if it also in our visible list and is not the ** object that generated this shadow, add this projector to it */ if ((VisibleDynamicObjectList.Contains(dyn_obj)) && (dyn_obj != tex_proj->Get_Projection_Object_ID())) { dyn_obj->Add_Effect_To_Me(effect); projector_update_needed = true; } dyn_obj = DynamicCullingSystem->Get_Next_Collected_Object(dyn_obj); } } // if this projector is needed, update it if (projector_update_needed) { tex_proj->Pre_Render_Update(camera.Get_Transform()); } REF_PTR_RELEASE(effect); } static void Create_Render_Target_Test(TextureClass* render_target) { Matrix4 old_view_transform; Matrix4 old_world_transform; Matrix4 old_projection_transform; DX8Wrapper::Get_Transform(D3DTS_VIEW,old_view_transform); DX8Wrapper::Get_Transform(D3DTS_WORLD,old_world_transform); DX8Wrapper::Get_Transform(D3DTS_PROJECTION,old_projection_transform); /* ** Set the render target */ DX8Wrapper::Set_Render_Target(render_target); /* ** Set up the camera */ // Configure_Camera(context->Camera); /* ** Render the object */ Vector3 color(0.0f,0.0f,0.0f); WW3D::Begin_Render(true,false,color); // false to zclear as we don't have z-buffer DX8Wrapper::Set_World_Identity(); DX8Wrapper::Set_View_Identity(); DX8Wrapper::Set_Transform(D3DTS_PROJECTION,Matrix4(true)); const int vertex_count=9; const int polygon_count=3; DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,vertex_count); { DynamicVBAccessClass::WriteLockClass lock(&vb_access); VertexFormatXYZNDUV2* verts=lock.Get_Formatted_Vertex_Array(); Vector3 vertices[vertex_count]={ Vector3(-1.0f, 1.0f,0.0f), Vector3(-1.0f,-1.0f,0.0f), Vector3( 0.0f,-1.0f,0.0f), Vector3(-1.0f,-1.0f,0.0f), Vector3( 1.0f,-1.0f,0.0f), Vector3( 1.0f, 0.0f,0.0f), Vector3( 1.0f,-1.0f,0.0f), Vector3( 1.0f, 1.0f,0.0f), Vector3( 0.0f, 1.0f,0.0f) }; unsigned colors[vertex_count]={ 0xff0000, 0xff0000, 0xff0000, 0x00ff00, 0x00ff00, 0x00ff00, 0x0000ff, 0x0000ff, 0x0000ff }; for (int v=0;vGet_Surface_Level(); SurfaceClass::SurfaceDescription desc; surf->Get_Description(desc); SurfaceClass * new_surf = NEW_REF(SurfaceClass,(desc.Width,desc.Height,desc.Format)); DX8Wrapper::_Copy_DX8_Rects(surf->Peek_D3D_Surface(),NULL,0,new_surf->Peek_D3D_Surface(),NULL); REF_PTR_RELEASE(surf); int pitch; unsigned char* tmpbits=(unsigned char*)new_surf->Lock(&pitch); unsigned color1,color2,color3,color4; BitmapHandlerClass::Read_B8G8R8A8( color1, tmpbits, desc.Format, 1*desc.Width/4, 1*desc.Height/4, desc.Width, desc.Height, NULL, 0); BitmapHandlerClass::Read_B8G8R8A8( color2, tmpbits, desc.Format, 3*desc.Width/4, 1*desc.Height/4, desc.Width, desc.Height, NULL, 0); BitmapHandlerClass::Read_B8G8R8A8( color3, tmpbits, desc.Format, 3*desc.Width/4, 3*desc.Height/4, desc.Width, desc.Height, NULL, 0); BitmapHandlerClass::Read_B8G8R8A8( color4, tmpbits, desc.Format, 1*desc.Width/4, 3*desc.Height/4, desc.Width, desc.Height, NULL, 0); new_surf->Unlock(); REF_PTR_RELEASE(new_surf); WWDEBUG_SAY(("Render target test: 0x%8.8x, 0x%8.8x, 0x%8.8x, 0x%8.8x\n", color1&0x00ffffff, color2&0x00ffffff, color3&0x00ffffff, color4&0x00ffffff)); // color1 is 0x00000000 if (color1&0x00ffffff) return false; // color2 is 0x000000ff if (color2&0x00ffff00) return false; if (!(color2&0x000000ff)) return false; // color3 is 0x0000ff00 if (color3&0x00ff00ff) return false; if (!(color3&0x0000ff00)) return false; // color4 is 0x00ff0000 if (color4&0x0000ffff) return false; if (!(color4&0x00ff0000)) return false; return true; } void PhysicsSceneClass::Invalidate_Static_Shadow_Projectors() { /* ** Collect a list of all static objects who want to generate a shadow ** Tell each to destroy their shadow */ RefPhysListClass shadow_gen_list; RefPhysListIterator static_anim_iterator(&StaticAnimList); for (static_anim_iterator.First(); !static_anim_iterator.Is_Done(); static_anim_iterator.Next()) { StaticAnimPhysClass * obj = (StaticAnimPhysClass *)static_anim_iterator.Peek_Obj(); if (obj != NULL) { StaticAnimPhysDefClass * def = obj->Get_StaticAnimPhysDef(); if (def && def->Shadow_Dynamic_Objs()) { obj->Set_Shadow(NULL); shadow_gen_list.Add(obj); } } } StaticProjectorsDirty=true; /* ** Release all of the textures we were using for static shadows */ _StaticShadowTexMgr.Reset(); } void PhysicsSceneClass::Generate_Static_Shadow_Projectors(void) { if (!StaticProjectorsDirty) return; // Don't operate if the device is lost! if (DX8Wrapper::Is_Device_Lost() || !DX8Wrapper::Is_Initted()) return; /* ** Collect a list of all static objects who want to generate a shadow ** Tell each to destroy their shadow */ RefPhysListClass shadow_gen_list; RefPhysListIterator static_anim_iterator(&StaticAnimList); for (static_anim_iterator.First(); !static_anim_iterator.Is_Done(); static_anim_iterator.Next()) { StaticAnimPhysClass * obj = (StaticAnimPhysClass *)static_anim_iterator.Peek_Obj(); if (obj != NULL) { StaticAnimPhysDefClass * def = obj->Get_StaticAnimPhysDef(); if (def && def->Shadow_Dynamic_Objs()) { obj->Set_Shadow(NULL); shadow_gen_list.Add(obj); } } } /* ** Release all of the textures we were using for static shadows */ _StaticShadowTexMgr.Reset(); /* ** Allocate a render target texture for all of the static shadows to share */ TextureClass * render_target = Create_Projector_Render_Target(STATIC_PROJECTOR_RESOLUTION,STATIC_PROJECTOR_RESOLUTION); // TextureClass * render_target = DX8Wrapper::Create_Render_Target(STATIC_PROJECTOR_RESOLUTION,STATIC_PROJECTOR_RESOLUTION); // Test render target functionality. Some NVidia driver versions have issues with rendering to a texture and // copying surface to another texture. If this fails, we'll not use static shadow projectors. Dynamic shadow // projectors may still work however, as they work differently. if (render_target) { Create_Render_Target_Test(render_target); if (!Test_Render_Target_Surface(render_target)) { WWDEBUG_SAY(("Render target locking doesn't work correctly. Disabling static projectors\n")); REF_PTR_RELEASE(render_target); } /* char* bits=new char[desc.Width*desc.Height*3]; //memcpy(bits,tmpbits,desc.Width*desc.Height*4); for (unsigned int y=0;yUnlock(); Targa targ; memset(&targ.Header,0,sizeof(targ.Header)); targ.Header.Width=desc.Width; targ.Header.Height=desc.Height; targ.Header.PixelDepth=24; targ.Header.ImageType=TGA_TRUECOLOR; targ.SetImage((char*)bits); targ.YFlip(); const char* filename="shadowtex.tga"; targ.Save(filename,TGAF_IMAGE,false); delete[] bits; */ } if (render_target != NULL) { SET_REF_OWNER(render_target); /* ** Generate a new shadow for each one. Each time we find a new object-lightsource ** combination, generate a new texture. */ RefPhysListIterator shadow_gen_iterator(&shadow_gen_list); for (shadow_gen_iterator.First(); !shadow_gen_iterator.Is_Done(); shadow_gen_iterator.Next()) { StaticAnimPhysClass * obj = (StaticAnimPhysClass *)shadow_gen_iterator.Peek_Obj(); /* ** Setup the shadow projector for this object */ Vector3 sunvector; Get_Sun_Light_Vector(&sunvector); Setup_Static_Directional_Shadow(*obj,sunvector,render_target); } DX8Wrapper::Set_Render_Target((IDirect3DSurface8 *)NULL); REF_PTR_RELEASE(render_target); } StaticProjectorsDirty=false; } void PhysicsSceneClass::Setup_Static_Directional_Shadow ( StaticAnimPhysClass & obj, const Vector3 & light_dir, TextureClass * render_target ) { /* ** Get the definition for this object */ StaticAnimPhysDefClass * def = obj.Get_StaticAnimPhysDef(); if (def == NULL) { return; } int type_id = def->Get_ID(); Quaternion obj_orientation(1); if (def->Shadow_Ignores_Z_Rotation() == false) { obj_orientation = Build_Quaternion(obj.Get_Transform()); } /* ** Create the projector */ PhysTexProjectClass * shadow_projector = NEW_REF(PhysTexProjectClass,()); shadow_projector->Set_Texture_Size(STATIC_PROJECTOR_RESOLUTION); shadow_projector->Set_Intensity(def->Shadow_Intensity(),true); if (def->Shadow_Is_Additive()) { shadow_projector->Init_Additive(); } else { shadow_projector->Init_Multiplicative(); /* ** (gth) override the static shadow intensity with the global setting. */ shadow_projector->Set_Intensity(Get_Shadow_Normal_Intensity(),true); } shadow_projector->Compute_Ortho_Projection(&obj,light_dir,def->Shadow_NearZ(),def->Shadow_FarZ()); shadow_projector->Enable_Attenuation(true); shadow_projector->Enable_Depth_Gradient(false); shadow_projector->Peek_Material_Pass()->Enable_On_Translucent_Meshes(false); /* ** See if we already have a suitable texture. */ TextureClass * existing_texture = _StaticShadowTexMgr.Peek_Shadow_Texture(type_id,obj_orientation); if (existing_texture != NULL) { shadow_projector->Set_Texture(existing_texture); } /* ** If no texture was a available, we have to generate it. ** Then add it to the cache so others can use it */ if (existing_texture == NULL) { shadow_projector->Set_Render_Target(render_target); shadow_projector->Compute_Texture(&obj,def->Shadow_Is_Additive()); SurfaceClass * surf = render_target->Get_Surface_Level(); SurfaceClass::SurfaceDescription desc; surf->Get_Description(desc); SurfaceClass * new_surf = NEW_REF(SurfaceClass,(desc.Width,desc.Height,desc.Format)); DX8Wrapper::_Copy_DX8_Rects(surf->Peek_D3D_Surface(),NULL,0,new_surf->Peek_D3D_Surface(),NULL); TextureClass * new_texture = NEW_REF(TextureClass,(new_surf)); shadow_projector->Set_Render_Target(NULL); shadow_projector->Set_Texture(new_texture); REF_PTR_RELEASE(surf); REF_PTR_RELEASE(new_surf); REF_PTR_RELEASE(new_texture); _StaticShadowTexMgr.Add_Shadow_Texture(type_id,obj_orientation,shadow_projector->Peek_Texture()); } /* ** Give the projector to the object */ obj.Set_Shadow(shadow_projector); /* ** Release resources */ REF_PTR_RELEASE(shadow_projector); }