Serious-Engine/Sources/Engine/Models/EditModel.cpp

2890 lines
95 KiB
C++
Raw Normal View History

2016-03-11 14:57:17 +01:00
/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include "stdh.h"
#include <Engine/Models/ModelObject.h>
#include <Engine/Models/ModelData.h>
#include <Engine/Models/EditModel.h>
#include <Engine/Models/MipMaker.h>
#include <Engine/Math/Geometry.inl>
#include <Engine/Models/Model_internal.h>
#include <Engine/Base/Stream.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/Models/Normals.h>
#include <Engine/Graphics/DrawPort.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Templates/Stock_CTextureData.h>
// Globally instanciated object containing routines for dealing with progres messages
CProgressRoutines ProgresRoutines;
// constants important to this module
#define MAX_ALLOWED_DISTANCE 0.0001f
#define PC_ALLWAYS_ON (1UL << 30)
#define PC_ALLWAYS_OFF (1UL << 31)
// origin triangle for transforming object
INDEX aiTransVtx[3] = {-1,-1,-1};
class CExtractSurfaceVertex
{
public:
INDEX esv_Surface;
SLONG esv_TextureVertexRemap;
INDEX esv_MipGlobalIndex;
};
CThumbnailSettings::CThumbnailSettings( void)
{
ts_bSet = FALSE;
}
void CThumbnailSettings::Read_t( CTStream *strFile)
{
*strFile>>ts_bSet;
*strFile>>ts_plLightPlacement;
*strFile>>ts_plModelPlacement;
*strFile>>ts_fTargetDistance;
*strFile>>ts_vTarget;
*strFile>>ts_angViewerOrientation;
*strFile>>ts_LightDistance;
*strFile>>ts_LightColor;
*strFile>>ts_colAmbientColor;
*strFile>>ts_PaperColor;
*strFile>>ts_InkColor;
*strFile>>ts_IsWinBcgTexture;
*strFile>>ts_WinBcgTextureName;
ts_RenderPrefs.Read_t( strFile);
}
void CThumbnailSettings::Write_t( CTStream *strFile)
{
*strFile<<ts_bSet;
*strFile<<ts_plLightPlacement;
*strFile<<ts_plModelPlacement;
*strFile<<ts_fTargetDistance;
*strFile<<ts_vTarget;
*strFile<<ts_angViewerOrientation;
*strFile<<ts_LightDistance;
*strFile<<ts_LightColor;
*strFile<<ts_colAmbientColor;
*strFile<<ts_PaperColor;
*strFile<<ts_InkColor;
*strFile<<ts_IsWinBcgTexture;
*strFile<<ts_WinBcgTextureName;
ts_RenderPrefs.Write_t( strFile);
}
CEditModel::CEditModel()
{
edm_md.md_bIsEdited = TRUE; // this model data is edited
edm_iActiveCollisionBox = 0;
}
CEditModel::~CEditModel()
{
FORDELETELIST( CTextureDataInfo, tdi_ListNode, edm_WorkingSkins, litDel3)
{
ASSERT( litDel3->tdi_TextureData != NULL);
_pTextureStock->Release( litDel3->tdi_TextureData);
delete &litDel3.Current();
}
}
CProgressRoutines::CProgressRoutines()
{
SetProgressMessage = NULL;
SetProgressRange = NULL;
SetProgressState = NULL;
}
//----------------------------------------------------------------------------------------------
/*
* This routine loads animation data from opened model script file and converts loaded data
* to model's frame vertices format
*/
struct VertexNeighbors { CStaticStackArray<INDEX> vp_aiNeighbors; };
void CEditModel::LoadModelAnimationData_t( CTStream *pFile, const FLOATmatrix3D &mStretch) // throw char *
{
try {
CObject3D::BatchLoading_t(TRUE);
INDEX i;
CObject3D OB3D;
CListHead FrameNamesList;
FLOATaabbox3D OneFrameBB;
FLOATaabbox3D AllFramesBB;
INDEX ctFramesBefore = edm_md.md_FramesCt;
edm_md.ClearAnimations();
OB3D.ob_aoscSectors.Lock();
// there must be at least one mip model loaded, throw if not
if( edm_md.md_VerticesCt == 0) {
throw( "Trying to update model's animations, but model doesn't exists!");
}
edm_md.LoadFromScript_t( pFile, &FrameNamesList); // load model's animation data from script
// if recreating animations, frame count must be the same
if( (ctFramesBefore != 0) && (FrameNamesList.Count() != ctFramesBefore) )
{
throw( "If you are updating animations, you can't change number of frames. \
If you want to add or remove some frames or animations, please recreate the model.");
}
edm_md.md_FramesCt = FrameNamesList.Count();
/*
* Now we will allocate frames and frames info array and array od 3D objects,
* one for each frame.
*/
if( ProgresRoutines.SetProgressMessage != NULL) {
ProgresRoutines.SetProgressMessage( "Calculating bounding boxes ...");
}
if( ProgresRoutines.SetProgressRange != NULL) {
ProgresRoutines.SetProgressRange( FrameNamesList.Count());
}
edm_md.md_FrameInfos.New( edm_md.md_FramesCt);
if( edm_md.md_Flags & MF_COMPRESSED_16BIT) {
edm_md.md_FrameVertices16.New( edm_md.md_FramesCt * edm_md.md_VerticesCt);
} else {
edm_md.md_FrameVertices8.New( edm_md.md_FramesCt * edm_md.md_VerticesCt);
}
INDEX iO3D = 0; // index used for progress dialog
CStaticStackArray<FLOAT3D> avVertices; // for caching all vertices in all frames
BOOL bOrigin = FALSE;
FLOATmatrix3D mOrientation;
// set bOrigin if aiTransVtx is valid
if((aiTransVtx[0] >=0) && (aiTransVtx[1] >=0) && (aiTransVtx[2] >=0))
{
bOrigin = TRUE;
}
{FOREACHINLIST( CFileNameNode, cfnn_Node, FrameNamesList, itFr)
{
CFileNameNode &fnnFileNameNode = itFr.Current();
if( ProgresRoutines.SetProgressState != NULL) ProgresRoutines.SetProgressState(iO3D);
OB3D.Clear();
OB3D.LoadAny3DFormat_t( CTString(itFr->cfnn_FileName), mStretch);
if( edm_md.md_VerticesCt != OB3D.ob_aoscSectors[0].osc_aovxVertices.Count()) {
ThrowF_t( "File %s, one of animation frame files has wrong number of points.",
(CTString)fnnFileNameNode.cfnn_FileName);
}
if(bOrigin)
{
// calc matrix for vertex transform
FLOAT3D vY = DOUBLEtoFLOAT(OB3D.ob_aoscSectors[0].osc_aovxVertices[aiTransVtx[2]]-OB3D.ob_aoscSectors[0].osc_aovxVertices[aiTransVtx[0]]);
FLOAT3D vZ = DOUBLEtoFLOAT(OB3D.ob_aoscSectors[0].osc_aovxVertices[aiTransVtx[0]]-OB3D.ob_aoscSectors[0].osc_aovxVertices[aiTransVtx[1]]);
FLOAT3D vX = vY*vZ;
vY = vZ*vX;
// make a rotation matrix from those vectors
vX.Normalize();
vY.Normalize();
vZ.Normalize();
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);
mOrientation = !mOrientation;
}
// normalize (clear) our Bounding Box
OB3D.ob_aoscSectors[0].LockAll();
OneFrameBB = FLOATaabbox3D();
// Bounding Box makes union with all points in this frame
for( i=0; i<edm_md.md_VerticesCt; i++) {
FLOAT3D vVtx = DOUBLEtoFLOAT( OB3D.ob_aoscSectors[0].osc_aovxVertices[i]);
if(bOrigin)
{
// transform vertex
vVtx -= DOUBLEtoFLOAT(OB3D.ob_aoscSectors[0].osc_aovxVertices[aiTransVtx[0]]);
vVtx *= mOrientation;
}
OneFrameBB |= FLOATaabbox3D(vVtx);
avVertices.Push() = vVtx; // cache vertex
}
OB3D.ob_aoscSectors[0].UnlockAll();
// remember this frame's Bounding Box
edm_md.md_FrameInfos[iO3D].mfi_Box = OneFrameBB;
// make union with Bounding Box of all frames
AllFramesBB |= OneFrameBB;
// load next frame
iO3D++;
}}
// calculate stretch vector
edm_md.md_Stretch = AllFramesBB.Size()/2.0f; // get size of bounding box
// correct invalid stretch factors
if( edm_md.md_Stretch(1) == 0.0f) edm_md.md_Stretch(1) = 1.0f;
if( edm_md.md_Stretch(2) == 0.0f) edm_md.md_Stretch(2) = 1.0f;
if( edm_md.md_Stretch(3) == 0.0f) edm_md.md_Stretch(3) = 1.0f;
// build links from vertices to polygons
CStaticArray<VertexNeighbors> avnVertices;
avnVertices.New( edm_md.md_VerticesCt);
// lost 1st frame (one frame is enough because all frames has same poly->edge->vertex links)
OB3D.Clear();
const CTString &fnmFirstFrame = LIST_HEAD( FrameNamesList, CFileNameNode, cfnn_Node)->cfnn_FileName;
OB3D.LoadAny3DFormat_t( fnmFirstFrame, mStretch);
OB3D.ob_aoscSectors[0].LockAll();
// loop thru polygons
INDEX iPolyNo=0;
{FOREACHINDYNAMICARRAY( OB3D.ob_aoscSectors[0].osc_aopoPolygons, CObjectPolygon, itPoly)
{
CObjectPolygon &opo = *itPoly;
// only triangles are supported!
ASSERT( opo.opo_PolygonEdges.Count() == 3);
if( opo.opo_PolygonEdges.Count() != 3) {
ThrowF_t( "Non-triangle polygon encountered in model file %s !", fnmFirstFrame);
}
// get all 3 vetrices of current polygon and sorted them
opo.opo_PolygonEdges.Lock();
CObjectPolygonEdge &opeCurr = opo.opo_PolygonEdges[0];
CObjectPolygonEdge &opeNext = opo.opo_PolygonEdges[1];
CObjectVertex *povxCurr, *povxPrev, *povxNext;
if( !opeCurr.ope_Backward) {
povxCurr = opeCurr.ope_Edge->oed_Vertex1;
povxPrev = opeCurr.ope_Edge->oed_Vertex0;
ASSERT( opeNext.ope_Edge->oed_Vertex0 == povxCurr);
} else {
povxCurr = opeCurr.ope_Edge->oed_Vertex0;
povxPrev = opeCurr.ope_Edge->oed_Vertex1;
ASSERT( opeNext.ope_Edge->oed_Vertex1 == povxCurr);
}
if( !opeNext.ope_Backward) {
povxNext = opeNext.ope_Edge->oed_Vertex1;
ASSERT( opeNext.ope_Edge->oed_Vertex0 == povxCurr);
} else {
povxNext = opeNext.ope_Edge->oed_Vertex0;
ASSERT( opeNext.ope_Edge->oed_Vertex1 == povxCurr);
}
INDEX iVtx0 = OB3D.ob_aoscSectors[0].osc_aovxVertices.Index(povxPrev);
INDEX iVtx1 = OB3D.ob_aoscSectors[0].osc_aovxVertices.Index(povxCurr);
INDEX iVtx2 = OB3D.ob_aoscSectors[0].osc_aovxVertices.Index(povxNext);
// add neighbor vertices for each of this vertices
avnVertices[iVtx0].vp_aiNeighbors.Push() = iVtx2;
avnVertices[iVtx0].vp_aiNeighbors.Push() = iVtx1;
avnVertices[iVtx1].vp_aiNeighbors.Push() = iVtx0;
avnVertices[iVtx1].vp_aiNeighbors.Push() = iVtx2;
avnVertices[iVtx2].vp_aiNeighbors.Push() = iVtx1;
avnVertices[iVtx2].vp_aiNeighbors.Push() = iVtx0;
// advance to next poly
opo.opo_PolygonEdges.Unlock();
iPolyNo++;
}}
// vertex->polygons links created
OB3D.ob_aoscSectors[0].UnlockAll();
// cache strecthing reciprocal for faster calc
FLOAT f1oStretchX, f1oStretchY, f1oStretchZ;
if( edm_md.md_Flags & MF_COMPRESSED_16BIT) {
f1oStretchX = 32767.0f / edm_md.md_Stretch(1);
f1oStretchY = 32767.0f / edm_md.md_Stretch(2);
f1oStretchZ = 32767.0f / edm_md.md_Stretch(3);
} else {
f1oStretchX = 127.0f / edm_md.md_Stretch(1);
f1oStretchY = 127.0f / edm_md.md_Stretch(2);
f1oStretchZ = 127.0f / edm_md.md_Stretch(3);
}
// remember center vector
FLOAT3D vCenter = AllFramesBB.Center(); // obtain bbox center
edm_md.md_vCenter = vCenter;
// prepare progress bar
if( ProgresRoutines.SetProgressMessage != NULL) {
ProgresRoutines.SetProgressMessage( "Calculating gouraud normals and stretching vertices ...");
}
if( ProgresRoutines.SetProgressRange != NULL) {
ProgresRoutines.SetProgressRange( edm_md.md_FramesCt);
}
// loop thru frames
iO3D=0; // index for progress
INDEX iFVtx=0; // count for all vertices in all frames
for( INDEX iFr=0; iFr<edm_md.md_FramesCt; iFr++)
{
// calculate all polygon normals for this frame
if( ProgresRoutines.SetProgressState != NULL) ProgresRoutines.SetProgressState(iO3D);
for( INDEX iVtx=0; iVtx<edm_md.md_VerticesCt; iVtx++) // for all vertices in one frame
{
FLOAT3D &vVtx = avVertices[iFVtx];
if( edm_md.md_Flags & MF_COMPRESSED_16BIT) {
edm_md.md_FrameVertices16[iFVtx].mfv_SWPoint = SWPOINT3D(
FloatToInt( (vVtx(1) - vCenter(1)) * f1oStretchX),
FloatToInt( (vVtx(2) - vCenter(2)) * f1oStretchY),
FloatToInt( (vVtx(3) - vCenter(3)) * f1oStretchZ) );
} else {
edm_md.md_FrameVertices8[iFVtx].mfv_SBPoint = SBPOINT3D(
FloatToInt( (vVtx(1) - vCenter(1)) * f1oStretchX),
FloatToInt( (vVtx(2) - vCenter(2)) * f1oStretchY),
FloatToInt( (vVtx(3) - vCenter(3)) * f1oStretchZ) );
}
// calculate vector of gouraud normal in this vertice
ANGLE aSum = 0;
FLOAT3D vSum( 0.0f, 0.0f, 0.0f);
INDEX iFrOffset = edm_md.md_VerticesCt * iFr;
VertexNeighbors &vnCurr = avnVertices[iVtx];
for( INDEX iNVtx=0; iNVtx<vnCurr.vp_aiNeighbors.Count(); iNVtx+=2) { // loop thru neighbors
INDEX iPrev = vnCurr.vp_aiNeighbors[iNVtx+0];
INDEX iNext = vnCurr.vp_aiNeighbors[iNVtx+1];
FLOAT3D v0 = avVertices[iPrev+iFrOffset] - vVtx;
FLOAT3D v1 = avVertices[iNext+iFrOffset] - vVtx;
v0.Normalize();
v1.Normalize();
FLOAT3D v = v1*v0;
FLOAT fLength = v.Length();
ANGLE a = ASin(fLength);
//ASSERT( a>=0 && a<=180);
aSum += a;
vSum += (v/fLength) * a;
}
// normalize sum of polygon normals
//ASSERT( aSum>=0);
vSum /= aSum;
vSum.Normalize();
// save compressed gouraud normal
if( edm_md.md_Flags & MF_COMPRESSED_16BIT) {
CompressNormal_HQ( vSum, edm_md.md_FrameVertices16[iFVtx].mfv_ubNormH,
edm_md.md_FrameVertices16[iFVtx].mfv_ubNormP);
} else {
edm_md.md_FrameVertices8[iFVtx].mfv_NormIndex = (UBYTE)GouraudNormal(vSum);
}
// advance to next vertex in model
iFVtx++;
}
// advance to next frame
iO3D++;
}
// list of filenames is no longer needed
FORDELETELIST( CFileNameNode, cfnn_Node, FrameNamesList, litDel) delete &litDel.Current();
// create compressed vector center that will be used for setting object handle
edm_md.md_vCompressedCenter(1) = -edm_md.md_vCenter(1) * f1oStretchX;
edm_md.md_vCompressedCenter(2) = -edm_md.md_vCenter(2) * f1oStretchY;
edm_md.md_vCompressedCenter(3) = -edm_md.md_vCenter(3) * f1oStretchZ;
// adjust stretching for compressed format
if( edm_md.md_Flags & MF_COMPRESSED_16BIT) {
edm_md.md_Stretch(1) /= 32767.0f;
edm_md.md_Stretch(2) /= 32767.0f;
edm_md.md_Stretch(3) /= 32767.0f;
} else {
edm_md.md_Stretch(1) /= 127.0f;
edm_md.md_Stretch(2) /= 127.0f;
edm_md.md_Stretch(3) /= 127.0f;
}
// all done
OB3D.ob_aoscSectors.Unlock();
CObject3D::BatchLoading_t(FALSE);
} catch (char*) {
CObject3D::BatchLoading_t(FALSE);
throw;
}
}
//--------------------------------------------------------------------------------------------
/*
* Routine saves model's .h file (#define ......)
*/
void CEditModel::SaveIncludeFile_t( CTFileName fnFileName, CTString strDefinePrefix) // throw char *
{
CTFileStream strmHFile;
char line[ 1024];
INDEX i;
strmHFile.Create_t( fnFileName, CTStream::CM_TEXT);
strcpy( line, strDefinePrefix);
strupr( line);
strDefinePrefix = CTString( line);
sprintf( line, "// Animation names\n");
strmHFile.Write_t( line, strlen( line));
// force animation prefix string to be upper case
char achrUprName[ 256];
strcpy( achrUprName, strDefinePrefix);
strcat( achrUprName, "_ANIM_");
CTString strAnimationPrefix = achrUprName;
edm_md.ExportAnimationNames_t( &strmHFile, achrUprName);
sprintf( line, "\n// Color names\n");
strmHFile.Write_t( line, strlen( line));
for( i=0; i<MAX_COLOR_NAMES; i++)
{
if( edm_md.md_ColorNames[ i] != "")
{
sprintf( line, "#define %s_PART_%s ((1L) << %d)\n", strDefinePrefix, edm_md.md_ColorNames[ i], i);
strmHFile.Write_t( line, strlen( line));
}
}
sprintf( line, "\n// Patch names\n");
strmHFile.Write_t( line, strlen( line));
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
CTString strPatchName = edm_md.md_mpPatches[ iPatch].mp_strName;
if( strPatchName != "")
{
sprintf( line, "#define %s_PATCH_%s %d\n", strDefinePrefix, strPatchName, i);
strmHFile.Write_t( line, strlen( line));
}
}
// save collision box names
sprintf( line, "\n// Names of collision boxes\n");
strmHFile.Write_t( line, strlen( line));
edm_md.md_acbCollisionBox.Lock();
// save all collision boxes
for( INDEX iCollisionBox=0; iCollisionBox<edm_md.md_acbCollisionBox.Count(); iCollisionBox++)
{
// prepare collision box name as define
sprintf( line, "#define %s_COLLISION_BOX_%s %d\n", strDefinePrefix, GetCollisionBoxName( iCollisionBox),
iCollisionBox);
strmHFile.Write_t( line, strlen( line));
}
edm_md.md_acbCollisionBox.Unlock();
// save all attaching positions
sprintf( line, "\n// Attaching position names\n");
strmHFile.Write_t( line, strlen( line));
INDEX iAttachingPlcement = 0;
FOREACHINDYNAMICARRAY(edm_aamAttachedModels, CAttachedModel, itam)
{
char achrUpper[ 256];
strcpy( achrUpper, itam->am_strName);
strupr( achrUpper);
sprintf( line, "#define %s_ATTACHMENT_%s %d\n", strDefinePrefix, achrUpper, iAttachingPlcement);
strmHFile.Write_t( line, strlen( line));
iAttachingPlcement++;
}
sprintf( line, "\n// Sound names\n");
strmHFile.Write_t( line, strlen( line));
for( INDEX iSound=0; iSound<edm_aasAttachedSounds.Count(); iSound++)
{
if( edm_aasAttachedSounds[iSound].as_fnAttachedSound != "")
{
CTString strLooping;
if( edm_aasAttachedSounds[iSound].as_bLooping) strLooping = "L";
else strLooping = "NL";
CTString strDelay = "";
if( edm_aasAttachedSounds[iSound].as_fDelay == 0.0f)
strDelay = "0.0f";
else
strDelay.PrintF( "%gf", edm_aasAttachedSounds[iSound].as_fDelay);
CAnimInfo aiInfo;
edm_md.GetAnimInfo( iSound, aiInfo);
CTString strWithQuotes;
strWithQuotes.PrintF( "\"%s\",", CTString(edm_aasAttachedSounds[iSound].as_fnAttachedSound));
sprintf( line, "//sound SOUND_%s_%-16s %-32s // %s, %s, %s\n",
strDefinePrefix,
aiInfo.ai_AnimName,
strWithQuotes,
strAnimationPrefix+aiInfo.ai_AnimName,
strLooping,
strDelay);
strmHFile.Write_t( line, strlen( line));
}
}
strmHFile.Close();
}
// overloaded save function
void CEditModel::Save_t( CTFileName fnFileName) // throw char *
{
CTFileName fnMdlFileName = fnFileName.FileDir() + fnFileName.FileName() + ".mdl";
edm_md.Save_t( fnMdlFileName);
CTFileName fnHFileName = fnFileName.FileDir() + fnFileName.FileName() + ".h";
CTString strPrefix = fnFileName.FileName();
if (strPrefix.Length()>0 && !isalpha(strPrefix[0]) && strPrefix[0]!='_') {
strPrefix="_"+strPrefix;
}
SaveIncludeFile_t( fnHFileName, strPrefix);
CTFileName fnIniFileName = fnFileName.FileDir() + fnFileName.FileName() + ".ini";
CSerial::Save_t( fnIniFileName);
}
// overloaded load function
void CEditModel::Load_t( CTFileName fnFileName)
{
CTFileName fnMdlFileName = fnFileName.FileDir() + fnFileName.FileName() + ".mdl";
edm_md.Load_t( fnMdlFileName);
CTFileName fnIniFileName = fnFileName.FileDir() + fnFileName.FileName() + ".ini";
// try to load ini file
try
{
CSerial::Load_t( fnIniFileName);
}
catch(char *strError)
{
// ignore errors
(void) strError;
CreateEmptyAttachingSounds();
}
}
CTextureDataInfo *CEditModel::AddTexture_t(const CTFileName &fnFileName, const MEX mexWidth,
const MEX mexHeight)
{
CTextureDataInfo *pNewTDI = new CTextureDataInfo;
pNewTDI->tdi_FileName = fnFileName;
try
{
pNewTDI->tdi_TextureData = _pTextureStock->Obtain_t( pNewTDI->tdi_FileName);
}
catch(char *strError)
{
(void) strError;
delete pNewTDI;
return NULL;
}
// reload the texture
pNewTDI->tdi_TextureData->Reload();
edm_WorkingSkins.AddTail( pNewTDI->tdi_ListNode);
return pNewTDI;
}
CAttachedModel::CAttachedModel(void)
{
am_strName = "No name";
am_iAnimation = 0;
am_bVisible = TRUE;
}
CAttachedModel::~CAttachedModel(void)
{
Clear();
}
void CAttachedModel::Clear(void)
{
am_moAttachedModel.mo_toTexture.SetData(NULL);
am_moAttachedModel.mo_toReflection.SetData(NULL);
am_moAttachedModel.mo_toSpecular.SetData(NULL);
am_moAttachedModel.mo_toBump.SetData(NULL);
am_moAttachedModel.SetData(NULL);
}
void CAttachedModel::Read_t( CTStream *pstrmFile) // throw char *
{
*pstrmFile >> am_bVisible;
*pstrmFile >> am_strName;
// this data is used no more
CTFileName fnModel, fnDummy;
*pstrmFile >> fnModel;
// new attached model format has saved index of animation
if( pstrmFile->PeekID_t() == CChunkID("AMAN"))
{
pstrmFile->ExpectID_t( CChunkID( "AMAN"));
*pstrmFile >> am_iAnimation;
}
else
{
*pstrmFile >> fnDummy; // ex model's texture
}
try
{
SetModel_t( fnModel);
}
catch(char *strError)
{
(void) strError;
try
{
SetModel_t( CTFILENAME("Models\\Editor\\Axis.mdl"));
}
catch(char *strError)
{
FatalError( strError);
}
}
}
void CAttachedModel::Write_t( CTStream *pstrmFile) // throw char *
{
*pstrmFile << am_bVisible;
*pstrmFile << am_strName;
*pstrmFile << am_moAttachedModel.GetName();
// new attached model format has saved index of animation
pstrmFile->WriteID_t( CChunkID("AMAN"));
*pstrmFile << am_iAnimation;
}
void CAttachedModel::SetModel_t(CTFileName fnModel)
{
am_moAttachedModel.SetData_t(fnModel);
am_moAttachedModel.AutoSetTextures();
}
CAttachedSound::CAttachedSound( void)
{
as_fDelay = 0.0f;
as_fnAttachedSound = CTString("");
as_bLooping = FALSE;
as_bPlaying = TRUE;
}
void CAttachedSound::Read_t(CTStream *strFile)
{
*strFile>>as_bLooping;
*strFile>>as_bPlaying;
*strFile>>as_fnAttachedSound;
*strFile>>as_fDelay;
}
void CAttachedSound::Write_t(CTStream *strFile)
{
*strFile<<as_bLooping;
*strFile<<as_bPlaying;
*strFile<<as_fnAttachedSound;
*strFile<<as_fDelay;
}
void CEditModel::CreateEmptyAttachingSounds( void)
{
ASSERT( edm_md.GetAnimsCt() > 0);
edm_aasAttachedSounds.Clear();
edm_aasAttachedSounds.New( edm_md.GetAnimsCt());
}
void CEditModel::Read_t( CTStream *pFile) // throw char *
{
CTFileName fnFileName;
INDEX i, iWorkingTexturesCt;
pFile->ExpectID_t( CChunkID( "WTEX"));
*pFile >> iWorkingTexturesCt;
for( i=0; i<iWorkingTexturesCt; i++)
{
*pFile >> fnFileName;
try
{
AddTexture_t( fnFileName, edm_md.md_Width, edm_md.md_Height);
}
// This is here because we want to load model even if its texture is not valid
catch( char *err_str){ (char *) err_str;}
}
// skip patches saved in old format (patches do not exist inside EditModel any more)
if( pFile->PeekID_t() == CChunkID("PATM"))
{
pFile->GetID_t();
ULONG ulDummySizeOfLong;
ULONG ulOldExistingPatches;
*pFile >> ulDummySizeOfLong;
*pFile >> ulOldExistingPatches;
for( i=0; i<MAX_TEXTUREPATCHES; i++)
{
if( ((1UL << i) & ulOldExistingPatches) != 0)
{
CTFileName fnPatchName;
*pFile >> fnPatchName;
}
}
}
// try to load attached models
try
{
pFile->ExpectID_t( CChunkID( "ATTM"));
INDEX ctSavedModels;
*pFile >> ctSavedModels;
// clamp no of saved attachments to no of model's data attached positions
INDEX ctMDAttachments = edm_md.md_aampAttachedPosition.Count();
INDEX ctToLoad = ClampUp( ctSavedModels, ctMDAttachments);
INDEX ctToSkip = ctSavedModels - ctToLoad;
// add attached models
edm_aamAttachedModels.Clear();
if( ctToLoad != 0)
{
edm_aamAttachedModels.New( ctSavedModels);
// read all attached models
FOREACHINDYNAMICARRAY(edm_aamAttachedModels, CAttachedModel, itam)
{
itam->Read_t(pFile);
}
}
// skip unused attached models
for( INDEX iSkip=0; iSkip<ctToSkip; iSkip++)
{
CAttachedModel atmDummy;
atmDummy.Read_t(pFile);
}
}
catch( char *strError)
{
(void) strError;
// clear attached models
edm_aamAttachedModels.Clear();
edm_md.md_aampAttachedPosition.Clear();
}
CreateEmptyAttachingSounds();
// try to load attached sounds
try
{
pFile->ExpectID_t( CChunkID( "ATSD"));
INDEX ctAttachedSounds;
*pFile >> ctAttachedSounds;
INDEX ctExisting = edm_aasAttachedSounds.Count();
INDEX ctToRead = ClampUp( ctAttachedSounds, ctExisting);
// read all saved attached sounds
for( INDEX iSound=0; iSound<ctToRead; iSound++)
{
CAttachedSound &as = edm_aasAttachedSounds[ iSound];
as.Read_t(pFile);
}
// skipped saved but now obsolite
INDEX ctToSkip = ctAttachedSounds - ctToRead;
for( INDEX iSkip=0; iSkip<ctToSkip; iSkip++)
{
CAttachedSound asDummy;
asDummy.Read_t(pFile);
}
}
catch( char *strError)
{
(void) strError;
}
try
{
// load last taken thumbnail settings
pFile->ExpectID_t( CChunkID( "TBST"));
edm_tsThumbnailSettings.Read_t( pFile);
}
catch( char *strError)
{
// ignore errors
(void) strError;
}
// load names of effect textures
// --- specular texture
try {
pFile->ExpectID_t( CChunkID( "FXTS"));
*pFile >> edm_fnSpecularTexture;
} catch( char *strError) { (void) strError; }
// --- reflection texture
try {
pFile->ExpectID_t( CChunkID( "FXTR"));
*pFile >> edm_fnReflectionTexture;
} catch( char *strError) { (void) strError; }
// --- bump texture
try {
pFile->ExpectID_t( CChunkID( "FXTB"));
*pFile >> edm_fnBumpTexture;
} catch( char *strError) { (void) strError; }
}
void CEditModel::Write_t( CTStream *pFile) // throw char *
{
pFile->WriteID_t( CChunkID( "WTEX"));
INDEX iWorkingTexturesCt = edm_WorkingSkins.Count();
*pFile << iWorkingTexturesCt;
FOREACHINLIST( CTextureDataInfo, tdi_ListNode, edm_WorkingSkins, it)
{
*pFile << it->tdi_FileName;
}
// CEditModel class has no patches in new patch data format
pFile->WriteID_t( CChunkID( "ATTM"));
INDEX ctAttachedModels = edm_aamAttachedModels.Count();
*pFile << ctAttachedModels;
// write all attached models
FOREACHINDYNAMICARRAY(edm_aamAttachedModels, CAttachedModel, itam)
{
itam->Write_t(pFile);
}
pFile->WriteID_t( CChunkID( "ATSD"));
INDEX ctAttachedSounds = edm_aasAttachedSounds.Count();
*pFile << ctAttachedSounds;
// write all attached models
FOREACHINSTATICARRAY(edm_aasAttachedSounds, CAttachedSound, itas)
{
itas->Write_t(pFile);
}
// save last taken thumbnail settings
pFile->WriteID_t( CChunkID( "TBST"));
edm_tsThumbnailSettings.Write_t( pFile);
// save names of effect textures
// --- specular texture
pFile->WriteID_t( CChunkID( "FXTS"));
*pFile << edm_fnSpecularTexture;
// --- reflection texture
pFile->WriteID_t( CChunkID( "FXTR"));
*pFile << edm_fnReflectionTexture;
// --- bump texture
pFile->WriteID_t( CChunkID( "FXTB"));
*pFile << edm_fnBumpTexture;
}
//----------------------------------------------------------------------------------------------
/*
* Routine saves defult script file containing only one animation with default data
* Input file name is .LWO file name, not .SCR
*/
void CEditModel::CreateScriptFile_t(CTFileName &fnO3D) // throw char *
{
CTFileName fnScriptName = fnO3D.FileDir() + fnO3D.FileName() + ".scr";
CTFileStream File;
char line[ 256];
File.Create_t( fnScriptName, CTStream::CM_TEXT);
File.PutLine_t( ";******* Creation settings");
File.PutLine_t( "TEXTURE_DIM 2.0 2.0");
File.PutLine_t( "SIZE 1.0");
File.PutLine_t( "MAX_SHADOW 0");
File.PutLine_t( "HI_QUALITY YES");
File.PutLine_t( "FLAT NO");
File.PutLine_t( "HALF_FLAT NO");
File.PutLine_t( "STRETCH_DETAIL NO");
File.PutLine_t( "");
File.PutLine_t( ";******* Mip models");
sprintf( line, "DIRECTORY %s", (CTString&)fnO3D.FileDir());
File.PutLine_t( line);
File.PutLine_t( "MIP_MODELS 1");
sprintf( line, " %s", (CTString&)(fnO3D.FileName() + fnO3D.FileExt()));
File.PutLine_t( line);
File.PutLine_t( "");
File.PutLine_t( "ANIM_START");
File.PutLine_t( ";******* Start of animation block");
File.PutLine_t( "");
sprintf( line, "DIRECTORY %s", (CTString&)fnO3D.FileDir());
File.PutLine_t( line);
File.PutLine_t( "ANIMATION Default");
File.PutLine_t( "SPEED 0.1");
sprintf( line, " %s", (CTString&)(fnO3D.FileName() + fnO3D.FileExt()));
File.PutLine_t( line);
File.PutLine_t( "");
File.PutLine_t( ";******* End of animation block");
File.PutLine_t( "ANIM_END");
File.PutLine_t( "");
File.PutLine_t( "END");
File.Close();
}
//----------------------------------------------------------------------------------------------
/*
* This routine load lines from script file and executes appropriate actions
*/
#define EQUAL_SUB_STR( str) (strnicmp( ld_line, str, strlen(str)) == 0)
void CEditModel::LoadFromScript_t(CTFileName &fnScriptName) // throw char *
{
try {
CObject3D::BatchLoading_t(TRUE);
INDEX i;
CTFileStream File;
CObject3D O3D;
CTFileName fnOpened, fnClosed, fnUnwrapped, fnImportMapping;
char ld_line[ 128];
char flag_str[ 128];
char base_path[ PATH_MAX] = "";
char file_name[ PATH_MAX];
char mapping_file_name[ PATH_MAX] = "";
char full_path[ PATH_MAX];
FLOATmatrix3D mStretch;
mStretch.Diagonal(1.0f);
BOOL bMappingDimFound ;
BOOL bAnimationsFound;
BOOL bLoadInitialMapping;
O3D.ob_aoscSectors.Lock();
File.Open_t( fnScriptName); // open script file for reading
// if these flags will not be TRUE at the end of script, throw error
bMappingDimFound = FALSE;
bAnimationsFound = FALSE;
bLoadInitialMapping = FALSE;
// to hold number of line's chars
int iLineChars;
FOREVER
{
do
{
File.GetLine_t(ld_line, 128);
iLineChars = strlen( ld_line);
}
while( (iLineChars == 0) || (ld_line[0]==';') );
// If key-word is "DIRECTORY", remember base path it and add "\" character at the
// end of new path if it is not yet there
if( EQUAL_SUB_STR( "DIRECTORY"))
{
_strupr( ld_line);
sscanf( ld_line, "DIRECTORY %s", base_path);
if( base_path[ strlen( base_path) - 1] != '\\')
strcat( base_path,"\\");
}
// Key-word "SIZE" defines stretch factor
else if( EQUAL_SUB_STR( "SIZE"))
{
_strupr( ld_line);
FLOAT fStretch = 1.0f;
sscanf( ld_line, "SIZE %g", &fStretch);
mStretch *= fStretch;
}
else if( EQUAL_SUB_STR( "TRANSFORM"))
{
_strupr( ld_line);
FLOATmatrix3D mTran;
mTran.Diagonal(1.0f);
sscanf( ld_line, "TRANSFORM %g %g %g %g %g %g %g %g %g",
&mTran(1,1), &mTran(1,2), &mTran(1,3),
&mTran(2,1), &mTran(2,2), &mTran(2,3),
&mTran(3,1), &mTran(3,2), &mTran(3,3));
mStretch *= mTran;
}
// Key-word "FLAT" means that model will be mapped as face - forward, using only
// zooming of texture
else if( EQUAL_SUB_STR( "FLAT"))
{
_strupr( ld_line);
sscanf( ld_line, "FLAT %s", flag_str);
if( strcmp( flag_str, "YES") == 0)
{
edm_md.md_Flags |= MF_FACE_FORWARD;
edm_md.md_Flags &= ~MF_HALF_FACE_FORWARD;
}
}
else if( EQUAL_SUB_STR( "HALF_FLAT"))
{
_strupr( ld_line);
sscanf( ld_line, "HALF_FLAT %s", flag_str);
if( strcmp( flag_str, "YES") == 0)
edm_md.md_Flags |= MF_FACE_FORWARD|MF_HALF_FACE_FORWARD;
}
else if( EQUAL_SUB_STR( "STRETCH_DETAIL"))
{
_strupr( ld_line);
sscanf( ld_line, "STRETCH_DETAIL %s", flag_str);
if( strcmp( flag_str, "YES") == 0)
{
edm_md.md_Flags |= MF_STRETCH_DETAIL;
}
}
else if( EQUAL_SUB_STR( "HI_QUALITY"))
{
_strupr( ld_line);
sscanf( ld_line, "HI_QUALITY %s", flag_str);
if( strcmp( flag_str, "YES") == 0)
{
edm_md.md_Flags |= MF_COMPRESSED_16BIT;
}
}
// Key-word "REFLECTIONS" has been used in old reflections
else if( EQUAL_SUB_STR( "REFLECTIONS"))
{
}
// Key-word "MAX_SHADOW" determines maximum quality of shading that model can obtain
else if( EQUAL_SUB_STR( "MAX_SHADOW"))
{
_strupr( ld_line);
INDEX iShadowQuality;
sscanf( ld_line, "MAX_SHADOW %d", &iShadowQuality);
edm_md.md_ShadowQuality = iShadowQuality;
}
// Key-word "MipModel" must follow name of this mipmodel file
else if( EQUAL_SUB_STR( "MIP_MODELS"))
{
INDEX iMipCt;
sscanf( ld_line, "MIP_MODELS %d", &iMipCt);
if( (iMipCt <= 0) || (iMipCt >= MAX_MODELMIPS))
{
ThrowF_t("Invalid number of mip models. Number must range from 0 to %d.", MAX_MODELMIPS-1);
}
if( ProgresRoutines.SetProgressMessage != NULL)
ProgresRoutines.SetProgressMessage( "Loading and creating mip-models ...");
if( ProgresRoutines.SetProgressRange != NULL)
ProgresRoutines.SetProgressRange( iMipCt);
for( i=0; i<iMipCt; i++)
{
if( ProgresRoutines.SetProgressState != NULL)
ProgresRoutines.SetProgressState( i);
do
{
File.GetLine_t(ld_line, 128);
}
while( (strlen( ld_line)== 0) || (ld_line[0]==';'));
_strupr( ld_line);
sscanf( ld_line, "%s", file_name);
sprintf( full_path, "%s%s", base_path, file_name);
// remember name of first mip to define UV mapping
if( i==0)
{
fnImportMapping = CTString( full_path);
}
O3D.Clear(); // clear possible existing O3D's data
O3D.LoadAny3DFormat_t( CTString(full_path), mStretch);
if( edm_md.md_VerticesCt == 0) // If there are no vertices in model, call New Model
{
if( bMappingDimFound == FALSE)
{
ThrowF_t("Found key word \"MIP_MODELS\" but texture dimension wasn't found.\n"
"There must be key word \"TEXTURE_DIM\" before key word \"MIP_MODELS\" in script file.");
}
NewModel( &O3D);
}
else
{
O3D.ob_aoscSectors[0].LockAll();
AddMipModel( &O3D); // else this is one of model's mip definitions so call Add Mip Model
O3D.ob_aoscSectors[0].UnlockAll();
}
}
// set default mip factors
// all mip models will be spreaded beetween distance 0 and default maximum distance
edm_md.SpreadMipSwitchFactors( 0, 5.0f);
}
// Key-word "DEFINE_MAPPING" must follow three lines with names of files used to define mapping
else if( EQUAL_SUB_STR( "DEFINE_MAPPING"))
{
if( edm_md.md_VerticesCt == 0)
{
ThrowF_t("Found key word \"DEFINE_MAPPING\" but model is not yet created.");
}
File.GetLine_t(ld_line, 128);
sscanf( ld_line, "%s", file_name);
sprintf( full_path, "%s%s", base_path, file_name);
fnOpened = CTString( full_path);
File.GetLine_t(ld_line, 128);
sscanf( ld_line, "%s", file_name);
sprintf( full_path, "%s%s", base_path, file_name);
fnClosed = CTString( full_path);
File.GetLine_t(ld_line, 128);
sscanf( ld_line, "%s", file_name);
sprintf( full_path, "%s%s", base_path, file_name);
fnUnwrapped = CTString( full_path);
}
else if( EQUAL_SUB_STR( "IMPORT_MAPPING"))
{
if( edm_md.md_VerticesCt == 0)
{
ThrowF_t("Found key word \"IMPORT_MAPPING\" but model is not yet created.");
}
File.GetLine_t(ld_line, 128);
sscanf( ld_line, "%s", file_name);
sprintf( full_path, "%s%s", base_path, file_name);
fnImportMapping = CTString( full_path);
}
/*
* Line containing key-word "TEXTURE_DIM" gives us texture dimensions
* so we can create default mapping
*/
else if( EQUAL_SUB_STR( "TEXTURE_DIM"))
{
_strupr( ld_line);
FLOAT fWidth, fHeight;
sscanf( ld_line, "TEXTURE_DIM %f %f", &fWidth, &fHeight); // read given texture dimensions
edm_md.md_Width = MEX_METERS( fWidth);
edm_md.md_Height = MEX_METERS( fHeight);
bMappingDimFound = TRUE;
}
// Key-word "ANIM_START" starts loading of Animation Data object
else if( EQUAL_SUB_STR( "ANIM_START"))
{
LoadModelAnimationData_t( &File, mStretch); // loads and sets model's animation data
// add one collision box
edm_md.md_acbCollisionBox.New();
// reset attaching sounds
CreateEmptyAttachingSounds();
bAnimationsFound = TRUE; // mark that we found animations section in script-file
}
else if( EQUAL_SUB_STR( "ORIGIN_TRI"))
{
sscanf( ld_line, "ORIGIN_TRI %d %d %d", &aiTransVtx[0], &aiTransVtx[1], &aiTransVtx[2]); // read given vertices
}
// Key-word "END" ends infinite loop and script loading is over
else if( EQUAL_SUB_STR( "END"))
{
break;
}
// ignore old key-words
else if( EQUAL_SUB_STR( "MAPPING")) {}
else if( EQUAL_SUB_STR( "TEXTURE_REFLECTION")) {}
else if( EQUAL_SUB_STR( "TEXTURE_SPECULAR")) {}
else if( EQUAL_SUB_STR( "TEXTURE_BUMP")) {}
else if( EQUAL_SUB_STR( "TEXTURE")) {}
// If none of known key-words isnt recognised, we have wierd key-word, so throw error
else
{
ThrowF_t("Unrecognizible key-word found in line: \"%s\".", ld_line);
}
}
/*
* At the end we check if we found animations in script file and if initial mapping was done
* during loading of script file what means that key-word 'TEXTURE_DIM' was found
*/
if( bAnimationsFound != TRUE)
throw( "There are no animations defined for this model, and that can't be. Probable cause: script missing key-word \"ANIM_START\".");
if( bMappingDimFound != TRUE)
throw( "Initial mapping not done, and that can't be. Probable cause: script missing key-word \"TEXTURE_DIM\".");
edm_md.LinkDataForSurfaces(TRUE);
// try to
try
{
// load mapping
LoadMapping_t( CTString(fnScriptName.NoExt()+".map"));
}
// if not successful
catch (char *strError)
{
// ignore error message
(void)strError;
}
// import mapping
if( (fnImportMapping != "") ||
((fnClosed != "") && (fnOpened != "") && (fnUnwrapped != "")) )
{
CObject3D o3dClosed, o3dOpened, o3dUnwrapped;
o3dClosed.Clear();
o3dOpened.Clear();
o3dUnwrapped.Clear();
// if mapping is defined using three files
if( (fnClosed != "") && (fnOpened != "") && (fnUnwrapped != "") )
{
o3dClosed.LoadAny3DFormat_t( fnOpened, mStretch);
o3dOpened.LoadAny3DFormat_t( fnClosed, mStretch);
o3dUnwrapped.LoadAny3DFormat_t( fnUnwrapped, mStretch);
}
// if mapping is defined using one file
else
{
o3dClosed.LoadAny3DFormat_t( fnImportMapping, mStretch, CObject3D::LT_NORMAL);
o3dOpened.LoadAny3DFormat_t( fnImportMapping, mStretch, CObject3D::LT_OPENED);
o3dUnwrapped.LoadAny3DFormat_t( fnImportMapping, mStretch, CObject3D::LT_UNWRAPPED);
// multiply coordinates with size of texture
o3dUnwrapped.ob_aoscSectors.Lock();
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Lock();
INDEX ctVertices = o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Count();
for(INDEX ivtx=0; ivtx<ctVertices; ivtx++)
{
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices[ivtx](1) *= edm_md.md_Width/1024.0f;
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices[ivtx](2) *= edm_md.md_Height/1024.0f;
}
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Unlock();
o3dUnwrapped.ob_aoscSectors.Unlock();
}
o3dClosed.ob_aoscSectors.Lock();
o3dOpened.ob_aoscSectors.Lock();
o3dUnwrapped.ob_aoscSectors.Lock();
INDEX ctModelVertices = edm_md.md_VerticesCt;
INDEX ctModelPolygons = edm_md.md_MipInfos[0].mmpi_PolygonsCt;
o3dClosed.ob_aoscSectors[0].osc_aovxVertices.Lock();
INDEX ctClosedVertices = o3dClosed.ob_aoscSectors[0].osc_aovxVertices.Count();
INDEX ctClosedPolygons = o3dClosed.ob_aoscSectors[0].osc_aopoPolygons.Count();
o3dClosed.ob_aoscSectors[0].osc_aovxVertices.Unlock();
o3dOpened.ob_aoscSectors[0].osc_aovxVertices.Lock();
INDEX ctOpenedVertices = o3dOpened.ob_aoscSectors[0].osc_aovxVertices.Count();
INDEX ctOpenedPolygons = o3dOpened.ob_aoscSectors[0].osc_aopoPolygons.Count();
o3dOpened.ob_aoscSectors[0].osc_aovxVertices.Unlock();
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Lock();
INDEX ctUnwrappedVertices = o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Count();
INDEX ctUnwrappedPolygons = o3dUnwrapped.ob_aoscSectors[0].osc_aopoPolygons.Count();
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Unlock();
if((ctModelPolygons != ctClosedPolygons) ||
(ctModelPolygons != ctOpenedPolygons) ||
(ctModelPolygons != ctUnwrappedPolygons) )
{
ThrowF_t("ERROR: Object used to create model and some of objects used to define mapping don't have same number of polygons!");
}
if( ctModelVertices != ctClosedVertices)
{
ThrowF_t("ERROR: Object used to create model and object that defines closed mapping don't have same number of vertices!");
}
if( ctUnwrappedVertices != ctOpenedVertices)
{
ThrowF_t("ERROR: Objects that define opened and unwrapped mapping don't have same number of vertices!");
}
o3dClosed.ob_aoscSectors.Unlock();
o3dOpened.ob_aoscSectors.Unlock();
o3dUnwrapped.ob_aoscSectors.Unlock();
CalculateUnwrappedMapping( o3dClosed, o3dOpened, o3dUnwrapped);
CalculateMappingForMips();
}
else
{
ThrowF_t("ERROR: Mapping not defined!");
}
O3D.ob_aoscSectors.Unlock();
File.Close();
if( edm_aasAttachedSounds.Count() == 0)
CreateEmptyAttachingSounds();
CObject3D::BatchLoading_t(FALSE);
} catch (char*) {
CObject3D::BatchLoading_t(FALSE);
throw;
}
}
//----------------------------------------------------------------------------------------------
/*
* Routine takes Object 3D class as input and creates new model (model data)
* with its polygons, vertices, surfaces
*/
void CEditModel::NewModel(CObject3D *pO3D)
{
pO3D->ob_aoscSectors.Lock();
pO3D->ob_aoscSectors[0].LockAll();
edm_md.md_VerticesCt = pO3D->ob_aoscSectors[0].osc_aovxVertices.Count(); // see how many vertices we will have
edm_md.md_TransformedVertices.New( edm_md.md_VerticesCt); // create buffer for rotated vertices
edm_md.md_MainMipVertices.New( edm_md.md_VerticesCt); // create buffer for main mip vertices
edm_md.md_VertexMipMask.New( edm_md.md_VerticesCt); // create buffer for vertex masks
for( INDEX i=0; i<edm_md.md_VerticesCt; i++)
{
// copy vertex coordinates into md_MainMipVertices array so we colud make
// mip-models later (we will search original coordinates in this array)
edm_md.md_MainMipVertices[ i] =
DOUBLEtoFLOAT(pO3D->ob_aoscSectors[0].osc_aovxVertices[ i]);
edm_md.md_VertexMipMask[ i] = 0L; // mark to all vertices that they don't exist in any mip-model
}
AddMipModel( pO3D); // we add main model, first mip-model
pO3D->ob_aoscSectors[0].UnlockAll();
pO3D->ob_aoscSectors.Unlock();
}
//----------------------------------------------------------------------------------------------
/*
* Routine takes 3D object as input and adds one mip model
* The main idea is: for every vertice get distances to all vertices in md_MainMipVertices
* array. If minimum distance is found, set that this vertice exists. Loop for all vertices.
* Throw error if minimum distance isn't found. Set also new mip-model polygons info.
*/
void CEditModel::AddMipModel( CObject3D *pO3D)
{
INDEX i, j;
BOOL same_found;
// this is mask for vertices in current mip level
ULONG mip_vtx_mask = (1L) << edm_md.md_MipCt;
struct ModelMipInfo *pmmpi = &edm_md.md_MipInfos[ edm_md.md_MipCt]; // point to mip model that we will create
// for each vertex
for( INDEX iVertex=0; iVertex<edm_md.md_VerticesCt; iVertex++)
{
// mark that it is not in this mip model
edm_md.md_VertexMipMask[ iVertex] &= ~mip_vtx_mask;
}
INDEX o3dvct = pO3D->ob_aoscSectors[0].osc_aovxVertices.Count();
/*
* For each vertex in 3D object we calculate distances to all vertices in main mip-model.
* If distance (size of vector that is result of substraction of two vertice vectors) is
* less than some minimal float number, we assume that these vertices are the same.
* Processed vertex of 3D object gets its main-mip-model-vertex-friend's index as tag and
* mask value showing that it exists in this mip-model.
*/
for( i=0; i<o3dvct; i++)
{
same_found = FALSE;
for( j=0; j<edm_md.md_VerticesCt; j++)
{
FLOAT3D vVertex = DOUBLEtoFLOAT(pO3D->ob_aoscSectors[0].osc_aovxVertices[ i]);
FLOAT fAbsoluteDistance = Abs( (vVertex - edm_md.md_MainMipVertices[ j]).Length() );
if( fAbsoluteDistance < MAX_ALLOWED_DISTANCE)
{
edm_md.md_VertexMipMask[ j] |= mip_vtx_mask;// we mark that this vertice exists in this mip model
pO3D->ob_aoscSectors[0].osc_aovxVertices[ i].ovx_Tag = j;// remapping verice index must be remembered
same_found = TRUE; // mark that this vertex's remap is found
break;
}
}
if( same_found == FALSE) // if no vertice close enough is found, we have error
{
ThrowF_t("Vertex from mip model %d with number %d, coordinates (%f,%f,%f), can't be found in main mip model.\n"
"There can't be new vertices in rougher mip-models,"
"but only vertices from main mip model can be removed and polygons reorganized.\n",
edm_md.md_MipCt, i,
pO3D->ob_aoscSectors[0].osc_aovxVertices[ i](1), pO3D->ob_aoscSectors[0].osc_aovxVertices[ i](2), pO3D->ob_aoscSectors[0].osc_aovxVertices[ i](3));
}
}
/*
* We will create three arays for this mip polygon info:
* 1) array for polygons
* 2) array for mapping surfaces
* 3) array for polygon vertices
* 4) array for texture vertices
*/
/*
* First we create array large enough to accept object 3D's polygons.
*/
pmmpi->mmpi_PolygonsCt = pO3D->ob_aoscSectors[0].osc_aopoPolygons.Count();
pmmpi->mmpi_Polygons.New( pmmpi->mmpi_PolygonsCt);
/*
* Then we will create array for mapping surfaces and set their names
*/
pmmpi->mmpi_MappingSurfaces.New( pO3D->ob_aoscSectors[0].osc_aomtMaterials.Count()); // create array for mapping surfaces
for( i=0; i<pO3D->ob_aoscSectors[0].osc_aomtMaterials.Count(); i++)
{
MappingSurface &ms = pmmpi->mmpi_MappingSurfaces[ i];
ms.ms_ulOnColor = PC_ALLWAYS_ON; // set default ON and OFF masking colors
ms.ms_ulOffColor = PC_ALLWAYS_OFF;
ms.ms_Name = CTFileName( pO3D->ob_aoscSectors[0].osc_aomtMaterials[ i].omt_Name);
ms.ms_vSurface2DOffset = FLOAT3D( 1.0f, 1.0f, 1.0f);
ms.ms_HPB = FLOAT3D( 0.0f, 0.0f, 0.0f);
ms.ms_Zoom = 1.0f;
ms.ms_colColor =
pO3D->ob_aoscSectors[0].osc_aomtMaterials[ i].omt_Color | CT_OPAQUE; // copy surface color, set no alpha
ms.ms_sstShadingType = SST_MATTE;
ms.ms_sttTranslucencyType = STT_OPAQUE;
ms.ms_ulRenderingFlags = SRF_DIFFUSE|SRF_NEW_TEXTURE_FORMAT;
}
/*
* Then we will count how many ModelPolygonVertices we need and create array for them.
* This number is equal to sum of all vertices used by all object 3D's polygons
*/
INDEX pvct = 0;
for( i=0; i<pmmpi->mmpi_PolygonsCt; i++)
{
pvct += pO3D->ob_aoscSectors[0].osc_aopoPolygons[ i].opo_PolygonEdges.Count(); // we have vertices as many as edges
}
/*
* Now we will create an static array of temporary structures used for extracting
* vertice-surface connection. We need this because we have to set for all model vertices:
* 1) their texture vertices
* 2) their transformed vertices.
* First we will set surface and transformed indexes to every polygon vertice
*/
INDEX esvct = 0; // for counting polygon vertices
CStaticArray< CExtractSurfaceVertex> aesv;
aesv.New( pvct); // array with same number of members as polygon vertex array
{FOREACHINDYNAMICARRAY( pO3D->ob_aoscSectors[0].osc_aopoPolygons, CObjectPolygon, it1)
{
INDEX iPolySurface = pO3D->ob_aoscSectors[0].osc_aomtMaterials.Index( it1->opo_Material); // this polygon's surface index
FOREACHINDYNAMICARRAY( it1->opo_PolygonEdges, CObjectPolygonEdge, it2)
{
aesv[ esvct].esv_Surface = iPolySurface; // all these vertices are members of same polygon so they have same surface index
aesv[ esvct].esv_MipGlobalIndex = pO3D->ob_aoscSectors[0].osc_aovxVertices.Index( it2->ope_Edge->oed_Vertex0); // global index
esvct++;
}
}}
/*
* Then we will choose one verice from this array and see if there is any vertice
* processed until now that have same surface and global index. If souch
* vertice exists, copy its remap value, if it doesn't exists, set its remap value
* to value of current texture vertex counter. After counting souch surface-dependent
* vertices (texture vertices, tvct) we will create array for them
*/
BOOL same_vtx_found;
INDEX tvct = 0;
for( i=0; i<pvct; i++)
{
same_vtx_found = FALSE;
for( INDEX j=0; j<i; j++)
{
if( (aesv[ j].esv_Surface == aesv[ i].esv_Surface) && // if surface and global
(aesv[ j].esv_MipGlobalIndex == aesv[ i].esv_MipGlobalIndex)) // vertex index are the same
{
same_vtx_found = TRUE; // if yes, copy remap value
aesv[ i].esv_TextureVertexRemap = aesv[ j].esv_TextureVertexRemap;
break;
}
}
if( same_vtx_found == FALSE) // if not, set value to current counter
{
aesv[ i].esv_TextureVertexRemap = tvct;
tvct ++;
}
}
pmmpi->mmpi_TextureVertices.New( tvct); // create array for texture vertices
/*
* Now we will set texture vertex data for all surface unique vertices. We will do it by
* looping this to all polygon vertices: copy coordinates of vertex from global vertex array
* to UVW coordinates of texture vertex. That way we will have little overhead (some
* vertices will be copied many times) but it doesn't really matter.
*/
for( i=0; i<pvct; i++)
{
pmmpi->mmpi_TextureVertices[ aesv[ i].esv_TextureVertexRemap].mtv_UVW =
DOUBLEtoFLOAT(pO3D->ob_aoscSectors[0].osc_aovxVertices[ aesv[ i].esv_MipGlobalIndex]);
}
/*
* Now we intend to create data for all polygons (that includes setting polygon's
* texture and transformed vertex ptrs)
*/
INDEX mpvct = 0; // start polygon vertex counter
for( i=0; i<pmmpi->mmpi_PolygonsCt; i++) // loop all model polygons
{
struct ModelPolygon *pmp = &pmmpi->mmpi_Polygons[ i]; // ptr to activ model polygon
pmp->mp_Surface = pO3D->ob_aoscSectors[0].osc_aomtMaterials.Index( pO3D->ob_aoscSectors[0].osc_aopoPolygons[ i].opo_Material); // copy surface index
pmp->mp_ColorAndAlpha =
pO3D->ob_aoscSectors[0].osc_aopoPolygons[ i].opo_Material->omt_Color | CT_OPAQUE; // copy surface color, set no alpha
INDEX ctVertices = pO3D->ob_aoscSectors[0].osc_aopoPolygons[ i].opo_PolygonEdges.Count(); // set no of polygon's vertices
pmp->mp_PolygonVertices.New( ctVertices); // create array for them
for( j=0; j<ctVertices; j++) // fill data for this polygon's vertices
{
/*
* Here we really remap one mip models's vertex in a way that we set its transformed
* vertex ptr after remapping it using link (tag) to its original mip-model's vertex
*/
ULONG trans_vtx_idx = pO3D->ob_aoscSectors[0].osc_aovxVertices[ aesv[ mpvct].esv_MipGlobalIndex].ovx_Tag;
pmp->mp_PolygonVertices[ j].mpv_ptvTransformedVertex =
&edm_md.md_TransformedVertices[ (INDEX) trans_vtx_idx ]; // remapped ptr to transformed vertex
pmp->mp_PolygonVertices[ j].mpv_ptvTextureVertex =
&pmmpi->mmpi_TextureVertices[ aesv[ mpvct].esv_TextureVertexRemap]; // ptr to unique vertex in surface
mpvct ++;
}
}
edm_md.md_MipCt ++; // finally, this mip-model is done.
}
//----------------------------------------------------------------------------------------------
/*
* Routine sets unwrapped mapping from given three objects
*/
void CEditModel::CalculateUnwrappedMapping( CObject3D &o3dClosed, CObject3D &o3dOpened, CObject3D &o3dUnwrapped)
{
o3dOpened.ob_aoscSectors.Lock();
o3dClosed.ob_aoscSectors.Lock();
o3dUnwrapped.ob_aoscSectors.Lock();
// get first mip model
struct ModelMipInfo *pMMI = &edm_md.md_MipInfos[ 0];
// for each surface in first mip model
for( INDEX iSurface = 0; iSurface < pMMI->mmpi_MappingSurfaces.Count(); iSurface++)
{
MappingSurface *pmsSurface = &pMMI->mmpi_MappingSurfaces[iSurface];
// for each texture vertex in surface
for(INDEX iSurfaceTextureVertex=0; iSurfaceTextureVertex<pmsSurface->ms_aiTextureVertices.Count(); iSurfaceTextureVertex++)
{
INDEX iGlobalTextureVertex = pmsSurface->ms_aiTextureVertices[iSurfaceTextureVertex];
ModelTextureVertex *pmtvTextureVertex = &pMMI->mmpi_TextureVertices[iGlobalTextureVertex];
// obtain index of model vertex
INDEX iModelVertex = pmtvTextureVertex->mtv_iTransformedVertex;
// for each polygon in opened with same surface
for(INDEX iOpenedPolygon=0; iOpenedPolygon< o3dOpened.ob_aoscSectors[0].osc_aopoPolygons.Count(); iOpenedPolygon++)
{
DOUBLE3D vClosedVertex;
DOUBLE3D vOpenedVertex;
// get coordinate from model vertex in closed
o3dClosed.ob_aoscSectors[0].osc_aovxVertices.Lock();
vClosedVertex = o3dClosed.ob_aoscSectors[0].osc_aovxVertices[ iModelVertex];
o3dClosed.ob_aoscSectors[0].osc_aovxVertices.Unlock();
// find vertex in opened with same coordinate
o3dOpened.ob_aoscSectors[0].osc_aopoPolygons.Lock();
CObjectPolygon *popoOpenedPolygon = &o3dOpened.ob_aoscSectors[0].osc_aopoPolygons[ iOpenedPolygon];
o3dOpened.ob_aoscSectors[0].osc_aopoPolygons.Unlock();
if( popoOpenedPolygon->opo_Material->omt_Name != pmsSurface->ms_Name) continue;
for( INDEX iOpenedPolyEdge=0; iOpenedPolyEdge<popoOpenedPolygon->opo_PolygonEdges.Count(); iOpenedPolyEdge++)
{
popoOpenedPolygon->opo_PolygonEdges.Lock();
CObjectVertex *povOpenedVertex;
if( !popoOpenedPolygon->opo_PolygonEdges[iOpenedPolyEdge].ope_Backward)
{
povOpenedVertex = popoOpenedPolygon->opo_PolygonEdges[iOpenedPolyEdge].ope_Edge->oed_Vertex0;
}
else
{
povOpenedVertex = popoOpenedPolygon->opo_PolygonEdges[iOpenedPolyEdge].ope_Edge->oed_Vertex1;
}
popoOpenedPolygon->opo_PolygonEdges.Unlock();
vOpenedVertex = *povOpenedVertex;
// if these two vertices have same coordinates
FLOAT fAbsoluteDistance = Abs( (vClosedVertex - vOpenedVertex).Length());
if( fAbsoluteDistance < MAX_ALLOWED_DISTANCE)
{
o3dClosed.ob_aoscSectors[0].osc_aovxVertices.Lock();
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Lock();
// find index in opened
o3dOpened.ob_aoscSectors[0].osc_aovxVertices.Lock();
INDEX iOpenedModelVertex = o3dOpened.ob_aoscSectors[0].osc_aovxVertices.Index( povOpenedVertex);
o3dOpened.ob_aoscSectors[0].osc_aovxVertices.Unlock();
// get coordinate from unwrapped using index
DOUBLE3D vMappingCoordinate = o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices[ iOpenedModelVertex];
// set new mapping coordinates
pmtvTextureVertex->mtv_UVW = DOUBLEtoFLOAT( vMappingCoordinate);
pmtvTextureVertex->mtv_UVW(2) = -pmtvTextureVertex->mtv_UVW(2);
MEX2D mexUV;
mexUV(1) = MEX_METERS(pmtvTextureVertex->mtv_UVW(1));
mexUV(2) = MEX_METERS(pmtvTextureVertex->mtv_UVW(2));
pmtvTextureVertex->mtv_UV = mexUV;
o3dClosed.ob_aoscSectors[0].osc_aovxVertices.Unlock();
o3dUnwrapped.ob_aoscSectors[0].osc_aovxVertices.Unlock();
}
}
}
// reset surface position, rotation and zoom
pmsSurface->ms_HPB = FLOAT3D( 0.0f, 0.0f, 0.0f);
pmsSurface->ms_Zoom = 1.0f;
pmsSurface->ms_vSurface2DOffset = FLOAT3D( 0.0f, 0.0f, 0.0f);
}
}
o3dOpened.ob_aoscSectors.Unlock();
o3dClosed.ob_aoscSectors.Unlock();
o3dUnwrapped.ob_aoscSectors.Unlock();
}
//----------------------------------------------------------------------------------------------
/*
* Routine calculate mapping for mip models (except for main mip)
*/
void CEditModel::CalculateMappingForMips( void)
{
// for each mip model except first
for( INDEX iCurMip = 1; iCurMip< edm_md.md_MipCt; iCurMip++)
{
// get current mip model
struct ModelMipInfo *pMMICur = &edm_md.md_MipInfos[ iCurMip];
// get previous mip model
struct ModelMipInfo *pMMIPrev = &edm_md.md_MipInfos[ iCurMip-1];
// for each surface in current mip model
for( INDEX iSurfaceCur = 0; iSurfaceCur < pMMICur->mmpi_MappingSurfaces.Count(); iSurfaceCur++)
{
MappingSurface *pmsSurfCur = &pMMICur->mmpi_MappingSurfaces[iSurfaceCur];
// for each texture vertex in surface
for(INDEX iSurfCurTV=0; iSurfCurTV<pmsSurfCur->ms_aiTextureVertices.Count(); iSurfCurTV++)
{
INDEX iCurGlobalTV = pmsSurfCur->ms_aiTextureVertices[iSurfCurTV];
ModelTextureVertex *pmtvCur = &pMMICur->mmpi_TextureVertices[iCurGlobalTV];
// obtain index of model vertex
INDEX iCurMV = pmtvCur->mtv_iTransformedVertex;
// get 3D coordinate of vertex from main mip
FLOAT3D vMainMipCoordCur = edm_md.md_MainMipVertices[ iCurMV];
// -------- Find closest vertex (using 3D coordinate) in previous mip
// in previous mip model find surface with same name
MappingSurface *pmsSurfPrev = NULL;
for( INDEX iSurfacePrev = 0; iSurfacePrev < pMMIPrev->mmpi_MappingSurfaces.Count(); iSurfacePrev++)
{
pmsSurfPrev = &pMMIPrev->mmpi_MappingSurfaces[iSurfacePrev];
if( pmsSurfCur->ms_Name == pmsSurfPrev->ms_Name)
{
break;
}
}
// new surfaces can't appear
ASSERT(pmsSurfPrev != NULL);
if( pmsSurfPrev == NULL)
{
WarningMessage( "Mip model %d has surface that does not exist in previous mip. That is not allowed.", iCurMip);
break;
}
// set hudge distance as current minimum
FLOAT fMinDistance = 99999999.0f;
ModelTextureVertex *pmtvClosestPrev = NULL;
// for each texture vertex in previous mip's surface with same name
for(INDEX iSurfPrevTV=0; iSurfPrevTV<pmsSurfPrev->ms_aiTextureVertices.Count(); iSurfPrevTV++)
{
INDEX iPrevGlobalTV = pmsSurfPrev->ms_aiTextureVertices[iSurfPrevTV];
ModelTextureVertex *pmtvPrev = &pMMIPrev->mmpi_TextureVertices[iPrevGlobalTV];
// obtain index of model vertex
INDEX iPrevMV = pmtvPrev->mtv_iTransformedVertex;
// get 3D coordinate of vertex from main mip
FLOAT3D vMainMipCoordPrev = edm_md.md_MainMipVertices[ iPrevMV];
// get distance of these two vertices
FLOAT fAbsoluteDistance = Abs( (vMainMipCoordPrev - vMainMipCoordCur).Length());
if( fAbsoluteDistance < fMinDistance)
{
// remember current texture vertex as closest one
fMinDistance = fAbsoluteDistance;
pmtvClosestPrev = pmtvPrev;
}
}
ASSERT( pmtvClosestPrev != NULL);
// copy mapping coordinates from closest mapping vertex in previous mip
pmtvCur->mtv_UVW = pmtvClosestPrev->mtv_UVW;
pmtvCur->mtv_UV = pmtvClosestPrev->mtv_UV;
pmtvCur->mtv_vU = pmtvClosestPrev->mtv_vU;
pmtvCur->mtv_vV = pmtvClosestPrev->mtv_vV;
}
}
}
}
/*
* This routine opens last script file loaded, repeats reading key-words until it finds
* key-word "ANIM_START". Then it calls animation data load from script routine.
*/
void CEditModel::UpdateAnimations_t(CTFileName &fnScriptName) // throw char *
{
CTFileStream File;
char ld_line[ 128];
CListHead FrameNamesList;
FLOATmatrix3D mStretch;
mStretch.Diagonal(1.0f);
File.Open_t( fnScriptName); // open script file for reading
FOREVER
{
do
{
File.GetLine_t(ld_line, 128);
}
while( (strlen( ld_line)== 0) || (ld_line[0]==';'));
if( EQUAL_SUB_STR( "SIZE"))
{
_strupr( ld_line);
FLOAT fStretch = 1.0f;
sscanf( ld_line, "SIZE %g", &fStretch);
mStretch *= fStretch;
}
else if( EQUAL_SUB_STR( "TRANSFORM"))
{
_strupr( ld_line);
FLOATmatrix3D mTran;
mTran.Diagonal(1.0f);
sscanf( ld_line, "TRANSFORM %g %g %g %g %g %g %g %g %g",
&mTran(1,1), &mTran(1,2), &mTran(1,3),
&mTran(2,1), &mTran(2,2), &mTran(2,3),
&mTran(3,1), &mTran(3,2), &mTran(3,3));
mStretch *= mTran;
}
else if( EQUAL_SUB_STR( "ANIM_START"))
{
LoadModelAnimationData_t( &File, mStretch); // load and set model's animation data
break; // we found our animations, we loaded them so we will stop forever loop
}
else if( EQUAL_SUB_STR( "ORIGIN_TRI"))
{
sscanf( ld_line, "ORIGIN_TRI %d %d %d", &aiTransVtx[0], &aiTransVtx[1], &aiTransVtx[2]); // read given vertices
}
}
File.Close();
CreateEmptyAttachingSounds();
}
//----------------------------------------------------------------------------------------------
void CEditModel::CreateMipModels_t(CObject3D &objRestFrame, CObject3D &objMipSourceFrame,
INDEX iVertexRemoveRate, INDEX iSurfacePreservingFactor)
{
// free possible mip-models except main mip model
INDEX iMipModel=1;
for( ; iMipModel<edm_md.md_MipCt; iMipModel++)
{
edm_md.md_MipInfos[ iMipModel].Clear();
}
edm_md.md_MipCt = 1;
// create mip model structure
CMipModel mmMipModel;
mmMipModel.FromObject3D_t( objRestFrame, objMipSourceFrame);
if( ProgresRoutines.SetProgressMessage != NULL)
ProgresRoutines.SetProgressMessage( "Calculating mip models ...");
INDEX ctVerticesInRestFrame = mmMipModel.mm_amvVertices.Count();
if( ProgresRoutines.SetProgressRange != NULL)
ProgresRoutines.SetProgressRange(ctVerticesInRestFrame);
// create maximum 32 mip models
for( iMipModel=0; iMipModel<31; iMipModel++)
{
// if unable to create mip models
if( !mmMipModel.CreateMipModel_t( iVertexRemoveRate, iSurfacePreservingFactor))
{
// stop creating more mip models
break;
}
if( ProgresRoutines.SetProgressState != NULL)
ProgresRoutines.SetProgressState(ctVerticesInRestFrame - mmMipModel.mm_amvVertices.Count());
CObject3D objMipModel;
mmMipModel.ToObject3D( objMipModel);
objMipModel.ob_aoscSectors.Lock();
objMipModel.ob_aoscSectors[0].LockAll();
AddMipModel( &objMipModel);
objMipModel.ob_aoscSectors[0].UnlockAll();
objMipModel.ob_aoscSectors.Unlock();
}
ProgresRoutines.SetProgressState(ctVerticesInRestFrame);
edm_md.SpreadMipSwitchFactors( 0, 5.0f);
edm_md.LinkDataForSurfaces(FALSE);
CalculateMappingForMips();
}
//----------------------------------------------------------------------------------------------
/*
* This routine discards all mip-models except main (mip-model 0). Then it opens script file
* with file name of last script loaded. Then it repeats reading key-words until it counts two
* key-words "MIPMODEL". For second and all other key-words routine calls add mip-map routine
*/
void CEditModel::UpdateMipModels_t(CTFileName &fnScriptName) // throw char *
{
try {
CObject3D::BatchLoading_t(TRUE);
CTFileStream File;
CObject3D O3D;
char base_path[ PATH_MAX] = "";
char file_name[ PATH_MAX];
char full_path[ PATH_MAX];
char ld_line[ 128];
FLOATmatrix3D mStretch;
mStretch.Diagonal(1.0f);
O3D.ob_aoscSectors.Lock();
ASSERT( edm_md.md_VerticesCt != 0);
File.Open_t( fnScriptName); // open script file for reading
INDEX i=1;
for( ; i<edm_md.md_MipCt; i++)
{
edm_md.md_MipInfos[ i].Clear(); // free possible mip-models except main mip model
}
edm_md.md_MipCt = 1;
FOREVER
{
do
{
File.GetLine_t(ld_line, 128);
}
while( (strlen( ld_line)== 0) || (ld_line[0]==';'));
if( EQUAL_SUB_STR( "SIZE"))
{
_strupr( ld_line);
FLOAT fStretch = 1.0f;
sscanf( ld_line, "SIZE %g", &fStretch);
mStretch *= fStretch;
}
else if( EQUAL_SUB_STR( "TRANSFORM"))
{
_strupr( ld_line);
FLOATmatrix3D mTran;
mTran.Diagonal(1.0f);
sscanf( ld_line, "TRANSFORM %g %g %g %g %g %g %g %g %g",
&mTran(1,1), &mTran(1,2), &mTran(1,3),
&mTran(2,1), &mTran(2,2), &mTran(2,3),
&mTran(3,1), &mTran(3,2), &mTran(3,3));
mStretch *= mTran;
}
else if( EQUAL_SUB_STR( "DIRECTORY"))
{
_strupr( ld_line);
sscanf( ld_line, "DIRECTORY %s", base_path);
if( base_path[ strlen( base_path) - 1] != '\\')
strcat( base_path,"\\");
}
else if( EQUAL_SUB_STR( "MIP_MODELS"))
{
_strupr( ld_line);
INDEX no_of_mip_models;
sscanf( ld_line, "MIP_MODELS %d", &no_of_mip_models);
// Jump over first mip model file name
File.GetLine_t(ld_line, 128);
for( i=0; i<no_of_mip_models-1; i++)
{
File.GetLine_t(ld_line, 128);
_strupr( ld_line);
sscanf( ld_line, "%s", file_name);
sprintf( full_path, "%s%s", base_path, file_name);
O3D.Clear(); // clear possible existing O3D's data
O3D.LoadAny3DFormat_t( CTString(full_path), mStretch);
if( edm_md.md_VerticesCt < O3D.ob_aoscSectors[0].osc_aovxVertices.Count())
{
ThrowF_t(
"It is unlikely that mip-model \"%s\" is valid.\n"
"It contains more vertices than main mip-model so it can't be mip-model.",
full_path);
}
if( edm_md.md_MipCt < MAX_MODELMIPS)
{
O3D.ob_aoscSectors[0].LockAll();;
AddMipModel( &O3D); // else this is one of model's mip definitions so call Add Mip Model
O3D.ob_aoscSectors[0].UnlockAll();;
}
else
{
ThrowF_t("There are too many mip-models defined in script file. Maximum of %d mip-models allowed.", MAX_MODELMIPS-1);
}
}
}
else if( EQUAL_SUB_STR( "ORIGIN_TRI"))
{
sscanf( ld_line, "ORIGIN_TRI %d %d %d", &aiTransVtx[0], &aiTransVtx[1], &aiTransVtx[2]); // read given vertices
}
else if( EQUAL_SUB_STR( "END"))
{
break;
}
}
O3D.ob_aoscSectors.Unlock();
edm_md.LinkDataForSurfaces(TRUE);
CObject3D::BatchLoading_t(FALSE);
} catch (char*) {
CObject3D::BatchLoading_t(FALSE);
throw;
}
}
/*
* Draws given surface in wire frame
*/
void CEditModel::DrawWireSurface( CDrawPort *pDP, INDEX iCurrentMip, INDEX iCurrentSurface,
FLOAT fMagnifyFactor, PIX offx, PIX offy,
COLOR clrVisible, COLOR clrInvisible)
{
FLOAT3D f3dTr0, f3dTr1, f3dTr2;
struct ModelTextureVertex *pVtx0, *pVtx1;
// for each polygon
for( INDEX iPoly=0; iPoly<edm_md.md_MipInfos[iCurrentMip].mmpi_PolygonsCt; iPoly++)
{
struct ModelPolygon *pPoly = &edm_md.md_MipInfos[iCurrentMip].mmpi_Polygons[iPoly];
if( pPoly->mp_Surface == iCurrentSurface)
{ // readout poly vertices
f3dTr0(1) = (FLOAT)pPoly->mp_PolygonVertices[0].mpv_ptvTextureVertex->mtv_UV(1);
f3dTr0(2) = (FLOAT)pPoly->mp_PolygonVertices[0].mpv_ptvTextureVertex->mtv_UV(2);
f3dTr0(3) = 0.0f;
f3dTr1(1) = (FLOAT)pPoly->mp_PolygonVertices[1].mpv_ptvTextureVertex->mtv_UV(1);
f3dTr1(2) = (FLOAT)pPoly->mp_PolygonVertices[1].mpv_ptvTextureVertex->mtv_UV(2);
f3dTr1(3) = 0.0f;
f3dTr2(1) = (FLOAT)pPoly->mp_PolygonVertices[2].mpv_ptvTextureVertex->mtv_UV(1);
f3dTr2(2) = (FLOAT)pPoly->mp_PolygonVertices[2].mpv_ptvTextureVertex->mtv_UV(2);
f3dTr2(3) = 0.0f;
// determine line visibility
FLOAT3D f3dNormal = (f3dTr2-f3dTr1)*(f3dTr0-f3dTr1);
COLOR clrWire;
ULONG ulLineType;
if( f3dNormal(3) < 0) {
clrWire = clrVisible;
ulLineType = _FULL_;
} else {
clrWire = clrInvisible;
ulLineType = _POINT_;
}
// draw lines
PIX pixX0, pixY0, pixX1, pixY1;
for( INDEX iVtx=0; iVtx<pPoly->mp_PolygonVertices.Count()-1; iVtx++) {
pVtx0 = pPoly->mp_PolygonVertices[iVtx+0].mpv_ptvTextureVertex;
pVtx1 = pPoly->mp_PolygonVertices[iVtx+1].mpv_ptvTextureVertex;
pixX0 = (PIX)(pVtx0->mtv_UV(1) * fMagnifyFactor) - offx;
pixY0 = (PIX)(pVtx0->mtv_UV(2) * fMagnifyFactor) - offy;
pixX1 = (PIX)(pVtx1->mtv_UV(1) * fMagnifyFactor) - offx;
pixY1 = (PIX)(pVtx1->mtv_UV(2) * fMagnifyFactor) - offy;
pDP->DrawLine( pixX0, pixY0, pixX1, pixY1, clrWire|CT_OPAQUE, ulLineType);
}
// draw last line
pVtx0 = pPoly->mp_PolygonVertices[0].mpv_ptvTextureVertex;
pixX0 = (PIX)(pVtx0->mtv_UV(1) * fMagnifyFactor) - offx;
pixY0 = (PIX)(pVtx0->mtv_UV(2) * fMagnifyFactor) - offy;
pDP->DrawLine( pixX0, pixY0, pixX1, pixY1, clrWire|CT_OPAQUE, ulLineType);
}
}
}
/*
* Flat fills given surface
*/
void CEditModel::DrawFilledSurface( CDrawPort *pDP, INDEX iCurrentMip, INDEX iCurrentSurface,
FLOAT fMagnifyFactor, PIX offx, PIX offy,
COLOR clrVisible, COLOR clrInvisible)
{
FLOAT3D f3dTr0, f3dTr1, f3dTr2;
struct ModelTextureVertex *pVtx0, *pVtx1, *pVtx2;
// for each polygon
for( INDEX iPoly=0; iPoly<edm_md.md_MipInfos[iCurrentMip].mmpi_PolygonsCt; iPoly++)
{
struct ModelPolygon *pPoly = &edm_md.md_MipInfos[iCurrentMip].mmpi_Polygons[iPoly];
if( pPoly->mp_Surface == iCurrentSurface)
{ // readout poly vertices
f3dTr0(1) = (FLOAT)pPoly->mp_PolygonVertices[0].mpv_ptvTextureVertex->mtv_UV(1);
f3dTr0(2) = (FLOAT)pPoly->mp_PolygonVertices[0].mpv_ptvTextureVertex->mtv_UV(2);
f3dTr0(3) = 0.0f;
f3dTr1(1) = (FLOAT)pPoly->mp_PolygonVertices[1].mpv_ptvTextureVertex->mtv_UV(1);
f3dTr1(2) = (FLOAT)pPoly->mp_PolygonVertices[1].mpv_ptvTextureVertex->mtv_UV(2);
f3dTr1(3) = 0.0f;
f3dTr2(1) = (FLOAT)pPoly->mp_PolygonVertices[2].mpv_ptvTextureVertex->mtv_UV(1);
f3dTr2(2) = (FLOAT)pPoly->mp_PolygonVertices[2].mpv_ptvTextureVertex->mtv_UV(2);
f3dTr2(3) = 0.0f;
// determine poly visibility
COLOR clrFill;
FLOAT3D f3dNormal = (f3dTr2-f3dTr1)*(f3dTr0-f3dTr1);
if( f3dNormal(3) < 0) clrFill = clrVisible|0xFF;
else clrFill = clrInvisible|0xFF;
// draw traingle(s) fan
pDP->InitTexture( NULL);
pVtx0 = pPoly->mp_PolygonVertices[0].mpv_ptvTextureVertex;
PIX pixX0 = (PIX)(pVtx0->mtv_UV(1) * fMagnifyFactor) - offx;
PIX pixY0 = (PIX)(pVtx0->mtv_UV(2) * fMagnifyFactor) - offy;
for( INDEX iVtx=1; iVtx<pPoly->mp_PolygonVertices.Count()-1; iVtx++) {
pVtx1 = pPoly->mp_PolygonVertices[iVtx+0].mpv_ptvTextureVertex;
pVtx2 = pPoly->mp_PolygonVertices[iVtx+1].mpv_ptvTextureVertex;
PIX pixX1 = (PIX)(pVtx1->mtv_UV(1) * fMagnifyFactor) - offx;
PIX pixY1 = (PIX)(pVtx1->mtv_UV(2) * fMagnifyFactor) - offy;
PIX pixX2 = (PIX)(pVtx2->mtv_UV(1) * fMagnifyFactor) - offx;
PIX pixY2 = (PIX)(pVtx2->mtv_UV(2) * fMagnifyFactor) - offy;
pDP->AddTriangle( pixX0,pixY0, pixX1,pixY1, pixX2,pixY2, clrFill);
}
// to buffer with it
pDP->FlushRenderingQueue();
}
}
}
/*
* Prints surface numbers
*/
void CEditModel::PrintSurfaceNumbers( CDrawPort *pDP, CFontData *pFont,
INDEX iCurrentMip, FLOAT fMagnifyFactor, PIX offx, PIX offy, COLOR clrInk)
{
char achrLine[ 256];
// clear Z-buffer
pDP->FillZBuffer( ZBUF_BACK);
// get mip model ptr
struct ModelMipInfo *pMMI = &edm_md.md_MipInfos[ iCurrentMip];
// for all surfaces
for( INDEX iSurf=0;iSurf<pMMI->mmpi_MappingSurfaces.Count(); iSurf++)
{
MappingSurface *pms= &pMMI->mmpi_MappingSurfaces[iSurf];
MEXaabbox2D boxSurface;
// for each texture vertex in surface
for(INDEX iSurfaceTextureVertex=0; iSurfaceTextureVertex<pms->ms_aiTextureVertices.Count(); iSurfaceTextureVertex++)
{
INDEX iGlobalTextureVertex = pms->ms_aiTextureVertices[iSurfaceTextureVertex];
ModelTextureVertex *pmtv = &pMMI->mmpi_TextureVertices[iGlobalTextureVertex];
boxSurface |= pmtv->mtv_UV;
}
MEX2D mexCenter = boxSurface.Center();
PIX2D pixCenter = PIX2D(mexCenter(1)*fMagnifyFactor-offx, mexCenter(2)*fMagnifyFactor-offy);
// print active surface's number into print line
sprintf( achrLine, "%d", iSurf);
// set font
pDP->SetFont( pFont);
// print line
pDP->PutText( achrLine, pixCenter(1)-strlen(achrLine)*4, pixCenter(2)-6);
}
}
/*
* Exports surface names and numbers under given file name
*/
void CEditModel::ExportSurfaceNumbersAndNames( CTFileName fnFile)
{
CTString strExport;
// get mip model ptr
struct ModelMipInfo *pMMI = &edm_md.md_MipInfos[ 0];
// for all surfaces
for( INDEX iSurf=0; iSurf<pMMI->mmpi_MappingSurfaces.Count(); iSurf++)
{
MappingSurface *pms= &pMMI->mmpi_MappingSurfaces[iSurf];
CTString strExportLine;
strExportLine.PrintF( "%d) %s\n", iSurf, pms->ms_Name);
strExport+=strExportLine;
}
try
{
strExport.Save_t( fnFile);
}
catch(char *strError)
{
// report error
WarningMessage( strError);
}
}
/*
* Retrieves given surface's name
*/
const char *CEditModel::GetSurfaceName(INDEX iCurrentMip, INDEX iCurrentSurface)
{
struct MappingSurface *pSurface;
pSurface = &edm_md.md_MipInfos[ iCurrentMip].mmpi_MappingSurfaces[ iCurrentSurface];
return( pSurface->ms_Name);
}
//--------------------------------------------------------------------------------------------
/*
* Sets first empty position in existing patches mask
*/
BOOL CEditModel::GetFirstEmptyPatchIndex( INDEX &iMaskBit)
{
iMaskBit = 0;
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
CTextureData *pTD = (CTextureData *) edm_md.md_mpPatches[ iPatch].mp_toTexture.GetData();
if( pTD == NULL)
{
iMaskBit = iPatch;
return TRUE;
}
}
return FALSE;
}
//--------------------------------------------------------------------------------------------
/*
* Sets first occupied position in existing patches mask
*/
BOOL CEditModel::GetFirstValidPatchIndex( INDEX &iMaskBit)
{
iMaskBit = 0;
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
CTextureData *pTD = (CTextureData *) edm_md.md_mpPatches[ iPatch].mp_toTexture.GetData();
if( pTD != NULL)
{
iMaskBit = iPatch;
return TRUE;
}
}
return FALSE;
}
//--------------------------------------------------------------------------------------------
/*
* Sets previous valid patch position in existing patches mask
*/
void CEditModel::GetPreviousValidPatchIndex( INDEX &iMaskBit)
{
ASSERT( (iMaskBit>=0) && (iMaskBit<MAX_TEXTUREPATCHES) );
for( INDEX iPatch=iMaskBit+MAX_TEXTUREPATCHES-1; iPatch>iMaskBit; iPatch--)
{
INDEX iCurrentPatch = iPatch%32;
CTString strPatchName = edm_md.md_mpPatches[ iCurrentPatch].mp_strName;
if( strPatchName != "")
{
iMaskBit = iCurrentPatch;
return;
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Sets next valid patch position in existing patches mask
*/
void CEditModel::GetNextValidPatchIndex( INDEX &iMaskBit)
{
ASSERT( (iMaskBit>=0) && (iMaskBit<MAX_TEXTUREPATCHES) );
for( INDEX iPatch=iMaskBit+1; iPatch<iMaskBit+MAX_TEXTUREPATCHES; iPatch++)
{
INDEX iCurrentPatch = iPatch%32;
CTString strPatchName = edm_md.md_mpPatches[ iCurrentPatch].mp_strName;
if( strPatchName != "")
{
iMaskBit = iCurrentPatch;
return;
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Moves patch relatively for given coordinates
*/
void CEditModel::MovePatchRelative( INDEX iMaskBit, MEX2D mexOffset)
{
CTFileName fnPatch = edm_md.md_mpPatches[ iMaskBit].mp_toTexture.GetName();
if( fnPatch == "") return;
edm_md.md_mpPatches[ iMaskBit].mp_mexPosition += mexOffset;
CalculatePatchesPerPolygon();
}
//--------------------------------------------------------------------------------------------
/*
* Sets patch stretch
*/
void CEditModel::SetPatchStretch( INDEX iMaskBit, FLOAT fNewStretch)
{
CTFileName fnPatch = edm_md.md_mpPatches[ iMaskBit].mp_toTexture.GetName();
if( fnPatch == "") return;
edm_md.md_mpPatches[ iMaskBit].mp_fStretch = fNewStretch;
CalculatePatchesPerPolygon();
}
//--------------------------------------------------------------------------------------------
/*
* Searches for first available empty patch position index and adds patch
*/
BOOL CEditModel::EditAddPatch( CTFileName fnPatchName, MEX2D mexPos, INDEX &iMaskBit)
{
if( !GetFirstEmptyPatchIndex( iMaskBit))
return FALSE;
try
{
edm_md.md_mpPatches[ iMaskBit].mp_toTexture.SetData_t( fnPatchName);
}
catch (char *strError)
{
(void)strError;
return FALSE;
}
edm_md.md_mpPatches[ iMaskBit].mp_mexPosition = mexPos;
edm_md.md_mpPatches[ iMaskBit].mp_fStretch = 1.0f;
edm_md.md_mpPatches[ iMaskBit].mp_strName.PrintF( "Patch%02d", iMaskBit);
CalculatePatchesPerPolygon();
return TRUE;
}
//--------------------------------------------------------------------------------------------
/*
* Removes patch with given index from existing mask and erases its file name
*/
void CEditModel::EditRemovePatch( INDEX iMaskBit)
{
edm_md.md_mpPatches[ iMaskBit].mp_toTexture.SetData(NULL);
CalculatePatchesPerPolygon();
}
void CEditModel::EditRemoveAllPatches(void)
{
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
edm_md.md_mpPatches[ iPatch].mp_toTexture.SetData(NULL);
}
CalculatePatchesPerPolygon();
}
INDEX CEditModel::CountPatches(void)
{
INDEX iResult = 0;
for(INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
if( edm_md.md_mpPatches[ iPatch].mp_toTexture.GetName() != "")
{
iResult++;
}
}
return iResult;
}
ULONG CEditModel::GetExistingPatchesMask(void)
{
ULONG ulResult = 0;
for(INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
if( edm_md.md_mpPatches[ iPatch].mp_toTexture.GetName() != "")
{
ulResult |= 1UL << iPatch;
}
}
return ulResult;
}
//--------------------------------------------------------------------------------------------
void CEditModel::CalculatePatchesPerPolygon(void)
{
// count existing patches
INDEX ctPatches = CountPatches();
// for each mip model
for( INDEX iMip=0; iMip<edm_md.md_MipCt; iMip++)
{
ModelMipInfo *pMMI = &edm_md.md_MipInfos[ iMip];
// clear previously existing array
pMMI->mmpi_aPolygonsPerPatch.Clear();
// if patches are visible in this mip model
if( (pMMI->mmpi_ulFlags & MM_PATCHES_VISIBLE) && (ctPatches != 0) )
{
// add description member for each patch
pMMI->mmpi_aPolygonsPerPatch.New( ctPatches);
INDEX iExistingPatch = 0;
// for each patch
for(INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
// if patch exists
if( edm_md.md_mpPatches[ iPatch].mp_toTexture.GetName() != "")
{
// allocate temporary array of indices for each polygon in mip model
CStaticArray<INDEX> aiPolygons;
aiPolygons.New( pMMI->mmpi_PolygonsCt);
// clear counter of occupied polygons
INDEX ctOccupiedPolygons = 0;
// get patch occupying box
CTextureData *pTD = (CTextureData *) edm_md.md_mpPatches[ iPatch].mp_toTexture.GetData();
ASSERT( pTD != NULL);
MEX2D mex2dPosition = edm_md.md_mpPatches[ iPatch].mp_mexPosition;
FLOAT fStretch = edm_md.md_mpPatches[ iPatch].mp_fStretch;
MEXaabbox2D boxPatch = MEXaabbox2D(
mex2dPosition, MEX2D( mex2dPosition(1)+pTD->GetWidth()*fStretch,
mex2dPosition(2)+pTD->GetHeight()*fStretch) );
// for each polygon
for(INDEX iPolygon=0; iPolygon<pMMI->mmpi_PolygonsCt; iPolygon++)
{
ModelPolygon *pMP = &pMMI->mmpi_Polygons[iPolygon];
// for all vertices in polygon
MEXaabbox2D boxMapping;
for( INDEX iVertex=0; iVertex<pMP->mp_PolygonVertices.Count(); iVertex++)
{
ModelTextureVertex *pMTV = pMP->mp_PolygonVertices[iVertex].mpv_ptvTextureVertex;
// calculate bounding box of mapping coordinates
boxMapping |= MEXaabbox2D(pMTV->mtv_UV);
}
// if bounding box of polygon's mapping coordinates touches patch
if( boxPatch.HasContactWith( boxMapping))
{
// add polygon index to list of occupied polygons
aiPolygons[ ctOccupiedPolygons] = iPolygon;
ctOccupiedPolygons++;
}
}
if( ctOccupiedPolygons != 0)
{
// copy temporary array of polygon indices to mip model's array of polygon indices
pMMI->mmpi_aPolygonsPerPatch[ iExistingPatch].ppp_iPolygons.New( ctOccupiedPolygons);
for( INDEX iOccupied=0; iOccupied<ctOccupiedPolygons; iOccupied++)
{
pMMI->mmpi_aPolygonsPerPatch[ iExistingPatch].ppp_iPolygons[iOccupied] =
aiPolygons[ iOccupied];
}
}
// count existing patches
iExistingPatch++;
}
}
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Writes settings of given mip model into file
*/
void CEditModel::WriteMipSettings_t( CTStream *ostrFile, INDEX iMip)
{
ASSERT( iMip < edm_md.md_MipCt);
// write indetification of one mip's mapping info
ostrFile->WriteID_t( CChunkID( "MIPS"));
// get count
INDEX iSurfacesCt = edm_md.md_MipInfos[ iMip].mmpi_MappingSurfaces.Count();
// write count
(*ostrFile) << iSurfacesCt;
// for all surfaces
FOREACHINSTATICARRAY(edm_md.md_MipInfos[ iMip].mmpi_MappingSurfaces, MappingSurface, itSurface)
{
// write setings for current surface
itSurface->WriteSettings_t( ostrFile);
}
}
//--------------------------------------------------------------------------------------------
/*
* Reads settigns of given mip model from file
*/
void CEditModel::ReadMipSettings_t(CTStream *istrFile, INDEX iMip)
{
MappingSurface msTmp;
ASSERT( iMip < edm_md.md_MipCt);
// check chunk
istrFile->ExpectID_t( CChunkID( "MIPS"));
// get count
INDEX iSurfacesCt;
*istrFile >> iSurfacesCt;
// for all saved surfaces
for( INDEX iSurface=0; iSurface<iSurfacesCt; iSurface++)
{
// read mapping surface settings
msTmp.ReadSettings_t( istrFile);
// for all surfaces in given mip
for( INDEX i=0; i<edm_md.md_MipInfos[ iMip].mmpi_MappingSurfaces.Count(); i++)
{
MappingSurface &ms = edm_md.md_MipInfos[ iMip].mmpi_MappingSurfaces[ i];
// are these surfaces the same?
if( ms == msTmp)
{
// try to set new position and angles
ms.ms_sstShadingType = msTmp.ms_sstShadingType;
ms.ms_sttTranslucencyType = msTmp.ms_sttTranslucencyType;
ms.ms_ulRenderingFlags = msTmp.ms_ulRenderingFlags;
ms.ms_colDiffuse = msTmp.ms_colDiffuse;
ms.ms_colReflections = msTmp.ms_colReflections;
ms.ms_colSpecular = msTmp.ms_colSpecular;
ms.ms_colBump = msTmp.ms_colBump;
ms.ms_ulOnColor = msTmp.ms_ulOnColor;
ms.ms_ulOffColor = msTmp.ms_ulOffColor;
}
}
}
}
//--------------------------------------------------------------------------------------------
/*
* Saves mapping data for whole model (iMip = -1) or for just one mip model
*/
void CEditModel::SaveMapping_t( CTFileName fnFileName, INDEX iMip /*=-1*/)
{
CTFileStream strmMappingFile;
// create file
strmMappingFile.Create_t( fnFileName, CTStream::CM_BINARY);
// write file ID
strmMappingFile.WriteID_t( CChunkID( "MPNG"));
// write version
strmMappingFile.WriteID_t( CChunkID(MAPPING_VERSION));
// set as we have only one mip
INDEX iStartCt = iMip;
INDEX iMaxCt = iMip+1;
// if iMip is -1 means that we want all mips in model
if( iMip == -1)
{
iStartCt = 0;
iMaxCt = edm_md.md_MipCt;
}
// for wanted mip models
for( INDEX iMipCt=iStartCt; iMipCt<iMaxCt; iMipCt++)
{
// write settings for current mip
WriteMipSettings_t( &strmMappingFile, iMipCt);
}
// save attached sounds
strmMappingFile<<edm_aasAttachedSounds.Count();
for( INDEX iSound=0; iSound<edm_aasAttachedSounds.Count(); iSound++)
{
edm_aasAttachedSounds[iSound].Write_t( &strmMappingFile);
}
// save attached models
INDEX ctAttachmentPositions = edm_aamAttachedModels.Count();
ASSERT( edm_md.md_aampAttachedPosition.Count() == ctAttachmentPositions);
strmMappingFile<<ctAttachmentPositions;
FOREACHINDYNAMICARRAY(edm_aamAttachedModels, CAttachedModel, itam)
{
itam->Write_t( &strmMappingFile);
}
FOREACHINDYNAMICARRAY(edm_md.md_aampAttachedPosition, CAttachedModelPosition, itamp)
{
itamp->Write_t( &strmMappingFile);
}
// save collision boxes
INDEX ctCollisionBoxes = edm_md.md_acbCollisionBox.Count();
ASSERT( ctCollisionBoxes>0);
if(ctCollisionBoxes == 0)
{
WarningMessage( "Trying to save 0 collision boxes into mapping file.");
}
strmMappingFile<<ctCollisionBoxes;
FOREACHINDYNAMICARRAY(edm_md.md_acbCollisionBox, CModelCollisionBox, itcb)
{
itcb->Write_t( &strmMappingFile);
}
// save patches
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
edm_md.md_mpPatches[ iPatch].Write_t( &strmMappingFile);
}
}
//--------------------------------------------------------------------------------------------
/*
* Loads mapping data for whole model (iMip = -1) or just for one mip model
*/
void CEditModel::LoadMapping_t( CTFileName fnFileName, INDEX iMip /*=-1*/)
{
CTFileStream strmMappingFile;
BOOL bReadPolygonsPerSurface = FALSE;
BOOL bReadSoundsAndAttachments = FALSE;
BOOL bReadCollision = FALSE;
BOOL bReadPatches = FALSE;
BOOL bReadSurfaceColors = FALSE;
// open binary file
strmMappingFile.Open_t( fnFileName);
// recognize file ID
strmMappingFile.ExpectID_t( CChunkID( "MPNG"));
// get version of mapping file
CChunkID cidVersion = strmMappingFile.GetID_t();
// act acording to version of mapping file
if( cidVersion == CChunkID(MAPPING_VERSION_WITHOUT_POLYGONS_PER_SURFACE) )
{
}
else if( cidVersion == CChunkID( MAPPING_VERSION_WITHOUT_SOUNDS_AND_ATTACHMENTS))
{
bReadPolygonsPerSurface = TRUE;
}
else if( cidVersion == CChunkID( MAPPING_VERSION_WITHOUT_COLLISION))
{
bReadPolygonsPerSurface = TRUE;
bReadSoundsAndAttachments = TRUE;
}
else if( cidVersion == CChunkID( MAPPING_VERSION_WITHOUT_PATCHES))
{
bReadPolygonsPerSurface = TRUE;
bReadSoundsAndAttachments = TRUE;
bReadCollision = TRUE;
}
else if( cidVersion == CChunkID( MAPPING_WITHOUT_SURFACE_COLORS))
{
bReadPolygonsPerSurface = TRUE;
bReadSoundsAndAttachments = TRUE;
bReadCollision = TRUE;
bReadPatches = TRUE;
}
else if( cidVersion == CChunkID( MAPPING_VERSION))
{
bReadPolygonsPerSurface = TRUE;
bReadSoundsAndAttachments = TRUE;
bReadCollision = TRUE;
bReadPatches = TRUE;
bReadSurfaceColors = TRUE;
}
else
{
throw( "Invalid version of mapping file.");
}
// set as we have only one mip
INDEX iStartCt = iMip;
INDEX iMaxCt = iMip+1;
// if iMip is -1 means that we want all mips in model
if( iMip == -1)
{
iStartCt = 0;
iMaxCt = edm_md.md_MipCt;
}
// for wanted mip models
for( INDEX iMipCt=iStartCt; iMipCt<iMaxCt; iMipCt++)
{
if( strmMappingFile.PeekID_t()==CChunkID("MIPS"))
{
// read mapping for current mip
ReadMipSettings_t( &strmMappingFile, iMipCt);
}
}
// skip data for mip models that were saved but haven't been
// readed in previous loop
while( strmMappingFile.PeekID_t()==CChunkID("MIPS"))
{
MappingSurface msDummy;
strmMappingFile.ExpectID_t( CChunkID( "MIPS"));
// for all saved surfaces
INDEX iSurfacesCt;
strmMappingFile >> iSurfacesCt;
for( INDEX iSurface=0; iSurface<iSurfacesCt; iSurface++)
{
// skip mapping surface
msDummy.ReadSettings_t( &strmMappingFile);
}
}
if( bReadSoundsAndAttachments)
{
// load attached sounds
INDEX ctSounds;
strmMappingFile>>ctSounds;
ASSERT(ctSounds > 0);
edm_aasAttachedSounds.Clear();
edm_aasAttachedSounds.New( ctSounds);
for( INDEX iSound=0; iSound<edm_aasAttachedSounds.Count(); iSound++)
{
edm_aasAttachedSounds[iSound].Read_t( &strmMappingFile);
}
// if number of animations does not match number of sounds saved in map file, reset sounds
if(ctSounds != edm_md.GetAnimsCt())
{
edm_aasAttachedSounds.Clear();
edm_aasAttachedSounds.New( edm_md.GetAnimsCt());
}
// load attached models
INDEX ctAttachmentPositions;
strmMappingFile>>ctAttachmentPositions;
edm_aamAttachedModels.Clear();
edm_md.md_aampAttachedPosition.Clear();
if( ctAttachmentPositions != 0)
{
edm_aamAttachedModels.New(ctAttachmentPositions);
edm_md.md_aampAttachedPosition.New(ctAttachmentPositions);
FOREACHINDYNAMICARRAY(edm_aamAttachedModels, CAttachedModel, itam)
{
try
{
itam->Read_t( &strmMappingFile);
}
catch( char *strError)
{
(void) strError;
edm_aamAttachedModels.Clear();
edm_md.md_aampAttachedPosition.Clear();
ThrowF_t( "Error ocured while reading attahment model, maybe model does"
" not exist.");
}
}
FOREACHINDYNAMICARRAY(edm_md.md_aampAttachedPosition, CAttachedModelPosition, itamp)
{
itamp->Read_t( &strmMappingFile);
}
}
}
if( bReadCollision)
{
// read collision boxes
edm_md.md_acbCollisionBox.Clear();
INDEX ctCollisionBoxes;
strmMappingFile>>ctCollisionBoxes;
ASSERT(ctCollisionBoxes>0);
if( ctCollisionBoxes>0)
{
edm_md.md_acbCollisionBox.New( ctCollisionBoxes);
FOREACHINDYNAMICARRAY(edm_md.md_acbCollisionBox, CModelCollisionBox, itcb)
{
itcb->Read_t( &strmMappingFile);
itcb->ReadName_t( &strmMappingFile);
}
}
else
{
edm_md.md_acbCollisionBox.New( 1);
throw( "Trying to load 0 collision boxes from mapping file.");
}
}
if( bReadPatches)
{
EditRemoveAllPatches();
for( INDEX iPatch=0; iPatch<MAX_TEXTUREPATCHES; iPatch++)
{
edm_md.md_mpPatches[ iPatch].Read_t( &strmMappingFile);
}
CalculatePatchesPerPolygon();
}
}
void CEditModel::AddCollisionBox(void)
{
// add one collision box
edm_md.md_acbCollisionBox.New();
// select newly added collision box
edm_iActiveCollisionBox = edm_md.md_acbCollisionBox.Count()-1;
}
void CEditModel::DeleteCurrentCollisionBox(void)
{
INDEX ctCollisionBoxes = edm_md.md_acbCollisionBox.Count();
// if we have more than 1 collision box
if( ctCollisionBoxes != 1)
{
edm_md.md_acbCollisionBox.Lock();
edm_md.md_acbCollisionBox.Delete( &edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox]);
edm_md.md_acbCollisionBox.Unlock();
// if this was last collision box
if( edm_iActiveCollisionBox == (ctCollisionBoxes-1) )
{
// select last collision box
edm_iActiveCollisionBox = ctCollisionBoxes-2;
}
}
}
void CEditModel::ActivatePreviousCollisionBox(void)
{
// get count of collision boxes
INDEX ctCollisionBoxes = edm_md.md_acbCollisionBox.Count();
if( edm_iActiveCollisionBox != 0)
{
edm_iActiveCollisionBox -= 1;
}
}
void CEditModel::ActivateNextCollisionBox(void)
{
// get count of collision boxes
INDEX ctCollisionBoxes = edm_md.md_acbCollisionBox.Count();
if( edm_iActiveCollisionBox != (ctCollisionBoxes-1) )
{
edm_iActiveCollisionBox += 1;
}
}
void CEditModel::SetCollisionBox(FLOAT3D vMin, FLOAT3D vMax)
{
edm_md.md_acbCollisionBox.Lock();
edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_vCollisionBoxMin = vMin;
edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_vCollisionBoxMax = vMax;
edm_md.md_acbCollisionBox.Unlock();
CorrectCollisionBoxSize();
}
CTString CEditModel::GetCollisionBoxName(INDEX iCollisionBox)
{
// get count of collision boxes
INDEX ctCollisionBoxes = edm_md.md_acbCollisionBox.Count();
ASSERT( iCollisionBox < ctCollisionBoxes);
if( iCollisionBox >= ctCollisionBoxes)
{
iCollisionBox = ctCollisionBoxes-1;
}
CTString strCollisionBoxName;
edm_md.md_acbCollisionBox.Lock();
strCollisionBoxName = edm_md.md_acbCollisionBox[ iCollisionBox].mcb_strName;
edm_md.md_acbCollisionBox.Unlock();
return strCollisionBoxName;
}
CTString CEditModel::GetCollisionBoxName(void)
{
CTString strCollisionBoxName;
edm_md.md_acbCollisionBox.Lock();
strCollisionBoxName = edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_strName;
edm_md.md_acbCollisionBox.Unlock();
return strCollisionBoxName;
}
void CEditModel::SetCollisionBoxName(CTString strNewName)
{
edm_md.md_acbCollisionBox.Lock();
edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_strName = strNewName;
edm_md.md_acbCollisionBox.Unlock();
}
void CEditModel::CorrectCollisionBoxSize(void)
{
// no correction needed if colliding as cube
if( edm_md.md_bCollideAsCube) return;
edm_md.md_acbCollisionBox.Lock();
// get equality radio initial value
INDEX iEqualityType = GetCollisionBoxDimensionEquality();
// get min and max vectors of currently active collision box
FLOAT3D vMin = edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_vCollisionBoxMin;
FLOAT3D vMax = edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_vCollisionBoxMax;
FLOAT3D vOldCenter;
vOldCenter(1) = (vMax(1)+vMin(1))/2.0f;
vOldCenter(3) = (vMax(3)+vMin(3))/2.0f;
// calculate vector of collision box diagonale
FLOAT3D vCorrectedDiagonale = vMax-vMin;
// apply minimal collision box conditions
if( vCorrectedDiagonale(1) < 0.1f) vCorrectedDiagonale(1) = 0.01f;
if( vCorrectedDiagonale(2) < 0.1f) vCorrectedDiagonale(2) = 0.01f;
if( vCorrectedDiagonale(3) < 0.1f) vCorrectedDiagonale(3) = 0.01f;
// according to equality type flag (which dimensions are same)
switch( iEqualityType)
{
case HEIGHT_EQ_WIDTH:
{
// don't allow that unlocked dimension is smaller than locked ones
if( vCorrectedDiagonale(3) < vCorrectedDiagonale(1) )
{
vCorrectedDiagonale(3) = vCorrectedDiagonale(1);
}
// height = width
vCorrectedDiagonale(2) = vCorrectedDiagonale(1);
break;
}
case LENGTH_EQ_WIDTH:
{
// don't allow that unlocked dimension is smaller than locked ones
if( vCorrectedDiagonale(2) < vCorrectedDiagonale(1) )
{
vCorrectedDiagonale(2) = vCorrectedDiagonale(1);
}
// lenght = width
vCorrectedDiagonale(3) = vCorrectedDiagonale(1);
break;
}
case LENGTH_EQ_HEIGHT:
{
// don't allow that unlocked dimension is smaller than locked ones
if( vCorrectedDiagonale(1) < vCorrectedDiagonale(2) )
{
vCorrectedDiagonale(1) = vCorrectedDiagonale(2);
}
// lenght = height
vCorrectedDiagonale(3) = vCorrectedDiagonale(2);
break;
}
default:
{
ASSERTALWAYS( "Invalid collision box dimension equality value found.");
}
}
// set new, corrected max vector
FLOAT3D vNewMin, vNewMax;
vNewMin(1) = vOldCenter(1)-vCorrectedDiagonale(1)/2.0f;
vNewMin(2) = vMin(2);
vNewMin(3) = vOldCenter(3)-vCorrectedDiagonale(3)/2.0f;
vNewMax(1) = vOldCenter(1)+vCorrectedDiagonale(1)/2.0f;
vNewMax(2) = vMin(2)+vCorrectedDiagonale(2);
vNewMax(3) = vOldCenter(3)+vCorrectedDiagonale(3)/2.0f;
edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_vCollisionBoxMin = vNewMin;
edm_md.md_acbCollisionBox[ edm_iActiveCollisionBox].mcb_vCollisionBoxMax = vNewMax;
edm_md.md_acbCollisionBox.Unlock();
}
//---------------------------------------------------------------------------------------------
// collision box handling functions
FLOAT3D &CEditModel::GetCollisionBoxMin(void)
{
edm_md.md_acbCollisionBox.Lock();
FLOAT3D &vMin = edm_md.md_acbCollisionBox[edm_iActiveCollisionBox].mcb_vCollisionBoxMin;
edm_md.md_acbCollisionBox.Unlock();
return vMin;
};
FLOAT3D &CEditModel::GetCollisionBoxMax(void)
{
edm_md.md_acbCollisionBox.Lock();
FLOAT3D &vMax = edm_md.md_acbCollisionBox[edm_iActiveCollisionBox].mcb_vCollisionBoxMax;
edm_md.md_acbCollisionBox.Unlock();
return vMax;
};
// returns HEIGHT_EQ_WIDTH, LENGHT_EQ_WIDTH or LENGHT_EQ_HEIGHT
INDEX CEditModel::GetCollisionBoxDimensionEquality()
{
return edm_md.GetCollisionBoxDimensionEquality(edm_iActiveCollisionBox);
};
// set new collision box equality value
void CEditModel::SetCollisionBoxDimensionEquality( INDEX iNewDimEqType)
{
edm_md.md_acbCollisionBox.Lock();
edm_md.md_acbCollisionBox[edm_iActiveCollisionBox].mcb_iCollisionBoxDimensionEquality =
iNewDimEqType;
edm_md.md_acbCollisionBox.Unlock();
CorrectCollisionBoxSize();
};
#if 0
// only triangles are supported!
ASSERT( opo.opo_PolygonEdges.Count() == 3);
if( opo.opo_PolygonEdges.Count() != 3) {
ThrowF_t( "Non-triangle polygon encountered in model file %s !", (CTString)itFr->cfnn_FileName);
}
CObjectPolygonEdge &ope0 = opo.opo_PolygonEdges[0];
CObjectPolygonEdge &ope1 = opo.opo_PolygonEdges[1];
if( ope0.ope_Backward) {
povx0 = ope0.ope_Edge->oed_Vertex1;
povx1 = ope0.ope_Edge->oed_Vertex0;
povx2 = ope1.ope_Edge->oed_Vertex0;
ASSERT( ope1.ope_Edge->oed_Vertex1 == povx1);
} else {
povx0 = ope0.ope_Edge->oed_Vertex0;
povx1 = ope0.ope_Edge->oed_Vertex1;
povx2 = ope1.ope_Edge->oed_Vertex1;
ASSERT( ope1.ope_Edge->oed_Vertex0 == povx1);
}
if( povx1==&ovxThis || povx0==&ovxThis || povx2==&ovxThis) {
DOUBLE3D v0 = (*povx0)-(*povx1);
DOUBLE3D v1 = (*povx2)-(*povx1);
v0.Normalize();
v1.Normalize();
ANGLE a = ASin( (v0*v1).Length());
ASSERT( a>=0 && a<=180);
aSum += a;
vSum += DOUBLEtoFLOAT( (DOUBLE3D&)*(itPoly->opo_Plane)) * a;
break;
}
#endif