// Assumes a MacOS Pascal string; the resulting string will have // a null byte at the end. void StringSet::Add(size_t Index, unsigned char *String) { if (Index < 0) return; // Replace string list with longer one if necessary size_t NewNumStrings = NumStrings; while (Index >= NewNumStrings) {NewNumStrings <<= 1;} if (NewNumStrings > NumStrings) { unsigned char **NewStrings = new unsigned char *[NewNumStrings]; objlist_clear(NewStrings+NumStrings,(NewNumStrings-NumStrings)); objlist_copy(NewStrings,Strings,NumStrings); delete []Strings; Strings = NewStrings; NumStrings = NewNumStrings; } // Delete the old string if necessary if (Strings[Index]) delete []Strings[Index]; unsigned short Length = String[0]; unsigned char *_String = new unsigned char[Length+2]; memcpy(_String,String,Length+2); _String[Length+1] = 0; // for making an in-place C string Strings[Index] = _String; }
void get_recording_header_data( short *number_of_players, short *level_number, uint32 *map_checksum, short *version, struct player_start_data *starts, struct game_data *game_information) { assert(replay.valid); *number_of_players= replay.header.num_players; *level_number= replay.header.level_number; *map_checksum= replay.header.map_checksum; *version= replay.header.version; objlist_copy(starts, replay.header.starts, MAXIMUM_NUMBER_OF_PLAYERS); obj_copy(*game_information, replay.header.game_information); }
void set_recording_header_data( short number_of_players, short level_number, uint32 map_checksum, short version, struct player_start_data *starts, struct game_data *game_information) { assert(!replay.valid); obj_clear(replay.header); replay.header.num_players= number_of_players; replay.header.level_number= level_number; replay.header.map_checksum= map_checksum; replay.header.version= version; objlist_copy(replay.header.starts, starts, MAXIMUM_NUMBER_OF_PLAYERS); obj_copy(replay.header.game_information, *game_information); // Use the packed size here!!! replay.header.length= SIZEOF_recording_header; }
// Neutral case: returns whether vertex-source data was used (present in animated models) bool Model3D::FindPositions_Neutral(bool UseModelTransform) { // Positions already there if (VtxSrcIndices.empty()) { return false; } // Straight copy of the vertices: size_t NumVertices = VtxSrcIndices.size(); Positions.resize(3*NumVertices); GLfloat *PP = PosBase(); GLushort *IP = VtxSIBase(); size_t NumVtxSources = VtxSources.size(); if (UseModelTransform) { for (size_t k=0; k<NumVertices; k++, IP++, PP+=3) { size_t VSIndex = *IP; if (VSIndex >= 0 && VSIndex < NumVtxSources) { Model3D_VertexSource& VS = VtxSources[VSIndex]; TransformPoint(PP,VS.Position,TransformPos); } else { GLfloat VP[3] = {0,0,0}; TransformPoint(PP,VP,TransformPos); } } } else { for (size_t k=0; k<NumVertices; k++, IP++) { size_t VSIndex = *IP; if (VSIndex >= 0 && VSIndex < NumVtxSources) { Model3D_VertexSource& VS = VtxSources[VSIndex]; GLfloat *VP = VS.Position; *(PP++) = *(VP++); *(PP++) = *(VP++); *(PP++) = *(VP++); } else { *(PP++) = 0; *(PP++) = 0; *(PP++) = 0; } } } // Copy in the normals Normals.resize(NormSources.size()); if (UseModelTransform) { GLfloat *NormPtr = NormBase(); GLfloat *NormBasePtr = NormSrcBase(); size_t NumNorms = NormSources.size()/3; for (size_t k=0; k<NumNorms; k++, NormPtr+=3, NormBasePtr+=3) { TransformVector(NormPtr, NormBasePtr, TransformNorm); } } else { objlist_copy(NormBase(),NormSrcBase(),NormSources.size()); } return true; }
// Normalize the normals void Model3D::AdjustNormals(int NormalType, float SmoothThreshold) { // Copy in normal sources for processing if (!NormSources.empty()) { Normals.resize(NormSources.size()); objlist_copy(NormBase(),NormSrcBase(),NormSources.size()); } // Which kind of special processing? switch(NormalType) { case None: Normals.clear(); break; case Original: case Reversed: default: // Normalize for (unsigned k=0; k<Normals.size()/3; k++) NormalizeNormal(&Normals[3*k]); break; case ClockwiseSide: case CounterclockwiseSide: // The really interesting stuff { // First, create a list of per-polygon normals size_t NumPolys = NumVI()/3; vector<FlaggedVector> PerPolygonNormalList(NumPolys); GLushort *IndxPtr = VIBase(); for (unsigned k=0; k<NumPolys; k++) { // The three vertices: GLfloat *P0 = &Positions[3*(*(IndxPtr++))]; GLfloat *P1 = &Positions[3*(*(IndxPtr++))]; GLfloat *P2 = &Positions[3*(*(IndxPtr++))]; // The two in-polygon vectors: GLfloat P01[3]; P01[0] = P1[0] - P0[0]; P01[1] = P1[1] - P0[1]; P01[2] = P1[2] - P0[2]; GLfloat P02[3]; P02[0] = P2[0] - P0[0]; P02[1] = P2[1] - P0[1]; P02[2] = P2[2] - P0[2]; // Those vectors' normal: FlaggedVector& PPN = PerPolygonNormalList[k]; PPN.Vec[0] = P01[1]*P02[2] - P01[2]*P02[1]; PPN.Vec[1] = P01[2]*P02[0] - P01[0]*P02[2]; PPN.Vec[2] = P01[0]*P02[1] - P01[1]*P02[0]; PPN.Flag = NormalizeNormal(PPN.Vec); } // Create a list of per-vertex normals size_t NumVerts = Positions.size()/3; vector<FlaggedVector> PerVertexNormalList(NumVerts); objlist_clear(&PerVertexNormalList[0],NumVerts); IndxPtr = VIBase(); for (unsigned k=0; k<NumPolys; k++) { FlaggedVector& PPN = PerPolygonNormalList[k]; for (unsigned c=0; c<3; c++) { GLushort VertIndx = *(IndxPtr++); GLfloat *V = PerVertexNormalList[VertIndx].Vec; *(V++) += PPN.Vec[0]; *(V++) += PPN.Vec[1]; *(V++) += PPN.Vec[2]; } } // Normalize the per-vertex normals for (unsigned k=0; k<NumVerts; k++) { FlaggedVector& PVN = PerVertexNormalList[k]; PVN.Flag = NormalizeNormal(PVN.Vec); } // Find the variance of each of the per-vertex normals; // use that to decide whether to keep them unsplit; // this also needs counting up the number of polygons per vertex. vector<GLfloat> Variances(NumVerts); objlist_clear(&Variances[0],NumVerts); vector<short> NumPolysPerVert(NumVerts); objlist_clear(&NumPolysPerVert[0],NumVerts); IndxPtr = VIBase(); for (unsigned k=0; k<NumPolys; k++) { FlaggedVector& PPN = PerPolygonNormalList[k]; for (unsigned c=0; c<3; c++) { GLushort VertIndx = *(IndxPtr++); FlaggedVector& PVN = PerVertexNormalList[VertIndx]; if (PVN.Flag) { GLfloat *V = PVN.Vec; GLfloat D0 = *(V++) - PPN.Vec[0]; GLfloat D1 = *(V++) - PPN.Vec[1]; GLfloat D2 = *(V++) - PPN.Vec[2]; Variances[VertIndx] += (D0*D0 + D1*D1 + D2*D2); NumPolysPerVert[VertIndx]++; } } } // Decide whether to split each vertex; // if the flag is "true", a vertex is not to be split for (unsigned k=0; k<NumVerts; k++) { // Vertices without contributions will automatically have // their flags be false, as a result of NormalizeNormal() unsigned NumVertPolys = NumPolysPerVert[k]; if (NumVertPolys > 0 && PerVertexNormalList[k].Flag) { PerVertexNormalList[k].Flag = sqrt(Variances[k]/NumVertPolys) <= SmoothThreshold; } } // The vertex flags are now set for whether to use that vertex's normal; // re-count the number of polygons per vertex. // Use NONE for unsplit ones objlist_clear(&NumPolysPerVert[0],NumVerts); IndxPtr = VIBase(); for (unsigned k=0; k<NumPolys; k++) { for (unsigned c=0; c<3; c++) { GLushort VertIndx = *(IndxPtr++); FlaggedVector& PVN = PerVertexNormalList[VertIndx]; if (PVN.Flag) { NumPolysPerVert[VertIndx] = NONE; } else{ NumPolysPerVert[VertIndx]++; } } } // Construct a polygon-association list; this will indicate // which polygons are associated with each of the resulting instances // of a split vertex (unsplit: NONE). // NumPolysPerVert will be recycled as a counter list, // after being used to construct a cumulative index-in-list array. // Finding that list will be used to find how many new vertices there are. vector<short> IndicesInList(NumVerts); short IndxInList = 0; for (unsigned k=0; k<NumVerts; k++) { IndicesInList[k] = IndxInList; FlaggedVector& PVN = PerVertexNormalList[k]; IndxInList += PVN.Flag ? 1 : NumPolysPerVert[k]; } GLushort NewNumVerts = IndxInList; vector<short> VertexPolygons(NewNumVerts); objlist_clear(&NumPolysPerVert[0],NumVerts); // In creating that list, also remap the triangles' vertices GLushort *VIPtr = VIBase(); for (unsigned k=0; k<NumPolys; k++) { for (unsigned c=0; c<3; c++) { GLushort VertIndx = *VIPtr; GLushort NewVertIndx = IndicesInList[VertIndx]; FlaggedVector& PVN = PerVertexNormalList[VertIndx]; if (PVN.Flag) { NumPolysPerVert[VertIndx] = NONE; VertexPolygons[NewVertIndx] = NONE; } else { NewVertIndx += (NumPolysPerVert[VertIndx]++); VertexPolygons[NewVertIndx] = k; } *VIPtr = NewVertIndx; VIPtr++; } } // Split the vertices vector<GLfloat> NewPositions(3*NewNumVerts); vector<GLfloat> NewTxtrCoords; vector<GLfloat> NewNormals(3*NewNumVerts); vector<GLfloat> NewColors; vector<GLushort> NewVtxSrcIndices; bool TCPresent = !TxtrCoords.empty(); if (TCPresent) { NewTxtrCoords.resize(2*NewNumVerts); } bool ColPresent = !Colors.empty(); if (ColPresent) { NewColors.resize(3*NewNumVerts); } bool VSPresent = !VtxSrcIndices.empty(); if (VSPresent) { NewVtxSrcIndices.resize(NewNumVerts); } // Use marching pointers to speed up the copy-over GLfloat *OldP = &Positions[0]; GLfloat *NewP = &NewPositions[0]; GLfloat *OldT = &TxtrCoords[0]; GLfloat *NewT = &NewTxtrCoords[0]; GLfloat *OldC = &Colors[0]; GLfloat *NewC = &NewColors[0]; GLushort *OldS = &VtxSrcIndices[0]; GLushort *NewS = &NewVtxSrcIndices[0]; GLfloat *NewN = &NewNormals[0]; for (unsigned k=0; k<NumVerts; k++) { FlaggedVector& PVN = PerVertexNormalList[k]; unsigned NumVertPolys = PVN.Flag ? 1 : NumPolysPerVert[k]; for (unsigned c=0; c<NumVertPolys; c++) { GLfloat *OldPP = OldP; *(NewP++) = *(OldPP++); *(NewP++) = *(OldPP++); *(NewP++) = *(OldPP++); } if (TCPresent) { for (unsigned c=0; c<NumVertPolys; c++) { GLfloat *OldTP = OldT; *(NewT++) = *(OldTP++); *(NewT++) = *(OldTP++); } } if (ColPresent) { for (unsigned c=0; c<NumVertPolys; c++) { GLfloat *OldCP = OldC; *(NewC++) = *(OldCP++); *(NewC++) = *(OldCP++); *(NewC++) = *(OldCP++); } } if (VSPresent) { for (unsigned c=0; c<NumVertPolys; c++) *(NewS++) = *OldS; } if (PVN.Flag) { GLfloat *VP = PVN.Vec; *(NewN++) = *(VP++); *(NewN++) = *(VP++); *(NewN++) = *(VP++); } else { // A reference so that the incrementing can work on it short& IndxInList = IndicesInList[k]; for (unsigned c=0; c<NumVertPolys; c++) { GLfloat *VP = PerPolygonNormalList[VertexPolygons[IndxInList++]].Vec; *(NewN++) = *(VP++); *(NewN++) = *(VP++); *(NewN++) = *(VP++); } } // Advance! OldP += 3; if (TCPresent) { OldT += 2; } if (ColPresent) { OldC += 3; } if (VSPresent) { OldS++; } } assert(OldP == &Positions[3*NumVerts]); assert(NewP == &NewPositions[3*NewNumVerts]); if (TCPresent) { assert(OldT == &TxtrCoords[2*NumVerts]); assert(NewT == &NewTxtrCoords[2*NewNumVerts]); } if (ColPresent) { assert(OldC == &Colors[3*NumVerts]); assert(NewC == &NewColors[3*NewNumVerts]); } if (VSPresent) { assert(OldS == &VtxSrcIndices[NumVerts]); assert(NewS == &NewVtxSrcIndices[NewNumVerts]); } assert(NewN == &NewNormals[3*NewNumVerts]); // Accept the new vectors Positions.swap(NewPositions); TxtrCoords.swap(NewTxtrCoords); Normals.swap(NewNormals); Colors.swap(NewColors); VtxSrcIndices.swap(NewVtxSrcIndices); } break; } // Now flip switch(NormalType) { case Reversed: case CounterclockwiseSide: { GLfloat *NormalPtr = NormBase(); for (unsigned k=0; k<Normals.size(); k++) *(NormalPtr++) *= -1; } } // Copy back out to the normal sources; // do the copying out if the vertices have sources, // which is the case for boned models. if (!VtxSources.empty()) { size_t NormSize = Normals.size(); if (NormSize > 0) { NormSources.resize(NormSize); objlist_copy(NormSrcBase(),NormBase(),NormSize); } else{ NormSources.clear(); } } else{ NormSources.clear(); } }
void OGL_ModelData::Load() { // Already loaded? if (ModelPresent()) { return; } // Load the model Model.Clear(); if (ModelFile == FileSpecifier()) { return; } if (!ModelFile.Exists()) { return; } bool Success = false; char *Type = &ModelType[0]; if (StringsEqual(Type,"wave",4)) { // Alias|Wavefront Success = LoadModel_Wavefront(ModelFile, Model); } else if (StringsEqual(Type,"3ds",3)) { // 3D Studio Max Success = LoadModel_Studio(ModelFile, Model); } else if (StringsEqual(Type,"dim3",4)) { // Brian Barnes's "Dim3" model format (first pass: model geometry) Success = LoadModel_Dim3(ModelFile, Model, LoadModelDim3_First); // Second and third passes: frames and sequences try { if (ModelFile1 == FileSpecifier()) { throw 0; } if (!ModelFile1.Exists()) { throw 0; } if (!LoadModel_Dim3(ModelFile1, Model, LoadModelDim3_Rest)) { throw 0; } } catch(...) {} // try { if (ModelFile2 == FileSpecifier()) { throw 0; } if (!ModelFile2.Exists()) { throw 0; } if (!LoadModel_Dim3(ModelFile2, Model, LoadModelDim3_Rest)) { throw 0; } } catch(...) {} } #if HAVE_QUESA else if (StringsEqual(Type, "qd3d") || StringsEqual(Type,"3dmf") || StringsEqual(Type,"quesa")) { // QuickDraw 3D / Quesa Success = LoadModel_QD3D(ModelFile, Model); } #endif if (!Success) { Model.Clear(); return; } // Calculate transformation matrix GLfloat Angle, Cosine, Sine; GLfloat RotMatrix[3][3], NewRotMatrix[3][3], IndivRotMatrix[3][3]; MatIdentity(RotMatrix); MatIdentity(IndivRotMatrix); Angle = (float)Degree2Radian*XRot; Cosine = (float)cos(Angle); Sine = (float)sin(Angle); IndivRotMatrix[1][1] = Cosine; IndivRotMatrix[1][2] = -Sine; IndivRotMatrix[2][1] = Sine; IndivRotMatrix[2][2] = Cosine; MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); MatCopy(NewRotMatrix,RotMatrix); MatIdentity(IndivRotMatrix); Angle = (float)Degree2Radian*YRot; Cosine = (float)cos(Angle); Sine = (float)sin(Angle); IndivRotMatrix[2][2] = Cosine; IndivRotMatrix[2][0] = -Sine; IndivRotMatrix[0][2] = Sine; IndivRotMatrix[0][0] = Cosine; MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); MatCopy(NewRotMatrix,RotMatrix); MatIdentity(IndivRotMatrix); Angle = (float)Degree2Radian*ZRot; Cosine = (float)cos(Angle); Sine = (float)sin(Angle); IndivRotMatrix[0][0] = Cosine; IndivRotMatrix[0][1] = -Sine; IndivRotMatrix[1][0] = Sine; IndivRotMatrix[1][1] = Cosine; MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); MatCopy(NewRotMatrix,RotMatrix); MatScalMult(NewRotMatrix,Scale); // For the position vertices if (Scale < 0) { MatScalMult(RotMatrix,-1); // For the normals } // Is model animated or static? // Test by trying to find neutral positions (useful for working with the normals later on) if (Model.FindPositions_Neutral(false)) { // Copy over the vector and normal transformation matrices: for (int k=0; k<3; k++) for (int l=0; l<3; l++) { Model.TransformPos.M[k][l] = NewRotMatrix[k][l]; Model.TransformNorm.M[k][l] = RotMatrix[k][l]; } Model.TransformPos.M[0][3] = XShift; Model.TransformPos.M[1][3] = YShift; Model.TransformPos.M[2][3] = ZShift; // Find the transformed bounding box: bool RestOfCorners = false; GLfloat NewBoundingBox[2][3]; // The indices i1, i2, and i3 are for selecting which of the box's two principal corners // to get coordinates from for (int i1=0; i1<2; i1++) { GLfloat X = Model.BoundingBox[i1][0]; for (int i2=0; i2<2; i2++) { GLfloat Y = Model.BoundingBox[i2][0]; for (int i3=0; i3<2; i3++) { GLfloat Z = Model.BoundingBox[i3][0]; GLfloat Corner[3]; for (int ic=0; ic<3; ic++) { GLfloat *Row = Model.TransformPos.M[ic]; Corner[ic] = Row[0]*X + Row[1]*Y + Row[2]*Z + Row[3]; } if (RestOfCorners) { // Find minimum and maximum for each coordinate for (int ic=0; ic<3; ic++) { NewBoundingBox[0][ic] = min(NewBoundingBox[0][ic],Corner[ic]); NewBoundingBox[1][ic] = max(NewBoundingBox[1][ic],Corner[ic]); } } else { // Simply copy it in: for (int ic=0; ic<3; ic++) NewBoundingBox[0][ic] = NewBoundingBox[1][ic] = Corner[ic]; RestOfCorners = true; } } } } for (int ic=0; ic<2; ic++) objlist_copy(Model.BoundingBox[ic],NewBoundingBox[ic],3); } else { // Static model size_t NumVerts = Model.Positions.size()/3; for (size_t k=0; k<NumVerts; k++) { GLfloat *Pos = Model.PosBase() + 3*k; GLfloat NewPos[3]; MatVecMult(NewRotMatrix,Pos,NewPos); // Has the scaling Pos[0] = NewPos[0] + XShift; Pos[1] = NewPos[1] + YShift; Pos[2] = NewPos[2] + ZShift; } size_t NumNorms = Model.Normals.size()/3; for (size_t k=0; k<NumNorms; k++) { GLfloat *Norms = Model.NormBase() + 3*k; GLfloat NewNorms[3]; MatVecMult(RotMatrix,Norms,NewNorms); // Not scaled objlist_copy(Norms,NewNorms,3); } // So as to be consistent with the new points Model.FindBoundingBox(); } Model.AdjustNormals(NormalType,NormalSplit); Model.CalculateTangents(); // Don't forget the skins OGL_SkinManager::Load(); }
// Src -> Dest static void MatCopy(const GLfloat SrcMat[3][3], GLfloat DestMat[3][3]) { objlist_copy(DestMat[0],SrcMat[0],9); }