/* ** 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 : WW3D * * * * $Archive:: /Commando/Code/ww3d2/texproject.cpp $* * * * Original Author:: Greg Hjelstrom * * * * $Author:: Jani_p $* * * * $Modtime:: 11/29/01 1:00p $* * * * $Revision:: 20 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * TexProjectClass::TexProjectClass -- Constructor * * TexProjectClass::~TexProjectClass -- Destructor * * TexProjectClass::Set_Texture_Size -- Set the size of texture to use * * TexProjectClass::Get_Texture_Size -- Returns the stored texture size * * TexProjectClass::Set_Flag -- Turn specified flag on or off * * TexProjectClass::Get_Flag -- Get the current state of specified flag * * TexProjectClass::Set_Intensity -- Set the intensity of this projector * * TexProjectClass::Get_Intensity -- returns the current "desired" intensity * * TexProjectClass::Set_Attenuation -- Set the attenuation factor * * TexProjectClass::Get_Attenuation -- Returns the attenuation value * * TexProjectClass::Enable_Attenuation -- Set the state of the ATTENUATE flag * * TexProjectClass::Is_Attenuation_Enabled -- Get the state of the ATTENUATE flag * * TexProjectClass::Is_Depth_Gradient_Enabled -- returns whether the depth gradient is enabl * * TexProjectClass::Enable_Depth_Gradient -- enable/disable depth gradient * * TexProjectClass::Init_Multiplicative -- Initialize this to be a multiplicative texture pr * * TexProjectClass::Is_Intensity_Zero -- check if we can eliminate this projector * * TexProjectClass::Init_Additive -- Set up the projector to be additive * * TexProjectClass::Set_Perspective_Projection -- Set up a perspective projection * * TexProjectClass::Set_Ortho_Projection -- Set up an orthographic projection * * TexProjectClass::Set_Texture -- Set the texture to be projected * * TexProjectClass::Get_Texture -- Returns the texture being projected * * TexProjectClass::Peek_Texture -- Returns the texture being projected * * TexProjectClass::Peek_Material_Pass -- Returns the material pass object * * TexProjectClass::Compute_Perspective_Projection -- Set up a perspective projection of an * * TexProjectClass::Compute_Perspective_Projection -- Set up a perspective projection of an * * TexProjectClass::Compute_Ortho_Projection -- Automatic Orthographic projection * * TexProjectClass::Compute_Ortho_Projection -- Automatic Orthographic projection * * TexProjectClass::Compute_Texture -- Generates texture by rendering an object * * TexProjectClass::Configure_Camera -- setup a camera to match this projector * * TexProjectClass::Pre_Render_Update -- Prepare the projector for rendering * * TexProjectClass::Update_WS_Bounding_Volume -- Recalculate the world-space bounding box * * TexProjectClass::Get_Surface -- Returns pointer to the texture surface * * TexProjectClass::Set_Render_Target -- Install a render target for this projector to use * * TexProjectClass::Needs_Render_Target -- returns wheter this projector needs a render targ * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "texproject.h" #include "vertmaterial.h" #include "shader.h" #include "texture.h" #include "rendobj.h" #include "rinfo.h" #include "camera.h" #include "matpass.h" #include "bwrender.h" #include "assetmgr.h" #include "dx8wrapper.h" // DEBUG DEBUG #include "mpu.h" #define DEBUG_SHADOW_RENDERING 0 //#define DEFAULT_TEXTURE_SIZE 64 const float INTENSITY_RATE_OF_CHANGE = 1.0f; // change in intensity per second /* ** ** Shadow mapping: from pre-projected view coordinates back to world space ** then to shadow space ** ** (1) Vshadow = PShadow * Mwrld-shadow * Vwrld ** ** (2) Vview = Mwrld-camera * Vwrld ** ** Using (2) to solve for Vwrld in terms of Vview ** -1 ** (3) Mwrld-camera * Vview = Vwrld ** ** Substituting (3) into (1) ** -1 ** (4) Vshadow = Pshadow * Mwrld-shadow * Mwrld-camera * Vview ** ** --------------------------------------------------------------------------------- ** ** Shadow mapping: from pre-projected view space to world space, to shadow space, ** then projecting. ** ** (1) Vshadow = Mwrld-shadow * Vwrld ** ** (2) Vview = Mwrld-camera * Vwrld ** ** solving (2) for Vwrld: ** -1 ** (3) Mwrld-camera * Vview = Vwrld ** ** substitute into (1) ** -1 ** (4) Vshadow = Mwrld-shadow * Mwrld-camera * Vview ** ** project shadow ** -1 ** (5) Vproj-shadow = Pshadow * (Mwrld-shadow * Mwrld-camera ) * Vview ** ** aaah! same thing :-) ** ** --------------------------------------------------------------------------------- ** ** Shadow mapping: from pre-projected view coordinates back to obj space ** then to shadow space ** ** (1) Vshadow = PShadow * Mwrld-shadow * Mobj-wrld * Vobj ** ** (2) Vview = Mwrld-camera * Mobj-wrld * Vobj ** -1 -1 ** (3) Mobj-wrld * Mwrld-camera * Vview = Vobj ** -1 -1 ** (4) Vshadow = PShadow * Mwrld-shadow * Mobj-wrld * Mobj-wrld * Mwrld-camera * Vview ** -1 ** (5) Vshadow = PShadow * Mwrld-shadow * Mvrld-camera * Vview ** ** ** Ideas: ** - Use Texture Projectors to implement spot lights and stained glass windows ** - Attenuate texture projections with distance from the projector ** - Should be able to handle lots of pre-calculated static texture projectors. They ** should cull well and we can pre-generate the textures. ** ** Ideas maybe used in conjunction with texture projections: ** - Light volumes: the problem is when the volume is cliped it looks funny, we can ** fill in the clipped face of a non-translucent object by rendering the backfaces ** - Use the backface-fill to make the camera slicing through the commando look like ** its *really* slicing through the commando :-) ** - The back-face-fill trick might be able to use el-cheapo screen mapping! ** */ /*********************************************************************************************** * TexProjectClass::TexProjectClass -- Constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ TexProjectClass::TexProjectClass(void) : Flags(DEFAULT_FLAGS), DesiredIntensity(1.0f), Intensity(1.0f), Attenuation(1.0f), MaterialPass(NULL), Mapper1(NULL), RenderTarget(NULL), HFov(90.0f), VFov(90.0f), XMin(-10.0f), XMax(10.0f), YMin(-10.0f), YMax(10.0f), ZNear(1.0f), ZFar(1000.0f) { // create a material pass class MaterialPass = NEW_REF(MaterialPassClass,()); MaterialPass->Set_Cull_Volume(&WorldBoundingVolume); // create a vertex material VertexMaterialClass * vmtl = NEW_REF(VertexMaterialClass,()); WWASSERT(vmtl != NULL); // Plug our parent's mapper into our vertex material // the mapper for stage1 will be allocated as needed vmtl->Set_Mapper(Mapper); MaterialPass->Set_Material(vmtl); vmtl->Release_Ref(); vmtl = NULL; // by default init our material pass to be multiplicative (shadow) Init_Multiplicative(); } /*********************************************************************************************** * TexProjectClass::~TexProjectClass -- Destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ TexProjectClass::~TexProjectClass(void) { REF_PTR_RELEASE(Mapper1); REF_PTR_RELEASE(MaterialPass); REF_PTR_RELEASE(RenderTarget); } /*********************************************************************************************** * TexProjectClass::Set_Texture_Size -- Set the size of texture to use * * * * This function stores the desired texture size in this TexProjectClass. Note that * * this size is only used if you have the TexProjectClass generate a texture. * * * * INPUT: * * size - dimension of the texture in pixels * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Texture_Size(int size) { WWASSERT(size > 0); WWASSERT(size <= 512); Flags &= ~SIZE_MASK; Flags |= (size << SIZE_SHIFT); } /*********************************************************************************************** * TexProjectClass::Get_Texture_Size -- Returns the stored texture size * * * * Returns the size stored in the flags variable. This can be different than the actual * * texture's size if you manually installed a texture. This size is only used when * * automatically generating a texture as is the case when creating shadows. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ int TexProjectClass::Get_Texture_Size(void) { return (Flags & SIZE_MASK) >> SIZE_SHIFT; } /*********************************************************************************************** * TexProjectClass::Set_Flag -- Turn specified flag on or off * * * * See the TexProjectClass header file for valid flags * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Flag(uint32 flag,bool onoff) { if (onoff) { Flags |= flag; } else { Flags &= ~flag; } } /*********************************************************************************************** * TexProjectClass::Get_Flag -- Get the current state of specified flag * * * * See the TexProjectClass header file for valid flags * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Get_Flag(uint32 flag) const { return (Flags & flag) == flag; } /*********************************************************************************************** * TexProjectClass::Set_Intensity -- Set the intensity of this projector * * * * The "intensity" is a value between 0 and 1. At 0, the projector will have no effect * * At 1, the projector will be at its highest strength. The intensity will automatically * * smoothly interpolate towards the value specified unless you use the immediate option. * * * * INPUT: * * intensity - desired intensity, 0 <= intensity <= 1 * * immediate - should the intensity be immediately set to the desired value? (default = false) * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Intensity(float intensity,bool immediate) { WWASSERT(intensity <= 1.0f); WWASSERT(intensity >= 0.0f); DesiredIntensity = intensity; if (immediate) { Intensity = DesiredIntensity; } } /*********************************************************************************************** * TexProjectClass::Get_Intensity -- returns the current "desired" intensity * * * * This will return the last value that was sent into this object through Set_Intensity. * * Note however that the actual intensity used in rendering may not have arrived at * * that value yet. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ float TexProjectClass::Get_Intensity(void) { return DesiredIntensity; } /*********************************************************************************************** * TexProjectClass::Is_Intensity_Zero -- check if we can eliminate this projector * * * * Only returns true if the current intensity is zero AND the target intensity is zero * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Is_Intensity_Zero(void) { return ((Intensity == 0.0f) && (DesiredIntensity == 0.0f)); } /*********************************************************************************************** * TexProjectClass::Set_Attenuation -- Set the attenuation factor * * * * Attenuation scales the intensity. I use attenuation to make shadows fade away as they * * get farther away from the light source or from the viewer. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Attenuation(float attenuation) { WWASSERT(attenuation >= 0.0f); WWASSERT(attenuation <= 1.0f); Attenuation = attenuation; } /*********************************************************************************************** * TexProjectClass::Get_Attenuation -- Returns the attenuation value * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ float TexProjectClass::Get_Attenuation(void) { return Attenuation; } /*********************************************************************************************** * TexProjectClass::Enable_Attenuation -- Set the state of the ATTENUATE flag * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Enable_Attenuation(bool onoff) { Set_Flag(ATTENUATE,onoff); } /*********************************************************************************************** * TexProjectClass::Is_Attenuation_Enabled -- Get the state of the ATTENUATE flag * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Is_Attenuation_Enabled(void) { return Get_Flag(ATTENUATE); } /*********************************************************************************************** * TexProjectClass::Enable_Depth_Gradient -- enable/disable depth gradient * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 2/25/2000 gth : Created. * *=============================================================================================*/ void TexProjectClass::Enable_Depth_Gradient(bool onoff) { Set_Flag(USE_DEPTH_GRADIENT,onoff); // re-setup the shader settings if (Get_Flag(ADDITIVE)) { Init_Additive(); } else { Init_Multiplicative(); } } /*********************************************************************************************** * TexProjectClass::Is_Depth_Gradient_Enabled -- returns whether the depth gradient is enabled * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 2/25/2000 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Is_Depth_Gradient_Enabled(bool onoff) { return Get_Flag(USE_DEPTH_GRADIENT); } /*********************************************************************************************** * TexProjectClass::Init_Multiplicative -- Initialize this to be a multiplicative texture proj * * * * Set up the internal materials so that the texture is multiplied with whatever it is * * projected onto. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/4/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Init_Multiplicative(void) { Set_Flag(ADDITIVE,false); /* ** Set up the shader */ static ShaderClass mult_shader( SHADE_CNST( ShaderClass::PASS_LEQUAL, //depth_compare, ShaderClass::DEPTH_WRITE_DISABLE, //depth_mask, ShaderClass::COLOR_WRITE_ENABLE, //color_mask, ShaderClass::SRCBLEND_ZERO, //src_blend, ShaderClass::DSTBLEND_SRC_COLOR, //dst_blend, ShaderClass::FOG_DISABLE, //fog, ShaderClass::GRADIENT_ADD, //pri_grad, ShaderClass::SECONDARY_GRADIENT_DISABLE, //sec_grad, ShaderClass::TEXTURING_ENABLE, //texture, ShaderClass::ALPHATEST_DISABLE, //alpha_test, ShaderClass::CULL_MODE_ENABLE, //cull mode 0, //post_det_color, 0) ); //post_det_alpha if (WW3DAssetManager::Get_Instance()->Get_Activate_Fog_On_Load()) { mult_shader.Enable_Fog ("TexProjectClass"); } if (Get_Flag(USE_DEPTH_GRADIENT)) { /* ** enable multi-texturing */ mult_shader.Set_Post_Detail_Color_Func(ShaderClass::DETAILCOLOR_ADD); /* ** plug the gradient texture into the second stage */ TextureClass * grad_tex = WW3DAssetManager::Get_Instance()->Get_Texture("MultProjectorGradient.tga"); if (grad_tex) { grad_tex->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); grad_tex->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); MaterialPass->Set_Texture(grad_tex,1); grad_tex->Release_Ref(); } else { WWDEBUG_SAY(("Could not find texture: MultProjectorGradient.tga!\n")); } } else { /* ** disable multi-texturing */ mult_shader.Set_Post_Detail_Color_Func(ShaderClass::DETAILCOLOR_DISABLE); /* ** remove the texture from the second stage */ MaterialPass->Set_Texture(NULL,1); } #if (DEBUG_SHADOW_RENDERING) // invert the shader so we can see what polygons it is hitting mult_shader.Set_Dst_Blend_Func(ShaderClass::DSTBLEND_ONE); mult_shader.Set_Src_Blend_Func(ShaderClass::SRCBLEND_ONE); #endif MaterialPass->Set_Shader(mult_shader); /* ** Set up the Vertex Material parameters */ VertexMaterialClass * vmtl = MaterialPass->Peek_Material(); vmtl->Set_Ambient(0,0,0); vmtl->Set_Diffuse(0,0,0); vmtl->Set_Specular(0,0,0); vmtl->Set_Emissive(0.0f,0.0f,0.0f); vmtl->Set_Opacity(1.0f); vmtl->Set_Lighting(true); // I need the emissive value to scale the intensity of the shadow /* ** Set up some mapper settings related to depth gradient */ if (Get_Flag(USE_DEPTH_GRADIENT)) { if (Mapper1 == NULL) { Mapper1 = NEW_REF(MatrixMapperClass,(1)); } Mapper1->Set_Type(MatrixMapperClass::DEPTH_GRADIENT); vmtl->Set_Mapper(Mapper1,1); } else { vmtl->Set_Mapper(NULL,1); } } /*********************************************************************************************** * TexProjectClass::Init_Additive -- Set up the projector to be additive * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Init_Additive(void) { Set_Flag(ADDITIVE,true); /* ** Set up the shader */ static ShaderClass add_shader( SHADE_CNST( ShaderClass::PASS_LEQUAL, //depth_compare, ShaderClass::DEPTH_WRITE_DISABLE, //depth_mask, ShaderClass::COLOR_WRITE_ENABLE, //color_mask, ShaderClass::SRCBLEND_ONE, //src_blend, ShaderClass::DSTBLEND_ONE, //dst_blend, ShaderClass::FOG_DISABLE, //fog, ShaderClass::GRADIENT_MODULATE, //pri_grad, ShaderClass::SECONDARY_GRADIENT_DISABLE, //sec_grad, ShaderClass::TEXTURING_ENABLE, //texture, ShaderClass::ALPHATEST_DISABLE, //alpha_test, ShaderClass::CULL_MODE_ENABLE, //cullmode, ShaderClass::DETAILCOLOR_DISABLE, //post_det_color, ShaderClass::DETAILALPHA_DISABLE) ); //post_det_alpha if (WW3DAssetManager::Get_Instance()->Get_Activate_Fog_On_Load()) { add_shader.Enable_Fog ("TexProjectClass"); } /* ** Additive projectors always use the normal gradient so they need multi-texturing */ add_shader.Set_Post_Detail_Color_Func(ShaderClass::DETAILCOLOR_SCALE); /* ** plug in the gradient texture */ TextureClass * grad_tex = WW3DAssetManager::Get_Instance()->Get_Texture("AddProjectorGradient.tga"); if (grad_tex) { grad_tex->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); grad_tex->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); MaterialPass->Set_Texture(grad_tex,1); grad_tex->Release_Ref(); } else { WWDEBUG_SAY(("Could not find texture: AddProjectorGradient.tga!\n")); } #if (DEBUG_SHADOW_RENDERING) // invert the shader so we can see what polygons it is hitting add_shader.Set_Dst_Blend_Func(ShaderClass::DSTBLEND_SRC_COLOR); add_shader.Set_Src_Blend_Func(ShaderClass::SRCBLEND_ZERO); #endif MaterialPass->Set_Shader(add_shader); /* ** Set up the Vertex Material parameters */ VertexMaterialClass * vmtl = MaterialPass->Peek_Material(); vmtl->Set_Ambient(0,0,0); vmtl->Set_Diffuse(0,0,0); vmtl->Set_Specular(0,0,0); vmtl->Set_Emissive(1,1,1); vmtl->Set_Opacity(1.0f); vmtl->Set_Lighting(true); //need emissive to scale the intensity of the projector /* ** Set up some mapper settings related to depth gradient ** Additive texture projections always use the normal gradient */ if (Mapper1 == NULL) { Mapper1 = NEW_REF(MatrixMapperClass,(1)); } Mapper1->Set_Type(MatrixMapperClass::NORMAL_GRADIENT); vmtl->Set_Mapper(Mapper1,1); } /*********************************************************************************************** * TexProjectClass::Set_Texture -- Set the texture to be projected * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Texture(TextureClass * texture) { if (texture != NULL) { texture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); texture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP); MaterialPass->Set_Texture(texture); } } /*********************************************************************************************** * TexProjectClass::Get_Texture -- Returns the texture being projected * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * the pointer is ref-counted! make sure to keep track of your references * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ TextureClass * TexProjectClass::Get_Texture(void) const { return MaterialPass->Get_Texture(); } /*********************************************************************************************** * TexProjectClass::Peek_Texture -- Returns the texture being projected * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ TextureClass * TexProjectClass::Peek_Texture(void) const { return MaterialPass->Peek_Texture(); } /*********************************************************************************************** * TexProjectClass::Peek_Material_Pass -- Returns the material pass object * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ MaterialPassClass * TexProjectClass::Peek_Material_Pass(void) { return MaterialPass; } /*********************************************************************************************** * TexProjectClass::Set_Perspective_Projection -- set up a perspective projection * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/27/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Perspective_Projection(float hfov,float vfov,float znear,float zfar) { HFov = hfov; VFov = vfov; ZNear = znear; ZFar = zfar; ProjectorClass::Set_Perspective_Projection(hfov,vfov,znear,zfar); Set_Flag(PERSPECTIVE,true); } /*********************************************************************************************** * TexProjectClass::Set_Ortho_Projection -- set up an orthographic projection * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/27/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Ortho_Projection(float xmin,float xmax,float ymin,float ymax,float znear,float zfar) { XMin = xmin; XMax = xmax; YMin = ymin; YMax = ymax; ZNear = znear; ZFar = zfar; ProjectorClass::Set_Ortho_Projection(xmin,xmax,ymin,ymax,znear,zfar); Set_Flag(PERSPECTIVE,false); } /*********************************************************************************************** * TexProjectClass::Compute_Perspective_Projection -- Set up a perspective projection of an ob * * * * This function automates the process of generating the perspective parameters needed to * * tightly bound the projection of an object. * * * * INPUT: * * model - object which we are created a projection of * * lightpos - positional light source * * znear - distance to near clipping plane for the projection (if -1.0, will be generated) * * zfar - distance to far clipping plane for the projection (if -1.0, will be generated) * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Compute_Perspective_Projection ( RenderObjClass * model, const Vector3 & lightpos, float znear, float zfar ) { if (model == NULL) { WWDEBUG_SAY(("Attempting to generate projection for a NULL model\r\n")); return false; } AABoxClass box; model->Get_Obj_Space_Bounding_Box(box); const Matrix3D & tm = model->Get_Transform(); return Compute_Perspective_Projection(box,tm,lightpos,znear,zfar); } /*********************************************************************************************** * TexProjectClass::Compute_Perspective_Projection -- Set up a perspective projection of an ob * * * * This function automates the process of generating the perspective parameters needed to * * tightly bound the projection of an object. * * * * INPUT: * * obj_box - object space bounding box of the object we are projecting * * tm - transform of the object we are projecting * * lightpos - positional light source * * user_znear - distance to near clipping plane for the projection (if -1.0, will be generated)* * user_zfar - distance to far clipping plane for the projection (if -1.0, will be generated) * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Compute_Perspective_Projection ( const AABoxClass & obj_box, const Matrix3D & tm, const Vector3 & lightpos, float user_znear, float user_zfar ) { /* ** Compute the center of the box in world-space */ Vector3 wrld_center; Matrix3D::Transform_Vector(tm,obj_box.Center,&wrld_center); /* ** Create a camera transform looking at the object. ** Set this as our transform later in this routine. */ Matrix3D texture_tm,texture_tm_inv; texture_tm.Look_At(lightpos,wrld_center,0.0f); texture_tm.Get_Orthogonal_Inverse(texture_tm_inv); /* ** Calculate the axis-aligned bounding box of the model in the camera's coordinate system. */ AABoxClass box = obj_box; Matrix3D obj_to_world = tm; Matrix3D obj_to_texture; Matrix3D::Multiply(texture_tm_inv,obj_to_world,&obj_to_texture); box.Transform(obj_to_texture); /* ** If the box is behind the viewpoint or the viewpoint is inside the box ** our FOV will be > 180 degrees. Have to give up */ if ((box.Center.Z > 0.0f) || (box.Extent.Z > WWMath::Fabs(box.Center.Z))) { return false; } /* ** Compute the frustum parameters. Remember that our z coordinates are negative but the ** projection code needs positive z distances. */ float znear = -box.Center.Z; //-(box.Center.Z + obj_box.Extent.Quick_Length()); float zfar = -(box.Center.Z - obj_box.Extent.Quick_Length()) * 2.0f; if (user_znear != -1.0f) { znear = box.Center.Z + user_znear; } if (user_zfar != -1.0f) { zfar = box.Center.Z + user_zfar; } float tan_hfov2 = WWMath::Fabs(box.Extent.X / (box.Center.Z + box.Extent.Z)); float tan_vfov2 = WWMath::Fabs(box.Extent.Y / (box.Center.Z + box.Extent.Z)); float hfov = 2.0f * WWMath::Atan(tan_hfov2); float vfov = 2.0f * WWMath::Atan(tan_vfov2); /* ** Plug in the results. */ Set_Perspective_Projection(hfov,vfov,znear,zfar); Set_Transform(texture_tm); return true; } /*********************************************************************************************** * TexProjectClass::Compute_Ortho_Projection -- Automatic Orthographic projection * * * * Generates the orthographic projection parameters to tightly bound an object * * * * INPUT: * * model - object which we are created a projection of * * lightdir - directional light source * * znear - distance to near clipping plane for the projection (if -1.0, will be generated) * * zfar - distance to far clipping plane for the projection (if -1.0, will be generated) * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Compute_Ortho_Projection ( RenderObjClass * model, const Vector3 & lightdir, float znear, float zfar ) { if (model == NULL) { WWDEBUG_SAY(("Attempting to generate projection for a NULL model\r\n")); return false; } AABoxClass box; model->Get_Obj_Space_Bounding_Box(box); const Matrix3D & tm = model->Get_Transform(); return Compute_Ortho_Projection(box,tm,lightdir,znear,zfar); } /*********************************************************************************************** * TexProjectClass::Compute_Ortho_Projection -- Automatic Orthographic projection * * * * Generates the orthographic projection parameters to tightly bound an object * * * * INPUT: * * obj_box - object space bounding box of the object we are projecting * * tm - transform of the object we are projecting * * lightdir - directional light * * user_znear - distance to near clipping plane for the projection (if -1.0, will be generated)* * user_zfar - distance to far clipping plane for the projection (if -1.0, will be generated) * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Compute_Ortho_Projection ( const AABoxClass & obj_box, const Matrix3D & tm, const Vector3 & lightdir, float user_znear, float user_zfar ) { /* ** Compute the center of the box in world-space */ AABoxClass wrldbox = obj_box; wrldbox.Transform(tm); /* ** Create a camera transform looking at the object. ** Set this as our transform later in this routine. */ Vector3 camera_target = wrldbox.Center; Vector3 camera_position = camera_target - 2.0f * wrldbox.Extent.Length() * lightdir; Matrix3D texture_tm,texture_tm_inv; texture_tm.Look_At(camera_position,camera_target,0.0f); texture_tm.Get_Orthogonal_Inverse(texture_tm_inv); /* ** Calculate the axis-aligned bounding box of the model in the camera's coordinate system. */ AABoxClass box = obj_box; Matrix3D obj_to_world = tm; Matrix3D obj_to_texture; Matrix3D::Multiply(texture_tm_inv,obj_to_world,&obj_to_texture); box.Transform(obj_to_texture); /* ** Expand the box to help with bounding box errors */ box.Extent *= 1.0f; /* ** Compute the frustum parameters. Note that znear and zfar are supposed to ** be positive distances for the projection code. */ float znear = -box.Center.Z; //-(box.Center.Z + obj_box.Extent.Quick_Length()); float zfar = -(box.Center.Z - obj_box.Extent.Quick_Length()) * 2.0f; if (user_znear != -1.0f) { znear = -box.Center.Z + user_znear; } if (user_zfar != -1.0f) { zfar = -box.Center.Z + user_zfar; } /* ** All done! */ Set_Ortho_Projection( box.Center.X - box.Extent.X, box.Center.X + box.Extent.X, box.Center.Y - box.Extent.Y, box.Center.Y + box.Extent.Y, znear, zfar ); Set_Transform(texture_tm); return true; } /*********************************************************************************************** * TexProjectClass::Compute_Texture -- Generates texture by rendering an object * * * * INPUT: * * model - pointer to the render object to generate a shadow texture for * * context - shadow render context which has been initialized to resolution * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Compute_Texture(RenderObjClass * model,SpecialRenderInfoClass * context) { if ((model == NULL) || (context == NULL)) { return false; } /* ** Render to texture */ TextureClass * rtarget = Peek_Render_Target(); if (rtarget != NULL) { /* ** Set the render target */ DX8Wrapper::Set_Render_Target(rtarget); /* ** Set up the camera */ Configure_Camera(context->Camera); /* ** Render the object */ Vector3 color(0.0f,0.0f,0.0f); if (Get_Flag(ADDITIVE) == false) { color.Set(1.0f,1.0f,1.0f); } WW3D::Begin_Render(true,false,color); // false to zclear as we don't have z-buffer WW3D::Render(*model,*context); WW3D::End_Render(false); DX8Wrapper::Set_Render_Target((IDirect3DSurface8 *)NULL); } #if 0 /* ** Render the object with the BW Renderer into our color surface */ BWRenderClass bwr((unsigned char*)shadow_surface->getDataPtr(),tex_size); bwr.Fill(0xff); context->BWRenderer = &bwr; model->Special_Render(*context); context->BWRenderer = NULL; #endif return true; } /*********************************************************************************************** * TexProjectClass::Needs_Render_Target -- returns wheter this projector needs a render target * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/17/2001 gth : Created. * *=============================================================================================*/ bool TexProjectClass::Needs_Render_Target(void) { return Get_Flag(TEXTURE_DIRTY); } /*********************************************************************************************** * TexProjectClass::Set_Render_Target -- Install a render target for this projector to use * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/16/2001 gth : Created. * *=============================================================================================*/ void TexProjectClass::Set_Render_Target(TextureClass * render_target) { REF_PTR_SET(RenderTarget,render_target); Set_Texture(RenderTarget); } /*********************************************************************************************** * TexProjectClass::Peek_Render_Target -- Returns pointer to the render target if we have one * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/5/2001 gth : Created. * *=============================================================================================*/ TextureClass * TexProjectClass::Peek_Render_Target(void) { return RenderTarget; } /*********************************************************************************************** * TexProjectClass::Configure_Camera -- set up a camera to match this projector * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Configure_Camera(CameraClass & camera) { camera.Set_Transform(Transform); camera.Set_Clip_Planes(0.01f,ZFar); if (Get_Flag(PERSPECTIVE)) { camera.Set_Projection_Type(CameraClass::PERSPECTIVE); camera.Set_View_Plane(HFov,VFov); } else { camera.Set_Projection_Type(CameraClass::ORTHO); camera.Set_View_Plane(Vector2(XMin,YMin),Vector2(XMax,YMax)); } // Set one-pixel borders to the texture to avoid "flooding" shadows... float size=Get_Texture_Size(); float inv_size=1.0f/size; Vector2 vmin(1.0f*inv_size,1.0f*inv_size); Vector2 vmax((size-1.0f)*inv_size,(size-1.0f)*inv_size); camera.Set_Viewport(vmin,vmax); } /*********************************************************************************************** * TexProjectClass::Pre_Render_Update -- Prepare the projector for rendering * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Pre_Render_Update(const Matrix3D & camera) { /* ** Mview-texture = PShadow * Mwrld-texture * Mcamera-vrld */ Matrix3D world_to_texture; Matrix3D tmp; Matrix4 view_to_texture; Transform.Get_Orthogonal_Inverse(world_to_texture); Matrix3D::Multiply(world_to_texture,camera,&tmp); Matrix4::Multiply(Projection,tmp,&view_to_texture); /* ** update the current intensity by iterating it towards the desired intensity */ float frame_time = (float)WW3D::Get_Frame_Time() / 1000.0f; float intensity_delta = DesiredIntensity - Intensity; float max_intensity_delta = INTENSITY_RATE_OF_CHANGE * frame_time; if (intensity_delta > max_intensity_delta) { Intensity += max_intensity_delta; } else if (intensity_delta < -max_intensity_delta) { Intensity -= max_intensity_delta; } else { Intensity = DesiredIntensity; } float actual_intensity = Intensity * Attenuation; /* ** install the current intensity */ VertexMaterialClass * vmat = MaterialPass->Peek_Material(); if (Get_Flag(ADDITIVE)) { vmat->Set_Emissive(actual_intensity,actual_intensity,actual_intensity); } else { vmat->Set_Emissive(1.0f - actual_intensity,1.0f - actual_intensity,1.0f - actual_intensity); } /* ** update the mappers */ if (Get_Flag(PERSPECTIVE)) { Mapper->Set_Type(MatrixMapperClass::PERSPECTIVE_PROJECTION); } else { Mapper->Set_Type(MatrixMapperClass::ORTHO_PROJECTION); } if (Get_Texture_Size() == 0) { // SurfaceClass::SurfaceDescription surface_desc; // MaterialPass->Peek_Texture()->Get_Level_Description(surface_desc); Set_Texture_Size(MaterialPass->Peek_Texture()->Get_Width()); WWASSERT(Get_Texture_Size() != 0); } Mapper->Set_Texture_Transform(view_to_texture,Get_Texture_Size()); if (Mapper1) { Mapper1->Set_Texture_Transform(view_to_texture,Get_Texture_Size()); } } /*********************************************************************************************** * TexProjectClass::Update_WS_Bounding_Volume -- Recalculate the world-space bounding box * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 1/11/00 gth : Created. * *=============================================================================================*/ void TexProjectClass::Update_WS_Bounding_Volume(void) { ProjectorClass::Update_WS_Bounding_Volume(); /* ** Tell our culling system that we've changed */ Vector3 extent; WorldBoundingVolume.Compute_Axis_Aligned_Extent(&extent); Set_Cull_Box(AABoxClass(WorldBoundingVolume.Center,extent)); }