This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/texproject.cpp

1347 lines
70 KiB
C++

/*
** 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 : 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 <TextureSize> 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));
}