/* 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 "stdh.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // 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<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 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 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 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=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; iam_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; iSound0 && !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< 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> 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> 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; iSkipExpectID_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; iSoundExpectID_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; iob_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; iob_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; iVertexob_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; iob_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; iob_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; immpi_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; immpi_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; immpi_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; immpi_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; job_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; iSurfaceTextureVertexms_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; iOpenedPolyEdgeopo_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; iSurfCurTVms_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; iSurfPrevTVms_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( ; iMipModelmp_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; iVtxmp_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; iPolymp_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; iVtxmp_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;iSurfmmpi_MappingSurfaces.Count(); iSurf++) { MappingSurface *pms= &pMMI->mmpi_MappingSurfaces[iSurf]; MEXaabbox2D boxSurface; // for each texture vertex in surface for(INDEX iSurfaceTextureVertex=0; iSurfaceTextureVertexms_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; iSurfmmpi_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=0) && (iMaskBitiMaskBit; 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) && (iMaskBitmmpi_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 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; iPolygonmmpi_PolygonsCt; iPolygon++) { ModelPolygon *pMP = &pMMI->mmpi_Polygons[iPolygon]; // for all vertices in polygon MEXaabbox2D boxMapping; for( INDEX iVertex=0; iVertexmp_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; iOccupiedmmpi_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; iSurfaceWrite_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<Write_t( &strmMappingFile); } // save patches for( INDEX iPatch=0; iPatch> iSurfacesCt; for( INDEX iSurface=0; iSurface>ctSounds; ASSERT(ctSounds > 0); edm_aasAttachedSounds.Clear(); edm_aasAttachedSounds.New( ctSounds); for( INDEX iSound=0; iSound>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= 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