Serious-Engine/Sources/Engine/Models/Model.cpp
Daniel Gibson 430e0c8a95 Remove "64-bit issue" STUBBED() in Model.cpp, as it only looks fishy
.. but is really fine (most probably).

I added comments instead; see them for explanation why it's fine ;-)
2016-04-21 16:21:47 +02:00

3100 lines
97 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include "Engine/StdH.h"
#include <Engine/Models/ModelObject.h>
#include <Engine/Models/ModelData.h>
#include <Engine/Models/RenderModel.h>
#include <Engine/Models/Model_internal.h>
#include <Engine/Models/Normals.h>
#include <Engine/Base/Stream.h>
#include <Engine/Base/CTString.inl>
#include <Engine/Math/Clipping.inl>
#include <Engine/Graphics/Color.h>
#include <Engine/Graphics/DrawPort.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Templates/DynamicContainer.cpp>
#include <Engine/Templates/Stock_CModelData.h>
template class CStaticArray<MappingSurface>;
template class CStaticArray<ModelPolygon>;
template class CStaticArray<ModelPolygonVertex>;
template class CStaticArray<ModelTextureVertex>;
template class CStaticArray<PolygonsPerPatch>;
template class CDynamicArray<CAttachedModelPosition>;
extern UBYTE aubGouraudConv[16384];
// model LOD biasing control
extern FLOAT mdl_fLODMul;
extern FLOAT mdl_fLODAdd;
extern INDEX mdl_iLODDisappear; // 0=never, 1=ignore bias, 2=with bias
extern INDEX mdl_bFineQuality; // 0=force to 8-bit, 1=optimal
CModelData::CModelData(const CModelData &c) { ASSERT(FALSE); };
CModelData &CModelData::operator=(const CModelData &c){ ASSERT(FALSE); return *this; };
// if any surface in model that we are currently reading has any transparency
BOOL _bHasAlpha;
// colors used to represent on and off bits
COLOR PaletteColorValues[] =
{
C_RED, C_GREEN, C_BLUE, C_CYAN,
C_MAGENTA, C_YELLOW, C_ORANGE, C_BROWN,
C_PINK, C_dGRAY, C_GRAY, C_lGRAY,
C_dRED, C_lRED, C_dGREEN, C_lGREEN,
C_dBLUE, C_lBLUE, C_dCYAN, C_lCYAN,
C_dMAGENTA, C_lMAGENTA, C_dYELLOW, C_lYELLOW,
C_dORANGE, C_lORANGE, C_dBROWN, C_lBROWN,
C_dPINK, C_lPINK, C_WHITE, C_BLACK,
};
/*
* Instanciated global rendering preferences object containing
* info about rendering of models
*/
CModelRenderPrefs _mrpModelRenderPrefs;
/*
* Functions dealing with 16-bit normal compression
*/
void CompressNormal_HQ(const FLOAT3D &vNormal, UBYTE &ubH, UBYTE &ubP)
{
ANGLE h, p;
const FLOAT &x = vNormal(1);
const FLOAT &y = vNormal(2);
const FLOAT &z = vNormal(3);
// calculate pitch
p = ASin(y);
// if y is near +1 or -1
if (y>0.99 || y<-0.99) {
// heading is irrelevant
h = 0;
// otherwise
} else {
// calculate heading
h = ATan2(-x, -z);
}
h = (h/360.0f)+0.5f;
p = (p/360.0f)+0.5f;
ASSERT(h>=0 && h<=1);
ASSERT(p>=0 && p<=1);
ubH = UBYTE(h*255);
ubP = UBYTE(p*255);
}
void DecompressNormal_HQ(FLOAT3D &vNormal, UBYTE ubH, UBYTE ubP)
{
ANGLE h = (ubH/255.0f)*360.0f-180.0f;
ANGLE p = (ubP/255.0f)*180.0f-90.0f;
FLOAT &x = vNormal(1);
FLOAT &y = vNormal(2);
FLOAT &z = vNormal(3);
x = -Sin(h)*Cos(p);
y = Sin(p);
z = -Cos(h)*Cos(p);
}
//--------------------------------------------------------------------------------------------
/*
* Function returns number of first setted bit in ULONG
*/
INDEX GetBit( ULONG ulSource)
{
for( INDEX i=0; i<32; i++)
{
if( (ulSource & (1<<i)) != 0) return i;
}
return 0;
}
//--------------------------------------------------------------------------------------------
/*
* Default constructor sets default rendering preferences
*/
CModelRenderPrefs::CModelRenderPrefs()
{
rp_BBoxFrameVisible = FALSE;
rp_BBoxAllVisible = FALSE;
rp_InkColor = C_BLACK;
rp_PaperColor = C_lGRAY;
rp_RenderType = RT_TEXTURE | RT_SHADING_PHONG;
rp_ShadowQuality = 0;
}
/*
* Routines managing (get/set) rendering preferences
*/
void CModelRenderPrefs::SetRenderType(ULONG rtNew)
{
rp_RenderType = rtNew;
}
void CModelRenderPrefs::SetTextureType(ULONG rtNew)
{
rp_RenderType = (rp_RenderType & (~RT_TEXTURE_MASK)) | rtNew;
}
void CModelRenderPrefs::SetShadingType(ULONG rtNew)
{
rp_RenderType = (rp_RenderType & (~RT_SHADING_MASK)) | rtNew;
}
void CModelRenderPrefs::SetWire(BOOL bWireOn)
{
if( bWireOn)
{
rp_RenderType = rp_RenderType | RT_WIRE_ON;
}
else
{
rp_RenderType = rp_RenderType & (~RT_WIRE_ON);
}
}
void CModelRenderPrefs::SetHiddenLines(BOOL bHiddenLinesOn)
{
if( bHiddenLinesOn)
{
rp_RenderType = rp_RenderType | RT_HIDDEN_LINES;
}
else
{
rp_RenderType = rp_RenderType & (~RT_HIDDEN_LINES);
}
}
ULONG CModelRenderPrefs::GetRenderType()
{
return( rp_RenderType);
}
void CModelRenderPrefs::SetShadowQuality(INDEX iNewQuality)
{
ASSERT( iNewQuality >= 0);
rp_ShadowQuality = iNewQuality;
}
void CModelRenderPrefs::DesreaseShadowQuality(void)
{
rp_ShadowQuality += 1;
}
void CModelRenderPrefs::IncreaseShadowQuality(void)
{
if( rp_ShadowQuality > 0)
rp_ShadowQuality -= 1;
}
INDEX CModelRenderPrefs::GetShadowQuality()
{
return( rp_ShadowQuality);
}
BOOL CModelRenderPrefs::BBoxFrameVisible()
{
return( rp_BBoxFrameVisible);
}
void CModelRenderPrefs::BBoxFrameShow( BOOL bShow)
{
rp_BBoxFrameVisible = bShow;
}
BOOL CModelRenderPrefs::BBoxAllVisible()
{
return( rp_BBoxAllVisible);
}
void CModelRenderPrefs::BBoxAllShow( BOOL bShow)
{
rp_BBoxAllVisible = bShow;
}
BOOL CModelRenderPrefs::WireOn()
{
return( (rp_RenderType & RT_WIRE_ON) != 0);
}
BOOL CModelRenderPrefs::HiddenLines()
{
return( (rp_RenderType & RT_HIDDEN_LINES) != 0);
}
void CModelRenderPrefs::SetInkColor(COLOR clrNew)
{
rp_InkColor = clrNew;
}
COLOR CModelRenderPrefs::GetInkColor()
{
return rp_InkColor;
}
void CModelRenderPrefs::SetPaperColor(COLOR clrNew)
{
rp_PaperColor = clrNew;
}
COLOR CModelRenderPrefs::GetPaperColor()
{
return rp_PaperColor;
}
// read and write functions
void CModelRenderPrefs::Read_t( CTStream *istrFile) // throw char *
{
}
void CModelRenderPrefs::Write_t( CTStream *ostrFile) // throw char *
{
}
//--------------------------------------------------------------------------------------------
CModelPatch::CModelPatch(void)
{
mp_strName = "";
mp_mexPosition = MEX2D( 1024, 1024);
mp_fStretch = 1.0f;
}
void CModelPatch::Read_t(CTStream *strFile)
{
*strFile >> mp_strName;
CTFileName fnPatchTexture;
*strFile >> fnPatchTexture;
try
{
mp_toTexture.SetData_t( fnPatchTexture);
}
catch( char *strError)
{
(void) strError;
}
*strFile >> mp_mexPosition;
*strFile >> mp_fStretch;
}
void CModelPatch::Write_t(CTStream *strFile)
{
*strFile << mp_strName;
*strFile << mp_toTexture.GetName();
*strFile << mp_mexPosition;
*strFile << mp_fStretch;
}
//--------------------------------------------------------------------------------------------
/*
* Default constructor sets invalid data
*/
CModelData::CModelData()
{
INDEX i;
md_bPreparedForRendering = FALSE;
md_VerticesCt = 0; // number of vertices in model
md_FramesCt = 0; // number of all frames used by this model
md_MipCt = 0; // number of mip-models
md_bIsEdited = FALSE; // not edited by default
// invalidate mip-model info data
for( i=0; i<MAX_MODELMIPS; i++) {
md_MipInfos[i].mmpi_PolygonsCt = 0;
}
md_Flags = 0; // model flags (flat, reflection mapping)
md_ShadowQuality = 0;
md_Stretch = FLOAT3D(1,1,1); // stretch vector (static one, dynamic one is in model object)
md_bCollideAsCube = FALSE; // collide as sphere
md_colDiffuse = C_WHITE|CT_OPAQUE;
md_colReflections = C_WHITE|CT_OPAQUE;
md_colSpecular = C_WHITE|CT_OPAQUE;
md_colBump = C_WHITE|CT_OPAQUE;
}
/*
* Clear all model data arrays
*/
void CModelData::Clear(void)
{
INDEX i;
md_bPreparedForRendering = FALSE;
CAnimData::Clear();
md_FrameVertices16.Clear();
md_FrameVertices8.Clear();
md_FrameInfos.Clear();
md_MainMipVertices.Clear();
md_TransformedVertices.Clear();
md_VertexMipMask.Clear();
md_aampAttachedPosition.Clear();
md_acbCollisionBox.Clear();
for( i=0; i<md_MipCt; i++) md_MipInfos[i].Clear();
for( i=0; i<MAX_COLOR_NAMES; i++) md_ColorNames[i].Clear();
for( i=0; i<MAX_TEXTUREPATCHES; i++) md_mpPatches[i].mp_toTexture.SetData_t( CTString(""));
md_VerticesCt = 0;
md_FramesCt = 0;
md_MipCt = 0;
}
// get amount of memory used by this object
SLONG CModelData::GetUsedMemory(void)
{
SLONG slUsed = sizeof(*this)+CAnimData::GetUsedMemory()-sizeof(CAnimData);
slUsed += md_FrameVertices16.Count()*sizeof(struct ModelFrameVertex16);
slUsed += md_FrameVertices8.Count()*sizeof(struct ModelFrameVertex8);
slUsed += md_FrameInfos.Count()*sizeof(struct ModelFrameInfo);
slUsed += md_MainMipVertices.Count()*sizeof(FLOAT3D);
slUsed += md_TransformedVertices.Count()*sizeof(struct TransformedVertexData);
slUsed += md_VertexMipMask.Count()*sizeof(ULONG);
slUsed += md_acbCollisionBox.Count()*sizeof(CModelCollisionBox);
slUsed += md_aampAttachedPosition.Count()*sizeof(CAttachedModelPosition);
for(INDEX i=0; i<md_MipCt; i++) {
slUsed += md_MipInfos[i].mmpi_aPolygonsPerPatch.Count()*sizeof(struct PolygonsPerPatch);
slUsed += md_MipInfos[i].mmpi_Polygons.Count()*sizeof(struct ModelPolygon);
slUsed += md_MipInfos[i].mmpi_TextureVertices.Count()*sizeof(struct ModelTextureVertex);
slUsed += md_MipInfos[i].mmpi_MappingSurfaces.Count()*sizeof(struct MappingSurface);
}
return slUsed;
}
// check if this kind of objects is auto-freed
BOOL CModelData::IsAutoFreed(void)
{
return FALSE;
}
void CModelData::ClearAnimations(void)
{
CAnimData::Clear();
md_FrameVertices16.Clear();
md_FrameVertices8.Clear();
md_FrameInfos.Clear();
md_FramesCt = 0;
}
//--------------------------------------------------------------------------------------------
// riches texture dimensions
void CModelData::GetTextureDimensions( MEX &mexWidth, MEX &mexHeight)
{
mexWidth = md_Width;
mexHeight = md_Height;
}
//--------------------------------------------------------------------------------------------
// calculates bounding box of all frames
void CModelData::GetAllFramesBBox( FLOATaabbox3D &MaxBB)
{
for( INDEX i=0; i<md_FramesCt; i++)
{
MaxBB |= md_FrameInfos[ i].mfi_Box;
}
}
FLOAT3D CModelData::GetCollisionBoxMin(INDEX iCollisionBox)
{
md_acbCollisionBox.Lock();
INDEX iCollisionBoxClamped = Clamp(iCollisionBox, 0, md_acbCollisionBox.Count()-1);
FLOAT3D vMin = md_acbCollisionBox[ iCollisionBoxClamped].mcb_vCollisionBoxMin;
md_acbCollisionBox.Unlock();
return vMin;
};
FLOAT3D CModelData::GetCollisionBoxMax(INDEX iCollisionBox=0)
{
md_acbCollisionBox.Lock();
INDEX iCollisionBoxClamped = Clamp(iCollisionBox, 0, md_acbCollisionBox.Count()-1);
FLOAT3D vMax = md_acbCollisionBox[ iCollisionBoxClamped].mcb_vCollisionBoxMax;
md_acbCollisionBox.Unlock();
return vMax;
};
// returns HEIGHT_EQ_WIDTH, LENGTH_EQ_WIDTH or LENGTH_EQ_HEIGHT
INDEX CModelData::GetCollisionBoxDimensionEquality(INDEX iCollisionBox=0)
{
md_acbCollisionBox.Lock();
iCollisionBox = Clamp(iCollisionBox, 0, md_acbCollisionBox.Count()-1);
INDEX iDimEq = md_acbCollisionBox[ iCollisionBox].mcb_iCollisionBoxDimensionEquality;
md_acbCollisionBox.Unlock();
return iDimEq;
};
ULONG CModelData::GetFlags(void)
{
return md_Flags;
};
//--------------------------------------------------------------------------------------------
void CModelData::SpreadMipSwitchFactors( INDEX iFirst, float fStartingFactor)
{
// set switch steep to cover all mips until max default range reached
FLOAT fSteep;
// if we allready skipped max range or we don't have any more mips
if( (fStartingFactor > MAX_SWITCH_FACTOR) || ((md_MipCt-iFirst) <= 0) )
{
// define next switch factor offset
fSteep = 1.2f;
}
// else divide factor from starting factor to max switch factor with number of mip
// models left
else
{
fSteep = (MAX_SWITCH_FACTOR - fStartingFactor) / (md_MipCt-iFirst);
}
// spread mip switch factors for rougher mip models
for( INDEX i=iFirst; i<md_MipCt; i++)
{
md_MipSwitchFactors[ i] = fStartingFactor + (i-iFirst+1)*fSteep;
}
}
//--------------------------------------------------------------------------------------------
/*
* Default destructor asserts invalid data and clears valid ones
*/
CModelData::~CModelData()
{
Clear();
}
//--------------------------------------------------------------------------------------------
ModelTextureVertex::ModelTextureVertex(void)
{
mtv_iTransformedVertex = 0;
mtv_Done = FALSE;
}
//------------------------------------------ WRITE
void ModelPolygonVertex::Write_t( CTStream *pFile) // throw char *
{
// DG: this looks like a 64-bit issue, but most probably isn't as PtrToIndices() should have been called before this
(*pFile) << (INDEX) (size_t) mpv_ptvTransformedVertex;
(*pFile) << (INDEX) (size_t) mpv_ptvTextureVertex;
}
//------------------------------------------ READ
void ModelPolygonVertex::Read_t( CTStream *pFile) // throw char *
{
INDEX itmp;
// DG: this looks like a 64-bit issue, but most probably isn't as IndicesToPtrs() should be
// called afterwards to restore actually valid pointers.
(*pFile) >> itmp;
mpv_ptvTransformedVertex = (struct TransformedVertexData *) (size_t) itmp;
(*pFile) >> itmp;
mpv_ptvTextureVertex = (ModelTextureVertex *) (size_t) itmp;
}
//--------------------------------------------------------------------------------------------
//------------------------------------------ WRITE
void ModelPolygon::Write_t( CTStream *pFile) // throw char *
{
pFile->WriteID_t( CChunkID("MDP2"));
INDEX ctVertices = mp_PolygonVertices.Count();
(*pFile) << ctVertices;
{FOREACHINSTATICARRAY(mp_PolygonVertices, ModelPolygonVertex, it)
{ it.Current().Write_t( pFile);}}
(*pFile) << mp_RenderFlags;
(*pFile) << mp_ColorAndAlpha;
(*pFile) << mp_Surface;
};
//------------------------------------------ READ
void ModelPolygon::Read_t( CTStream *pFile) // throw char *
{
INDEX ctVertices;
ULONG ulDummy;
if( pFile->PeekID_t() == CChunkID("MDPL"))
{
pFile->ExpectID_t( CChunkID("MDPL"));
pFile->ReadFullChunk_t( CChunkID("IMPV"), &ctVertices, sizeof(INDEX));
BYTESWAP(ctVertices);
mp_PolygonVertices.New( ctVertices);
{FOREACHINSTATICARRAY(mp_PolygonVertices, ModelPolygonVertex, it)
{ it.Current().Read_t( pFile);}}
(*pFile) >> mp_RenderFlags;
(*pFile) >> mp_ColorAndAlpha;
(*pFile) >> mp_Surface;
(*pFile) >> ulDummy; // ex on color
(*pFile) >> ulDummy; // ex off color
}
else
{
pFile->ExpectID_t( CChunkID("MDP2"));
(*pFile) >> ctVertices;
mp_PolygonVertices.New( ctVertices);
{FOREACHINSTATICARRAY(mp_PolygonVertices, ModelPolygonVertex, it)
{ it.Current().Read_t( pFile);}}
(*pFile) >> mp_RenderFlags;
(*pFile) >> mp_ColorAndAlpha;
(*pFile) >> mp_Surface;
}
};
//----------------------------------------------------------------------------------
// TEMPORARY - REMOVE THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// POLYGON RENDER CONSTANTS
// new_render_types = rendertype (0-7)<<0 + phongstrength(0-7)<<3 + alphatype(0-3)<<6
// poly render types
#define PR_PHONGSHADING (0x00<<0) // textures phong shading
#define PR_LAMBERTSHADING (0x01<<0) // textures lambert shading (no phong/gouraud)
#define PR_ALPHAGOURAUD (0x02<<0) // alpha gouraud shading
#define PR_FRONTPROJECTION (0x03<<0) // front projection shading (no rotation, 2D zoom)
#define PR_SHADOWBLENDING (0x04<<0) // shadow blending (black alpha gouraud)
#define PR_COLORFILLING (0x05<<0) // flat filling (single color poly, no texture)
#define PR_MASK (0x07<<0) // mask for render types
// phong strengths (shading types)
#define PS_MATTE (0x00<<3) // same as the gouraud
#define PS_SHINY (0x01<<3) // mild shininig
#define PS_METAL (0x02<<3) // shine as hell
#define PS_MASK (0x07<<3) // mask for phong strengths
// texture's alpha channel flag
#define AC_SKIPALPHACHANNEL (0x00<<6) // opaque rendering regardless of alpha channel presence
#define AC_USEALPHACHANNEL (0x01<<6) // texture's alpha ch. will be taken in consideration
#define AC_ZEROTRANSPARENCY (0x02<<6) // texture's zero values are transparent (no alpha ch.)
#define AC_MASK (0x03<<6) // mask for texture's alpha flag
// polygon's control flags
#define PCF_DOUBLESIDED (0x01<<9) // double sided polygon
#define PCF_NOSHADING (0x02<<9) // this polygon will not be shaded anyhow (?)
#define PCF_CLIPPOLYGON (0x04<<9) // polygon is clipped, instead rejected
#define PCF_REFLECTIONS (0x08<<9) // use reflection mapping
//----------------------------------------------------------------------------------
BOOL MappingSurface::operator==(const MappingSurface &msOther) const {
return msOther.ms_Name == ms_Name;
};
// convert old polygon flags from CTGfx into new rendering parameters
void MappingSurface::SetRenderingParameters(ULONG ulOldFlags)
{
// find rendering type
if (ulOldFlags&PCF_NOSHADING) {
ms_sstShadingType = SST_FULLBRIGHT;
} else {
switch (ulOldFlags&PS_MASK) {
case PS_MATTE:
ms_sstShadingType = SST_MATTE;
break;
case PS_SHINY:
ms_sstShadingType = SST_MATTE;
break;
case PS_METAL:
ms_sstShadingType = SST_MATTE;
break;
default:
ms_sstShadingType = SST_MATTE;
}
}
// find translucency type
if ((ulOldFlags&PR_MASK)==PR_ALPHAGOURAUD) {
ms_sttTranslucencyType = STT_ALPHAGOURAUD;
} else if ((ulOldFlags&AC_MASK)==AC_ZEROTRANSPARENCY) {
ms_sttTranslucencyType = STT_TRANSLUCENT;
} else {
ms_sttTranslucencyType = STT_OPAQUE;
}
// find flags
ms_ulRenderingFlags = 0;
if (ulOldFlags&PCF_DOUBLESIDED) {
ms_ulRenderingFlags|=SRF_DOUBLESIDED;
}
if (ulOldFlags&PCF_REFLECTIONS) {
ms_ulRenderingFlags|=SRF_REFLECTIONS;
}
}
//------------------------------------------ WRITE
void MappingSurface::Write_t( CTStream *pFile) // throw char *
{
(*pFile) << ms_Name;
pFile->Write_t( &ms_vSurface2DOffset, sizeof(FLOAT3D));
pFile->Write_t( &ms_HPB, sizeof(FLOAT3D));
pFile->Write_t( &ms_Zoom, sizeof(float));
pFile->Write_t( &ms_sstShadingType, sizeof(SurfaceShadingType));
pFile->Write_t( &ms_sttTranslucencyType, sizeof(SurfaceTranslucencyType));
(*pFile) << ms_ulRenderingFlags;
INDEX ctPolygons = ms_aiPolygons.Count();
(*pFile) << ctPolygons;
if( ctPolygons != 0)
{
pFile->Write_t( &ms_aiPolygons[0], sizeof( INDEX)*ctPolygons);
}
INDEX ctTextureVertices = ms_aiTextureVertices.Count();
(*pFile) << ctTextureVertices;
if( ctTextureVertices != 0)
{
pFile->Write_t( &ms_aiTextureVertices[0], sizeof( INDEX)*ctTextureVertices);
}
(*pFile) << ms_colColor;
(*pFile) << ms_colDiffuse;
(*pFile) << ms_colReflections;
(*pFile) << ms_colSpecular;
(*pFile) << ms_colBump;
(*pFile) << ms_ulOnColor;
(*pFile) << ms_ulOffColor;
}
void MappingSurface::WriteSettings_t( CTStream *pFile) // throw char *
{
(*pFile) << ms_Name;
(*pFile) << (INDEX &)ms_sstShadingType;
(*pFile) << (INDEX &)ms_sttTranslucencyType;
(*pFile) << ms_ulRenderingFlags;
(*pFile) << ms_colDiffuse;
(*pFile) << ms_colReflections;
(*pFile) << ms_colSpecular;
(*pFile) << ms_colBump;
(*pFile) << ms_ulOnColor;
(*pFile) << ms_ulOffColor;
}
void MappingSurface::ReadSettings_t( CTStream *pFile) // throw char *
{
(*pFile) >> ms_Name;
(*pFile) >> (INDEX&) ms_sstShadingType;
(*pFile) >> (INDEX&) ms_sttTranslucencyType;
(*pFile) >> ms_ulRenderingFlags;
(*pFile) >> ms_colDiffuse;
(*pFile) >> ms_colReflections;
(*pFile) >> ms_colSpecular;
(*pFile) >> ms_colBump;
(*pFile) >> ms_ulOnColor;
(*pFile) >> ms_ulOffColor;
}
//------------------------------------------ READ
void MappingSurface::Read_t( CTStream *pFile, BOOL bReadPolygonsPerSurface,
BOOL bReadSurfaceColors) // throw char *
{
(*pFile) >> ms_Name;
(*pFile) >> ms_vSurface2DOffset;
(*pFile) >> ms_HPB;
(*pFile) >> ms_Zoom;
if( bReadPolygonsPerSurface)
{
(*pFile) >> (INDEX &) ms_sstShadingType;
// WARNING !!! All shading types bigger than matte will be remaped into flat shading
// this was done when SHINY and METAL were removed
if( ms_sstShadingType > SST_MATTE)
{
ms_sstShadingType = SST_FLAT;
}
(*pFile) >> (INDEX &) ms_sttTranslucencyType;
(*pFile) >> ms_ulRenderingFlags;
if( (ms_ulRenderingFlags&SRF_NEW_TEXTURE_FORMAT) == 0)
ms_ulRenderingFlags |= SRF_DIFFUSE|SRF_NEW_TEXTURE_FORMAT;
if (ms_sttTranslucencyType==STT_TRANSLUCENT || ms_sttTranslucencyType==STT_ALPHAGOURAUD
||ms_sttTranslucencyType==STT_ADD||ms_sttTranslucencyType==STT_MULTIPLY) {
_bHasAlpha = TRUE;
}
ms_aiPolygons.Clear();
INDEX ctPolygons;
(*pFile) >> ctPolygons;
ms_aiPolygons.New( ctPolygons);
for (INDEX i = 0; i < ctPolygons; i++)
(*pFile) >> ms_aiPolygons[i];
ms_aiTextureVertices.Clear();
INDEX ctTextureVertices;
(*pFile) >> ctTextureVertices;
ms_aiTextureVertices.New( ctTextureVertices);
for (INDEX i = 0; i < ctTextureVertices; i++)
(*pFile) >> ms_aiTextureVertices[i];
(*pFile) >> ms_colColor;
}
if( bReadSurfaceColors)
{
(*pFile) >> ms_colDiffuse;
(*pFile) >> ms_colReflections;
(*pFile) >> ms_colSpecular;
(*pFile) >> ms_colBump;
(*pFile) >> ms_ulOnColor;
(*pFile) >> ms_ulOffColor;
}
}
void CModelData::LinkDataForSurfaces(BOOL bFirstMip)
{
INDEX iMipStart=1;
if( bFirstMip)
{
iMipStart=0;
}
// for each mip model
for( INDEX iMip=iMipStart; iMip<md_MipCt; iMip ++)
{
ModelMipInfo *pMMI = &md_MipInfos[ iMip];
// --------------------- Set index of transformed vertex to texture vertex
{for( INDEX iPolygon = 0; iPolygon<pMMI->mmpi_Polygons.Count(); iPolygon++)
{
for( INDEX iVertex = 0; iVertex<pMMI->mmpi_Polygons[iPolygon].mp_PolygonVertices.Count(); iVertex++)
{
ModelPolygonVertex *pmpvPolygonVertex = &pMMI->mmpi_Polygons[iPolygon].mp_PolygonVertices[iVertex];
INDEX iTransformed = md_TransformedVertices.Index( pmpvPolygonVertex->mpv_ptvTransformedVertex);
pmpvPolygonVertex->mpv_ptvTextureVertex->mtv_iTransformedVertex = iTransformed;
}
}}
// --------------------- Linking polygons for surface
// array telling how many polygons are in each surface
CStaticArray<INDEX> actPolygonsInSurface;
INDEX ctSurfaces = pMMI->mmpi_MappingSurfaces.Count();
actPolygonsInSurface.New( ctSurfaces);
// for each surface
{for( INDEX iSurface=0; iSurface<ctSurfaces; iSurface++)
{
// reset count of polygons
actPolygonsInSurface[iSurface] = 0;
}}
// for each polygon
{for( INDEX iPolygon=0; iPolygon<pMMI->mmpi_Polygons.Count(); iPolygon++)
{
// increment count of polygons in its surface
actPolygonsInSurface[ pMMI->mmpi_Polygons[iPolygon].mp_Surface]++;
}}
// for each surface
{for( INDEX iSurface=0; iSurface<ctSurfaces; iSurface++)
{
// allocate array for polygon indices
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons.Clear();
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons.New( actPolygonsInSurface[iSurface]);
if( actPolygonsInSurface[iSurface] != 0)
{
// last place in array will contain counter of added polygons
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons[actPolygonsInSurface[iSurface]-1] = 0;
}
}}
// for each polygon
for( INDEX iPolygon=0; iPolygon<pMMI->mmpi_Polygons.Count(); iPolygon++)
{
// get surface, polygons in surface and last remembered index of polygon in surface
INDEX iSurface = pMMI->mmpi_Polygons[iPolygon].mp_Surface;
INDEX ctPolygonsInSurface = actPolygonsInSurface[iSurface];
if( ctPolygonsInSurface != 0)
{
INDEX iLastSet = pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons[ ctPolygonsInSurface-1];
// remember last set polygon index
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons[ ctPolygonsInSurface-1] = iLastSet+1;
// remember index of polygon
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons[ iLastSet] = iPolygon;
}
}
// --------------------- Linking texture vertices for surface
// for each surface
{for( INDEX iSurface=0; iSurface<ctSurfaces; iSurface++)
{
CDynamicContainer<ModelTextureVertex> cmtvInSurface;
// for each polygon in surface
FOREACHINSTATICARRAY( pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons, INDEX, itimpo)
{
ModelPolygon &mpPolygon = pMMI->mmpi_Polygons[itimpo.Current()];
// for each vertex in polygon
for( INDEX iVertex=0; iVertex<mpPolygon.mp_PolygonVertices.Count(); iVertex++)
{
// get texture vertex
ModelTextureVertex *ptv =
mpPolygon.mp_PolygonVertices[ iVertex].mpv_ptvTextureVertex;
// if it is not added yet
if( !cmtvInSurface.IsMember( ptv))
{
// add it
cmtvInSurface.Add( ptv);
}
}
}
// add needed number of texture vertices
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiTextureVertices.Clear();
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiTextureVertices.New( cmtvInSurface.Count());
INDEX cttv = 0;
// for each texture vertex in container
FOREACHINDYNAMICCONTAINER(cmtvInSurface, ModelTextureVertex, itmtv)
{
INDEX idxtv = pMMI->mmpi_TextureVertices.Index( itmtv);
pMMI->mmpi_MappingSurfaces[iSurface].ms_aiTextureVertices[cttv] = idxtv;
cttv++;
}
}}
}
}
// Default constructor
ModelMipInfo::ModelMipInfo(void)
{
mmpi_ulFlags = MM_PATCHES_VISIBLE | MM_ATTACHED_MODELS_VISIBLE;
}
//--------------------------------------------------------------------------------------------
//------------------------------------------ WRITE
/*
* This is write function of one mip-model. It saves all mip's arrays eather by saving
* them really or calling their write functions.
*/
void ModelMipInfo::Write_t( CTStream *pFile) // throw char *
{
INDEX iMembersCt;
// Save count, call write for array of model polygons
pFile->WriteFullChunk_t( CChunkID("IPOL"), &mmpi_PolygonsCt, sizeof(INDEX));
{FOREACHINSTATICARRAY(mmpi_Polygons, ModelPolygon, it)
{ it.Current().Write_t( pFile);}}
// Save count, array of texture vertices
iMembersCt = mmpi_TextureVertices.Count();
(*pFile) << iMembersCt;
pFile->WriteFullChunk_t( CChunkID("TXV2"), &mmpi_TextureVertices[ 0], iMembersCt *
sizeof(struct ModelTextureVertex));
// Save count, call write for array of mapping surfaces
iMembersCt = mmpi_MappingSurfaces.Count();
(*pFile) << iMembersCt;
{FOREACHINSTATICARRAY(mmpi_MappingSurfaces, MappingSurface, it)
{ it.Current().Write_t( pFile);}}
// write mip model flags
(*pFile) << mmpi_ulFlags;
// write info of polygons occupied by patch
INDEX ctPatches = mmpi_aPolygonsPerPatch.Count();
(*pFile) << ctPatches;
// for each patch
for( INDEX iPatch=0; iPatch<ctPatches; iPatch++)
{
// write no of occupied polygons
INDEX ctOccupied = mmpi_aPolygonsPerPatch[iPatch].ppp_iPolygons.Count();
(*pFile) << ctOccupied;
if( ctOccupied != 0)
{
pFile->WriteFullChunk_t( CChunkID("OCPL"),
&mmpi_aPolygonsPerPatch[iPatch].ppp_iPolygons[ 0], ctOccupied * sizeof(INDEX));
}
}
}
//------------------------------------------ READ
/*
* This is read function of one mip-model
*/
void ModelMipInfo::Read_t(CTStream *pFile,
BOOL bReadPolygonalPatches,
BOOL bReadPolygonsPerSurface,
BOOL bReadSurfaceColors)
{
INDEX iMembersCt;
// Load count, allocate array and call Read for array of model polygons
pFile->ReadFullChunk_t( CChunkID("IPOL"), &mmpi_PolygonsCt, sizeof(INDEX));
BYTESWAP(mmpi_PolygonsCt);
mmpi_Polygons.New( mmpi_PolygonsCt);
{FOREACHINSTATICARRAY(mmpi_Polygons, ModelPolygon, it)
{
it.Current().Read_t( pFile);
}}
// Load count, allocate and load array of texture vertices
(*pFile) >> iMembersCt;
mmpi_TextureVertices.New( iMembersCt);
if( bReadPolygonsPerSurface)
{
// chunk ID will tell us if we should read new format that contains bump normals
CChunkID idChunk = pFile->GetID_t();
// jump over chunk size
ULONG ulDummySize;
(*pFile) >> ulDummySize;
// if bump normals are saved (new format)
if( idChunk == CChunkID("TXV2"))
{
for (SLONG i = 0; i < iMembersCt; i++)
(*pFile)>>mmpi_TextureVertices[i];
} else {
// bump normals are not saved
for( INDEX iVertex = 0; iVertex<iMembersCt; iVertex++)
{
(*pFile)>>mmpi_TextureVertices[ iVertex].mtv_UVW;
(*pFile)>>mmpi_TextureVertices[ iVertex].mtv_UV;
(*pFile)>>mmpi_TextureVertices[ iVertex].mtv_Done;
(*pFile)>>mmpi_TextureVertices[ iVertex].mtv_iTransformedVertex;
mmpi_TextureVertices[ iVertex].mtv_vU = FLOAT3D(0,0,0);
mmpi_TextureVertices[ iVertex].mtv_vV = FLOAT3D(0,0,0);
}
}
}
else
{
pFile->ExpectID_t( CChunkID("TXVT"));
// jump over chunk size
ULONG ulDummySize;
(*pFile) >> ulDummySize;
// read models in old format
for( INDEX iVertex = 0; iVertex<iMembersCt; iVertex++)
{
(*pFile)>>mmpi_TextureVertices[ iVertex].mtv_UVW;
(*pFile)>>mmpi_TextureVertices[ iVertex].mtv_UV;
(*pFile)>>mmpi_TextureVertices[ iVertex].mtv_Done;
mmpi_TextureVertices[ iVertex].mtv_iTransformedVertex = 0;
mmpi_TextureVertices[ iVertex].mtv_vU = FLOAT3D(0,0,0);
mmpi_TextureVertices[ iVertex].mtv_vV = FLOAT3D(0,0,0);
}
}
// Load count, allcate array and call Read for array of mapping surfaces
(*pFile) >> iMembersCt;
mmpi_MappingSurfaces.New( iMembersCt);
INDEX iIndexOfSurface = 0;
{FOREACHINSTATICARRAY(mmpi_MappingSurfaces, MappingSurface, it)
{
it.Current().Read_t( pFile, bReadPolygonsPerSurface, bReadSurfaceColors);
// obtain color per surface from polygons (old model format)
if( !bReadPolygonsPerSurface)
{
// we will copy color from first polygon found with this surface into color of surface
it->ms_colColor = C_WHITE;
// for all polygons in this mip level
for( INDEX iPolygon=0;iPolygon<mmpi_PolygonsCt;iPolygon++)
{
if( mmpi_Polygons[ iPolygon].mp_Surface == iIndexOfSurface)
{
it->ms_colColor = mmpi_Polygons[ iPolygon].mp_ColorAndAlpha;
break;
}
}
iIndexOfSurface++;
}
}}
if( bReadPolygonalPatches)
{
// read mip model flags
(*pFile) >> mmpi_ulFlags;
// read no of patches
INDEX ctPatches;
(*pFile) >> ctPatches;
if( ctPatches != 0)
{
mmpi_aPolygonsPerPatch.New( ctPatches);
// read info for polygonal patches
for( INDEX iPatch=0; iPatch<ctPatches; iPatch++)
{
// read no of occupied polygons
INDEX ctOccupied;
(*pFile) >> ctOccupied;
if( ctOccupied != 0)
{
mmpi_aPolygonsPerPatch[iPatch].ppp_iPolygons.New( ctOccupied);
pFile->ReadFullChunk_t( CChunkID("OCPL"),
&mmpi_aPolygonsPerPatch[iPatch].ppp_iPolygons[ 0], ctOccupied * sizeof(INDEX));
#if PLATFORM_BIGENDIAN
for (INDEX i = 0; i < ctOccupied; i++)
{
BYTESWAP(mmpi_aPolygonsPerPatch[iPatch].ppp_iPolygons[i]);
}
#endif
}
}
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Routine converts mpv_ptvTransformedVertex and mpv_ptvTextureVertex from ptrs to Indices
*/
void CModelData::PtrsToIndices()
{
INDEX i, j;
for( i=0; i<md_MipCt; i++)
{
FOREACHINSTATICARRAY(md_MipInfos[ i].mmpi_Polygons, ModelPolygon, it1)
{
FOREACHINSTATICARRAY(it1.Current().mp_PolygonVertices, ModelPolygonVertex, it2)
{
for( j=0; j<md_TransformedVertices.Count(); j++)
{
if( it2.Current().mpv_ptvTransformedVertex == &md_TransformedVertices[ j])
break;
}
it2.Current().mpv_ptvTransformedVertex = (struct TransformedVertexData *)(size_t)j;
for( j=0; j<md_MipInfos[ i].mmpi_TextureVertices.Count(); j++)
{
if( it2.Current().mpv_ptvTextureVertex == &md_MipInfos[ i].mmpi_TextureVertices[ j])
break;
}
it2.Current().mpv_ptvTextureVertex = (ModelTextureVertex *)(size_t)j;
}
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Routine converts mpv_ptvTransformedVertex and mpv_ptvTextureVertex from Indices to ptrs
*/
void CModelData::IndicesToPtrs()
{
INDEX i, j;
for( i=0; i<md_MipCt; i++)
{
FOREACHINSTATICARRAY(md_MipInfos[ i].mmpi_Polygons, ModelPolygon, it1)
{
FOREACHINSTATICARRAY(it1.Current().mp_PolygonVertices, ModelPolygonVertex, it2)
{
struct ModelPolygonVertex * pMPV = &it2.Current();
// DG: this looks like a 64-bit issue but is most probably ok, as the pointers
// should contain indices from PtrToIndices()
j = (INDEX) (size_t) it2.Current().mpv_ptvTransformedVertex;
it2.Current().mpv_ptvTransformedVertex = &md_TransformedVertices[ j];
// DG: same here
j = (INDEX) (size_t) it2.Current().mpv_ptvTextureVertex;
it2.Current().mpv_ptvTextureVertex = &md_MipInfos[ i].mmpi_TextureVertices[ j];
}
}
}
}
CModelCollisionBox::CModelCollisionBox(void)
{
mcb_vCollisionBoxMin = FLOAT3D( -0.5f, 0.0f,-0.5f);
mcb_vCollisionBoxMax = FLOAT3D( 0.5f, 2.0f, 0.5f);
mcb_iCollisionBoxDimensionEquality = LENGTH_EQ_WIDTH;
mcb_strName = "PART_NAME";
}
void CModelCollisionBox::Read_t(CTStream *istrFile)
{
// Read collision box min
(*istrFile) >> mcb_vCollisionBoxMin;
// Read collision box size
(*istrFile) >> mcb_vCollisionBoxMax;
// Get "colision box dimensions equality" value
if( (mcb_vCollisionBoxMax(2)-mcb_vCollisionBoxMin(2)) ==
(mcb_vCollisionBoxMax(1)-mcb_vCollisionBoxMin(1)) )
{
mcb_iCollisionBoxDimensionEquality = HEIGHT_EQ_WIDTH;
}
else if( (mcb_vCollisionBoxMax(3)-mcb_vCollisionBoxMin(3)) ==
(mcb_vCollisionBoxMax(1)-mcb_vCollisionBoxMin(1)) )
{
mcb_iCollisionBoxDimensionEquality = LENGTH_EQ_WIDTH;
}
else if( (mcb_vCollisionBoxMax(3)-mcb_vCollisionBoxMin(3)) ==
(mcb_vCollisionBoxMax(2)-mcb_vCollisionBoxMin(2)) )
{
mcb_iCollisionBoxDimensionEquality = LENGTH_EQ_HEIGHT;
}
else
{
/*
// Force them to be legal (Length = Width)
mcb_vCollisionBoxMax(3) = mcb_vCollisionBoxMin(3) +
(mcb_vCollisionBoxMax(1)-mcb_vCollisionBoxMin(1));
*/
mcb_iCollisionBoxDimensionEquality = LENGTH_EQ_WIDTH;
}
}
void CModelCollisionBox::ReadName_t(CTStream *istrFile)
{
// read collision box name
(*istrFile)>>mcb_strName;
}
void CModelCollisionBox::Write_t(CTStream *ostrFile)
{
// Write collision box min
(*ostrFile) << mcb_vCollisionBoxMin;
// Write collision box size
(*ostrFile) << mcb_vCollisionBoxMax;
// write collision box name
(*ostrFile) << mcb_strName;
}
CAttachedModelPosition::CAttachedModelPosition( void)
{
amp_iCenterVertex = 0;
amp_iFrontVertex = 1;
amp_iUpVertex = 2;
amp_plRelativePlacement = CPlacement3D( FLOAT3D(0,0,0), ANGLE3D(0,0,0));
}
void CAttachedModelPosition::Read_t( CTStream *strFile)
{
*strFile >> amp_iCenterVertex;
*strFile >> amp_iFrontVertex;
*strFile >> amp_iUpVertex;
*strFile >> amp_plRelativePlacement;
}
void CAttachedModelPosition::Write_t( CTStream *strFile)
{
*strFile << amp_iCenterVertex;
*strFile << amp_iFrontVertex;
*strFile << amp_iUpVertex;
*strFile << amp_plRelativePlacement;
}
//--------------------------------------------------------------------------------------------
//------------------------------------------ WRITE
void CModelData::Write_t( CTStream *pFile) // throw char *
{
INDEX i;
PtrsToIndices();
// Save main ID
pFile->WriteID_t( CChunkID("MDAT"));
// Save version number
pFile->WriteID_t( CChunkID( MODEL_VERSION));
// Save flags
(*pFile) << md_Flags;
#if PLATFORM_BIGENDIAN
STUBBED("byte order");
#endif
// Save vertices and frames ct
pFile->WriteFullChunk_t( CChunkID("IVTX"), &md_VerticesCt, sizeof(INDEX));
pFile->WriteFullChunk_t( CChunkID("IFRM"), &md_FramesCt, sizeof(INDEX));
// write array of 8-bit or 16-bit compressed vertices
if( md_Flags & MF_COMPRESSED_16BIT)
{
pFile->WriteFullChunk_t( CChunkID("AV17"), &md_FrameVertices16[ 0], md_VerticesCt * md_FramesCt *
sizeof(struct ModelFrameVertex16));
}
else
{
pFile->WriteFullChunk_t( CChunkID("AFVX"), &md_FrameVertices8[ 0], md_VerticesCt * md_FramesCt *
sizeof(struct ModelFrameVertex8));
}
// Save frame info array
pFile->WriteFullChunk_t( CChunkID("AFIN"), &md_FrameInfos[ 0], md_FramesCt *
sizeof(struct ModelFrameInfo));
// Save frame main mip vertices array
pFile->WriteFullChunk_t( CChunkID("AMMV"), &md_MainMipVertices[ 0], md_VerticesCt *
sizeof(FLOAT3D));
// Save vertex mip-mask array
pFile->WriteFullChunk_t( CChunkID("AVMK"), &md_VertexMipMask[ 0], md_VerticesCt *
sizeof(ULONG));
// Save mip levels counter
pFile->WriteFullChunk_t( CChunkID("IMIP"), &md_MipCt, sizeof(INDEX));
// Save mip factors array
pFile->WriteFullChunk_t( CChunkID("FMIP"), &md_MipSwitchFactors[ 0], MAX_MODELMIPS * sizeof(float));
// Save all model mip infos
for( i=0; i<md_MipCt; i++) md_MipInfos[ i].Write_t( pFile);
// Save patches
pFile->WriteID_t( CChunkID("PTC2"));
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++) md_mpPatches[ iPatch].Write_t(pFile);
// Save texture width and height in MEX-es
pFile->WriteFullChunk_t( CChunkID("STXW"), &md_Width, sizeof(MEX));
pFile->WriteFullChunk_t( CChunkID("STXH"), &md_Height, sizeof(MEX));
// Save value for shading type
pFile->Write_t( &md_ShadowQuality, sizeof(SLONG));
// Save static stretch value
pFile->Write_t( &md_Stretch, sizeof(FLOAT3D));
// Save model offset
pFile->Write_t( &md_vCenter, sizeof(FLOAT3D));
// Save count of collision boxes
INDEX ctCollisionBoxes = md_acbCollisionBox.Count();
pFile->Write_t( &ctCollisionBoxes, sizeof(INDEX));
md_acbCollisionBox.Lock();
// save all collision boxes
for( INDEX iCollisionBox=0; iCollisionBox<ctCollisionBoxes; iCollisionBox++)
{
// save current collision box
md_acbCollisionBox[ iCollisionBox].Write_t( pFile);
}
md_acbCollisionBox.Unlock();
// save boolean defining collision type for this model
pFile->WriteID_t( CChunkID( "COLI"));
*pFile << md_bCollideAsCube;
// Save count of attached positions
INDEX ctAttachedPositions = md_aampAttachedPosition.Count();
*pFile << ctAttachedPositions;
FOREACHINDYNAMICARRAY(md_aampAttachedPosition, CAttachedModelPosition, itamp)
{
itamp->Write_t(pFile);
}
// Save color names (get count of valid names, write count and then write existing names)
INDEX iValidColorsCt = 0;
for( i=0; i<MAX_COLOR_NAMES; i++)
if( md_ColorNames[ i] != "")
iValidColorsCt++;
pFile->WriteFullChunk_t( CChunkID("ICLN"), &iValidColorsCt, sizeof(INDEX));
for( i=0; i<MAX_COLOR_NAMES; i++)
{
if( md_ColorNames[ i] != "")
{
*pFile << i;
*pFile << md_ColorNames[ i];
}
}
// Save AnimData
CAnimData::Write_t( pFile);
IndicesToPtrs();
*pFile << md_colDiffuse;
*pFile << md_colReflections;
*pFile << md_colSpecular;
*pFile << md_colBump;
}
//------------------------------------------ READ
void CModelData::Read_t( CTStream *pFile) // throw char *
{
INDEX i;
_bHasAlpha = FALSE;
// Read main ID
pFile->ExpectID_t( CChunkID("MDAT"));
// Check version number
BOOL bHasSavedCenter = FALSE;
BOOL bHasMultipleCollisionBoxes = FALSE;
BOOL bHasAttachedPositions = FALSE;
BOOL bHasPolygonalPatches = FALSE;
BOOL bHasPolygonsPerSurface = FALSE;
BOOL bHasSavedFlagsOnStart = FALSE;
BOOL bHasColorForReflectionAndSpecularity = FALSE;
BOOL bHasDiffuseColor = FALSE;
// get version ID
CChunkID idVersion = pFile->GetID_t();
// if this is version without stretch center then it doesn't contain multiple
// collision boxes also
if( CChunkID( MODEL_VERSION_WITHOUT_STRETCH_CENTER) == idVersion)
{
}
// if model has stretch center but does not have multiple collision boxes
else if( CChunkID( MODEL_VERSION_WITHOUT_MULTIPLE_COLLISION_BOXES) == idVersion)
{
bHasSavedCenter = TRUE;
}
else if( CChunkID( MODEL_VERSION_WITHOUT_ATTACHED_POSITIONS) == idVersion)
{
bHasSavedCenter = TRUE;
bHasMultipleCollisionBoxes = TRUE;
}
else if( CChunkID( MODEL_VERSION_WITHOUT_POLYGONAL_PATCHES) == idVersion)
{
bHasSavedCenter = TRUE;
bHasMultipleCollisionBoxes = TRUE;
bHasAttachedPositions = TRUE;
}
else if( CChunkID( MODEL_VERSION_WITHOUT_POLYGONS_PER_SURFACE) == idVersion)
{
bHasSavedCenter = TRUE;
bHasMultipleCollisionBoxes = TRUE;
bHasAttachedPositions = TRUE;
bHasPolygonalPatches = TRUE;
}
else if( CChunkID( MODEL_VERSION_WITHOUT_16_BIT_COMPRESSION) == idVersion)
{
bHasSavedCenter = TRUE;
bHasMultipleCollisionBoxes = TRUE;
bHasAttachedPositions = TRUE;
bHasPolygonalPatches = TRUE;
bHasPolygonsPerSurface = TRUE;
}
// if has saved flags on start - because 16-bit compression
else if( CChunkID( MODEL_VERSION_WITHOUT_REFLECTION_AND_SPECULARITY) == idVersion)
{
bHasSavedCenter = TRUE;
bHasMultipleCollisionBoxes = TRUE;
bHasAttachedPositions = TRUE;
bHasPolygonalPatches = TRUE;
bHasPolygonsPerSurface = TRUE;
bHasSavedFlagsOnStart = TRUE;
}
// has saved color for reflection and specularity
else if( CChunkID( MODEL_VERSION_WITHOUT_DIFFUSE_COLOR) == idVersion)
{
bHasSavedCenter = TRUE;
bHasMultipleCollisionBoxes = TRUE;
bHasAttachedPositions = TRUE;
bHasPolygonalPatches = TRUE;
bHasPolygonsPerSurface = TRUE;
bHasSavedFlagsOnStart = TRUE;
bHasColorForReflectionAndSpecularity = TRUE;
}
// has saved diffuse color
else if( CChunkID( MODEL_VERSION) == idVersion)
{
bHasSavedCenter = TRUE;
bHasMultipleCollisionBoxes = TRUE;
bHasAttachedPositions = TRUE;
bHasPolygonalPatches = TRUE;
bHasPolygonsPerSurface = TRUE;
bHasSavedFlagsOnStart = TRUE;
bHasColorForReflectionAndSpecularity = TRUE;
bHasDiffuseColor = TRUE;
}
else
{
throw(TRANS("Invalid model version."));
}
if( bHasSavedFlagsOnStart)
{
(*pFile)>>md_Flags;
}
// Read vertices and frames ct
pFile->ReadFullChunk_t( CChunkID("IVTX"), &md_VerticesCt, sizeof(INDEX));
BYTESWAP(md_VerticesCt);
md_TransformedVertices.New( md_VerticesCt);
pFile->ReadFullChunk_t( CChunkID("IFRM"), &md_FramesCt, sizeof(INDEX));
BYTESWAP(md_FramesCt);
// read array of 8-bit or 16-bit compressed vertices
if( md_Flags & MF_COMPRESSED_16BIT)
{
md_FrameVertices16.New( md_VerticesCt * md_FramesCt);
CChunkID cidVerticesChunk = pFile->PeekID_t();
// if we are loading model in old 16-bit compressed format (normals use 1 byte)
if( cidVerticesChunk == CChunkID("AV16"))
{
CChunkID cidDummy = pFile->GetID_t();
ULONG ulDummy;
// skip chunk size
*pFile >> ulDummy;
for( INDEX iVtx=0; iVtx<md_VerticesCt * md_FramesCt; iVtx++)
{
(*pFile)>>md_FrameVertices16[iVtx];
// convert 8-bit normal from index into normal defined using heading and pitch
INDEX i8BitNormalIndex = md_FrameVertices16[iVtx].mfv_ubNormH;
const FLOAT3D &vNormal = avGouraudNormals[i8BitNormalIndex];
CompressNormal_HQ( vNormal, md_FrameVertices16[iVtx].mfv_ubNormH, md_FrameVertices16[iVtx].mfv_ubNormP);
}
}
// load new 16-bit compressed format (normals use 2 byte) model
else if( cidVerticesChunk == CChunkID("AV17"))
{
pFile->ReadFullChunk_t( CChunkID("AV17"), &md_FrameVertices16[ 0], md_VerticesCt * md_FramesCt *
sizeof(struct ModelFrameVertex16));
#if PLATFORM_BIGENDIAN
for (ULONG i = 0; i < md_VerticesCt * md_FramesCt; i++)
{
BYTESWAP(md_FrameVertices16[i].mfv_SWPoint.vector[0]);
BYTESWAP(md_FrameVertices16[i].mfv_SWPoint.vector[1]);
BYTESWAP(md_FrameVertices16[i].mfv_SWPoint.vector[2]);
}
#endif
}
else
{
ThrowF_t( TRANS("Expecting chunk ID for model frame vertices but found %s"), (const char *) cidVerticesChunk);
}
}
else
{
md_FrameVertices8.New( md_VerticesCt * md_FramesCt);
pFile->ReadFullChunk_t( CChunkID("AFVX"), &md_FrameVertices8[ 0], md_VerticesCt * md_FramesCt *
sizeof(struct ModelFrameVertex8));
}
// Allocate and Read frame info array
md_FrameInfos.New( md_FramesCt);
pFile->ReadFullChunk_t( CChunkID("AFIN"), &md_FrameInfos[0], md_FramesCt * sizeof(struct ModelFrameInfo));
#if PLATFORM_BIGENDIAN
for (ULONG i = 0; i < md_FramesCt; i++)
{
BYTESWAP(md_FrameInfos[i].mfi_Box.minvect.vector[0]);
BYTESWAP(md_FrameInfos[i].mfi_Box.minvect.vector[1]);
BYTESWAP(md_FrameInfos[i].mfi_Box.minvect.vector[2]);
BYTESWAP(md_FrameInfos[i].mfi_Box.maxvect.vector[0]);
BYTESWAP(md_FrameInfos[i].mfi_Box.maxvect.vector[1]);
BYTESWAP(md_FrameInfos[i].mfi_Box.maxvect.vector[2]);
}
#endif
// Allocate Read frame main mip vertices array
md_MainMipVertices.New( md_VerticesCt);
pFile->ReadFullChunk_t( CChunkID("AMMV"), &md_MainMipVertices[0], md_VerticesCt * sizeof(FLOAT3D));
#if PLATFORM_BIGENDIAN
for (ULONG i = 0; i < md_VerticesCt; i++)
{
BYTESWAP(md_MainMipVertices[i].vector[0]);
BYTESWAP(md_MainMipVertices[i].vector[1]);
BYTESWAP(md_MainMipVertices[i].vector[2]);
}
#endif
// Allocate and Read vertex mip-mask array
md_VertexMipMask.New( md_VerticesCt);
pFile->ReadFullChunk_t( CChunkID("AVMK"), &md_VertexMipMask[0], md_VerticesCt * sizeof(ULONG));
#if PLATFORM_BIGENDIAN
for (ULONG i = 0; i < md_VerticesCt; i++)
{
BYTESWAP(md_VertexMipMask[i]);
}
#endif
// Read mip levels counter
pFile->ReadFullChunk_t( CChunkID("IMIP"), &md_MipCt, sizeof(INDEX));
BYTESWAP(md_MipCt);
// Read mip factors array
pFile->ReadFullChunk_t( CChunkID("FMIP"), &md_MipSwitchFactors[0], MAX_MODELMIPS * sizeof(float));
#if PLATFORM_BIGENDIAN
for (ULONG i = 0; i < MAX_MODELMIPS; i++)
{
BYTESWAP(md_MipSwitchFactors[i]);
}
#endif
// Read all model mip infos
INDEX ctMipsRejected=0;
for( i=0; i<md_MipCt; i++) {
ModelMipInfo mmiDummy; // need one dummy mipmodel info in case of mip level rejection
// reject mip model in case its even, and not last
if( !mdl_bFineQuality && (i%2)==1 && i!=(md_MipCt-1)) {
mmiDummy.Read_t( pFile, bHasPolygonalPatches, bHasPolygonsPerSurface, bHasDiffuseColor);
mmiDummy.Clear();
ctMipsRejected++;
} else {
// Notice that model's difuse color has been saved in same model format change when surface color has been added
md_MipInfos[i-ctMipsRejected].Read_t( pFile, bHasPolygonalPatches, bHasPolygonsPerSurface, bHasDiffuseColor);
// readjust mip scaling factors (if)
if( i>0) md_MipSwitchFactors[i-ctMipsRejected-1] = md_MipSwitchFactors[i-1];
}
}
// readjust last mip scaling factor
md_MipSwitchFactors[i-ctMipsRejected-1] = md_MipSwitchFactors[i-1];
// reduce mip level count
md_MipCt -= ctMipsRejected;
// if patches are saved in old format
CChunkID cidPatchChunkID = pFile->PeekID_t();
if( cidPatchChunkID == CChunkID("STMK"))
{
ULONG ulOldExistingPatches;
pFile->ReadFullChunk_t( CChunkID("STMK"), &ulOldExistingPatches, sizeof(ULONG));
BYTESWAP(ulOldExistingPatches);
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
if( ((1UL << iPatch) & ulOldExistingPatches) != 0)
{
CTFileName fnPatchName;
*pFile >> fnPatchName;
try
{
md_mpPatches[ iPatch].mp_toTexture.SetData_t( fnPatchName);
}
catch(char *strError)
{
(void) strError;
}
}
}
}
// if patches are saved in new format
else if( cidPatchChunkID == CChunkID("PTC2"))
{
pFile->ExpectID_t( CChunkID("PTC2"));
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
try
{
md_mpPatches[ iPatch].Read_t(pFile);
}
catch(char *strError)
{
(void) strError;
}
}
}
else
{
ThrowF_t(TRANS("Expecting chunk containing patch data but found unrecognisable chunk ID."));
}
// Read texture width and height in MEX-es
pFile->ReadFullChunk_t( CChunkID("STXW"), &md_Width, sizeof(MEX));
pFile->ReadFullChunk_t( CChunkID("STXH"), &md_Height, sizeof(MEX));
BYTESWAP(md_Width);
BYTESWAP(md_Height);
// in old patch format, now patch postiions are loaded
if( cidPatchChunkID == CChunkID("STMK"))
{
pFile->ExpectID_t( CChunkID("POSS"));
ULONG ulChunkSize;
*pFile >> ulChunkSize;
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
// Read patch position
*pFile >> md_mpPatches[ iPatch].mp_mexPosition;
}
}
if( !bHasSavedFlagsOnStart)
{
// Read flags
(*pFile)>>md_Flags;
}
// Read value for shading type
(*pFile)>>md_ShadowQuality;
// Read static stretch value
(*pFile)>>md_Stretch;
// if this is model with saved center
if( bHasSavedCenter) { // read it
(*pFile)>>md_vCenter;
}
// this model has been saved without center point
else { // so just reset it
md_vCenter = FLOAT3D(0,0,0);
}
// convert model to 8-bit if requested and needed
if( !mdl_bFineQuality && (md_Flags&MF_COMPRESSED_16BIT))
{
// prepare 8-bit frame vertices array
const INDEX ctVtx = md_VerticesCt * md_FramesCt;
md_FrameVertices8.New(ctVtx);
// loop thru vertices
for( INDEX iVtx=0; iVtx<ctVtx; iVtx++) {
ModelFrameVertex16 &mfv16 = md_FrameVertices16[iVtx];
ModelFrameVertex8 &mfv8 = md_FrameVertices8[iVtx];
// convert vertex coordinate
mfv8.mfv_SBPoint(1) = mfv16.mfv_SWPoint(1) >>8;
mfv8.mfv_SBPoint(2) = mfv16.mfv_SWPoint(2) >>8;
mfv8.mfv_SBPoint(3) = mfv16.mfv_SWPoint(3) >>8;
// convert normal
const INDEX iHofs = mfv16.mfv_ubNormH>>1;
const INDEX iPofs = mfv16.mfv_ubNormP>>1;
mfv8.mfv_NormIndex = aubGouraudConv[iHofs*128+iPofs];
}
// done with conversion
md_Stretch *= 256.0f;
md_FrameVertices16.Clear();
md_Flags &= ~MF_COMPRESSED_16BIT;
}
// create compressed vector center that will be used for setting object handle
md_vCompressedCenter(1) = -md_vCenter(1)/md_Stretch(1);
md_vCompressedCenter(2) = -md_vCenter(2)/md_Stretch(2);
md_vCompressedCenter(3) = -md_vCenter(3)/md_Stretch(3);
// if model has been saved with multiple collision boxes
if( bHasMultipleCollisionBoxes)
{
INDEX ctCollisionBoxes;
// get count of collision boxes
(*pFile)>>ctCollisionBoxes;
// add needed ammount of members
md_acbCollisionBox.New( ctCollisionBoxes);
md_acbCollisionBox.Lock();
// for all saved collision boxes
for( INDEX iCollisionBox=0; iCollisionBox<ctCollisionBoxes; iCollisionBox++)
{
// load current collision box from stream (without name)
md_acbCollisionBox[iCollisionBox].Read_t( pFile);
// load name manualy
md_acbCollisionBox[iCollisionBox].ReadName_t( pFile);
}
md_acbCollisionBox.Unlock();
}
// else add one collision box and load it manually
else
{
// add one collision box
md_acbCollisionBox.New();
md_acbCollisionBox.Lock();
// read one collision box manualy (without name)
md_acbCollisionBox[ 0].Read_t( pFile);
md_acbCollisionBox.Unlock();
}
// peek chunk ID and see if we should read boolean defining collision type (speheres or cube)
if ( pFile->PeekID_t()==CChunkID("COLI"))
{
pFile->ExpectID_t("COLI");
*pFile >> md_bCollideAsCube;
}
else
{
md_bCollideAsCube = FALSE;
}
// if we should read attached positions
if( bHasAttachedPositions)
{
// read count of attached positions
INDEX ctAttachedPositions;
*pFile >> ctAttachedPositions;
md_aampAttachedPosition.New(ctAttachedPositions);
FOREACHINDYNAMICARRAY(md_aampAttachedPosition, CAttachedModelPosition, itamp)
{
itamp->Read_t(pFile);
// clamp vertices to no of model data vertices
itamp->amp_iCenterVertex = Clamp( itamp->amp_iCenterVertex, (INDEX) 0, md_MainMipVertices.Count());
itamp->amp_iFrontVertex = Clamp( itamp->amp_iFrontVertex, (INDEX) 0, md_MainMipVertices.Count());
itamp->amp_iUpVertex = Clamp( itamp->amp_iUpVertex, (INDEX) 0, md_MainMipVertices.Count());
}
}
// Read color names (Read count, read existing names)
INDEX iValidColorsCt;
pFile->ReadFullChunk_t( CChunkID("ICLN"), &iValidColorsCt, sizeof(INDEX));
for( i=0; i<iValidColorsCt; i++)
{
INDEX iExistingColorName;
*pFile >> iExistingColorName;
*pFile >> md_ColorNames[ iExistingColorName];
}
// Read AnimData
CAnimData::Read_t( pFile);
IndicesToPtrs();
// old models don't have saved polygons per surface
if( !bHasPolygonsPerSurface)
{
// so link them manually
LinkDataForSurfaces(TRUE);
// for each mip model
for( INDEX iMip = 0; iMip<md_MipCt; iMip ++)
{
ModelMipInfo *pMMI = &md_MipInfos[ iMip];
// for each surface
for( INDEX iSurface=0; iSurface<pMMI->mmpi_MappingSurfaces.Count(); iSurface++)
{
// convert rendering flags into new flags format (per surface)
if (pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons.Count()>0)
{
ULONG ulFlags = pMMI->mmpi_Polygons[pMMI->mmpi_MappingSurfaces[iSurface].ms_aiPolygons[0]].mp_RenderFlags;
pMMI->mmpi_MappingSurfaces[iSurface].SetRenderingParameters(ulFlags);
}
}
}
}
// turn on diffuse map for all models of old format
if( !bHasColorForReflectionAndSpecularity)
{
for( INDEX iMip = 0; iMip<md_MipCt; iMip ++)
{
ModelMipInfo *pMMI = &md_MipInfos[ iMip];
for( INDEX iSurface=0; iSurface<pMMI->mmpi_MappingSurfaces.Count(); iSurface++)
{
pMMI->mmpi_MappingSurfaces[iSurface].ms_ulRenderingFlags |= SRF_DIFFUSE|SRF_NEW_TEXTURE_FORMAT;
}
}
}
if( bHasDiffuseColor)
{
*pFile >> md_colDiffuse;
}
// old models don't have saved colors for reflection and specularity
if( bHasColorForReflectionAndSpecularity)
{
// load colors for reflections, specularity and bump
*pFile >> md_colReflections;
*pFile >> md_colSpecular;
*pFile >> md_colBump;
}
md_bHasAlpha = _bHasAlpha;
// precalculate rendering data
extern void PrepareModelForRendering(CModelData &md);
PrepareModelForRendering(*this);
}
// reference counting (override from CAnimData)
void CModelData::RemReference_internal(void)
{
// if this model is part of edit model object
if (md_bIsEdited) {
// just unreference it
MarkUnused();
// if this model is part of model stock
} else {
// release it
_pModelStock->Release(this);
}
}
/* Get the description of this object. */
CTString CModelData::GetDescription(void)
{
CTString str;
ModelMipInfo &mmi0 = md_MipInfos[0];
str.PrintF("%d mips, %d anims, %d frames, %d vtx, %d svx, %d tri",
md_MipCt, ad_NumberOfAnims, md_FramesCt,
mmi0.mmpi_ctMipVx, mmi0.mmpi_ctSrfVx, mmi0.mmpi_ctTriangles);
return str;
}
//--------------------------------------------------------------------------------------------
void ModelMipInfo::Clear()
{
mmpi_PolygonsCt = 0; // reset number of polygons
mmpi_Polygons.Clear(); // clear static arrays ...
mmpi_TextureVertices.Clear();
mmpi_MappingSurfaces.Clear();
mmpi_aPolygonsPerPatch.Clear();
}
//--------------------------------------------------------------------------------------------
ModelPolygon::ModelPolygon()
{
mp_RenderFlags = 0;
}
//--------------------------------------------------------------------------------------------
ModelPolygon::~ModelPolygon()
{
mp_PolygonVertices.Clear();
}
//--------------------------------------------------------------------------------------------
ModelMipInfo::~ModelMipInfo()
{
Clear();
}
//--------------------------------------------------------------------------------------------
MappingSurface::~MappingSurface()
{
}
//--------------------------------------------------------------------------------------------
MappingSurface::MappingSurface()
{
ms_ulRenderingFlags &= ~SRF_SELECTED;
ms_colDiffuse = C_WHITE|CT_OPAQUE;
ms_colReflections = C_WHITE|CT_OPAQUE;
ms_colSpecular = C_WHITE|CT_OPAQUE;
ms_colBump = C_WHITE|CT_OPAQUE;
ms_ulOnColor = SC_ALLWAYS_ON;
ms_ulOffColor = SC_ALLWAYS_OFF;
}
//--------------------------------------------------------------------------------------------
/*
* Object constructor
*/
CModelObject::CModelObject()
{
mo_colBlendColor = 0xFFFFFFFF;
mo_ColorMask = 0x7FFFFFFF;
mo_PatchMask = 0;
mo_iManualMipLevel = 0;
mo_AutoMipModeling = TRUE;
mo_Stretch = FLOAT3D(1,1,1);
}
/*
* Destructor
*/
CModelObject::~CModelObject()
{
for(INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
HidePatch( iPatch);
}
RemoveAllAttachmentModels();
}
// copy from another object of same class
void CModelObject::Copy(CModelObject &moOther)
{
CAnimObject::Copy(moOther);
mo_PatchMask = moOther.mo_PatchMask ;
mo_iManualMipLevel = moOther.mo_iManualMipLevel;
mo_AutoMipModeling = moOther.mo_AutoMipModeling;
mo_Stretch = moOther.mo_Stretch ;
mo_ColorMask = moOther.mo_ColorMask ;
mo_iLastRenderMipLevel = moOther.mo_iLastRenderMipLevel;
mo_colBlendColor = moOther.mo_colBlendColor ;
mo_toTexture .Copy(moOther.mo_toTexture );
mo_toReflection .Copy(moOther.mo_toReflection );
mo_toSpecular .Copy(moOther.mo_toSpecular );
mo_toBump .Copy(moOther.mo_toBump );
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, moOther.mo_lhAttachments, itamo) {
CAttachmentModelObject &amoOther = *itamo;
CAttachmentModelObject &amo = *AddAttachmentModel(amoOther.amo_iAttachedPosition);
amo.amo_plRelative = amoOther.amo_plRelative;
amo.amo_moModelObject.Copy(amoOther.amo_moModelObject);
}
}
// synchronize with another model (copy animations/attachments positions etc from there)
void CModelObject::Synchronize(CModelObject &moOther)
{
// synchronize animation
CAnimObject::Synchronize(moOther);
// synchronize misc parameters
mo_PatchMask = moOther.mo_PatchMask ;
mo_Stretch = moOther.mo_Stretch ;
mo_ColorMask = moOther.mo_ColorMask ;
mo_colBlendColor = moOther.mo_colBlendColor ;
CModelData *pmd = GetData();
CModelData *pmdOther = moOther.GetData();
if (pmd==NULL || pmdOther==NULL) {
return;
}
// for each attachment in another object
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, moOther.mo_lhAttachments, itamo) {
CAttachmentModelObject *pamoOther = itamo;
INDEX iap = pamoOther ->amo_iAttachedPosition;
// get one here with same index
CAttachmentModelObject *pamo = GetAttachmentModel(iap);
// if found
if (pamo!=NULL) {
// sync the model itself
pamo->amo_moModelObject.Synchronize(pamoOther->amo_moModelObject);
// get original placements of both attachments
pmd->md_aampAttachedPosition.Lock();
pmdOther->md_aampAttachedPosition.Lock();
CPlacement3D plOrg = pmd->md_aampAttachedPosition[iap].amp_plRelativePlacement;
CPlacement3D plOtherOrg = pmdOther->md_aampAttachedPosition[iap].amp_plRelativePlacement;
pmd->md_aampAttachedPosition.Unlock();
pmdOther->md_aampAttachedPosition.Unlock();
FLOAT3D &v2 = pamo->amo_plRelative.pl_PositionVector;
FLOAT3D &v1 = pamoOther->amo_plRelative.pl_PositionVector;
FLOAT3D &v2O = plOrg.pl_PositionVector;
FLOAT3D &v1O = plOtherOrg.pl_PositionVector;
ANGLE3D &a2 = pamo->amo_plRelative.pl_OrientationAngle;
ANGLE3D &a1 = pamoOther->amo_plRelative.pl_OrientationAngle;
ANGLE3D &a2O = plOrg.pl_OrientationAngle;
ANGLE3D &a1O = plOtherOrg.pl_OrientationAngle;
v2 = v2O+v1-v1O;
a2 = a2O+a1-a1O;
}
}
}
//--------------------------------------------------------------------------------------------
//------------------------------------------ WRITE
void CModelObject::Write_t( CTStream *pFile) // throw char *
{
CAnimObject::Write_t( pFile);
pFile->WriteID_t( CChunkID( "MODT"));
*pFile << mo_colBlendColor;
pFile->Write_t( &mo_PatchMask, sizeof(ULONG));
pFile->Write_t( &mo_Stretch, sizeof(FLOAT3D));
pFile->Write_t( &mo_ColorMask, sizeof(ULONG));
}
//------------------------------------------ READ
void CModelObject::Read_t( CTStream *pFile) // throw char *
{
CAnimObject::Read_t( pFile);
if( pFile->PeekID_t()==CChunkID("MODT"))
{
pFile->ExpectID_t( CChunkID( "MODT"));
*pFile >> mo_colBlendColor;
}
// model object is saved without dynamic blend color
else
{
mo_colBlendColor = 0xFFFFFFFF;
}
(*pFile)>>mo_PatchMask;
(*pFile)>>mo_Stretch;
(*pFile)>>mo_ColorMask;
for( INDEX i=0; i<MAX_TEXTUREPATCHES; i++)
{
if( (mo_PatchMask & ((1UL) << i)) != 0)
{
ShowPatch( i);
}
}
}
// retrieves model's texture width
MEX CModelObject::GetWidth()
{
return GetData()->md_Width;
};
// retrieves model's texture height
MEX CModelObject::GetHeight()
{
return GetData()->md_Height;
};
//--------------------------------------------------------------------------------------------
/*
* Routine retrives full model data
*/
void CModelObject::GetModelInfo(CModelInfo &miInfo)
{
CModelData *pMD = (CModelData *) GetData();
ASSERT( pMD != NULL);
// copy model data values
miInfo.mi_VerticesCt = pMD->md_VerticesCt;
miInfo.mi_FramesCt = pMD->md_FramesCt;
miInfo.mi_MipCt = pMD->md_MipCt;
for( INDEX i=0; i<pMD->md_MipCt; i++)
{
miInfo.mi_MipInfos[ i].mi_PolygonsCt = pMD->md_MipInfos[ i].mmpi_PolygonsCt;
// calculate triangeles
miInfo.mi_MipInfos[ i].mi_TrianglesCt = 0;
for( INDEX iPolygon = 0; iPolygon<pMD->md_MipInfos[ i].mmpi_PolygonsCt; iPolygon++)
{
miInfo.mi_MipInfos[ i].mi_TrianglesCt +=
pMD->md_MipInfos[ i].mmpi_Polygons[ iPolygon].mp_PolygonVertices.Count()-2;
}
ULONG ulMipMask = (1L) << i; // working mip model's mask
INDEX iVertexCt = 0;
// count vertices that exists in this mip model
for( INDEX j=0; j<pMD->md_VerticesCt; j++)
if( pMD->md_VertexMipMask[ j] & ulMipMask)
iVertexCt ++;
miInfo.mi_MipInfos[ i].mi_VerticesCt = iVertexCt;
}
miInfo.mi_Width = pMD->md_Width;
miInfo.mi_Height = pMD->md_Height;
miInfo.mi_Flags = pMD->md_Flags;
miInfo.mi_ShadowQuality = pMD->md_ShadowQuality;
miInfo.mi_Stretch = pMD->md_Stretch;
}
//--------------------------------------------------------------------------------------------
/*
* Is model visible for given mip factor
*/
BOOL CModelObject::IsModelVisible( FLOAT fMipFactor)
{
CModelData *pMD = (CModelData*)GetData();
ASSERT( pMD != NULL);
ASSERT( pMD->md_MipCt>0);
// visible if no mip models or disappearence not allowed
if( pMD->md_MipCt==0 || mdl_iLODDisappear==0) return TRUE;
// adjust mip factor in case of dynamic stretch factor
if( mo_Stretch != FLOAT3D(1,1,1)) {
fMipFactor -= Log2( Max(mo_Stretch(1),Max(mo_Stretch(2),mo_Stretch(3))));
}
// eventually adjusted mip factor with LOD control variables
if( mdl_iLODDisappear==2) fMipFactor = fMipFactor*mdl_fLODMul +mdl_fLODAdd;
// return true if mip factor is smaller than last in model's mip switch factors array
return( fMipFactor < pMD->md_MipSwitchFactors[pMD->md_MipCt-1]);
}
//--------------------------------------------------------------------------------------------
/*
* Routine retrieves activ mip model's index
*/
INDEX CModelObject::GetMipModel( FLOAT fMipFactor)
{
CModelData *pMD = (CModelData*)GetData();
ASSERT( pMD != NULL);
if( !mo_AutoMipModeling) return mo_iManualMipLevel;
// calculate current mip model
INDEX i;
for( i=0; i<pMD->md_MipCt; i++) {
if( fMipFactor < pMD->md_MipSwitchFactors[i]) return i;
}
return i-1;
}
//--------------------------------------------------------------------------------------------
/*
* retrieves bounding box of given frame
*/
FLOATaabbox3D CModelObject::GetFrameBBox( INDEX iFrameNo)
{
CModelData *pMD = (CModelData *) GetData();
ASSERT( pMD != NULL);
return pMD->md_FrameInfos[ iFrameNo].mfi_Box;
}
//--------------------------------------------------------------------------------------------
/*
* Routine returns mo_AutoMipModeling flag
*/
BOOL CModelObject::IsAutoMipModeling()
{
return mo_AutoMipModeling;
}
//--------------------------------------------------------------------------------------------
/*
* Sets mo_AutoMipModeling flag to on
*/
void CModelObject::AutoMipModelingOn()
{
mo_AutoMipModeling = TRUE;
MarkChanged();
}
//--------------------------------------------------------------------------------------------
/*
* Sets mo_AutoMipModeling flag to off
*/
void CModelObject::AutoMipModelingOff()
{
mo_AutoMipModeling = FALSE;
MarkChanged();
}
//--------------------------------------------------------------------------------------------
/*
* Routine retrieves current mip level
*/
INDEX CModelObject::GetManualMipLevel()
{
return mo_iManualMipLevel;
}
//--------------------------------------------------------------------------------------------
/*
* Routine sets current mip level
*/
void CModelObject::SetManualMipLevel(INDEX iNewMipLevel)
{
mo_iManualMipLevel = iNewMipLevel;
MarkChanged();
}
//--------------------------------------------------------------------------------------------
/*
* Routine sets given mip-level's switch factor
*/
void CModelObject::SetMipSwitchFactor(INDEX iMipLevel, float fMipFactor)
{
CModelData *pMD = (CModelData *) GetData();
ASSERT( iMipLevel < pMD->md_MipCt);
pMD->md_MipSwitchFactors[ iMipLevel] = fMipFactor;
MarkChanged();
}
//--------------------------------------------------------------------------------------------
/*
* Select one rougher mip model level
*/
void CModelObject::NextManualMipLevel()
{
CModelData *pMD = (CModelData *) GetData();
if( mo_iManualMipLevel < pMD->md_MipCt-1)
{
mo_iManualMipLevel += 1;
MarkChanged();
}
}
//--------------------------------------------------------------------------------------------
/*
* Select one more precize mip model level
*/
void CModelObject::PrevManualMipLevel()
{
if( mo_iManualMipLevel > 0)
{
mo_iManualMipLevel -= 1;
MarkChanged();
}
}
// this function returns current value of patches mask
ULONG CModelObject::GetPatchesMask()
{
return mo_PatchMask;
};
// use this function to set new patches combination
void CModelObject::SetPatchesMask(ULONG new_patches_mask)
{
mo_PatchMask = new_patches_mask;
}
//--------------------------------------------------------------------------------------------
/*
* Sets new name to a color with given index
*/
void CModelObject::SetColorName( INDEX iColor, CTString &strNewName)
{
CModelData *pMD = (CModelData *) GetData();
ASSERT( iColor < MAX_COLOR_NAMES);
pMD->md_ColorNames[ iColor] = strNewName;
}
//--------------------------------------------------------------------------------------------
/*
* Retrieves name of color with given index
*/
CTString CModelObject::GetColorName( INDEX iColor)
{
CModelData *pMD = (CModelData *) GetData();
ASSERT( iColor < MAX_COLOR_NAMES);
return pMD->md_ColorNames[ iColor];
}
//--------------------------------------------------------------------------------------------
/*
* Retrieves color of given surface
*/
COLOR CModelObject::GetSurfaceColor( INDEX iCurrentMip, INDEX iCurrentSurface)
{
struct ModelPolygon *pPoly;
CModelData *pMD = (CModelData *) GetData();
if( (iCurrentMip>=pMD->md_MipCt) ||
(iCurrentSurface>=pMD->md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces.Count()) )
{
return (COLOR) -1; // !!! FIXME COLOR is unsigned, right?
}
for( INDEX i=0; i<pMD->md_MipInfos[ iCurrentMip].mmpi_PolygonsCt; i++)
{
pPoly = &pMD->md_MipInfos[ iCurrentMip].mmpi_Polygons[ i];
if( pPoly->mp_Surface == iCurrentSurface)
{
return pPoly->mp_ColorAndAlpha;
}
}
return 0;
}
//--------------------------------------------------------------------------------------------
/*
* Changes color of given surface
*/
void CModelObject::SetSurfaceColor( INDEX iCurrentMip, INDEX iCurrentSurface,
COLOR colNewColorAndAlpha)
{
struct ModelPolygon *pPoly;
CModelData *pMD = (CModelData *) GetData();
if( (iCurrentMip>=pMD->md_MipCt) ||
(iCurrentSurface>=pMD->md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces.Count()) )
{
return;
}
pMD->md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces[iCurrentSurface].ms_colColor = colNewColorAndAlpha;
for( INDEX i=0; i<pMD->md_MipInfos[ iCurrentMip].mmpi_PolygonsCt; i++)
{
pPoly = &pMD->md_MipInfos[ iCurrentMip].mmpi_Polygons[ i];
if( pPoly->mp_Surface == iCurrentSurface)
{
pPoly->mp_ColorAndAlpha = colNewColorAndAlpha;
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Retrieves rendering flags of given surface
*/
void CModelObject::GetSurfaceRenderFlags( INDEX iCurrentMip, INDEX iCurrentSurface,
enum SurfaceShadingType &sstShading, enum SurfaceTranslucencyType &sttTranslucency,
ULONG &ulRenderingFlags)
{
CModelData *pMD = (CModelData *) GetData();
if( (iCurrentMip>=pMD->md_MipCt) ||
(iCurrentSurface>=pMD->md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces.Count()) )
{
return;
}
MappingSurface *pms = &pMD->md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces[iCurrentSurface];
sstShading = pms->ms_sstShadingType;
sttTranslucency = pms->ms_sttTranslucencyType;
ulRenderingFlags = pms->ms_ulRenderingFlags;
}
//--------------------------------------------------------------------------------------------
/*
* Changes rendering of given surface
*/
void CModelObject::SetSurfaceRenderFlags( INDEX iCurrentMip, INDEX iCurrentSurface,
enum SurfaceShadingType sstShading, enum SurfaceTranslucencyType sttTranslucency,
ULONG ulRenderingFlags)
{
CModelData *pMD = (CModelData *) GetData();
if( (iCurrentMip>=pMD->md_MipCt) ||
(iCurrentSurface>=pMD->md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces.Count()) )
{
return;
}
// convert surface rendering parameters from old polygon flags -- temporary !!!!
MappingSurface *pms = &pMD->md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces[iCurrentSurface];
pms->ms_sstShadingType = sstShading;
pms->ms_sttTranslucencyType = sttTranslucency;
pms->ms_ulRenderingFlags = ulRenderingFlags;
}
//--------------------------------------------------------------------------------------------
void CModelObject::ProjectFrameVertices( CProjection3D *pProjection, INDEX iMipModel)
{
FLOAT3D f3dVertex;
CModelData *pMD = (CModelData *) GetData();
pProjection->ObjectHandleL() = pMD->md_vCompressedCenter;
pProjection->ObjectStretchL() = pMD->md_Stretch;
// apply dynamic stretch
pProjection->ObjectStretchL()(1) *= mo_Stretch(1);
pProjection->ObjectStretchL()(2) *= mo_Stretch(2);
pProjection->ObjectStretchL()(3) *= mo_Stretch(3);
pProjection->ObjectFaceForwardL() = pMD->md_Flags & (MF_FACE_FORWARD|MF_HALF_FACE_FORWARD);
pProjection->ObjectHalfFaceForwardL() = pMD->md_Flags & MF_HALF_FACE_FORWARD;
pProjection->Prepare();
INDEX iCurrentFrame = GetFrame();
ULONG ulVtxMask = (1L) << iMipModel;
if( pMD->md_Flags & MF_COMPRESSED_16BIT)
{
ModelFrameVertex16 *pFrame = &pMD->md_FrameVertices16[ iCurrentFrame * pMD->md_VerticesCt];
for( INDEX i=0; i<pMD->md_VerticesCt; i++)
{
if( pMD->md_VertexMipMask[ i] & ulVtxMask)
{
f3dVertex(1) = (float) pFrame[ i].mfv_SWPoint(1);
f3dVertex(2) = (float) pFrame[ i].mfv_SWPoint(2);
f3dVertex(3) = (float) pFrame[ i].mfv_SWPoint(3);
pProjection->ProjectCoordinate( f3dVertex, pMD->md_TransformedVertices[ i].tvd_TransformedPoint);
}
}
}
else
{
ModelFrameVertex8 *pFrame = &pMD->md_FrameVertices8[ iCurrentFrame * pMD->md_VerticesCt];
for( INDEX i=0; i<pMD->md_VerticesCt; i++)
{
if( pMD->md_VertexMipMask[ i] & ulVtxMask)
{
f3dVertex(1) = (float) pFrame[ i].mfv_SBPoint(1);
f3dVertex(2) = (float) pFrame[ i].mfv_SBPoint(2);
f3dVertex(3) = (float) pFrame[ i].mfv_SBPoint(3);
pProjection->ProjectCoordinate( f3dVertex, pMD->md_TransformedVertices[ i].tvd_TransformedPoint);
}
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Colorizes surfaces touching given box
*/
void CModelObject::ColorizeRegion( CDrawPort *pDP, CProjection3D *pProjection, PIXaabbox2D box,
INDEX iChoosedColor, BOOL bOnColorMode)
{
struct ModelPolygon *pPoly;
CModelData *pMD = (CModelData *) GetData();
struct TransformedVertexData *pTransformedVertice;
PIX pixDPHeight = pDP->GetHeight();
// project vertices for given mip model
ProjectFrameVertices( pProjection, mo_iLastRenderMipLevel);
for( INDEX j=0; j<pMD->md_MipInfos[ mo_iLastRenderMipLevel].mmpi_PolygonsCt; j++)
{
pPoly = &pMD->md_MipInfos[ mo_iLastRenderMipLevel].mmpi_Polygons[ j];
for( INDEX i=0; i<pPoly->mp_PolygonVertices.Count(); i++)
{
pTransformedVertice = pPoly->mp_PolygonVertices[ i].mpv_ptvTransformedVertex;
PIXaabbox2D ptBox = PIXaabbox2D( PIX2D( (SWORD) pTransformedVertice->tvd_TransformedPoint(1),
pixDPHeight - (SWORD) pTransformedVertice->tvd_TransformedPoint(2)));
if( !((box & ptBox).IsEmpty()) )
{
MappingSurface &ms = pMD->md_MipInfos[ mo_iLastRenderMipLevel].mmpi_MappingSurfaces[ pPoly->mp_Surface];
if( bOnColorMode)
{
//pPoly->mp_OnColor = 1UL << iChoosedColor;
ms.ms_ulOnColor = 1UL << iChoosedColor;
}
else
{
//pPoly->mp_OffColor = 1UL << iChoosedColor;
ms.ms_ulOffColor = 1UL << iChoosedColor;
}
break;
}
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Colorizes polygons touching given box
*/
void CModelObject::ApplySurfaceToPolygonsInRegion( CDrawPort *pDP, CProjection3D *pProjection,
PIXaabbox2D box, INDEX iSurface, COLOR colSurfaceColor)
{
// project vertices for given mip model
ProjectFrameVertices( pProjection, mo_iLastRenderMipLevel);
struct ModelPolygon *pPoly;
struct TransformedVertexData *pTransformedVertice;
CModelData *pMD = (CModelData *) GetData();
PIX pixDPHeight = pDP->GetHeight();
for( INDEX j=0; j<pMD->md_MipInfos[ mo_iLastRenderMipLevel].mmpi_PolygonsCt; j++)
{
pPoly = &pMD->md_MipInfos[ mo_iLastRenderMipLevel].mmpi_Polygons[ j];
for( INDEX i=0; i<pPoly->mp_PolygonVertices.Count(); i++)
{
pTransformedVertice = pPoly->mp_PolygonVertices[ i].mpv_ptvTransformedVertex;
PIXaabbox2D ptBox = PIXaabbox2D( PIX2D( (SWORD) pTransformedVertice->tvd_TransformedPoint(1),
pixDPHeight - (SWORD) pTransformedVertice->tvd_TransformedPoint(2)));
if( !((box & ptBox).IsEmpty()) )
{
pPoly->mp_Surface = iSurface;
pPoly->mp_ColorAndAlpha = colSurfaceColor;
break;
}
}
}
}
// unpack a vertex
void CModelObject::UnpackVertex(INDEX iFrame, INDEX iVertex, FLOAT3D &vVertex)
{
CModelData *pmd = (CModelData *) GetData();
// get decompression/stretch factors
FLOAT3D &vDataStretch = pmd->md_Stretch;
FLOAT3D &vObjectStretch = mo_Stretch;
FLOAT3D vStretch;
vStretch(1) = vDataStretch(1)*vObjectStretch(1);
vStretch(2) = vDataStretch(2)*vObjectStretch(2);
vStretch(3) = vDataStretch(3)*vObjectStretch(3);
FLOAT3D vOffset = pmd->md_vCompressedCenter;
if( pmd->md_Flags & MF_COMPRESSED_16BIT)
{
struct ModelFrameVertex16 *pFrame16 = &pmd->md_FrameVertices16[iFrame * pmd->md_VerticesCt];
vVertex(1) = (pFrame16[iVertex].mfv_SWPoint(1)-vOffset(1))*vStretch(1);
vVertex(2) = (pFrame16[iVertex].mfv_SWPoint(2)-vOffset(2))*vStretch(2);
vVertex(3) = (pFrame16[iVertex].mfv_SWPoint(3)-vOffset(3))*vStretch(3);
} else {
struct ModelFrameVertex8 *pFrame8 = &pmd->md_FrameVertices8[iFrame * pmd->md_VerticesCt];
vVertex(1) = (pFrame8[iVertex].mfv_SBPoint(1)-vOffset(1))*vStretch(1);
vVertex(2) = (pFrame8[iVertex].mfv_SBPoint(2)-vOffset(2))*vStretch(2);
vVertex(3) = (pFrame8[iVertex].mfv_SBPoint(3)-vOffset(3))*vStretch(3);
}
}
CPlacement3D CModelObject::GetAttachmentPlacement(CAttachmentModelObject &amo)
{
// project reference points to view space
FLOAT3D vCenter, vFront, vUp;
CModelData *pmd = (CModelData *) GetData();
pmd->md_aampAttachedPosition.Lock();
INDEX iPosition = amo.amo_iAttachedPosition;
INDEX iCenter = pmd->md_aampAttachedPosition[iPosition].amp_iCenterVertex;
INDEX iFront = pmd->md_aampAttachedPosition[iPosition].amp_iFrontVertex;
INDEX iUp = pmd->md_aampAttachedPosition[iPosition].amp_iUpVertex;
INDEX iFrame = GetFrame();
UnpackVertex( iFrame, iCenter, vCenter);
UnpackVertex( iFrame, iFront, vFront);
UnpackVertex( iFrame, iUp, vUp);
// make axis vectors in absolute space
FLOAT3D &vO = vCenter;
FLOAT3D vY = vUp-vCenter;
FLOAT3D vZ = vCenter-vFront;
FLOAT3D vX = vY*vZ;
vY = vZ*vX;
// make a rotation matrix from those vectors
vX.Normalize();
vY.Normalize();
vZ.Normalize();
FLOATmatrix3D mOrientation;
mOrientation(1,1) = vX(1); mOrientation(1,2) = vY(1); mOrientation(1,3) = vZ(1);
mOrientation(2,1) = vX(2); mOrientation(2,2) = vY(2); mOrientation(2,3) = vZ(2);
mOrientation(3,1) = vX(3); mOrientation(3,2) = vY(3); mOrientation(3,3) = vZ(3);
// make reference placement in absolute space
CPlacement3D plPoints;
plPoints.pl_PositionVector = vO;
DecomposeRotationMatrixNoSnap(plPoints.pl_OrientationAngle, mOrientation);
CPlacement3D pl = amo.amo_plRelative;
pl.RelativeToAbsoluteSmooth(plPoints);
pmd->md_aampAttachedPosition.Unlock();
return pl;
}
//--------------------------------------------------------------------------------------------
/*
* Find hitted polygon
*/
struct ModelPolygon *CModelObject::PolygonHit(
CPlacement3D plRay, CPlacement3D plObject, INDEX iCurrentMip, FLOAT &fHitDistance)
{
struct ModelPolygon *pResultPoly = NULL;
fHitDistance = 100000.0f;
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
FLOAT fHit;
CPlacement3D plAttachment = GetAttachmentPlacement(*itamo);
plAttachment.RelativeToAbsolute(plObject);
struct ModelPolygon *pmp = itamo->amo_moModelObject.PolygonHit(plRay, plAttachment, iCurrentMip, fHit);
if (fHit < fHitDistance) {
fHitDistance = fHit;
pResultPoly = pmp;
}
}
FLOAT fHit;
struct ModelPolygon *pmp = PolygonHitModelData((CModelData*)GetData(), plRay, plObject, iCurrentMip, fHit);
if (fHit < fHitDistance) {
fHitDistance = fHit;
pResultPoly = pmp;
}
return pResultPoly;
}
struct ModelPolygon *CModelObject::PolygonHitModelData(CModelData *pMD,
CPlacement3D plRay, CPlacement3D plObject, INDEX iCurrentMip, FLOAT &fHitDistance)
{
FLOAT fClosest = -100000.0f;
struct ModelPolygon *pPoly, *pResultPoly = NULL;
CIntersector Intersector;
CSimpleProjection3D spProjection;
spProjection.ViewerPlacementL() = plRay;
spProjection.ObjectPlacementL() = plObject;
// project vertices for given mip model
ProjectFrameVertices( &spProjection, iCurrentMip);
for( INDEX j=0; j<pMD->md_MipInfos[ iCurrentMip].mmpi_PolygonsCt; j++)
{
Intersector.Clear();
pPoly = &pMD->md_MipInfos[ iCurrentMip].mmpi_Polygons[ j];
for( INDEX i=0; i<pPoly->mp_PolygonVertices.Count(); i++)
{
// get next vertex index (first is i)
INDEX next = (i+1) % pPoly->mp_PolygonVertices.Count();
// add edge to intersection object
Intersector.AddEdge( pPoly->mp_PolygonVertices[ i].mpv_ptvTransformedVertex->tvd_TransformedPoint(1),
pPoly->mp_PolygonVertices[ i].mpv_ptvTransformedVertex->tvd_TransformedPoint(2),
pPoly->mp_PolygonVertices[ next].mpv_ptvTransformedVertex->tvd_TransformedPoint(1),
pPoly->mp_PolygonVertices[ next].mpv_ptvTransformedVertex->tvd_TransformedPoint(2));
}
if( Intersector.IsIntersecting())
{
FLOAT3D f3dTr0 = pPoly->mp_PolygonVertices[ 0].mpv_ptvTransformedVertex->tvd_TransformedPoint;
FLOAT3D f3dTr1 = pPoly->mp_PolygonVertices[ 1].mpv_ptvTransformedVertex->tvd_TransformedPoint;
FLOAT3D f3dTr2 = pPoly->mp_PolygonVertices[ 2].mpv_ptvTransformedVertex->tvd_TransformedPoint;
FLOATplane3D fplPlane = FLOATplane3D( f3dTr0, f3dTr1, f3dTr2);
FLOAT3D f3dHitted3DPoint = FLOAT3D(0,0,0);
fplPlane.GetCoordinate( 3, f3dHitted3DPoint);
if( f3dHitted3DPoint(3)<=0.0f && f3dHitted3DPoint(3)> fClosest)
{
fClosest = f3dHitted3DPoint(3);
pResultPoly = pPoly;
}
}
}
// return closest hit polygon and the distance where it was hit
fHitDistance = -fClosest;
return pResultPoly;
}
//--------------------------------------------------------------------------------------------
/*
* Colorizes hitted polygon
*/
void CModelObject::ColorizePolygon( CDrawPort *pDP, CProjection3D *projection, PIX x1, PIX y1,
INDEX iChoosedColor, BOOL bOnColorMode)
{
CPlacement3D plRay;
CPlacement3D plObjectPlacement;
projection->Prepare();
projection->RayThroughPoint( FLOAT3D( (FLOAT)x1, (FLOAT)(pDP->GetHeight()-y1), 0.0f),
plRay);
plObjectPlacement = projection->ObjectPlacementR();
FLOAT fHitDistance;
struct ModelPolygon *pPoly = PolygonHit( plRay, plObjectPlacement, mo_iLastRenderMipLevel, fHitDistance);
if( pPoly != NULL)
{
CModelData *pMD = (CModelData *) GetData();
MappingSurface &ms = pMD->md_MipInfos[ mo_iLastRenderMipLevel].mmpi_MappingSurfaces[ pPoly->mp_Surface];
if( bOnColorMode)
{
//pPoly->mp_OnColor = 1UL << iChoosedColor;
ms.ms_ulOnColor = 1UL << iChoosedColor;
}
else
{
//pPoly->mp_OffColor = 1UL << iChoosedColor;
ms.ms_ulOffColor = 1UL << iChoosedColor;
}
}
}
void CModelObject::ApplySurfaceToPolygon( CDrawPort *pDP, CProjection3D *projection,
PIX x1, PIX y1, INDEX iSurface, COLOR colSurfaceColor)
{
CPlacement3D plRay;
CPlacement3D plObjectPlacement;
projection->Prepare();
projection->RayThroughPoint( FLOAT3D( (FLOAT)x1, (FLOAT)(pDP->GetHeight()-y1), 0.0f),
plRay);
plObjectPlacement = projection->ObjectPlacementR();
FLOAT fHitDistance;
struct ModelPolygon *pPoly = PolygonHit( plRay, plObjectPlacement, mo_iLastRenderMipLevel, fHitDistance);
if( pPoly != NULL)
{
pPoly->mp_ColorAndAlpha = colSurfaceColor;
pPoly->mp_Surface = iSurface;
}
}
//--------------------------------------------------------------------------------------------
/*
* Picks color from hitted polygon
*/
void CModelObject::PickPolyColor( CDrawPort *pDP, CProjection3D *projection, PIX x1, PIX y1,
INDEX &iPickedColorNo, BOOL bOnColorMode)
{
CPlacement3D plRay;
CPlacement3D plObjectPlacement;
projection->Prepare();
projection->RayThroughPoint( FLOAT3D( (FLOAT)x1, (FLOAT)(pDP->GetHeight()-y1), 0.0f),
plRay);
plObjectPlacement = projection->ObjectPlacementR();
FLOAT fHitDistance;
struct ModelPolygon *pPoly = PolygonHit( plRay, plObjectPlacement, mo_iLastRenderMipLevel, fHitDistance);
if( pPoly != NULL)
{
CModelData *pMD = (CModelData *) GetData();
MappingSurface &ms = pMD->md_MipInfos[ mo_iLastRenderMipLevel].mmpi_MappingSurfaces[ pPoly->mp_Surface];
if( bOnColorMode)
{
iPickedColorNo = GetBit( ms.ms_ulOnColor);
}
else
{
iPickedColorNo = GetBit( ms.ms_ulOffColor);
}
}
}
//--------------------------------------------------------------------------------------------
INDEX CModelObject::PickPolySurface( CDrawPort *pDP, CProjection3D *projection, PIX x1, PIX y1)
{
CPlacement3D plRay;
CPlacement3D plObjectPlacement;
projection->Prepare();
projection->RayThroughPoint( FLOAT3D( (FLOAT)x1, (FLOAT)(pDP->GetHeight()-y1), 0.0f),
plRay);
plObjectPlacement = projection->ObjectPlacementR();
FLOAT fHitDistance;
struct ModelPolygon *pPoly = PolygonHit( plRay, plObjectPlacement, mo_iLastRenderMipLevel, fHitDistance);
if( pPoly != NULL)
{
return pPoly->mp_Surface;
}
return -1;
}
//--------------------------------------------------------------------------------------------
/*
* Obtains index of closest vertex
*/
INDEX CModelObject::PickVertexIndex( CDrawPort *pDP, CProjection3D *pProjection, PIX x1, PIX y1,
FLOAT3D &vClosestVertex)
{
CModelData *pMD = (CModelData *) GetData();
// project vertices for given mip model
ProjectFrameVertices( pProjection, mo_iLastRenderMipLevel);
FLOAT fClosest = 64.0f;
FLOAT iClosest = -1;
INDEX iCurrentFrame = GetFrame();
FLOAT3D vTargetPoint = FLOAT3D( x1, pDP->GetHeight()-y1, 0.0f);
ULONG ulVtxMask = (1L) << mo_iLastRenderMipLevel;
// Find closest vertice
for( INDEX iVertex=0; iVertex<pMD->md_VerticesCt; iVertex++)
{
if( pMD->md_VertexMipMask[ iVertex] & ulVtxMask)
{
FLOAT3D vProjected = pMD->md_TransformedVertices[ iVertex].tvd_TransformedPoint;
vProjected(3) = 0.0f;
FLOAT3D vUncompressedVertex;
if( pMD->md_Flags & MF_COMPRESSED_16BIT)
{
ModelFrameVertex16 *pFrame = &pMD->md_FrameVertices16[ iCurrentFrame * pMD->md_VerticesCt];
vUncompressedVertex(1) = (float) pFrame[ iVertex].mfv_SWPoint(1);
vUncompressedVertex(2) = (float) pFrame[ iVertex].mfv_SWPoint(2);
vUncompressedVertex(3) = (float) pFrame[ iVertex].mfv_SWPoint(3);
}
else
{
ModelFrameVertex8 *pFrame = &pMD->md_FrameVertices8[ iCurrentFrame * pMD->md_VerticesCt];
vUncompressedVertex(1) = (float) pFrame[ iVertex].mfv_SBPoint(1);
vUncompressedVertex(2) = (float) pFrame[ iVertex].mfv_SBPoint(2);
vUncompressedVertex(3) = (float) pFrame[ iVertex].mfv_SBPoint(3);
}
FLOAT fDistance = Abs( ( vProjected-vTargetPoint).Length());
if( fDistance < fClosest)
{
fClosest = fDistance;
iClosest = iVertex;
vClosestVertex(1) = vUncompressedVertex(1)*pMD->md_Stretch(1);
vClosestVertex(2) = vUncompressedVertex(2)*pMD->md_Stretch(2);
vClosestVertex(3) = vUncompressedVertex(3)*pMD->md_Stretch(3);
}
}
}
return ((INDEX) iClosest);
}
/*
* Retrieves current frame's bounding box
*/
void CModelObject::GetCurrentFrameBBox( FLOATaabbox3D &MaxBB)
{
// obtain model data ptr
CModelData *pMD = (CModelData *)GetData();
ASSERT( pMD != NULL);
// get current frame
INDEX iCurrentFrame = GetFrame();
ASSERT( iCurrentFrame < pMD->md_FramesCt);
// set current frame's bounding box
MaxBB = pMD->md_FrameInfos[ iCurrentFrame].mfi_Box;
}
/*
* Retrieves bounding box of all frames
*/
void CModelObject::GetAllFramesBBox( FLOATaabbox3D &MaxBB)
{
// obtain model data ptr
CModelData *pMD = (CModelData *)GetData();
ASSERT( pMD != NULL);
// get all frames bounding box
pMD->GetAllFramesBBox( MaxBB);
}
FLOAT3D CModelObject::GetCollisionBoxMin(INDEX iCollisionBox)
{
return GetData()->GetCollisionBoxMin(iCollisionBox);
}
FLOAT3D CModelObject::GetCollisionBoxMax(INDEX iCollisionBox)
{
return GetData()->GetCollisionBoxMax(iCollisionBox);
}
// returns HEIGHT_EQ_WIDTH, LENGTH_EQ_WIDTH or LENGTH_EQ_HEIGHT
INDEX CModelObject::GetCollisionBoxDimensionEquality(INDEX iCollisionBox)
{
return GetData()->GetCollisionBoxDimensionEquality(iCollisionBox);
}
// test it the model has alpha blending
BOOL CModelObject::HasAlpha(void)
{
return GetData()->md_bHasAlpha || (mo_colBlendColor&0xFF)!=0xFF;
}
// retrieves number of surfaces used in given mip model
INDEX CModelObject::SurfacesCt(INDEX iMipModel){
ASSERT( GetData() != NULL);
return GetData()->md_MipInfos[ iMipModel].mmpi_MappingSurfaces.Count();
};
// retrieves number of polygons in given surface in given mip model
INDEX CModelObject::PolygonsInSurfaceCt(INDEX iMipModel, INDEX iSurface)
{
ASSERT( GetData() != NULL);
return GetData()->md_MipInfos[ iMipModel].mmpi_MappingSurfaces[iSurface].ms_aiPolygons.Count();
};
//--------------------------------------------------------------------------------------------
/*
* Adds and shows given patch
*/
void CModelObject::ShowPatch( INDEX iMaskBit)
{
CModelData *pMD = (CModelData *)GetData();
ASSERT( pMD != NULL);
if( pMD == NULL) return;
if( (mo_PatchMask & ((1UL) << iMaskBit)) != 0) return;
mo_PatchMask |= (1UL) << iMaskBit;
}
//--------------------------------------------------------------------------------------------
/*
* Hides given patch
*/
void CModelObject::HidePatch( INDEX iMaskBit)
{
CModelData *pMD = (CModelData *)GetData();
if( pMD == NULL) return;
if( (mo_PatchMask & ((1UL) << iMaskBit)) == 0) return;
mo_PatchMask &= ~((1UL) << iMaskBit);
}
//--------------------------------------------------------------------------------------------
/*
* Retrieves index of mip model that casts shadow
*/
BOOL CModelObject::HasShadow(INDEX iModelMip)
{
CModelData *pMD = (CModelData *) GetData();
SLONG slShadowQuality = _mrpModelRenderPrefs.GetShadowQuality();
ASSERT( slShadowQuality >= 0);
SLONG res = iModelMip + slShadowQuality + pMD->md_ShadowQuality;
if( res >= pMD->md_MipCt)
return FALSE;
return TRUE;
}
/*
* Set texture data for main texture in surface of this model.
*/
void CModelObject::SetTextureData(CTextureData *ptdNewMainTexture)
{
mo_toTexture.SetData(ptdNewMainTexture);
}
CTFileName CModelObject::GetName(void)
{
CModelData *pmd = (CModelData *) GetData();
if( pmd == NULL) return CTString( "");
return pmd->GetName();
}
// obtain model and set it for this object
void CModelObject::SetData_t(const CTFileName &fnmModel) // throw char *
{
// if the filename is empty
if (fnmModel=="") {
// release current texture
SetData(NULL);
// if the filename is not empty
} else {
// obtain it (adds one reference)
CModelData *pmd = _pModelStock->Obtain_t(fnmModel);
// set it as data (adds one more reference, and remove old reference)
SetData(pmd);
// release it (removes one reference)
_pModelStock->Release(pmd);
// total reference count +1+1-1 = +1 for new data -1 for old data
}
}
void CModelObject::SetData(CModelData *pmd)
{
RemoveAllAttachmentModels();
CAnimObject::SetData(pmd);
}
CModelData *CModelObject::GetData(void)
{
return (CModelData*)CAnimObject::GetData();
}
void CModelObject::AutoSetTextures(void)
{
CTFileName fnModel = GetName();
CTFileName fnDiffuse;
INDEX ctDiffuseTextures;
CTFileName fnReflection;
CTFileName fnSpecular;
CTFileName fnBump;
// extract from model's ini file informations about attachment model's textures
try
{
CTFileName fnIni = fnModel.NoExt()+".ini";
CTFileStream strmIni;
strmIni.Open_t( fnIni);
SLONG slFileSize = strmIni.GetStreamSize();
// NEVER!NEVER! read after EOF
while(strmIni.GetPos_t()<(slFileSize-4))
{
CChunkID id = strmIni.PeekID_t();
if( id == CChunkID("WTEX"))
{
CChunkID idDummy = strmIni.GetID_t();
strmIni >> ctDiffuseTextures;
strmIni >> fnDiffuse;
}
else if( id == CChunkID("FXTR"))
{
CChunkID idDummy = strmIni.GetID_t();
strmIni >> fnReflection;
}
else if( id == CChunkID("FXTS"))
{
CChunkID idDummy = strmIni.GetID_t();
strmIni >> fnSpecular;
}
else if( id == CChunkID("FXTB"))
{
CChunkID idDummy = strmIni.GetID_t();
strmIni >> fnBump;
}
else
{
strmIni.Seek_t(1,CTStream::SD_CUR);
}
}
}
catch( char *strError){ (void) strError;}
try
{
if( fnDiffuse != "") mo_toTexture.SetData_t( fnDiffuse);
if( fnReflection != "") mo_toReflection.SetData_t( fnReflection);
if( fnSpecular != "") mo_toSpecular.SetData_t( fnSpecular);
if( fnBump != "") mo_toBump.SetData_t( fnBump);
}
catch( char *strError){ (void) strError;}
}
void CModelObject::AutoSetAttachments(void)
{
CTFileName fnModel = GetName();
RemoveAllAttachmentModels();
// extract from model's ini file informations about attachment model's textures
try
{
CTFileName fnIni = fnModel.NoExt()+".ini";
CTFileStream strmIni;
strmIni.Open_t( fnIni);
SLONG slFileSize = strmIni.GetStreamSize();
// NEVER!NEVER! read after EOF
while(strmIni.GetPos_t()<(slFileSize-4))
{
CChunkID id = strmIni.PeekID_t();
if( id == CChunkID("ATTM"))
{
CChunkID idDummy = strmIni.GetID_t();
// try to load attached models
INDEX ctAttachedModels;
strmIni >> ctAttachedModels;
// read all attached models
for( INDEX iAtt=0; iAtt<ctAttachedModels; iAtt++)
{
BOOL bVisible;
CTString strName;
CTFileName fnModel, fnDummy;
strmIni >> bVisible;
strmIni >> strName;
// this data is used no more
strmIni >> fnModel;
INDEX iAnimation = 0;
// new attached model format has saved index of animation
if( strmIni.PeekID_t() == CChunkID("AMAN"))
{
strmIni.ExpectID_t( CChunkID( "AMAN"));
strmIni >> iAnimation;
}
else
{
strmIni >> fnDummy; // ex model's texture
}
if( bVisible)
{
CAttachmentModelObject *pamo = AddAttachmentModel( iAtt);
pamo->amo_moModelObject.SetData_t( fnModel);
pamo->amo_moModelObject.AutoSetTextures();
pamo->amo_moModelObject.StartAnim( iAnimation);
}
}
}
else
{
strmIni.Seek_t(1,CTStream::SD_CUR);
}
}
}
catch( char *strError)
{
(void) strError;
RemoveAllAttachmentModels();
}
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo)
{
itamo->amo_moModelObject.AutoSetAttachments();
}
}
CAttachmentModelObject *CModelObject::AddAttachmentModel( INDEX iAttachedPosition)
{
CModelData *pMD = (CModelData *) GetData();
if (pMD->md_aampAttachedPosition.Count()==0) {
return NULL;
}
ASSERT( iAttachedPosition >= 0);
ASSERT( iAttachedPosition < pMD->md_aampAttachedPosition.Count());
iAttachedPosition = Clamp(iAttachedPosition, INDEX(0), INDEX(pMD->md_aampAttachedPosition.Count()-1));
CAttachmentModelObject *pamoNew = new CAttachmentModelObject;
mo_lhAttachments.AddTail( pamoNew->amo_lnInMain);
pamoNew->amo_iAttachedPosition = iAttachedPosition;
pMD->md_aampAttachedPosition.Lock();
pamoNew->amo_plRelative = pMD->md_aampAttachedPosition[iAttachedPosition].amp_plRelativePlacement;
pMD->md_aampAttachedPosition.Unlock();
return pamoNew;
}
CAttachmentModelObject *CModelObject::GetAttachmentModel( INDEX ipos)
{
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
CAttachmentModelObject &amo = *itamo;
if (amo.amo_iAttachedPosition == ipos) {
return &amo;
}
}
return NULL;
}
CAttachmentModelObject *CModelObject::GetAttachmentModelList( INDEX ipos, ...)
{
va_list marker;
va_start(marker, ipos);
CAttachmentModelObject *pamo = NULL;
CModelObject *pmo = this;
// while not end of list
while(ipos>=0) {
// get attachment
pamo = pmo->GetAttachmentModel(ipos);
// if not found
if (pamo==NULL) {
// return failure
va_end(marker);
return NULL;
}
// get next attachment in list
pmo = &pamo->amo_moModelObject;
ipos = va_arg( marker, INDEX);
}
va_end(marker);
// return current attachment
ASSERT(pamo!=NULL);
return pamo;
}
void CModelObject::ResetAttachmentModelPosition( INDEX iAttachedPosition)
{
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo)
{
if (itamo->amo_iAttachedPosition == iAttachedPosition)
{
CModelData *pMD = (CModelData *) GetData();
pMD->md_aampAttachedPosition.Lock();
itamo->amo_plRelative = pMD->md_aampAttachedPosition[iAttachedPosition].amp_plRelativePlacement;
pMD->md_aampAttachedPosition.Unlock();
return;
}
}
}
void CModelObject::RemoveAttachmentModel( INDEX iAttachedPosition)
{
FORDELETELIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
if (itamo->amo_iAttachedPosition == iAttachedPosition) {
itamo->amo_lnInMain.Remove();
delete &*itamo;
return;
}
}
}
void CModelObject::RemoveAllAttachmentModels(void)
{
FORDELETELIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
itamo->amo_lnInMain.Remove();
delete &*itamo;
}
}
void CModelObject::StretchModel(const FLOAT3D &vStretch)
{
mo_Stretch = vStretch;
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
itamo->amo_moModelObject.StretchModel(vStretch);
}
}
void CModelObject::StretchModelRelative(const FLOAT3D &vStretch)
{
mo_Stretch(1) *= vStretch(1);
mo_Stretch(2) *= vStretch(2);
mo_Stretch(3) *= vStretch(3);
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itamo) {
itamo->amo_moModelObject.StretchModelRelative(vStretch);
}
}
void CModelObject::StretchSingleModel(const FLOAT3D &vStretch)
{
mo_Stretch = vStretch;
}
// get amount of memory used by this object
SLONG CModelObject::GetUsedMemory(void)
{
// initial size
SLONG slUsedMemory = sizeof(CModelObject);
// add attachment(s) size
FOREACHINLIST( CAttachmentModelObject, amo_lnInMain, mo_lhAttachments, itat) {
slUsedMemory += sizeof(CAttachmentModelObject) - sizeof(CModelObject);
itat->amo_moModelObject.GetUsedMemory();
}
// done
return slUsedMemory;
}