1411 lines
70 KiB
C++
1411 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/>.
|
|
*/
|
|
|
|
/* $Header: /Commando/Code/ww3d2/rendobj.cpp 21 1/07/03 3:51p Bhayes $ */
|
|
/***********************************************************************************************
|
|
*** Confidential - Westwood Studios ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : Commando / G 3D Engine *
|
|
* *
|
|
* $Archive:: /Commando/Code/ww3d2/rendobj.cpp $*
|
|
* *
|
|
* Author:: Greg_h *
|
|
* *
|
|
* $Modtime:: 1/07/03 3:06p $*
|
|
* *
|
|
* $Revision:: 21 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* RenderObjClass::RenderObjClass -- constructor *
|
|
* RenderObjClass::RenderObjClass -- copy constructor *
|
|
* RenderObjClass::operator == -- assignment operator *
|
|
* RenderObjClass::Calculate_Texture_Reduction_Factor -- calculate texture reduction factor *
|
|
* RenderObjClass::Set_Texture_Reduction_Factor -- set texture reduction factor. *
|
|
* RenderObjClass::Get_Screen_Size -- get normalized area of object. *
|
|
* RenderObjClass::Get_Scene -- returns the (add_ref'd) scene pointer *
|
|
* RenderObjClass::Set_Container -- sets the container pointer *
|
|
* RenderObjClass::Get_Container -- returns the container pointer *
|
|
* RenderObjClass::Set_Transform -- set the transform for this object *
|
|
* RenderObjClass::Set_Position -- Sets the position of this object *
|
|
* RenderObjClass::Get_Transform -- returns the transform for the object *
|
|
* RenderObjClass::Get_Position -- returns the position of this render object *
|
|
* RenderObjClass::Validate_Transform -- If the transform is dirty, this causes it to be re- *
|
|
* RenderObjClass::Get_Sub_Object -- returns pointer to first sub-obj with given name *
|
|
* RenderObjClass::Add_Sub_Object_To_Bone -- add an object to a named bone *
|
|
* RenderObjClass::Prepare_LOD -- prepare object for predictive and texture LOD processing. *
|
|
* RenderObjClass::Get_Cost -- get object rendering cost for predictive LOD processing. *
|
|
* RenderObjClass::Update_Sub_Object_Bits -- updates our bits according to our sub-objects *
|
|
* RenderObjClass::Update_Sub_Object_Transforms -- re-evaluate the transforms my sub-objects *
|
|
* RenderObjClass::Add -- Generic add for render objects *
|
|
* RenderObjClass::Remove -- Generic Remove for Render Objects *
|
|
* RenderObjClass::Notify_Added -- notifies the object that it is in a scene *
|
|
* RenderObjClass::Notify_Removed -- notifies an object that it has been removed *
|
|
* RenderObjClass::Update_Cached_Bounding_Volumes -- default collision sphere. *
|
|
* RenderObjClass::Get_Obj_Space_Bounding_Sphere -- default collision sphere. *
|
|
* RenderObjClass::Get_Obj_Space_Bounding_Box -- default collision box. *
|
|
* RenderObjClass::Intersect - Returns true if specified intersection object *
|
|
* RenderObjClass::Intersect_Sphere -- tests for intersection with the bounding sphere *
|
|
* RenderObjClass::Intersect_Sphere_Quick -- tests for intersection with the bounding sphere *
|
|
* RenderObjClass::Build_Dependency_List -- Generates a list of files this obj depends on. *
|
|
* RenderObjClass::Build_Texture_List -- Builds a list of texture files this obj depends on. *
|
|
* RenderObjClass::Add_Dependencies_To_List -- Add dependent files to the list. *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
|
|
#include "rendobj.h"
|
|
#include "assetmgr.h"
|
|
#include "_mono.h"
|
|
#include "bsurface.h"
|
|
#include "pot.h"
|
|
#include "scene.h"
|
|
#include "colmath.h"
|
|
#include "coltest.h"
|
|
#include "inttest.h"
|
|
#include "wwdebug.h"
|
|
#include "matinfo.h"
|
|
#include "htree.h"
|
|
#include "predlod.h"
|
|
#include "camera.h"
|
|
#include "ww3d.h"
|
|
#include "chunkio.h"
|
|
#include "persistfactory.h"
|
|
#include "saveload.h"
|
|
#include "ww3dids.h"
|
|
#include "intersec.h"
|
|
|
|
|
|
|
|
// Definitions of static members:
|
|
const float RenderObjClass::AT_MIN_LOD = FLT_MAX;
|
|
const float RenderObjClass::AT_MAX_LOD = -1.0f;
|
|
|
|
// Local inline functions
|
|
StringClass
|
|
Filename_From_Asset_Name (const char *asset_name)
|
|
{
|
|
StringClass filename;
|
|
if (asset_name != NULL) {
|
|
|
|
//
|
|
// Copy the model name into a new filename buffer
|
|
//
|
|
::lstrcpy (filename.Get_Buffer (::lstrlen (asset_name) + 5), asset_name);
|
|
|
|
//
|
|
// Do we need to strip off the model's suffix?
|
|
//
|
|
char *suffix = ::strchr (filename, '.');
|
|
if (suffix != NULL) {
|
|
suffix[0] = 0;
|
|
}
|
|
|
|
//
|
|
// Concat the w3d file extension
|
|
//
|
|
filename += ".w3d";
|
|
}
|
|
|
|
return filename;
|
|
}
|
|
|
|
static inline bool Check_Is_Transform_Identity(const Matrix3D& m)
|
|
{
|
|
const float zero=0.0f;
|
|
const float one=1.0f;
|
|
|
|
unsigned d=
|
|
((unsigned&)m[0][0]^(unsigned&)one) |
|
|
((unsigned&)m[0][1]^(unsigned&)zero) |
|
|
((unsigned&)m[0][2]^(unsigned&)zero) |
|
|
((unsigned&)m[0][3]^(unsigned&)zero) |
|
|
((unsigned&)m[1][0]^(unsigned&)zero) |
|
|
((unsigned&)m[1][1]^(unsigned&)one) |
|
|
((unsigned&)m[1][2]^(unsigned&)zero) |
|
|
((unsigned&)m[1][3]^(unsigned&)zero) |
|
|
((unsigned&)m[2][0]^(unsigned&)zero) |
|
|
((unsigned&)m[2][1]^(unsigned&)zero) |
|
|
((unsigned&)m[2][2]^(unsigned&)one) |
|
|
((unsigned&)m[2][3]^(unsigned&)zero);
|
|
return !d;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::RenderObjClass -- constructor *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/04/1997 GH : Created. *
|
|
*=============================================================================================*/
|
|
RenderObjClass::RenderObjClass(void) :
|
|
Bits(DEFAULT_BITS),
|
|
Transform(1),
|
|
NativeScreenSize(WW3D::Get_Default_Native_Screen_Size()),
|
|
Scene(NULL),
|
|
Container(NULL),
|
|
User_Data(NULL),
|
|
CachedBoundingSphere(Vector3(0,0,0),1.0f),
|
|
CachedBoundingBox(Vector3(0,0,0),Vector3(1,1,1)),
|
|
IsTransformIdentity(false)
|
|
{
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::RenderObjClass -- copy constructor *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/5/97 GTH : Created. *
|
|
* 1/28/98 EHC : Created. *
|
|
*=============================================================================================*/
|
|
RenderObjClass::RenderObjClass(const RenderObjClass & src) :
|
|
Bits(src.Bits),
|
|
Transform(src.Transform),
|
|
NativeScreenSize(src.NativeScreenSize),
|
|
Scene(NULL),
|
|
Container(NULL),
|
|
User_Data(NULL),
|
|
CachedBoundingSphere(src.CachedBoundingSphere),
|
|
CachedBoundingBox(src.CachedBoundingBox),
|
|
IsTransformIdentity(src.IsTransformIdentity)
|
|
{
|
|
// Even though we're copying an object which might be in a scene
|
|
// this copy won't be so I'm clearing the scene pointer, same logic
|
|
// follows for things like the Container pointer.
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass -- assignment operator *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/5/97 GTH : Created. *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
RenderObjClass & RenderObjClass::operator = (const RenderObjClass & that)
|
|
{
|
|
// don't do anything if we're assigning this to this
|
|
if (this != &that) {
|
|
Set_Hidden(that.Is_Hidden());
|
|
Set_Animation_Hidden(that.Is_Animation_Hidden());
|
|
Set_Force_Visible(that.Is_Force_Visible());
|
|
Set_Collision_Type(that.Get_Collision_Type());
|
|
Set_Native_Screen_Size(that.Get_Native_Screen_Size());
|
|
IsTransformIdentity=false;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Calculate_Texture_Reduction_Factor -- calculate texture reduction factor. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 04/08/99 NH : Created. *
|
|
*=============================================================================================*/
|
|
/*
|
|
float RenderObjClass::Calculate_Texture_Reduction_Factor(float norm_screensize)
|
|
{
|
|
// NOTE: The texture reduction factor represents the number of powers of two that the texture
|
|
// must be reduced by in both dimensions. The reason that such an inherently integral quantity
|
|
// is represented as a float is for the texture reduction algorithms to incorporate hysteresis
|
|
// properly.
|
|
float reduction = sqrt(Get_Native_Screen_Size() / norm_screensize);
|
|
reduction = MAX(1.0f, reduction);
|
|
|
|
// We want to calculate the log base 2. Since the standard libraries have no log-base-2
|
|
// function, we use the following: log-base-2(x) = log(x)/log(2) where log is the natural
|
|
// logarithm (which does exist in the stadard libraries).
|
|
// We precalculare 1/log(2) as 1.442695f.
|
|
return log(reduction) * 1.442695f;
|
|
}
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Set_Texture_Reduction_Factor -- set texture reduction factor. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 04/08/99 NH : Created. *
|
|
*=============================================================================================*/
|
|
/*
|
|
void RenderObjClass::Set_Texture_Reduction_Factor(float trf)
|
|
{
|
|
WWASSERT(0); // Texture reduction system is broken! Don't call!
|
|
MaterialInfoClass *minfo = Get_Material_Info();
|
|
if (minfo) {
|
|
minfo->Set_Texture_Reduction_Factor(trf);
|
|
minfo->Release_Ref();
|
|
} else {
|
|
int num_obj = Get_Num_Sub_Objects();
|
|
RenderObjClass *sub_obj;
|
|
|
|
for (int i = 0; i < num_obj; i++) {
|
|
sub_obj = Get_Sub_Object(i);
|
|
if (sub_obj) {
|
|
sub_obj->Set_Texture_Reduction_Factor(trf);
|
|
sub_obj->Release_Ref();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Screen_Size -- get normalized area of object. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/12/99 NH : Created. *
|
|
*=============================================================================================*/
|
|
float RenderObjClass::Get_Screen_Size(CameraClass &camera)
|
|
{
|
|
// Currently this works by projecting the bounding sphere to the screen
|
|
// (as if the object was at the center) - in future this may be made more
|
|
// accurate (perhaps by using the object-space bounding-box)
|
|
Vector3 cam = camera.Get_Position();
|
|
|
|
ViewportClass viewport = camera.Get_Viewport();
|
|
Vector2 vpr_min, vpr_max;
|
|
camera.Get_View_Plane(vpr_min, vpr_max);
|
|
float width_factor = viewport.Width() / (vpr_max.X - vpr_min.X);
|
|
float height_factor = viewport.Height() / (vpr_max.Y - vpr_min.Y);
|
|
|
|
const SphereClass & sphere = Get_Bounding_Sphere();
|
|
float dist = (sphere.Center - cam).Length();
|
|
float radius = 0.0f;
|
|
if (dist) {
|
|
radius = sphere.Radius / dist;
|
|
}
|
|
|
|
// Return area in normalized units.
|
|
return WWMATH_PI * radius * radius * width_factor * height_factor;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Scene -- returns the (add_ref'd) scene pointer *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/4/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
SceneClass * RenderObjClass::Get_Scene(void)
|
|
{
|
|
if (Scene != NULL) {
|
|
Scene->Add_Ref();
|
|
}
|
|
return Scene;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Set_Container -- sets the container pointer *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/4/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Set_Container(RenderObjClass * con)
|
|
{
|
|
// Either we arent currently in a container or we are clearing our container, otherwise
|
|
// Houston, there is a problem!
|
|
WWASSERT((con == NULL) || (Container == NULL));
|
|
Container = con;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Container -- returns the container pointer *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/4/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
RenderObjClass * RenderObjClass::Get_Container(void) const
|
|
{
|
|
return Container;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Set_Transform -- set the transform for this object *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Set_Transform(const Matrix3D &m)
|
|
{
|
|
Transform = m;
|
|
IsTransformIdentity=Check_Is_Transform_Identity(m);
|
|
Invalidate_Cached_Bounding_Volumes();
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Set_Position -- Sets the position of this object *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
* 07/14/2001 SKB : Add Check_Is_Transform_Identity *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Set_Position(const Vector3 &v)
|
|
{
|
|
Transform.Set_Translation(v);
|
|
IsTransformIdentity=Check_Is_Transform_Identity(Transform);
|
|
Invalidate_Cached_Bounding_Volumes();
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Validate_Transform -- If the transform is dirty, this causes it to be re-ca *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 6/15/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Validate_Transform(void) const
|
|
{
|
|
/*
|
|
** Recurse up the tree to see if any of my parents are saying that their sub-object
|
|
** transforms are dirty
|
|
*/
|
|
RenderObjClass * con = Get_Container();
|
|
bool dirty=false;
|
|
if (con != NULL) {
|
|
dirty = con->Are_Sub_Object_Transforms_Dirty();
|
|
|
|
while (con->Get_Container() != NULL) {
|
|
dirty |= con->Are_Sub_Object_Transforms_Dirty();
|
|
con = con->Get_Container();
|
|
}
|
|
|
|
/*
|
|
** If the transforms are dirty, update them
|
|
*/
|
|
if (dirty) {
|
|
con->Update_Sub_Object_Transforms();
|
|
}
|
|
}
|
|
if (dirty) IsTransformIdentity=Check_Is_Transform_Identity(Transform);
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Position -- returns the position of this render object *
|
|
* *
|
|
* Similar to Get_Transform but returns only the position. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
Vector3 RenderObjClass::Get_Position(void) const
|
|
{
|
|
Validate_Transform();
|
|
return Transform.Get_Translation();
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Sub_Object -- returns pointer to first sub-obj with given name *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/4/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
RenderObjClass * RenderObjClass::Get_Sub_Object_By_Name(const char * name) const
|
|
{
|
|
int i;
|
|
|
|
// first try the un-altered name
|
|
for (i=0; i<Get_Num_Sub_Objects(); i++) {
|
|
RenderObjClass * robj = Get_Sub_Object(i);
|
|
if (robj) {
|
|
if (stricmp(robj->Get_Name(),name) == 0) {
|
|
return robj;
|
|
} else {
|
|
robj->Release_Ref();
|
|
}
|
|
}
|
|
}
|
|
|
|
// check the given name against the "suffix" names of each sub-object
|
|
for (i=0; i<Get_Num_Sub_Objects(); i++) {
|
|
RenderObjClass * robj = Get_Sub_Object(i);
|
|
if (robj) {
|
|
const char * subobjname = strchr(robj->Get_Name(),'.');
|
|
if (subobjname == NULL) {
|
|
subobjname = robj->Get_Name();
|
|
} else {
|
|
// skip past the period.
|
|
subobjname = subobjname+1;
|
|
}
|
|
|
|
if (stricmp(subobjname,name) == 0) {
|
|
return robj;
|
|
} else {
|
|
robj->Release_Ref();
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Add_Sub_Object_To_Bone -- add an object to a named bone *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: If the bone name is unknown then this function will add the the object to the *
|
|
* root transform rather than failing. This is due to the fact that GetBoneIndex *
|
|
* returns the root tranform for unknown bones. *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/4/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
int RenderObjClass::Add_Sub_Object_To_Bone(RenderObjClass * subobj,const char * bname)
|
|
{
|
|
int bindex = Get_Bone_Index(bname);
|
|
return Add_Sub_Object_To_Bone(subobj,bindex);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Remove_Sub_Objects_From_Bone -- remove all objects from a named bone *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/4/99 NH : Created. *
|
|
*=============================================================================================*/
|
|
int RenderObjClass::Remove_Sub_Objects_From_Bone(const char * bname)
|
|
{
|
|
int boneidx = Get_Bone_Index(bname);
|
|
int count = Get_Num_Sub_Objects_On_Bone(boneidx);
|
|
int remove_count = 0;
|
|
for (int i = count-1; i >= 0; i--) {
|
|
RenderObjClass *robj = Get_Sub_Object_On_Bone(i, boneidx);
|
|
if ( robj ) {
|
|
remove_count += Remove_Sub_Object(robj);
|
|
robj->Release_Ref();
|
|
}
|
|
}
|
|
return remove_count;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Prepare_LOD -- prepare object for predictive and texture LOD processing. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/11/99 NH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Prepare_LOD(CameraClass &camera)
|
|
{
|
|
// Since most RenderObjClass derivatives are not LOD-capable, the default
|
|
// implementation just sets the texture reduction factor and doesn't do any
|
|
// predictive LOD preparation (except for adding the objects' cost to the
|
|
// total static (nonoptimizeable) cost).
|
|
|
|
// Find the maximum screen dimension of the object in pixels
|
|
// float norm_area = Get_Screen_Size(camera);
|
|
|
|
// Find and set texture reduction factor
|
|
// Jani: Don't set tex reduction, it's broken!
|
|
// Set_Texture_Reduction_Factor(Calculate_Texture_Reduction_Factor(norm_area));
|
|
|
|
// Since we are not adding this object to the predictive LOD optimizer,
|
|
// at least add its cost in.
|
|
PredictiveLODOptimizerClass::Add_Cost(Get_Cost());
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Cost -- get object rendering cost for predictive LOD processing. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/11/99 NH : Created. *
|
|
*=============================================================================================*/
|
|
float RenderObjClass::Get_Cost(void) const
|
|
{
|
|
int polycount = Get_Num_Polys();
|
|
// If polycount is zero set Cost to a small nonzero amount to avoid divisions by zero.
|
|
float cost = (polycount != 0)? polycount : 0.000001f;
|
|
return cost;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Calculate_Cost_Value_Arrays -- calc arrays for predictive LOD processing. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNING: This member function assumes that the arrays passed are large enough (# LODs for *
|
|
* the cost array and # LODs + 1 for the value array) to contain the desired data. *
|
|
* *
|
|
* NOTE: This member function is usually only called internally inside LOD objects for *
|
|
* initialization purposes, or externally by specialized LOD objects like the G *
|
|
* predictive LOD PIP proxy. *
|
|
* *
|
|
* NOTE: The screen area used is in normalized units. *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/11/99 NH : Created. *
|
|
*=============================================================================================*/
|
|
int RenderObjClass::Calculate_Cost_Value_Arrays(float screen_area, float *values, float *costs) const
|
|
{
|
|
values[0] = AT_MIN_LOD;
|
|
values[1] = AT_MAX_LOD;
|
|
costs[0] = Get_Cost();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Update_Sub_Object_Bits -- updates our bits according to our sub-objects *
|
|
* *
|
|
* This should be called by any object that contains other objects whenever a sub object is *
|
|
* added or removed. It updates the status of the attribute bits which are supposed to be *
|
|
* the union of all of the sub-objects attributes. (I.e. if one of our sub-objects is *
|
|
* translucent, then we should be marked as translucent). *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Update_Sub_Object_Bits(void)
|
|
{
|
|
// this doesn't do anything for non-composite objects
|
|
if (Get_Num_Sub_Objects() == 0) return;
|
|
|
|
// go through all of our sub-objects
|
|
int coltype = 0;
|
|
int istrans = 0;
|
|
|
|
for (int ni = 0; ni < Get_Num_Sub_Objects(); ni++) {
|
|
RenderObjClass * robj = Get_Sub_Object(ni);
|
|
coltype |= robj->Get_Collision_Type();
|
|
istrans |= robj->Is_Translucent();
|
|
robj->Release_Ref();
|
|
}
|
|
|
|
Set_Collision_Type(coltype);
|
|
Set_Translucent(istrans);
|
|
|
|
// if we are a sub-object, tell our container to do this
|
|
if (Container) {
|
|
Container->Update_Sub_Object_Bits();
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Update_Sub_Object_Transforms -- re-evaluate the transforms my sub-objects *
|
|
* *
|
|
* The default implementation is empty, derived classes which have sub-objects should *
|
|
* implement it to update the transforms of their sub-objects *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Update_Sub_Object_Transforms(void)
|
|
{
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Add -- Generic add for render objects *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/04/1997 GH : Created. *
|
|
* 2/25/99 GTH : Moved to the base RenderObjClass *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Add(SceneClass * scene)
|
|
{
|
|
WWASSERT(scene);
|
|
WWASSERT(Container == NULL);
|
|
Scene = scene;
|
|
Scene->Add_Render_Object(this);
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Remove -- Generic Remove for Render Objects *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/04/1997 GH : Created. *
|
|
* 2/25/99 GTH : moved to the base RenderObjClass *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Remove(void)
|
|
{
|
|
// All render objects have their scene pointers set. To check if this is a "top level"
|
|
// object, (i.e. directly in the scene) you see if its Container pointer is NULL.
|
|
#if 1
|
|
if (Container == NULL) {
|
|
if (Scene != NULL) {
|
|
Scene->Remove_Render_Object(this);
|
|
return;
|
|
}
|
|
} else {
|
|
Container->Remove_Sub_Object(this);
|
|
return;
|
|
}
|
|
#else
|
|
if (!Scene) return;
|
|
Scene->Remove_Render_Object(this);
|
|
Scene = NULL;
|
|
#endif
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Notify_Added -- notifies the object that it is in a scene *
|
|
* *
|
|
* This function will be called whenever an object is directly or indirectly added to a scene *
|
|
* An example of "indirect" addition would be if you were added as a sub-object to an HModel *
|
|
* that was already in a scene. *
|
|
* *
|
|
* Override this function if you want to register your object for per-frame-updating or *
|
|
* as a VertexProcessor. (See the Register method of SceneClass, ParticleBufferClass, etc) *
|
|
* *
|
|
* Container objects must forward this notification to their sub objects. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* Due to implementation details of some derived scenes, the SceneClass calls this function. *
|
|
* Please dont move the call to this to RenderObjClass::Add *
|
|
* Derived classes should also call the base class to ensure that the scene pointer is set *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Notify_Added(SceneClass * scene)
|
|
{
|
|
Scene = scene;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Notify_Removed -- notifies an object that it has been removed *
|
|
* *
|
|
* Works similar to the Notify_Added function. You can override and Unregister yourself from *
|
|
* any scene based special processing. Container objects must recurse to their sub objects. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* Derived classes should also call the base class to ensure that the scene pointer is cleared *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Notify_Removed(SceneClass * scene)
|
|
{
|
|
Scene = NULL;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Update_Cached_Bounding_Volumes -- default collision sphere. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 11/7/97 GTH : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Update_Cached_Bounding_Volumes(void) const
|
|
{
|
|
Get_Obj_Space_Bounding_Box(CachedBoundingBox);
|
|
Get_Obj_Space_Bounding_Sphere(CachedBoundingSphere);
|
|
|
|
CachedBoundingSphere.Center = Get_Transform() * CachedBoundingSphere.Center;
|
|
CachedBoundingBox.Transform(Get_Transform());
|
|
|
|
Validate_Cached_Bounding_Volumes();
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Obj_Space_Bounding_Sphere -- default collision sphere. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 28/8/97 NH : Created. *
|
|
* 2/25/99 GTH : Moved into RenderObjClass *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
|
|
{
|
|
sphere.Center.Set(0,0,0);
|
|
sphere.Radius = 1.0f;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Get_Obj_Space_Bounding_Box -- default collision box. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 28/8/97 NH : Created. *
|
|
* 2/25/99 GTH : Moved into RenderObjClass *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
|
|
{
|
|
box.Center.Set(0,0,0);
|
|
box.Extent.Set(0,0,0);
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Intersect - Returns true if specified intersection object *
|
|
* intersects this renderobject *
|
|
* *
|
|
* *
|
|
* *
|
|
* *
|
|
* INPUT: A properly configured Intersection object specifying ray direction & vector *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Moved into RenderObjClass *
|
|
*=============================================================================================*/
|
|
bool RenderObjClass::Intersect(IntersectionClass *Intersection, IntersectionResultClass *Final_Result)
|
|
{
|
|
|
|
// do the quick sphere test just to make sure it is worth the more expensive intersection test
|
|
if (Intersect_Sphere_Quick(Intersection, Final_Result)) {
|
|
|
|
CastResultStruct castresult;
|
|
LineSegClass lineseg;
|
|
|
|
Vector3 end = *Intersection->RayLocation + *Intersection->RayDirection * Intersection->MaxDistance;
|
|
lineseg.Set(* Intersection->RayLocation, end);
|
|
|
|
RayCollisionTestClass ray(lineseg, &castresult);
|
|
ray.CollisionType = COLLISION_TYPE_ALL;
|
|
|
|
if (Cast_Ray(ray)) {
|
|
lineseg.Compute_Point(ray.Result->Fraction,&(Final_Result->Intersection));
|
|
Final_Result->Intersects = true;
|
|
Final_Result->IntersectionType = IntersectionResultClass::GENERIC;
|
|
if (Intersection->IntersectionNormal)
|
|
* Intersection->IntersectionNormal = castresult.Normal;
|
|
Final_Result->IntersectedRenderObject = this;
|
|
Final_Result->ModelMatrix = Transform;
|
|
return true;
|
|
}
|
|
}
|
|
Final_Result->Intersects = false;
|
|
return false;
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Intersect_Sphere -- tests for intersection with the bounding sphere *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
bool RenderObjClass::Intersect_Sphere(IntersectionClass *Intersection, IntersectionResultClass *Final_Result)
|
|
{
|
|
SphereClass sphere = Get_Bounding_Sphere();
|
|
return Intersection->Intersect_Sphere(sphere, Final_Result);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Intersect_Sphere_Quick -- tests for intersection with the bounding sphere *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/25/99 GTH : Created. *
|
|
*=============================================================================================*/
|
|
bool RenderObjClass::Intersect_Sphere_Quick(IntersectionClass *Intersection, IntersectionResultClass *Final_Result)
|
|
{
|
|
SphereClass sphere = Get_Bounding_Sphere();
|
|
return Intersection->Intersect_Sphere_Quick(sphere, Final_Result);
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Build_Dependency_List -- Generates a list of files this obj depends on. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/18/99 PDS : Created. *
|
|
*=============================================================================================*/
|
|
bool RenderObjClass::Build_Dependency_List (DynamicVectorClass<StringClass> &file_list, bool recursive)
|
|
{
|
|
if (recursive)
|
|
{
|
|
// Loop through all this object's subobj's
|
|
int subobj_count = Get_Num_Sub_Objects ();
|
|
for (int index = 0; index < subobj_count; index ++) {
|
|
|
|
// Ask this subobj to add all of its file dependencies to the list
|
|
RenderObjClass *psub_obj = Get_Sub_Object (index);
|
|
if (psub_obj != NULL) {
|
|
psub_obj->Build_Dependency_List (file_list);
|
|
psub_obj->Release_Ref ();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now add all of this object's dependencies to the list
|
|
Add_Dependencies_To_List (file_list);
|
|
|
|
// Return the true/false result code
|
|
return (file_list.Count () > 0);
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Build_Texture_List -- Builds a list of texture files this obj depends on. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/18/99 PDS : Created. *
|
|
*=============================================================================================*/
|
|
bool RenderObjClass::Build_Texture_List
|
|
(
|
|
DynamicVectorClass<StringClass> & texture_file_list,
|
|
bool recursive
|
|
)
|
|
{
|
|
if (recursive) {
|
|
|
|
//
|
|
// Loop through all this object's subobj's
|
|
//
|
|
int subobj_count = Get_Num_Sub_Objects ();
|
|
for (int index = 0; index < subobj_count; index ++) {
|
|
|
|
//
|
|
// Ask this subobj to add all of its texture file dependencies to the list
|
|
//
|
|
RenderObjClass *sub_obj = Get_Sub_Object (index);
|
|
if (sub_obj != NULL) {
|
|
sub_obj->Build_Texture_List (texture_file_list);
|
|
sub_obj->Release_Ref ();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now add all of this object's texture dependencies to the list
|
|
//
|
|
Add_Dependencies_To_List (texture_file_list, true);
|
|
|
|
// Return the true/false result code
|
|
return (texture_file_list.Count () > 0);
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
* RenderObjClass::Add_Dependencies_To_List -- Add dependent files to the list. *
|
|
* *
|
|
* INPUT: *
|
|
* *
|
|
* OUTPUT: *
|
|
* *
|
|
* WARNINGS: *
|
|
* *
|
|
* HISTORY: *
|
|
* 3/18/99 PDS : Created. *
|
|
*=============================================================================================*/
|
|
void RenderObjClass::Add_Dependencies_To_List
|
|
(
|
|
DynamicVectorClass<StringClass> &file_list,
|
|
bool textures_only
|
|
)
|
|
{
|
|
//
|
|
// Should we add W3D files to the list?
|
|
//
|
|
if (textures_only == false) {
|
|
|
|
//
|
|
// Main W3D file
|
|
//
|
|
const char *model_name = Get_Name ();
|
|
file_list.Add (::Filename_From_Asset_Name (model_name));
|
|
|
|
//
|
|
// External hierarchy file
|
|
//
|
|
const HTreeClass *phtree = Get_HTree ();
|
|
if (phtree != NULL) {
|
|
const char *htree_name = phtree->Get_Name ();
|
|
if (::lstrcmpi (htree_name, model_name) != 0) {
|
|
|
|
//
|
|
// Add this file to the list
|
|
//
|
|
file_list.Add (::Filename_From_Asset_Name (htree_name));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Original W3D file (if an aggregate)
|
|
//
|
|
const char *base_model_name = Get_Base_Model_Name ();
|
|
if (base_model_name != NULL) {
|
|
|
|
//
|
|
// Add this file to the list
|
|
//
|
|
file_list.Add (::Filename_From_Asset_Name (base_model_name));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************************
|
|
|
|
|
|
RenderObjClass - Persistant object support.
|
|
|
|
NOTE: For now, the render obj PersistFactory is going to cheat by simply storing
|
|
the name of the render object that was saved. At load time, it will ask the
|
|
asset manager for that object again. If the asset manager fails to re-create the
|
|
object,
|
|
|
|
|
|
****************************************************************************************/
|
|
|
|
class RenderObjPersistFactoryClass : public PersistFactoryClass
|
|
{
|
|
virtual uint32 Chunk_ID(void) const;
|
|
virtual PersistClass * Load(ChunkLoadClass & cload) const;
|
|
virtual void Save(ChunkSaveClass & csave,PersistClass * obj) const;
|
|
|
|
enum
|
|
{
|
|
RENDOBJFACTORY_CHUNKID_VARIABLES = 0x00555040,
|
|
RENDOBJFACTORY_CHUNKID_USERLIGHTING,
|
|
|
|
RENDOBJFACTORY_VARIABLE_OBJPOINTER = 0x00,
|
|
RENDOBJFACTORY_VARIABLE_NAME,
|
|
RENDOBJFACTORY_VARIABLE_TRANSFORM,
|
|
};
|
|
};
|
|
|
|
static RenderObjPersistFactoryClass _RenderObjPersistFactory;
|
|
|
|
uint32 RenderObjPersistFactoryClass::Chunk_ID(void) const
|
|
{
|
|
return WW3D_PERSIST_CHUNKID_RENDEROBJ;
|
|
}
|
|
|
|
PersistClass * RenderObjPersistFactoryClass::Load(ChunkLoadClass & cload) const
|
|
{
|
|
RenderObjClass * old_obj = NULL;
|
|
RenderObjClass * new_obj = NULL;
|
|
Matrix3D tm(1);
|
|
char name[64];
|
|
|
|
while (cload.Open_Chunk()) {
|
|
switch (cload.Cur_Chunk_ID()) {
|
|
|
|
case RENDOBJFACTORY_CHUNKID_VARIABLES:
|
|
{
|
|
while (cload.Open_Micro_Chunk()) {
|
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
|
READ_MICRO_CHUNK(cload,RENDOBJFACTORY_VARIABLE_OBJPOINTER,old_obj);
|
|
READ_MICRO_CHUNK(cload,RENDOBJFACTORY_VARIABLE_TRANSFORM,tm);
|
|
READ_MICRO_CHUNK_STRING(cload,RENDOBJFACTORY_VARIABLE_NAME,name,sizeof(name));
|
|
}
|
|
cload.Close_Micro_Chunk();
|
|
}
|
|
|
|
// if the object we saved didn't have a name, replace it with null
|
|
if (strlen(name) == 0) {
|
|
static int count = 0;
|
|
if ( ++count < 10 ) {
|
|
WWDEBUG_SAY(("RenderObjPersistFactory attempted to load an un-named render object!\r\n"));
|
|
WWDEBUG_SAY(("Replacing it with a NULL render object!\r\n"));
|
|
}
|
|
strcpy(name,"NULL");
|
|
}
|
|
|
|
new_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj(name);
|
|
|
|
if (new_obj == NULL) {
|
|
static int count = 0;
|
|
if ( ++count < 10 ) {
|
|
WWDEBUG_SAY(("RenderObjPersistFactory failed to create object: %s!!\r\n",name));
|
|
WWDEBUG_SAY(("Either the asset for this object is gone or you tried to save a procedural object.\r\n"));
|
|
WWDEBUG_SAY(("Replacing it with a NULL render object!\r\n"));
|
|
}
|
|
strcpy(name,"NULL");
|
|
new_obj = WW3DAssetManager::Get_Instance()->Create_Render_Obj(name);
|
|
}
|
|
|
|
WWASSERT(new_obj != NULL);
|
|
if (new_obj) {
|
|
new_obj->Set_Transform(tm);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RENDOBJFACTORY_CHUNKID_USERLIGHTING:
|
|
if (new_obj != NULL) {
|
|
new_obj->Load_User_Lighting(cload);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",(int)cload.Cur_Chunk_ID(), __FILE__,__LINE__));
|
|
break;
|
|
};
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
|
|
SaveLoadSystemClass::Register_Pointer(old_obj,new_obj);
|
|
return new_obj;
|
|
}
|
|
|
|
void RenderObjPersistFactoryClass::Save(ChunkSaveClass & csave,PersistClass * obj) const
|
|
{
|
|
RenderObjClass * robj = (RenderObjClass *)obj;
|
|
const char * name = robj->Get_Name();
|
|
Matrix3D tm = robj->Get_Transform();
|
|
|
|
csave.Begin_Chunk(RENDOBJFACTORY_CHUNKID_VARIABLES);
|
|
WRITE_MICRO_CHUNK(csave,RENDOBJFACTORY_VARIABLE_OBJPOINTER,robj);
|
|
WRITE_MICRO_CHUNK_STRING(csave,RENDOBJFACTORY_VARIABLE_NAME,name);
|
|
WRITE_MICRO_CHUNK(csave,RENDOBJFACTORY_VARIABLE_TRANSFORM,tm);
|
|
csave.End_Chunk();
|
|
|
|
if (robj->Has_User_Lighting()) {
|
|
csave.Begin_Chunk(RENDOBJFACTORY_CHUNKID_USERLIGHTING);
|
|
robj->Save_User_Lighting(csave);
|
|
csave.End_Chunk();
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
**
|
|
** RenderObj save-load.
|
|
**
|
|
***********************************************************************/
|
|
const PersistFactoryClass & RenderObjClass::Get_Factory (void) const
|
|
{
|
|
return _RenderObjPersistFactory;
|
|
}
|
|
|
|
bool RenderObjClass::Save (ChunkSaveClass &csave)
|
|
{
|
|
// This should never hit with the persist factory we're using...
|
|
// Yes this looks like a design flaw but the way we're saving render objects is
|
|
// a "shortcut". We specifically designed this capability into the persistant
|
|
// object system so that we could avoid making all render object's save and
|
|
// load themselves if possible.
|
|
WWASSERT(0);
|
|
return true;
|
|
}
|
|
|
|
bool RenderObjClass::Load (ChunkLoadClass &cload)
|
|
{
|
|
WWASSERT(0); // this should never hit with the persist factory we're using.
|
|
return true;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
**
|
|
** RenderObj User-Lighting save-load.
|
|
**
|
|
***********************************************************************/
|
|
enum
|
|
{
|
|
CHUNKID_SUBOBJ_USER_LIGHTING = 0x02191159,
|
|
CHUNKID_SUBOBJ_NAME,
|
|
CHUNKD_SUBOBJ_BONE_INDEX,
|
|
CHUNKID_SUBOBJ_LIGHTING_DATA
|
|
};
|
|
|
|
void RenderObjClass::Save_User_Lighting (ChunkSaveClass & csave)
|
|
{
|
|
if (Has_User_Lighting()) {
|
|
for (int bi=0; bi<Get_Num_Bones(); bi++) {
|
|
int bone_obj_count = Get_Num_Sub_Objects_On_Bone(bi);
|
|
for (int ri=0; ri<bone_obj_count; ri++) {
|
|
RenderObjClass * sub_obj = Get_Sub_Object_On_Bone(ri,bi);
|
|
|
|
if ( sub_obj &&
|
|
(sub_obj->Class_ID() == RenderObjClass::CLASSID_MESH) &&
|
|
(sub_obj->Has_User_Lighting()))
|
|
{
|
|
csave.Begin_Chunk(CHUNKID_SUBOBJ_USER_LIGHTING);
|
|
Save_Sub_Object_User_Lighting(csave,sub_obj,bi);
|
|
csave.End_Chunk();
|
|
|
|
REF_PTR_RELEASE(sub_obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderObjClass::Save_Sub_Object_User_Lighting(ChunkSaveClass & csave,RenderObjClass * sub_obj,int bone_index)
|
|
{
|
|
csave.Begin_Chunk(CHUNKID_SUBOBJ_NAME);
|
|
const char * name = sub_obj->Get_Name();
|
|
csave.Write(name,strlen(name) + 1);
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk(CHUNKD_SUBOBJ_BONE_INDEX);
|
|
csave.Write(&bone_index,sizeof(int));
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk(CHUNKID_SUBOBJ_LIGHTING_DATA);
|
|
sub_obj->Save_User_Lighting(csave);
|
|
csave.End_Chunk();
|
|
|
|
Set_Has_User_Lighting(true);
|
|
}
|
|
|
|
void RenderObjClass::Load_User_Lighting (ChunkLoadClass & cload)
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
|
|
switch (cload.Cur_Chunk_ID()) {
|
|
case CHUNKID_SUBOBJ_USER_LIGHTING:
|
|
Load_Sub_Object_User_Lighting(cload);
|
|
break;
|
|
}
|
|
|
|
cload.Close_Chunk();
|
|
Set_Has_User_Lighting(true);
|
|
}
|
|
}
|
|
|
|
void RenderObjClass::Load_Sub_Object_User_Lighting(ChunkLoadClass & cload)
|
|
{
|
|
const int BUFFER_SIZE = 256;
|
|
char tmp_string[BUFFER_SIZE];
|
|
int bone_index;
|
|
|
|
/*
|
|
** Load the name of the object
|
|
*/
|
|
cload.Open_Chunk();
|
|
|
|
WWASSERT(cload.Cur_Chunk_ID() == CHUNKID_SUBOBJ_NAME);
|
|
WWASSERT(cload.Cur_Chunk_Length() < BUFFER_SIZE);
|
|
|
|
cload.Read(tmp_string,cload.Cur_Chunk_Length());
|
|
tmp_string[BUFFER_SIZE-1] = 0;
|
|
cload.Close_Chunk();
|
|
|
|
/*
|
|
** Load the bone index for the object
|
|
*/
|
|
cload.Open_Chunk();
|
|
WWASSERT(cload.Cur_Chunk_ID() == CHUNKD_SUBOBJ_BONE_INDEX);
|
|
cload.Read(&bone_index,sizeof(int));
|
|
cload.Close_Chunk();
|
|
|
|
if ((bone_index < 0) || (bone_index >= Get_Num_Bones())) {
|
|
WWDEBUG_SAY(("Invalid Bone Index %d referenced in Object %s.\r\n",bone_index, tmp_string));
|
|
WWASSERT(0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** Find the object and tell it to load its lighting data
|
|
*/
|
|
cload.Open_Chunk();
|
|
WWASSERT(cload.Cur_Chunk_ID() == CHUNKID_SUBOBJ_LIGHTING_DATA);
|
|
|
|
RenderObjClass * obj = NULL;
|
|
int bone_obj_count = Get_Num_Sub_Objects_On_Bone(bone_index);
|
|
for (int obj_index=0; (obj_index<bone_obj_count) && (obj == NULL); obj_index++) {
|
|
RenderObjClass * sub_obj = Get_Sub_Object_On_Bone(obj_index,bone_index);
|
|
if (stricmp(sub_obj->Get_Name(), tmp_string) == 0) {
|
|
obj = sub_obj;
|
|
}
|
|
REF_PTR_RELEASE(sub_obj);
|
|
}
|
|
|
|
if (obj != NULL) {
|
|
obj->Load_User_Lighting(cload);
|
|
}
|
|
|
|
cload.Close_Chunk();
|
|
}
|