mirror of
https://github.com/thug1src/thug.git
synced 2025-01-22 05:43:47 +00:00
566 lines
21 KiB
C++
566 lines
21 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// p_NxImposter.cpp
|
||
|
|
||
|
#include "sys/timer.h"
|
||
|
#include "gfx/xbox/p_NxGeom.h"
|
||
|
#include "gfx/xbox/p_NxImposter.h"
|
||
|
#include "gfx/xbox/nx/nx_init.h"
|
||
|
#include "gfx/xbox/nx/render.h"
|
||
|
|
||
|
namespace Nx
|
||
|
{
|
||
|
|
||
|
|
||
|
const int XBOX_IMPOSTER_UPDATE_LIMIT = 30;
|
||
|
const int XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE = 128;
|
||
|
const int XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE = 128;
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
static void frustum_project_box( Mth::CBBox &bbox, XGMATRIX *p_view_matrix, Mth::Vector *p_max_x, Mth::Vector *p_max_y, Mth::Vector *p_mid )
|
||
|
{
|
||
|
float max_projected_xx, max_projected_xz; // The camera space position of the point with the greatest x axis value when projected to z = mid_z.
|
||
|
float max_projected_x_mid_z; // The projected x axis value when this point is projected to z = mid_z;
|
||
|
|
||
|
float max_projected_yy, max_projected_yz; // The camera space position of the point furthest along the y axis when projected to z = mid_z.
|
||
|
float max_projected_y_mid_z; // The projected y axis value when this point is projected to z = mid_z;
|
||
|
|
||
|
float min_x = bbox.GetMin().GetX();
|
||
|
float min_y = bbox.GetMin().GetY();
|
||
|
float min_z = bbox.GetMin().GetZ();
|
||
|
float max_x = bbox.GetMax().GetX();
|
||
|
float max_y = bbox.GetMax().GetY();
|
||
|
float max_z = bbox.GetMax().GetZ();
|
||
|
|
||
|
// Project the midpoint of the box, since this is the point through which the imposter polygon will pass.
|
||
|
XGVECTOR3 mid_in( min_x + ( 0.5f * ( max_x - min_x )), min_y + ( 0.5f * ( max_y - min_y )), min_z + ( 0.5f * ( max_z - min_z )));
|
||
|
XGVECTOR4 mid_out;
|
||
|
XGVec3Transform( &mid_out, &mid_in, p_view_matrix );
|
||
|
|
||
|
for( uint32 v = 0; v < 8; ++v )
|
||
|
{
|
||
|
XGVECTOR3 in;
|
||
|
XGVECTOR4 out;
|
||
|
|
||
|
in.x = ( v & 0x04 ) ? max_x : min_x;
|
||
|
in.y = ( v & 0x02 ) ? max_y : min_y;
|
||
|
in.z = ( v & 0x01 ) ? max_z : min_z;
|
||
|
|
||
|
XGVec3Transform( &out, &in, p_view_matrix );
|
||
|
|
||
|
out.x = fabsf( out.x );
|
||
|
out.y = fabsf( out.y );
|
||
|
|
||
|
float projected_x_mid_z = out.x * ( mid_out.z / out.z );
|
||
|
float projected_y_mid_z = out.y * ( mid_out.z / out.z );
|
||
|
|
||
|
if( v == 0 )
|
||
|
{
|
||
|
max_projected_x_mid_z = projected_x_mid_z;
|
||
|
max_projected_xx = out.x;
|
||
|
max_projected_xz = out.z;
|
||
|
|
||
|
max_projected_y_mid_z = projected_y_mid_z;
|
||
|
max_projected_yy = out.y;
|
||
|
max_projected_yz = out.z;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( projected_x_mid_z > max_projected_x_mid_z )
|
||
|
{
|
||
|
max_projected_xx = out.x;
|
||
|
max_projected_xz = out.z;
|
||
|
max_projected_x_mid_z = projected_x_mid_z;
|
||
|
}
|
||
|
|
||
|
if( projected_y_mid_z > max_projected_y_mid_z )
|
||
|
{
|
||
|
max_projected_yy = out.y;
|
||
|
max_projected_yz = out.z;
|
||
|
max_projected_y_mid_z = projected_y_mid_z;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p_max_x->Set( max_projected_xx, 0.0f, max_projected_xz, 0.0f );
|
||
|
p_max_y->Set( 0.0f, max_projected_yy, max_projected_yz, 0.0f );
|
||
|
p_mid->Set( mid_out.x, mid_out.y, mid_out.z );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
CImposterGroup* CImposterManager::plat_create_imposter_group( void )
|
||
|
{
|
||
|
return new CXboxImposterGroup;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CImposterManager::plat_pre_render_imposters( void )
|
||
|
{
|
||
|
// Set up the common material attributes for the imposters.
|
||
|
// NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_BLEND );
|
||
|
NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_ONE_INV_SRC_ALPHA );
|
||
|
|
||
|
NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
|
||
|
NxXbox::set_pixel_shader( PixelShader2 );
|
||
|
|
||
|
NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
|
||
|
NxXbox::set_render_state( RS_ALPHACUTOFF, 1 );
|
||
|
NxXbox::set_render_state( RS_ZWRITEENABLE, 1 );
|
||
|
NxXbox::set_render_state( RS_ZTESTENABLE, 1 );
|
||
|
NxXbox::set_render_state( RS_CULLMODE, D3DCULL_NONE );
|
||
|
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
|
||
|
D3DDevice_SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CImposterManager::plat_post_render_imposters( void )
|
||
|
{
|
||
|
// Clean up the common material attributes for the imposters.
|
||
|
NxXbox::set_texture( 0, NULL );
|
||
|
NxXbox::set_render_state( RS_CULLMODE, D3DCULL_CW );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||
|
// Private classes
|
||
|
//
|
||
|
// Here's a machine specific implementation of the CImposterGroup
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
CXboxImposterGroup::CXboxImposterGroup()
|
||
|
{
|
||
|
mp_texture = NULL;
|
||
|
m_update_count = Mth::Rnd( XBOX_IMPOSTER_UPDATE_LIMIT );
|
||
|
mp_removed_textures_list = new Lst::Head <sRemovedTextureDetails>;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
CXboxImposterGroup::~CXboxImposterGroup()
|
||
|
{
|
||
|
if( mp_texture )
|
||
|
{
|
||
|
mp_texture->Release();
|
||
|
mp_texture = NULL;
|
||
|
}
|
||
|
|
||
|
// Remove all nodes from the removed textures table.
|
||
|
Lst::Node<sRemovedTextureDetails> *p_node, *p_next;
|
||
|
for( p_node = mp_removed_textures_list->GetNext(); p_node; p_node = p_next )
|
||
|
{
|
||
|
p_next = p_node->GetNext();
|
||
|
|
||
|
sRemovedTextureDetails *p_details = p_node->GetData();
|
||
|
IDirect3DTexture8 *p_texture = p_details->mp_texture;
|
||
|
p_texture->Release();
|
||
|
|
||
|
delete p_details;
|
||
|
delete p_node;
|
||
|
}
|
||
|
|
||
|
// Remove the table itself.
|
||
|
delete mp_removed_textures_list;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CXboxImposterGroup::process_removed_textures( void )
|
||
|
{
|
||
|
Lst::Node<sRemovedTextureDetails> *p_node, *p_next;
|
||
|
for( p_node = mp_removed_textures_list->GetNext(); p_node; p_node = p_next )
|
||
|
{
|
||
|
p_next = p_node->GetNext();
|
||
|
|
||
|
sRemovedTextureDetails *p_details = p_node->GetData();
|
||
|
int time = p_details->m_time_removed;
|
||
|
if((( NxXbox::EngineGlobals.render_start_time - time ) > 250 ) || ( time > NxXbox::EngineGlobals.render_start_time ))
|
||
|
{
|
||
|
IDirect3DTexture8* p_texture = p_details->mp_texture;
|
||
|
p_texture->Release();
|
||
|
|
||
|
delete p_details;
|
||
|
delete p_node;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CXboxImposterGroup::plat_process( void )
|
||
|
{
|
||
|
process_removed_textures();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CXboxImposterGroup::plat_create_imposter_polygon( void )
|
||
|
{
|
||
|
Dbg_Assert( !m_imposter_polygon_exists );
|
||
|
|
||
|
// Generate a camera matrix that will point the camera directly at the midpoint of the bounding box.
|
||
|
XGMATRIX cam_mat;
|
||
|
XGVECTOR3 look_at( m_composite_bbox_mid[X], m_composite_bbox_mid[Y], m_composite_bbox_mid[Z] );
|
||
|
XGMatrixLookAtRH( &cam_mat, &NxXbox::EngineGlobals.cam_position, &look_at, &NxXbox::EngineGlobals.cam_up );
|
||
|
|
||
|
// Using this camera matrix, project all eight corners of the bounding box, in order to determine the best size for the texture.
|
||
|
Mth::Vector proj_max_x, proj_max_y, proj_mid;
|
||
|
frustum_project_box( m_composite_bbox, &cam_mat, &proj_max_x, &proj_max_y, &proj_mid );
|
||
|
|
||
|
// Now project the minimum and maximum x and y values onto the projection plane - the plane through the midpoint of the box.
|
||
|
float max_projected_x = proj_max_x[X] * ( proj_mid[Z] / proj_max_x[Z] );
|
||
|
float max_projected_y = proj_max_y[Y] * ( proj_mid[Z] / proj_max_y[Z] );
|
||
|
|
||
|
// Calculate the maximum width and height at the near plane.
|
||
|
float wnp = 2.0f * proj_max_x[X] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_x[Z] ));
|
||
|
float hnp = 2.0f * proj_max_y[Y] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_y[Z] ));
|
||
|
|
||
|
m_tex_width = 16 + Ftoi_ASM(( 640.0f * wnp ) / NxXbox::EngineGlobals.near_plane_width );
|
||
|
m_tex_height = 16 + Ftoi_ASM(( 480.0f * hnp ) / NxXbox::EngineGlobals.near_plane_height );
|
||
|
|
||
|
// Round texture to the nearest 16 pixel limit.
|
||
|
m_tex_width = (( m_tex_width + 0x0F ) & ~0x0F );
|
||
|
m_tex_height = (( m_tex_height + 0x0F ) & ~0x0F );
|
||
|
|
||
|
// Clamp texture to maximum allowed size.
|
||
|
m_tex_width = ( m_tex_width > XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE : m_tex_width;
|
||
|
m_tex_height = ( m_tex_height > XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE : m_tex_height;
|
||
|
|
||
|
// Calculate the corresponding projection matrix.
|
||
|
XGMATRIX proj_mat;
|
||
|
XGMatrixPerspectiveRH( &proj_mat, wnp, hnp, NxXbox::EngineGlobals.near_plane, NxXbox::EngineGlobals.far_plane );
|
||
|
|
||
|
// Set the calculated view and projection matrices.
|
||
|
D3DDevice_SetTransform( D3DTS_VIEW, &cam_mat );
|
||
|
D3DDevice_SetTransform( D3DTS_PROJECTION, &proj_mat );
|
||
|
|
||
|
// Create a render target texture into which the object will be drawn.
|
||
|
HRESULT hr;
|
||
|
if( mp_texture == NULL )
|
||
|
{
|
||
|
hr = D3DDevice_CreateTexture( m_tex_width, m_tex_height, 1, 0, D3DFMT_LIN_A8R8G8B8, 0, &mp_texture );
|
||
|
Dbg_Assert( hr == D3D_OK );
|
||
|
}
|
||
|
|
||
|
// Create a corresponding depth texture (we need this only for the render - then it can be removed).
|
||
|
IDirect3DTexture8* p_depth_buffer;
|
||
|
hr = D3DDevice_CreateTexture( m_tex_width, m_tex_height, 1, 0, D3DFMT_LIN_D24S8, 0, &p_depth_buffer );
|
||
|
Dbg_Assert( hr == D3D_OK );
|
||
|
|
||
|
// This call will increase the reference count of the IDirect3DTexture8 object.
|
||
|
LPDIRECT3DSURFACE8 p_surface, p_depth_surface;
|
||
|
mp_texture->GetSurfaceLevel( 0, &p_surface );
|
||
|
p_depth_buffer->GetSurfaceLevel( 0, &p_depth_surface );
|
||
|
|
||
|
// This call will increase the reference count of the IDirect3DSurface8 object.
|
||
|
D3DDevice_SetRenderTarget( p_surface, p_depth_surface );
|
||
|
|
||
|
// Clear the render target.
|
||
|
D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0 );
|
||
|
// D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x80800000, 1.0f, 0 );
|
||
|
|
||
|
// The imposter polygon has now been created.
|
||
|
m_imposter_polygon_exists = true;
|
||
|
|
||
|
// Set the camera position at the time of creation.
|
||
|
m_cam_pos.Set( NxXbox::EngineGlobals.cam_position.x, NxXbox::EngineGlobals.cam_position.y, NxXbox::EngineGlobals.cam_position.z );
|
||
|
|
||
|
// Build a list of meshes, so we can sort them dynamically into draw order.
|
||
|
NxXbox::sMesh* mesh_list[256];
|
||
|
uint32 num_meshes = 0;
|
||
|
|
||
|
Lst::Node<Nx::CGeom> *node, *next;
|
||
|
for( node = mp_geom_list->GetNext(); node; node = next )
|
||
|
{
|
||
|
next = node->GetNext();
|
||
|
Nx::CXboxGeom *p_xbox_geom = static_cast<Nx::CXboxGeom*>( node->GetData());
|
||
|
|
||
|
for( uint32 m = 0; m < p_xbox_geom->m_num_mesh; ++m )
|
||
|
{
|
||
|
Dbg_Assert( num_meshes < 256 );
|
||
|
NxXbox::sMesh* p_mesh = p_xbox_geom->m_mesh_array[m];
|
||
|
mesh_list[num_meshes++] = p_mesh;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( num_meshes > 0 )
|
||
|
{
|
||
|
// Sort the array of pointers into draw order.
|
||
|
qsort( mesh_list, num_meshes, sizeof( NxXbox::sMesh* ), NxXbox::sort_by_material_draw_order );
|
||
|
}
|
||
|
|
||
|
// Render each mesh into the render target.
|
||
|
NxXbox::EngineGlobals.blend_mode_override = NxXbox::vBLEND_MODE_ONE_INV_SRC_ALPHA;
|
||
|
NxXbox::sMaterial* p_material = NULL;
|
||
|
for( uint32 m = 0; m < num_meshes; ++m )
|
||
|
{
|
||
|
NxXbox::sMesh* p_mesh = mesh_list[m];
|
||
|
if( p_mesh->mp_material != p_material )
|
||
|
{
|
||
|
p_material = p_mesh->mp_material;
|
||
|
p_material->Submit();
|
||
|
}
|
||
|
p_mesh->Submit();
|
||
|
}
|
||
|
NxXbox::EngineGlobals.blend_mode_override = 0;
|
||
|
|
||
|
// Can now set the meshes in the geom inactive.
|
||
|
for( node = mp_geom_list->GetNext(); node; node = next )
|
||
|
{
|
||
|
next = node->GetNext();
|
||
|
Nx::CXboxGeom *p_xbox_geom = static_cast<Nx::CXboxGeom*>( node->GetData());
|
||
|
p_xbox_geom->SetActive( false );
|
||
|
}
|
||
|
|
||
|
// Remove references to surfaces.
|
||
|
p_surface->Release();
|
||
|
p_depth_surface->Release();
|
||
|
p_depth_buffer->Release();
|
||
|
|
||
|
// Restore the default render target.
|
||
|
D3DDevice_SetRenderTarget( NxXbox::EngineGlobals.p_RenderSurface, NxXbox::EngineGlobals.p_ZStencilSurface );
|
||
|
|
||
|
// Restore the view and projection transforms.
|
||
|
D3DDevice_SetTransform( D3DTS_VIEW, &NxXbox::EngineGlobals.view_matrix );
|
||
|
D3DDevice_SetTransform( D3DTS_PROJECTION, &NxXbox::EngineGlobals.projection_matrix );
|
||
|
|
||
|
// Now figure the vertex positions for the polygon.
|
||
|
Mth::Vector at = m_composite_bbox_mid - m_cam_pos;
|
||
|
at.Normalize();
|
||
|
Mth::Vector right = Mth::CrossProduct( Mth::Vector( NxXbox::EngineGlobals.cam_up.x, NxXbox::EngineGlobals.cam_up.y, NxXbox::EngineGlobals.cam_up.z ), at );
|
||
|
right.Normalize();
|
||
|
Mth::Vector up = Mth::CrossProduct( at, right );
|
||
|
|
||
|
Mth::Vector verts[4];
|
||
|
verts[0] = m_composite_bbox_mid - ( max_projected_x * right ) + ( max_projected_y * up );
|
||
|
verts[1] = m_composite_bbox_mid + ( max_projected_x * right ) + ( max_projected_y * up );
|
||
|
verts[2] = m_composite_bbox_mid + ( max_projected_x * right ) - ( max_projected_y * up );
|
||
|
verts[3] = m_composite_bbox_mid - ( max_projected_x * right ) - ( max_projected_y * up );
|
||
|
|
||
|
for( int v = 0; v < 4; ++v )
|
||
|
{
|
||
|
m_vertex_buffer[v].x = verts[v][X];
|
||
|
m_vertex_buffer[v].y = verts[v][Y];
|
||
|
m_vertex_buffer[v].z = verts[v][Z];
|
||
|
}
|
||
|
|
||
|
// The texture is a linear format, so the uv's are in texel space.
|
||
|
m_vertex_buffer[0].u = (float)m_tex_width;
|
||
|
m_vertex_buffer[0].v = 0.0f;
|
||
|
m_vertex_buffer[1].u = 0.0f;
|
||
|
m_vertex_buffer[1].v = 0.0f;
|
||
|
m_vertex_buffer[2].u = 0.0f;
|
||
|
m_vertex_buffer[2].v = (float)m_tex_height;
|
||
|
m_vertex_buffer[3].u = (float)m_tex_width;
|
||
|
m_vertex_buffer[3].v = (float)m_tex_height;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CXboxImposterGroup::plat_remove_imposter_polygon( void )
|
||
|
{
|
||
|
if( m_imposter_polygon_exists )
|
||
|
{
|
||
|
m_imposter_polygon_exists = false;
|
||
|
|
||
|
if( mp_texture )
|
||
|
{
|
||
|
// At this point move the texture resource into a list of removed textures. Here it will remain
|
||
|
// for sufficient time to ensure that the GPU will no longer try to access it during push buffer processing.
|
||
|
sRemovedTextureDetails *p_new_details = new sRemovedTextureDetails;
|
||
|
p_new_details->mp_texture = mp_texture;
|
||
|
p_new_details->m_time_removed = NxXbox::EngineGlobals.render_start_time;
|
||
|
|
||
|
Lst::Node<sRemovedTextureDetails> *p_new_node = new Lst::Node<sRemovedTextureDetails>( p_new_details );
|
||
|
mp_removed_textures_list->AddToTail( p_new_node );
|
||
|
|
||
|
mp_texture = NULL;
|
||
|
}
|
||
|
|
||
|
Lst::Node<Nx::CGeom> *node, *next;
|
||
|
for( node = mp_geom_list->GetNext(); node; node = next )
|
||
|
{
|
||
|
next = node->GetNext();
|
||
|
Nx::CXboxGeom *p_xbox_geom = static_cast<Nx::CXboxGeom*>( node->GetData());
|
||
|
|
||
|
// Can now set the meshes in the geom active.
|
||
|
p_xbox_geom->SetActive( true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
bool CXboxImposterGroup::plat_update_imposter_polygon( void )
|
||
|
{
|
||
|
// Calculate the new vector from bounding box midpoint to camera.
|
||
|
Mth::Vector new_vec( NxXbox::EngineGlobals.cam_position.x - m_composite_bbox_mid[X],
|
||
|
NxXbox::EngineGlobals.cam_position.y - m_composite_bbox_mid[Y],
|
||
|
NxXbox::EngineGlobals.cam_position.z - m_composite_bbox_mid[Z] );
|
||
|
new_vec.Normalize();
|
||
|
|
||
|
// Calculate the old vector from bounding box midpoint to camera when the imposter was created.
|
||
|
Mth::Vector old_vec = m_cam_pos - m_composite_bbox_mid;
|
||
|
old_vec.Normalize();
|
||
|
|
||
|
float angle_change = acosf( Mth::DotProduct( new_vec, old_vec ));
|
||
|
|
||
|
// Rebuild the imposter polygon if the angle change is beyond some limit.
|
||
|
if( angle_change > Mth::DegToRad( 5.0f ))
|
||
|
{
|
||
|
RemoveImposterPolygon();
|
||
|
CreateImposterPolygon();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check intermittently to see if the projected screen area of the imposter has changed sufficienty
|
||
|
// to warrant regenerating a new texture.
|
||
|
if( m_update_count >= XBOX_IMPOSTER_UPDATE_LIMIT )
|
||
|
{
|
||
|
m_update_count = 0;
|
||
|
|
||
|
// Generate a camera matrix that will point the camera directly at the midpoint of the bounding box.
|
||
|
XGMATRIX cam_mat;
|
||
|
XGVECTOR3 look_at( m_composite_bbox_mid[X], m_composite_bbox_mid[Y], m_composite_bbox_mid[Z] );
|
||
|
XGMatrixLookAtRH( &cam_mat, &NxXbox::EngineGlobals.cam_position, &look_at, &NxXbox::EngineGlobals.cam_up );
|
||
|
|
||
|
// Using this camera matrix, project all eight corners of the bounding box, in order to determine the best size for the texture.
|
||
|
Mth::Vector proj_max_x, proj_max_y, proj_mid;
|
||
|
frustum_project_box( m_composite_bbox, &cam_mat, &proj_max_x, &proj_max_y, &proj_mid );
|
||
|
|
||
|
// Calculate the maximum width and height at the near plane.
|
||
|
float wnp = 2.0f * proj_max_x[X] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_x[Z] ));
|
||
|
float hnp = 2.0f * proj_max_y[Y] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_y[Z] ));
|
||
|
|
||
|
// Round texture to the nearest 16 pixel limit.
|
||
|
int tex_width = 16 + Ftoi_ASM(( 640.0f * wnp ) / NxXbox::EngineGlobals.near_plane_width );
|
||
|
int tex_height = 16 + Ftoi_ASM(( 480.0f * hnp ) / NxXbox::EngineGlobals.near_plane_height );
|
||
|
tex_width = ( tex_width + 0x0F ) & ~0x0F;
|
||
|
tex_height = ( tex_height + 0x0F ) & ~0x0F;
|
||
|
|
||
|
// Clamp texture to maximum allowed size.
|
||
|
tex_width = ( tex_width > XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE : tex_width;
|
||
|
tex_height = ( tex_height > XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE : tex_height;
|
||
|
|
||
|
if(( tex_width != m_tex_width ) || ( tex_height != m_tex_height ))
|
||
|
{
|
||
|
RemoveImposterPolygon();
|
||
|
CreateImposterPolygon();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
void CXboxImposterGroup::plat_draw_imposter_polygon( void )
|
||
|
{
|
||
|
// Have to clear texture 0 before switching to the imposter texture, because it is a linear format.
|
||
|
NxXbox::set_texture( 0, NULL );
|
||
|
NxXbox::set_texture( 0, mp_texture );
|
||
|
|
||
|
NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADLIST, 1, m_vertex_buffer, sizeof( sImposterPolyVert ));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/******************************************************************/
|
||
|
/* */
|
||
|
/* */
|
||
|
/******************************************************************/
|
||
|
float CXboxImposterGroup::plat_check_distance( void )
|
||
|
{
|
||
|
// First check the visibility, using the bounding sphere.
|
||
|
bool visible = NxXbox::frustum_check_sphere((D3DXVECTOR3*)&( m_composite_bbox_mid[X] ), m_composite_bsphere_radius );
|
||
|
|
||
|
if( !visible )
|
||
|
return 0.0f;
|
||
|
|
||
|
float min_x = m_composite_bbox.GetMin().GetX();
|
||
|
float min_y = m_composite_bbox.GetMin().GetY();
|
||
|
float min_z = m_composite_bbox.GetMin().GetZ();
|
||
|
float max_x = m_composite_bbox.GetMax().GetX();
|
||
|
float max_y = m_composite_bbox.GetMax().GetY();
|
||
|
float max_z = m_composite_bbox.GetMax().GetZ();
|
||
|
|
||
|
// The camera-space distance to the nearest point on the composite bounding box of the imposter.
|
||
|
float nearest = NxXbox::EngineGlobals.far_plane;
|
||
|
|
||
|
for( uint32 v = 0; v < 8; ++v )
|
||
|
{
|
||
|
XGVECTOR3 test_in( ( v & 0x04 ) ? max_x : min_x, ( v & 0x02 ) ? max_y : min_y, ( v & 0x01 ) ? max_z : min_z );
|
||
|
XGVECTOR4 test_mid;
|
||
|
|
||
|
XGVec3Transform( &test_mid, &test_in, &NxXbox::EngineGlobals.view_matrix );
|
||
|
|
||
|
// Do z-checking here.
|
||
|
if( -test_mid.z < m_switch_distance )
|
||
|
{
|
||
|
return -test_mid.z;
|
||
|
}
|
||
|
else if( -test_mid.z < nearest )
|
||
|
{
|
||
|
nearest = -test_mid.z;
|
||
|
}
|
||
|
}
|
||
|
return nearest;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
} // Namespace Nx
|
||
|
|
||
|
|