mirror of
https://github.com/ioquake/jedi-outcast.git
synced 2024-11-10 07:11:42 +00:00
607 lines
19 KiB
C++
607 lines
19 KiB
C++
// Filename:- g2export.cpp
|
|
//
|
|
|
|
#include "q3data.h"
|
|
#include <io.h>
|
|
/*
|
|
#include "system.h" // stuff needed for caller program, standar headers etc
|
|
#include "oddbits.h"
|
|
//
|
|
*/
|
|
#include "matrix4.h"
|
|
#include "mdx_format.h"
|
|
#include "matcomp.h"
|
|
//
|
|
#include "g2export_interface.h"
|
|
#include "g2export.h"
|
|
|
|
#define BASEDIRNAME "base"
|
|
|
|
|
|
static LPCSTR Filename_StripQuakeBase(LPCSTR psFullPathedName)
|
|
{
|
|
static char sTemp[1024];
|
|
|
|
int iQuakeLen = strlen(gamedir);
|
|
strcpy(sTemp,&psFullPathedName[iQuakeLen]);
|
|
|
|
return &sTemp[0];
|
|
}
|
|
|
|
static void ForwardSlash(char *psLocalName)
|
|
{
|
|
while (*psLocalName)
|
|
{
|
|
if (*psLocalName == '\\')
|
|
*psLocalName = '/';
|
|
|
|
psLocalName++;
|
|
}
|
|
}
|
|
|
|
// saves clogging the param stack...
|
|
//
|
|
static char *ExportGhoul2FromMD3_Main(FILE *fhGLM, /*FILE *fhGLA,*/ LPCSTR psFullPathedNameGLM)
|
|
{
|
|
char *psErrMess = NULL;
|
|
|
|
const int iMallocSize = 1024*1024*20; // 20MB should be enough for one model... :-)
|
|
byte *pbBuffer = (byte *) malloc(iMallocSize);
|
|
if (pbBuffer)
|
|
{
|
|
// filename used in both files...
|
|
//
|
|
// char sLocalNameGLA[MAX_QPATH];
|
|
// strcpy( sLocalNameGLA,Filename_WithoutExt(Filename_StripQuakeBase(psFullPathedNameGLM)));
|
|
// ForwardSlash(sLocalNameGLA);
|
|
|
|
// start writing...
|
|
//
|
|
byte *at = pbBuffer;
|
|
|
|
// write GLM Header...
|
|
//
|
|
mdxmHeader_t *pMDXMHeader = (mdxmHeader_t *) at;
|
|
at += sizeof(mdxmHeader_t);
|
|
|
|
{// GLM brace-match for skipping...
|
|
|
|
pMDXMHeader->ident = MDXM_IDENT;
|
|
pMDXMHeader->version = MDXM_VERSION;
|
|
strcpy( pMDXMHeader->name,Filename_StripQuakeBase(psFullPathedNameGLM));
|
|
strcpy( pMDXMHeader->animName,sDEFAULT_GLA_NAME);//sLocalNameGLA);
|
|
pMDXMHeader->animIndex = 0; // ingame use only
|
|
pMDXMHeader->numBones = 1; // ... and a fake one at that.
|
|
pMDXMHeader->numLODs = giNumLODs;
|
|
// pMDXMHeader->ofsLODs = ?????????????????????????????????????????????
|
|
pMDXMHeader->numSurfaces = giNumSurfaces + giNumTags;
|
|
// pMDXMHeader->ofsSurfHierarchy= ?????????????????????????????????????????????
|
|
// pMDXMHeader->ofsEnd = ?????????????????????????????????????????????
|
|
|
|
|
|
|
|
// for hierarchiy purposes, I'm going to just write out the first surface as the parent,
|
|
// then make every other surface a child of that one...
|
|
//
|
|
// G2 surfaces come from MD3 meshes first, then the MD3 tags (since G2 uses tag surfaces)
|
|
//
|
|
mdxmHierarchyOffsets_t *pHierarchyOffsets = (mdxmHierarchyOffsets_t *) at;
|
|
at += sizeof(mdxmHierarchyOffsets_t) * pMDXMHeader->numSurfaces;
|
|
|
|
pMDXMHeader->ofsSurfHierarchy = at - (byte *) pMDXMHeader;
|
|
|
|
for (int iSurfaceIndex = 0; iSurfaceIndex < pMDXMHeader->numSurfaces; iSurfaceIndex++)
|
|
{
|
|
// Note: bool bSurfaceIsTag == (iSurfaceIndex < giNumSurfaces)
|
|
//
|
|
mdxmSurfHierarchy_t *pSurfHierarchy = (mdxmSurfHierarchy_t *) at;
|
|
//
|
|
// store this offset...
|
|
//
|
|
pHierarchyOffsets->offsets[iSurfaceIndex] = (byte *)pSurfHierarchy - (byte *)pHierarchyOffsets;
|
|
|
|
// fill in surf hierarchy struct...
|
|
//
|
|
strcpy( pSurfHierarchy->name, G2Exporter_Surface_GetName(iSurfaceIndex));
|
|
|
|
pSurfHierarchy->flags = 0;
|
|
|
|
if ( iSurfaceIndex >= giNumSurfaces)
|
|
{
|
|
pSurfHierarchy->flags |= G2SURFACEFLAG_ISBOLT;
|
|
}
|
|
if (!strnicmp(&pSurfHierarchy->name[strlen(pSurfHierarchy->name)-4],"_off",4))
|
|
{
|
|
pSurfHierarchy->flags |= G2SURFACEFLAG_OFF;
|
|
}
|
|
|
|
|
|
strcpy( pSurfHierarchy->shader, G2Exporter_Surface_GetShaderName(iSurfaceIndex));
|
|
pSurfHierarchy->shaderIndex = 0; // ingame use only
|
|
pSurfHierarchy->parentIndex = iSurfaceIndex?0:-1;
|
|
pSurfHierarchy->numChildren = iSurfaceIndex?0:pMDXMHeader->numSurfaces-1;
|
|
if (!iSurfaceIndex)
|
|
{
|
|
for (int i=0; i<pSurfHierarchy->numChildren; i++)
|
|
{
|
|
pSurfHierarchy->childIndexes[i] = i+1;
|
|
}
|
|
}
|
|
|
|
int iThisHierarchySize = (int)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ pSurfHierarchy->numChildren ] );
|
|
|
|
at += iThisHierarchySize;
|
|
}
|
|
|
|
|
|
|
|
// write out LODs...
|
|
//
|
|
pMDXMHeader->ofsLODs = at - (byte *) pMDXMHeader;
|
|
for (int iLODIndex = 0; iLODIndex < pMDXMHeader->numLODs; iLODIndex++)
|
|
{
|
|
mdxmLOD_t *pLOD = (mdxmLOD_t *) at;
|
|
at += sizeof(mdxmLOD_t);
|
|
|
|
mdxmLODSurfOffset_t *pLODSurfOffsets = (mdxmLODSurfOffset_t *) at;
|
|
at += sizeof(mdxmLODSurfOffset_t) * pMDXMHeader->numSurfaces;
|
|
|
|
for (int iSurfaceIndex = 0; iSurfaceIndex < pMDXMHeader->numSurfaces; iSurfaceIndex++)
|
|
{
|
|
mdxmSurface_t *pSurface = (mdxmSurface_t *) at;
|
|
at += sizeof(mdxmSurface_t);
|
|
//
|
|
// store this offset...
|
|
//
|
|
pLODSurfOffsets->offsets[iSurfaceIndex] = (byte *)pSurface - (byte *) pLODSurfOffsets;
|
|
//
|
|
// fill in this surface struct...
|
|
//
|
|
pSurface->ident = 0; // ingame-use only, defaulted to 0 here
|
|
pSurface->thisSurfaceIndex = iSurfaceIndex;
|
|
pSurface->ofsHeader = (byte *)pMDXMHeader - (byte *)pSurface; // offset back to main header
|
|
pSurface->numVerts = G2Exporter_Surface_GetNumVerts(iSurfaceIndex, iLODIndex);
|
|
pSurface->numTriangles = G2Exporter_Surface_GetNumTris (iSurfaceIndex, iLODIndex);
|
|
pSurface->maxVertBoneWeights= 1; // :-)
|
|
|
|
//
|
|
// write out triangles...
|
|
//
|
|
pSurface->ofsTriangles = at - (byte *)pSurface;
|
|
for (int iTriangleIndex = 0; iTriangleIndex < pSurface->numTriangles; iTriangleIndex++)
|
|
{
|
|
mdxmTriangle_t *pTriangle = (mdxmTriangle_t *) at;
|
|
at += sizeof(mdxmTriangle_t);
|
|
|
|
for (int i=0; i<3; i++)
|
|
{
|
|
pTriangle->indexes[i] = G2Exporter_Surface_GetTriIndex(iSurfaceIndex,iTriangleIndex,i, iLODIndex);
|
|
}
|
|
}
|
|
|
|
//
|
|
// write out verts...(when exporting from MD3 these are all weighted to only 1 bone)
|
|
//
|
|
pSurface->ofsVerts = at - (byte *)pSurface;
|
|
for (int iVertIndex = 0; iVertIndex < pSurface->numVerts; iVertIndex++)
|
|
{
|
|
mdxmVertex_t *pVert = (mdxmVertex_t *) at;
|
|
|
|
memcpy(pVert->normal, G2Exporter_Surface_GetVertNormal(iSurfaceIndex,iVertIndex,iLODIndex),sizeof(vec3_t));
|
|
memcpy(pVert->vertCoords,G2Exporter_Surface_GetVertCoords(iSurfaceIndex,iVertIndex,iLODIndex),sizeof(vec3_t));
|
|
memcpy(pVert->texCoords, G2Exporter_Surface_GetTexCoords (iSurfaceIndex,iVertIndex,iLODIndex),sizeof(vec2_t));
|
|
pVert->numWeights = 1; // force-weight every vert...
|
|
|
|
for (int iWeight=0; iWeight<pVert->numWeights; iWeight++)
|
|
{
|
|
pVert->weights[iWeight].boneIndex = 0;
|
|
pVert->weights[iWeight].boneWeight = 1.0f;
|
|
}
|
|
|
|
at = (byte *) &pVert->weights[pVert->numWeights];
|
|
}
|
|
|
|
// remaining surface struct fields...
|
|
//
|
|
pSurface->numBoneReferences = 1;
|
|
pSurface->ofsBoneReferences = at - (byte *) pSurface;
|
|
int *piBonesUsed = (int *) at;
|
|
at += pSurface->numBoneReferences * sizeof(int);
|
|
piBonesUsed[0] = 0; // the one and only bone ref
|
|
|
|
pSurface->ofsEnd = at - (byte *) pSurface;
|
|
}
|
|
|
|
pLOD->ofsEnd = at - (byte *) pLOD;
|
|
}
|
|
|
|
pMDXMHeader->ofsEnd = at - (byte *) pMDXMHeader;
|
|
}
|
|
|
|
|
|
/*
|
|
// now create GLA file...
|
|
//
|
|
mdxaHeader_t *pMDXAHeader = (mdxaHeader_t *) at;
|
|
at += sizeof(mdxaHeader_t);
|
|
{// for brace-skipping...
|
|
|
|
pMDXAHeader->ident = MDXA_IDENT;
|
|
pMDXAHeader->version = MDXA_VERSION;
|
|
strncpy(pMDXAHeader->name,sLocalNameGLA,sizeof(pMDXAHeader->name));
|
|
pMDXAHeader->name[sizeof(pMDXAHeader->name)-1]='\0';
|
|
pMDXAHeader->fScale = 1.0f;
|
|
pMDXAHeader->numFrames = 1; // inherently, when doing MD3 to G2 files
|
|
// pMDXAHeader->ofsFrames = ??????????????????
|
|
pMDXAHeader->numBones = 1; // inherently, when doing MD3 to G2 files
|
|
// pMDXAHeader->ofsCompBonePool= ??????????????????
|
|
// pMDXAHeader->ofsSkel = ??????????????????
|
|
// pMDXAHeader->ofsEnd = ??????????????????
|
|
|
|
// write out bone hierarchy...
|
|
//
|
|
mdxaSkelOffsets_t * pSkelOffsets = (mdxaSkelOffsets_t *) at;
|
|
at += (int)( &((mdxaSkelOffsets_t *)0)->offsets[ pMDXAHeader->numBones ] );
|
|
|
|
pMDXAHeader->ofsSkel = at - (byte *) pMDXAHeader;
|
|
for (int iSkelIndex = 0; iSkelIndex < pMDXAHeader->numBones; iSkelIndex++)
|
|
{
|
|
mdxaSkel_t *pSkel = (mdxaSkel_t *) at;
|
|
|
|
pSkelOffsets->offsets[iSkelIndex] = (byte *) pSkel - (byte *) pSkelOffsets;
|
|
|
|
// setup flags...
|
|
//
|
|
pSkel->flags = 0;
|
|
strcpy( pSkel->name, "Generated by Q3Data"); // doesn't matter what this is called I believe.
|
|
pSkel->parent= -1; // index of bone that is parent to this one, -1 = NULL/root
|
|
|
|
Matrix4 BasePose;
|
|
BasePose.Identity();
|
|
BasePose.To3x4(pSkel->BasePoseMat.matrix);
|
|
|
|
Matrix4 BasePoseInverse;
|
|
BasePoseInverse.Inverse(BasePose);
|
|
BasePoseInverse.To3x4(pSkel->BasePoseMatInv.matrix);
|
|
|
|
pSkel->numChildren = 0; // inherently, when doing MD3 to G2 files
|
|
|
|
int iThisSkelSize = (int)( &((mdxaSkel_t *)0)->children[ pSkel->numChildren ] );
|
|
|
|
at += iThisSkelSize;
|
|
}
|
|
|
|
|
|
// write out frames...
|
|
//
|
|
pMDXAHeader->ofsFrames = at - (byte *) pMDXAHeader;
|
|
int iFrameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ pMDXAHeader->numBones ] );
|
|
for (int i=0; i<pMDXAHeader->numFrames; i++)
|
|
{
|
|
mdxaFrame_t *pFrame = (mdxaFrame_t *) at;
|
|
at += iFrameSize; // variable sized struct
|
|
|
|
pFrame->boneIndexes[0] = 0; // inherently, when doing MD3 to G2 files
|
|
}
|
|
|
|
// now write out compressed bone pool...
|
|
//
|
|
pMDXAHeader->ofsCompBonePool = at - (byte *) pMDXAHeader;
|
|
for (int iCompBoneIndex = 0; iCompBoneIndex < 1 //CompressedBones.size()
|
|
; iCompBoneIndex++)
|
|
{
|
|
mdxaCompBone_t *pCompBone = (mdxaCompBone_t *) at;
|
|
at += sizeof(mdxaCompBone_t);
|
|
|
|
float matThis[3][4];
|
|
|
|
Matrix4 Bone;
|
|
Bone.Identity();
|
|
Bone.To3x4(matThis);
|
|
|
|
byte Comp[sizeof(mdxaCompBone_t)];
|
|
MC_Compress(matThis,Comp);
|
|
|
|
memcpy(pCompBone->Comp,Comp,sizeof(Comp));
|
|
}
|
|
|
|
// int iPoolBytesUsed = (CompressedBones.size()*MC_COMP_BYTES) + (iStats_NumBoneRefs*sizeof(int));
|
|
// printf("( Compressed bone bytes: %d, using pool = %d. Saving = %d bytes )\n",iOldBoneSize,iPoolBytesUsed, iOldBoneSize - iPoolBytesUsed);
|
|
|
|
// done...
|
|
//
|
|
pMDXAHeader->ofsEnd = at - (byte *) pMDXAHeader;
|
|
}
|
|
*/
|
|
|
|
// write 'em out to disk...
|
|
//
|
|
int iGLMSize = fwrite(pMDXMHeader,1,/*(byte *)pMDXAHeader - (byte *)pMDXMHeader*/ pMDXMHeader->ofsEnd, fhGLM);
|
|
assert(iGLMSize == pMDXMHeader->ofsEnd);
|
|
|
|
// int iGLASize = fwrite(pMDXAHeader,1,at - (byte *)pMDXAHeader, fhGLA);
|
|
// assert(iGLASize == pMDXAHeader->ofsEnd);
|
|
|
|
// and finally...
|
|
//
|
|
free(pbBuffer);
|
|
}
|
|
else
|
|
{
|
|
psErrMess = va("Error! Unable to allocate %d bytes for workspace!\n",iMallocSize);
|
|
}
|
|
|
|
return psErrMess;
|
|
}
|
|
|
|
|
|
GLM_ImportModel_t ImportedModel;
|
|
|
|
|
|
// return = NULL for no error, else string of error for caller to display...
|
|
//
|
|
char *ExportGhoul2FromMD3_ImportExisting(LPCSTR psFullPathedFilename)
|
|
{
|
|
char *psErrorMess = NULL;
|
|
void *pvData = NULL;
|
|
|
|
ImportedModel.ImportedLODs.clear();
|
|
ImportedModel.SurfaceIndexRemaps.clear();
|
|
|
|
FILE *fhHandle = fopen( psFullPathedFilename, "rb" );
|
|
if (fhHandle)
|
|
{
|
|
long lLen = filelength( fileno( fhHandle ) );
|
|
|
|
pvData = malloc( lLen );
|
|
if (pvData)
|
|
{
|
|
long lBytesRead = fread( pvData, 1, lLen, fhHandle );
|
|
|
|
if (lBytesRead == lLen)
|
|
{
|
|
mdxmHeader_t *pMDXMHeader = (mdxmHeader_t *) pvData;
|
|
|
|
if (pMDXMHeader->ident == MDXM_IDENT)
|
|
{
|
|
if (pMDXMHeader->version == MDXM_VERSION)
|
|
{
|
|
if (!strcmp(pMDXMHeader->animName, sDEFAULT_GLA_NAME))
|
|
{
|
|
if (pMDXMHeader->numBones == 1) // actually we can skip this because of the sDEFAULT_GLA_NAME check, but wtf?
|
|
{
|
|
if (pMDXMHeader->numSurfaces == giNumSurfaces + giNumTags)
|
|
{
|
|
// now for the important stuff, let's read some data...
|
|
//
|
|
// setup remap tables so export code can query imported model transparently with same surface indexes as
|
|
// q3data version (they should be the same, but it's possible to not be and still be legal)...
|
|
//
|
|
mdxmHierarchyOffsets_t *pHierarchyOffsets = (mdxmHierarchyOffsets_t *) ((byte*)pMDXMHeader + sizeof(*pMDXMHeader));
|
|
//
|
|
// scan imported surface names...
|
|
//
|
|
for (int iSurface = 0; iSurface < pMDXMHeader->numSurfaces; iSurface++)
|
|
{
|
|
mdxmSurfHierarchy_t *pSurfHierarchy = (mdxmSurfHierarchy_t *) ((byte*)pHierarchyOffsets + pHierarchyOffsets->offsets[iSurface]);
|
|
LPCSTR psSurfaceName = pSurfHierarchy->name;
|
|
//
|
|
// ... to match current qdata surface names...
|
|
//
|
|
for (int iQ3Surface = 0; iQ3Surface < pMDXMHeader->numSurfaces/*the same at this point*/; iQ3Surface++)
|
|
{
|
|
LPCSTR psQ3SurfaceName = G2Exporter_Surface_GetName(iQ3Surface);
|
|
|
|
if (!stricmp(psSurfaceName,psQ3SurfaceName))
|
|
{
|
|
ImportedModel.SurfaceIndexRemaps[iQ3Surface] = iSurface; // new remap table entry
|
|
break;
|
|
}
|
|
}
|
|
if (iQ3Surface == pMDXMHeader->numSurfaces)
|
|
{
|
|
psErrorMess = va("Unable to find imported surface \"%s\" in current Q3data model\n",psSurfaceName);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (psErrorMess == NULL) // worth carrying on?
|
|
{
|
|
// now read the actual imported data...
|
|
//
|
|
ImportedModel.ImportedLODs.resize(pMDXMHeader->numLODs);
|
|
|
|
mdxmLOD_t *pLOD = (mdxmLOD_t *) ((byte*) pMDXMHeader + pMDXMHeader->ofsLODs);
|
|
for (int iLOD = 0; iLOD < pMDXMHeader->numLODs; iLOD++)
|
|
{
|
|
mdxmLODSurfOffset_t *pLODSurfOffset = (mdxmLODSurfOffset_t *) &pLOD[1];
|
|
|
|
for (iSurface = 0; iSurface < pMDXMHeader->numSurfaces; iSurface++)
|
|
{
|
|
GLM_ImportSurface_t GLM_ImportSurface;
|
|
|
|
mdxmSurface_t *pSurface = (mdxmSurface_t *) ((byte *) pLODSurfOffset + pLODSurfOffset->offsets[iSurface]);
|
|
|
|
// triangles...
|
|
//
|
|
mdxmTriangle_t *pTriangle = (mdxmTriangle_t *) ((byte*) pSurface + pSurface->ofsTriangles);
|
|
for (int iTriangle = 0; iTriangle < pSurface->numTriangles; iTriangle++, pTriangle++)
|
|
{
|
|
GLM_ImportSurface.ImportedTris.push_back(*pTriangle);
|
|
}
|
|
|
|
// verts...
|
|
//
|
|
mdxmVertex_t *pVert = (mdxmVertex_t *) ((byte*) pSurface + pSurface->ofsVerts);
|
|
for (int iVert = 0; iVert < pSurface->numVerts; iVert++)
|
|
{
|
|
GLM_ImportVertex_t GLM_ImportVertex;
|
|
|
|
memcpy(GLM_ImportVertex.normal, pVert->normal, sizeof(GLM_ImportVertex.normal));
|
|
memcpy(GLM_ImportVertex.vertCoords, pVert->vertCoords, sizeof(GLM_ImportVertex.vertCoords));
|
|
memcpy(GLM_ImportVertex.texCoords, pVert->texCoords, sizeof(GLM_ImportVertex.texCoords));
|
|
|
|
GLM_ImportSurface.ImportedVerts.push_back(GLM_ImportVertex);
|
|
|
|
pVert = (mdxmVertex_t *)&pVert->weights[/*pVert->numWeights*/pSurface->maxVertBoneWeights];
|
|
}
|
|
|
|
// done, so add this imported surface to the imported model...
|
|
//
|
|
ImportedModel.ImportedLODs[iLOD].ImportedSurfaces.push_back(GLM_ImportSurface);
|
|
}
|
|
|
|
pLOD = (mdxmLOD_t *)((byte *)pLOD + pLOD->ofsEnd);
|
|
}
|
|
|
|
// sanity...
|
|
//
|
|
assert(ImportedModel.SurfaceIndexRemaps.size() == pMDXMHeader->numSurfaces);
|
|
assert(ImportedModel.ImportedLODs.size() == pMDXMHeader->numLODs);
|
|
assert(ImportedModel.ImportedLODs[0].ImportedSurfaces.size() == pMDXMHeader->numSurfaces);
|
|
|
|
// and finally, tell the export q3data-query code how many LODS there now are,
|
|
// so it knows when to query this data as opposed to the current q3data model...
|
|
//
|
|
giNumLODs += pMDXMHeader->numLODs;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): found %d surfaces instead of %d\nFile:\n\"%s\"\n",pMDXMHeader->numSurfaces, giNumSurfaces + giNumTags, psFullPathedFilename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): found %d bones instead of %d\nFile: \"%s\"\n",pMDXMHeader->numBones,1, psFullPathedFilename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): File did not have \"%s\" as animName.\nFile:\"%s\"\n",sDEFAULT_GLA_NAME, psFullPathedFilename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Bad version (%d, expecting %d) on file:\n\"%s\"\n",pMDXMHeader->version, MDXM_VERSION, psFullPathedFilename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Bad ident on file \"%s\"\n",psFullPathedFilename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Short read (%d bytes instead of %d)\nFile = \"%s\"\n",lBytesRead,lLen,psFullPathedFilename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting(): Unable to allocate %d bytes\n",lLen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("ExportGhoul2FromMD3_ImportExisting():\nUnable to open file \"%s\" for append!\n",psFullPathedFilename);
|
|
}
|
|
|
|
if (fhHandle)
|
|
{
|
|
fclose(fhHandle);
|
|
// fhHandle = NULL;
|
|
}
|
|
|
|
if (pvData)
|
|
{
|
|
free(pvData);
|
|
pvData = NULL;
|
|
}
|
|
|
|
return psErrorMess;
|
|
}
|
|
|
|
|
|
|
|
// assumes 'psFullPathedFilename' is full-pathed filename with ".glm" on the end,
|
|
// ppsFullPathedNameGLA is optional feedback param if you want to know what the GLA name was.
|
|
//
|
|
// return = NULL for no error, else string of error for caller to display...
|
|
//
|
|
extern "C" void CreatePath (const char *path);
|
|
char *ExportGhoul2FromMD3(LPCSTR psFullPathedFilename, int iNumLODs, int iNumSurfaces, int iNumTags
|
|
// ,LPCSTR *ppsFullPathedNameGLA /* = NULL */
|
|
)
|
|
{
|
|
char *psErrorMess = NULL;
|
|
|
|
giNumLODs = iNumLODs;
|
|
giNumSurfaces = iNumSurfaces;
|
|
giNumTags = iNumTags;
|
|
|
|
CreatePath(psFullPathedFilename); // this has already been done earlier in q3data, but wtf?
|
|
|
|
// new, we need to work out if this file is to be an output, or a LOD append (ie are we "<modelname>_1.glm") ?
|
|
//
|
|
bool bAppending = false;
|
|
char sTemp[2048];
|
|
strcpy(sTemp, Filename_WithoutExt( psFullPathedFilename ));
|
|
char *psSuffixPos = &sTemp[strlen(sTemp)-2];
|
|
if (psSuffixPos[0] == '_' && isdigit(psSuffixPos[1]))
|
|
{
|
|
bAppending = true;
|
|
|
|
// since we're appending, we first of all need to get the data from the file already out there...
|
|
//
|
|
*psSuffixPos='\0'; // chop off trailing "_1" etc
|
|
strcat(sTemp, ".glm");
|
|
psErrorMess = ExportGhoul2FromMD3_ImportExisting( sTemp );
|
|
if (psErrorMess)
|
|
return psErrorMess; // which will cause an app-exit
|
|
}
|
|
|
|
FILE *fhGLM = fopen(bAppending?sTemp:psFullPathedFilename,"wb");
|
|
if ( fhGLM)
|
|
{
|
|
static char sNameGLA[MAX_QPATH];
|
|
|
|
// strcpy(sNameGLA, Filename_WithoutExt(psFullPathedFilename) );
|
|
// strcat(sNameGLA, ".gla");
|
|
strcpy(sNameGLA, sDEFAULT_GLA_NAME );
|
|
|
|
// CreatePath(sNameGLA);
|
|
// FILE *fhGLA = fopen(sNameGLA,"wb");
|
|
// if ( fhGLA)
|
|
{
|
|
// if ( ppsFullPathedNameGLA)
|
|
// {
|
|
// *ppsFullPathedNameGLA = &sNameGLA[0];
|
|
// }
|
|
|
|
psErrorMess = ExportGhoul2FromMD3_Main(fhGLM, /*fhGLA,*/ psFullPathedFilename);
|
|
|
|
// fclose(fhGLA);
|
|
}
|
|
// else
|
|
// {
|
|
// psErrorMess = va("Error: Unable to open file '%s'!\n");
|
|
// }
|
|
|
|
fclose(fhGLM);
|
|
}
|
|
else
|
|
{
|
|
psErrorMess = va("Error: Unable to open file '%s'!\n");
|
|
}
|
|
|
|
if (bAppending && !psErrorMess)
|
|
{
|
|
printf("\n( Appended LOD to \"%s\" )\n\n",sTemp);
|
|
}
|
|
return psErrorMess;
|
|
}
|
|
|
|
|
|
|
|
/////////////////// eof ////////////////////
|
|
|