/*=========================================================================== * * File: Df3dobj.CPP * Author: Dave Humphrey (uesp@m0use.net) * Created On: Wednesday, June 20, 2001 * * Implements the CDF3dObject class for handling 3D object files. * *=========================================================================*/ /* Include Files */ #include "df3dobj.h" #include "common/dl_time.h" #include //#include "common/utility/profile.h" /*=========================================================================== * * Begin Local Variable Definitions * *=========================================================================*/ DEFINE_FILE(); /*=========================================================================== * End of Local Variable Definitions *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Constructor * *=========================================================================*/ CDF3dObject::CDF3dObject () { //DEFINE_FUNCTION("CDF3dObject::CDF3dObject()"); m_FileSize = 0; m_StartOffset = 0; m_Version = DF3D_VERSION_27; /* Initialize sub-object arrays */ m_pPoints = NULL; m_pFaces = NULL; m_pNormals = NULL; m_pData1 = NULL; m_pData2 = NULL; /* Clear header */ memset(&m_Header, 0, sizeof(df3dheader_t)); } /*=========================================================================== * End of Class CDF3dObject Constr uctor *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - void Destroy (void); * *=========================================================================*/ void CDF3dObject::Destroy (void) { //DEFINE_FUNCTION("CDF3dObject::Destroy()"); /* Unallocate arrays */ DestroyArrayPointer(m_pPoints); DestroyArrayPointer(m_pFaces); DestroyArrayPointer(m_pNormals); DestroyArrayPointer(m_pData1); DestroyArrayPointer(m_pData2); } /*=========================================================================== * End of Class Method CDF3dObject::Destroy() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - void ClearFaceTags (void); * * Reset the tag for each face in the object. Function is considered const * as the Tag member data is not truely part of the face data. * *=========================================================================*/ void CDF3dObject::ClearFaceTags (void) const { DEFINE_FUNCTION("CDF3dObject::ClearFaceTags()"); long FaceIndex; /* Clear the tag for each face in object */ for (FaceIndex = 0; FaceIndex < GetNumFaces(); FaceIndex++) { m_pFaces[FaceIndex].Tag = 0; } } /*=========================================================================== * End of Class Method CDF3dObject::ClearFaceTags() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - int ForEachFace (fnCallBack, pCallbackData); * * Calls the given callback function for each face in the object, along * with the custom callback data. Returns a non-zero value on any error. * *=========================================================================*/ int CDF3dObject::ForEachFace (PFNDF3D_FOREACHFACE fnCallBack, void* pCallbackData) { DEFINE_FUNCTION("CDF3dObject::ForEachFace()"); int Result; long FaceIndex; /* Ensure valid input */ ASSERT(fnCallBack != NULL); /* Perform the operation for each face in object */ for (FaceIndex = 0; FaceIndex < GetNumFaces(); FaceIndex++) { Result = fnCallBack(m_pFaces[FaceIndex], pCallbackData); if (Result != 0) return (Result); } /* Return success */ return (0); } /*=========================================================================== * End of Class Method CDF3dObject::ForEachFace() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - int ForEachFaceC (fnCallBack, pCallbackData) const; * * Calls the given callback function for each face in the object, along * with the custom callback data. Returns a non-zero value on any error. * Constant version of the ForEachFace() method. * *=========================================================================*/ int CDF3dObject::ForEachFaceC (PFNDF3D_FOREACHFACEC fnCallBack, void* pCallbackData) const { DEFINE_FUNCTION("CDF3dObject::ForEachFaceC()"); int Result; long FaceIndex; /* Ensure valid input */ ASSERT(fnCallBack != NULL); /* Perform the operation for each face in object */ for (FaceIndex = 0; FaceIndex < GetNumFaces(); FaceIndex++) { Result = fnCallBack(m_pFaces[FaceIndex], pCallbackData); if (Result != 0) return (Result); } /* Return success */ return (0); } /*=========================================================================== * End of Class Method CDF3dObject::ForEachFaceC() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - int ForEachPoint (fnCallBack, pCallbackData); * * Calls the given callback function for each point in the object, along * with the custom callback data. Returns a non-zero value on any error. * *=========================================================================*/ int CDF3dObject::ForEachPoint (PFNDF3D_FOREACHPOINT fnCallBack, void* pCallbackData) { DEFINE_FUNCTION("CDF3dObject::ForEachPoint()"); int Result; long PointIndex; /* Ensure valid input */ ASSERT(fnCallBack != NULL); /* Perform the operation for each point in object */ for (PointIndex = 0; PointIndex < GetNumPoints(); PointIndex++) { Result = fnCallBack(m_pPoints[PointIndex], pCallbackData); if (Result != 0) return (Result); } /* Return success */ return (0); } /*=========================================================================== * End of Class Method CDF3dObject::ForEachPoint() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - int ForEachPointC (fnCallBack, pCallbackData) const; * * Calls the given callback function for each point in the object, along * with the custom callback data. Returns a non-zero value on any error. * Constant version of the ForEachPoint() method. * *=========================================================================*/ int CDF3dObject::ForEachPointC (PFNDF3D_FOREACHPOINTC fnCallBack, void* pCallbackData) const { DEFINE_FUNCTION("CDF3dObject::ForEachPointC()"); int Result; long PointIndex; /* Ensure valid input */ ASSERT(fnCallBack != NULL); /* Perform the operation for each point in object */ for (PointIndex = 0; PointIndex < GetNumPoints(); PointIndex++) { Result = fnCallBack(m_pPoints[PointIndex], pCallbackData); if (Result != 0) return (Result); } /* Return success */ return (0); } /*=========================================================================== * End of Class Method CDF3dObject::ForEachPointC() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - long GetMaxY (void) const; * long GetMinY (void) const; * * Returns the maximum/minimum Y point coordinate. * *=========================================================================*/ long CDF3dObject::GetMaxY (void) const { int Index; long MaxValue; if (m_Header.NumPoints == 0) return (0); MaxValue = m_pPoints[0].Y; for (Index = 1; Index < m_Header.NumPoints; Index++) { if (m_pPoints[Index].Y > MaxValue) MaxValue = m_pPoints[Index].Y; } return (MaxValue); } long CDF3dObject::GetMinY (void) const { int Index; long MinValue; if (m_Header.NumPoints == 0) return (0); MinValue = m_pPoints[0].Y; for (Index = 1; Index < m_Header.NumPoints; Index++) { if (m_pPoints[Index].Y < MinValue) MinValue = m_pPoints[Index].Y; } return (MinValue); } /*=========================================================================== * End of Class Method CDF3dObject::GetMaxY() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - boolean Read (pFileHandle); * * Attempt to read the 3D object from the current position in the given file * stream. Returns FALSE on any error. * *=========================================================================*/ boolean CDF3dObject::Read (FILE* pFileHandle) { DEFINE_FUNCTION("CDF3dObject::Read()"); long EndPointOffset; long EndFaceOffset; long EndNormalOffset; long EndData1Offset; long EndData2Offset; long EndOffset; boolean Result; /* Ensure valid input */ ASSERT(pFileHandle != NULL); ASSERT(!feof(pFileHandle) && !ferror(pFileHandle)); /* Destroy the current object contents */ Destroy(); m_StartOffset = ftell(pFileHandle); /* Read header information */ Result = ReadHeader(pFileHandle); if (!Result) return (FALSE); /* Move to points data and input */ fseek(pFileHandle, m_StartOffset + m_Header.PointOffset, SEEK_SET); Result = ReadPoints(pFileHandle); if (!Result) return (FALSE); EndPointOffset = ftell(pFileHandle); /* Move to face data and input */ fseek(pFileHandle, m_StartOffset + m_Header.FaceOffset, SEEK_SET); Result = ReadFaces(pFileHandle); if (!Result) return (FALSE); EndFaceOffset = ftell(pFileHandle); /* Move to normal data and input */ fseek(pFileHandle, m_StartOffset + m_Header.NormalOffset, SEEK_SET); Result = ReadNormals(pFileHandle); if (!Result) return (FALSE); EndNormalOffset = ftell(pFileHandle); /* Move to data1 section and input */ fseek(pFileHandle, m_StartOffset + m_Header.Data1Offset, SEEK_SET); Result = ReadData1(pFileHandle); if (!Result) return (FALSE); EndData1Offset = ftell(pFileHandle); /* Move to data2 section and input if required */ fseek(pFileHandle, m_StartOffset + m_Header.Data2Offset, SEEK_SET); Result = ReadData2(pFileHandle); if (!Result) return (FALSE); EndData2Offset = ftell(pFileHandle); /* Check offsets for validity */ EndOffset = EndPointOffset; if (EndFaceOffset > EndOffset) EndOffset = EndFaceOffset; if (EndNormalOffset > EndOffset) EndOffset = EndNormalOffset; if (EndData1Offset > EndOffset) EndOffset = EndData1Offset; if (EndData2Offset > EndOffset) EndOffset = EndData2Offset; /* Ensure we didn't read past end of data */ if (EndOffset > m_StartOffset + m_FileSize) { ErrorHandler.AddError(DFERR_3D_READPASTEND, "%ld bytes", EndOffset - m_StartOffset - m_FileSize); return (FALSE); } else if (EndOffset < m_StartOffset + (long)sizeof(df3dheader_t)) { ErrorHandler.AddError(DFERR_3D_READPASTSTART, "%ld bytes", EndOffset - m_StartOffset); return (FALSE); } /* Move to end of read data */ fseek(pFileHandle, EndOffset, SEEK_SET); /* Check offsets in debug builds */ #if defined(_DEBUG) if (m_Header.PointOffset < m_Header.FaceOffset) ASSERT(EndPointOffset < EndFaceOffset) else ASSERT(EndPointOffset > EndFaceOffset); if (m_Header.PointOffset < m_Header.NormalOffset) ASSERT(EndPointOffset < EndNormalOffset) else ASSERT(EndPointOffset > EndNormalOffset); if (m_Header.PointOffset < m_Header.Data1Offset) ASSERT(EndPointOffset < EndData1Offset) else ASSERT(EndPointOffset > EndData1Offset); if (m_Header.FaceOffset < m_Header.NormalOffset) ASSERT(EndFaceOffset < EndNormalOffset) else ASSERT(EndFaceOffset > EndNormalOffset); if (m_Header.FaceOffset < m_Header.Data1Offset) ASSERT(EndFaceOffset < EndData1Offset) else ASSERT(EndFaceOffset > EndData1Offset); if (m_Header.NormalOffset < m_Header.Data1Offset) ASSERT(EndNormalOffset < EndData1Offset) else ASSERT(EndNormalOffset > EndData1Offset); if (m_Header.NumData2 > 0) { if (m_Header.PointOffset < m_Header.Data2Offset) ASSERT(EndPointOffset < EndData2Offset) else ASSERT(EndPointOffset > EndData2Offset); if (m_Header.FaceOffset < m_Header.Data2Offset) ASSERT(EndFaceOffset < EndData2Offset) else ASSERT(EndFaceOffset > EndData2Offset); if (m_Header.NormalOffset < m_Header.Data2Offset) ASSERT(EndNormalOffset < EndData2Offset) else ASSERT(EndNormalOffset > EndData2Offset); if (m_Header.Data1Offset < m_Header.Data2Offset) ASSERT(EndData1Offset < EndData2Offset) else ASSERT(EndData1Offset > EndData2Offset); } #endif return (TRUE); } /*=========================================================================== * End of Class Method CDF3dObject::Read() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - boolean ReadHeader (pFileHandle); * * Protected class method to input the header data from the current position * in the file stream. Returns FALSE on any error. * *=========================================================================*/ boolean CDF3dObject::ReadHeader (FILE* pFileHandle) { size_t Result; /* Attempt to read the header all at once */ Result = fread(&m_Header, 1, sizeof(df3dheader_t), pFileHandle); /* Ensure header was read successfully */ if (Result != sizeof(df3dheader_t)) { ErrorHandler.AddError(ERR_SYSTEM, errno); ErrorHandler.AddError(DFERR_3D_BADHEADER); return (FALSE); } /* Compute the integral version value */ if (IsVersion("v2.5")) m_Version = DF3D_VERSION_25; else if (IsVersion("v2.6")) m_Version = DF3D_VERSION_26; else if (IsVersion("v2.7")) m_Version = DF3D_VERSION_27; else { m_Version = DF3D_VERSION_UNKNOWN; ErrorHandler.AddError(DFERR_3D_BADVERSION, "Version='%4.4s'", m_Header.Version); return (FALSE); } return (TRUE); } /*=========================================================================== * End of Class Method CDF3dObject::ReadHeader() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - boolean ReadPoints (pFileHandle); * * Protected class method to input the points data from the current position * in the file stream. Returns FALSE on any error. The points array is * destroyed and allocated as required. * *=========================================================================*/ boolean CDF3dObject::ReadPoints (FILE* pFileHandle) { DEFINE_FUNCTION("CDF3dObject::ReadPoints()"); size_t Result; /* Delete the previous array, if any */ DestroyArrayPointer(m_pPoints); /* Reallocate points array */ ASSERT(GetNumPoints() > 0); CreateArrayPointer(m_pPoints, df3dpoint_t, GetNumPoints()); /* Attempt to read the points all at once */ Result = fread(m_pPoints, sizeof(df3dpoint_t), GetNumPoints(), pFileHandle); /* Ensure points were read successfully */ if (Result != (size_t) GetNumPoints()) { ErrorHandler.AddError(ERR_SYSTEM, errno); ErrorHandler.AddError(DFERR_3D_BADPOINTS); return (FALSE); } return (TRUE); } /*=========================================================================== * End of Class Method CDF3dObject::ReadPoints() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - boolean ReadFaces (pFileHandle); * * Protected class method to input the face data from the current position * in the file stream. Returns FALSE on any error. The face array is * destroyed and allocated as required. * *=========================================================================*/ boolean CDF3dObject::ReadFaces (FILE* pFileHandle) { DEFINE_FUNCTION("CDF3dObject::ReadFaces()"); size_t Result; size_t Index; /* Delete the previous array, if any */ DestroyArrayPointer(m_pFaces); /* Reallocate face array */ ASSERT(GetNumFaces() > 0); CreateArrayPointer(m_pFaces, df3dface_t, GetNumFaces()); /* Read in faces one by one */ for (Index = 0; Index < (size_t)GetNumFaces(); Index++) { /* Read in the first 8 bytes of face data */ Result = fread (&(m_pFaces[Index]), 1, DF3D_FACE_RECORDSIZE, pFileHandle); if (Result != DF3D_FACE_RECORDSIZE) goto READFACE_ERROR; /* Read in remaining face data */ Result = fread(&(m_pFaces[Index].FaceData[0]), sizeof(df3dfacedata_t), m_pFaces[Index].NumPoints, pFileHandle); if (Result != m_pFaces[Index].NumPoints) goto READFACE_ERROR; } /* Return success */ return (TRUE); /* Error handling code for method */ READFACE_ERROR: ErrorHandler.AddError(ERR_SYSTEM, errno); ErrorHandler.AddError(DFERR_3D_BADFACES); return (FALSE); } /*=========================================================================== * End of Class Method CDF3dObject::ReadFaces() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - boolean ReadNormals (pFileHandle); * * Protected class method to input the normal data from the given position * in the file stream. Returns FALSE on any error. The normals array is * destroyed and allocated as required. * *=========================================================================*/ boolean CDF3dObject::ReadNormals (FILE* pFileHandle) { DEFINE_FUNCTION("CDF3dObject::ReadNormals()"); size_t Result; /* Delete the previous array, if any */ DestroyArrayPointer(m_pNormals); /* Reallocate normals array */ ASSERT(GetNumFaces() > 0); CreateArrayPointer(m_pNormals, df3dpoint_t, GetNumFaces()); /* Attempt to read the normals all at once */ Result = fread(m_pNormals, sizeof(df3dpoint_t), GetNumFaces(), pFileHandle); /* Ensure normals were read successfully */ if (Result != (size_t) GetNumFaces()) { ErrorHandler.AddError(ERR_SYSTEM, errno); ErrorHandler.AddError(DFERR_3D_BADNORMALS); return (FALSE); } return (TRUE); } /*=========================================================================== * End of Class Method CDF3dObject::ReadNormals() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - boolean ReadData1 (pFileHandle); * * Protected class method to input the data1 section from the current position * in the file stream. Returns FALSE on any error. The data1 array is * destroyed and allocated as required. * *=========================================================================*/ boolean CDF3dObject::ReadData1 (FILE* pFileHandle) { DEFINE_FUNCTION("CDF3dObject::ReadData1()"); size_t Result; /* Delete the previous array, if any */ DestroyArrayPointer(m_pData1); /* Reallocate data1 array */ ASSERT(GetNumFaces() > 0); CreateArrayPointer(m_pData1, df3ddata1_t, GetNumFaces()); /* Attempt to read the data1 records all at once */ Result = fread(m_pData1, sizeof(df3ddata1_t), GetNumFaces(), pFileHandle); /* Ensure normals were read successfully */ if (Result != (size_t) GetNumFaces()) { ErrorHandler.AddError(ERR_SYSTEM, errno); ErrorHandler.AddError(DFERR_3D_BADDATA1); return (FALSE); } return (TRUE); } /*=========================================================================== * End of Class Method CDF3dObject::ReadData1() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - boolean ReadData2 (pFileHandle); * * Protected class method to input the data2 section from the current position * in the file stream. Returns FALSE on any error. The data2 array is * destroyed and allocated as required. * *=========================================================================*/ boolean CDF3dObject::ReadData2 (FILE* pFileHandle) { DEFINE_FUNCTION("CDF3dObject::ReadData2()"); //DEFINE_STATIC_PROFILE(ReadData2Profile); size_t Result; int SeekResult; long Offset; size_t Index; /* Delete the previous array, if any */ DestroyArrayPointer(m_pData2); /* Ignore if no data2 records to load */ if (GetNumData2() == 0) return (TRUE); /* Reallocate data2 array */ CreateArrayPointer(m_pData2, df3ddata2_t, GetNumData2()); /* Read in data2 records one by one */ for (Index = 0; Index < (size_t)GetNumData2(); Index++) { /* Read in the first 18 bytes of data2 record */ Result = fread (&(m_pData2[Index]), 1, DF3D_DATA2_RECORDSIZE, pFileHandle); if (Result != DF3D_DATA2_RECORDSIZE) goto READDATA2_ERROR; /* Compute the size of the data2 subrecords, ensuring its valid */ Offset = m_pData2[Index].NumSubRecords * (long)sizeof(df3ddata2_subrecord_t); if (Offset < 0 || Offset + ftell(pFileHandle) > m_StartOffset + m_FileSize) { ErrorHandler.AddError(DFERR_3D_DATA2SUBRECORD); return (FALSE); } /* TODO: Skip the remaining subrecords for now */ SeekResult = fseek(pFileHandle, Offset, SEEK_CUR); if (SeekResult != 0) goto READDATA2_ERROR; } return (TRUE); /* Error handling code for method */ READDATA2_ERROR: ErrorHandler.AddError(ERR_SYSTEM, errno); ErrorHandler.AddError(DFERR_3D_BADDATA2); return (FALSE); } /*=========================================================================== * End of Class Method CDF3dObject::ReadData2() *=========================================================================*/ /*=========================================================================== * * Class CDF3dObject Method - int TagFaceTexture (TextureIndex, ImageIndex); * * Tag all faces that have the same texture and image indices. Returns * the number of faces tagged. Is considered a const method as the tag * data is not considered part of the DF face data. * *=========================================================================*/ int CDF3dObject::TagFaceTexture (const int TextureIndex, const int ImageIndex) const { DEFINE_FUNCTION("CDF3dObject::TagFaceTexture()"); int Count = 0; int FaceIndex; for (FaceIndex = 0; FaceIndex < GetNumFaces(); FaceIndex++) { if (m_pFaces[FaceIndex].TextureIndex == TextureIndex && m_pFaces[FaceIndex].SubImageIndex == ImageIndex) { m_pFaces[FaceIndex].Tag = 1; Count++; } } return (Count); } /*=========================================================================== * End of Class Method CDF3dObject::TagFaceTexture() *=========================================================================*/