📄 model.c
字号:
{
if (Brush_GetModelId (pBrush) == pModel->Id)
{
Brush_Transform (pBrush, pXfm);
}
pBrush = BrushList_GetNext (&bi);
}
}
void Model_TransformFromTo
(
Model const *pModel,
geXForm3d const *pXfmFrom,
geXForm3d const *pXfmTo,
BrushList *pList
)
{
geXForm3d XfmDoIt;
assert (pModel != NULL);
assert (pXfmFrom != NULL);
assert (pXfmTo != NULL);
assert (pList != NULL);
// back out old transform
geXForm3d_Multiply (&(pModel->XfmObjectToWorld), pXfmFrom, &XfmDoIt);
geXForm3d_GetTranspose (&XfmDoIt, &XfmDoIt);
// add the new TO matrix
geXForm3d_Multiply (pXfmTo, &XfmDoIt, &XfmDoIt);
// ...and world-relative positioning
geXForm3d_Multiply (&(pModel->XfmObjectToWorld), &XfmDoIt, &XfmDoIt);
Model_Transform (pModel, &XfmDoIt, pList);
}
void Model_GetCurrentPos
(
Model const *pModel,
geVec3d *pVecPos
)
{
geXForm3d XfmWork, XfmCurrent;
geVec3d VecWork;
assert (pModel != NULL);
assert (pVecPos != NULL);
VecWork = pModel->XfmObjectToWorld.Translation;
// get current position
Model_GetKeyframe (pModel, pModel->CurrentKeyTime, &XfmCurrent);
// and build world-relative transform
geXForm3d_GetTranspose (&(pModel->XfmObjectToWorld), &XfmWork);
geXForm3d_Multiply (&XfmCurrent, &XfmWork, &XfmWork);
geXForm3d_Multiply (&(pModel->XfmObjectToWorld), &XfmWork, &XfmWork);
geXForm3d_Transform (&XfmWork, &VecWork, pVecPos);
}
geXForm3d const * Model_GetObjectToWorldXfm
(
Model const *pModel
)
{
assert (pModel != NULL);
return &(pModel->XfmObjectToWorld);
}
void Model_UpdateOrigin
(
Model *pModel,
int MoveRotate,
geVec3d const *pVecDelta
)
{
geXForm3d *pXfm;
assert (pModel != NULL);
assert (pVecDelta != NULL);
pXfm = &(pModel->XfmObjectToWorld);
switch (MoveRotate)
{
case BRUSH_MOVE :
{
// simple translation
geXForm3d_Translate (pXfm, pVecDelta->X, pVecDelta->Y, pVecDelta->Z);
break;
}
case BRUSH_ROTATE :
{
if (Model_IsRotationLocked (pModel))
{
geXForm3d XfmRotate;
geVec3d VecXlate;
// build rotation transform from angles
geXForm3d_SetEulerAngles (&XfmRotate, pVecDelta);
// and apply it
/*
The problem here is that this new rotation has to be
applied AFTER the existing rotations. So I need to
back out the current translations, apply this rotation,
and then re-apply the translations.
*/
VecXlate = pXfm->Translation;
geVec3d_Clear (&(pXfm->Translation));
geXForm3d_Multiply (&XfmRotate, pXfm, pXfm);
pXfm->Translation = VecXlate;
}
break;
}
default:
// what did you want to do?
assert (0);
break;
}
}
void Model_AddBrushes
(
Model *pModel,
SelBrushList *pSelList
)
{
int i;
int NumBrushes;
assert (pModel != NULL);
NumBrushes = SelBrushList_GetSize (pSelList);
for (i = 0; i < NumBrushes; ++i)
{
Brush *pBrush = SelBrushList_GetBrush (pSelList, i);
Brush_SetModelId (pBrush, pModel->Id);
}
}
void Model_RemoveBrushes
(
Model *pModel,
SelBrushList *pSelList
)
{
int i;
int NumBrushes;
assert (pModel != NULL);
NumBrushes = SelBrushList_GetSize (pSelList);
for (i = 0; i < NumBrushes; ++i)
{
Brush *pBrush = SelBrushList_GetBrush (pSelList, i);
if (Brush_GetModelId (pBrush) == pModel->Id)
{
Brush_SetModelId (pBrush, 0);
}
}
}
geFloat Model_GetCurrentKeyTime
(
Model *pModel
)
{
assert (pModel != NULL);
return pModel->CurrentKeyTime;
}
geBoolean Model_GetKeyframe
(
Model const *pModel,
geFloat Time,
geXForm3d *pXfm
)
{
gePath *pPath;
int iKey;
int KeyframeCount;
assert (pModel != NULL);
assert (pXfm != NULL);
pPath = Model_GetPath ((Model *)pModel);
if (pPath == NULL)
{
return GE_FALSE;
}
// Since there's no Path function to get the keyframe for a particular time,
// we'll iterate the path's keyframes looking for it. If it doesn't exist,
// then we'll return GE_FALSE.
KeyframeCount = gePath_GetKeyframeCount (pPath, GE_PATH_ROTATION_CHANNEL);
for (iKey = 0; iKey < KeyframeCount; ++iKey)
{
geFloat kTime;
geXForm3d XfmRotate;
gePath_GetKeyframe (pPath, iKey, GE_PATH_ROTATION_CHANNEL, &kTime, &XfmRotate);
if (fabs (kTime - Time) < 0.0001f)
{
geXForm3d XfmXlate;
gePath_GetKeyframe (pPath, iKey, GE_PATH_TRANSLATION_CHANNEL, &kTime, &XfmXlate);
geXForm3d_Translate (&XfmRotate, XfmXlate.Translation.X, XfmXlate.Translation.Y, XfmXlate.Translation.Z);
*pXfm = XfmRotate;
return GE_TRUE;
}
}
return GE_FALSE;
}
void Model_SetCurrentKeyTime
(
Model *pModel,
geFloat KeyTime
)
{
assert (pModel != NULL);
pModel->CurrentKeyTime = KeyTime;
}
/*
The old version of the Genesis libraries did all their I/O through FILE * pointers.
Since the new version works with geVFile, any engine I/O done by the editor must
be done through geVFile. But since we don't have time to rewrite all editor I/O,
we redirect the FILE * I/O to geVFile. It's not pretty, but it works.
*/
static geMotion *Model_CreateMotionFromFile (FILE *f, const char *Filename)
{
geMotion *m = NULL;
geVFile *vfile;
vfile = geVFile_OpenNewSystem (NULL, GE_VFILE_TYPE_DOS, Filename, NULL, GE_VFILE_OPEN_READONLY);
if (vfile != NULL)
{
// get current file position
const size_t CurrentFilePos = ftell (f);
// position vfile to that position
if (geVFile_Seek (vfile, CurrentFilePos, GE_VFILE_SEEKSET) != GE_FALSE)
{
long NewFilePos;
// read the motion from that position
m = geMotion_CreateFromFile (vfile);
// get new file position and set the FILE * to that position
geVFile_Tell (vfile, &NewFilePos);
fseek (f, NewFilePos, SEEK_SET);
}
geVFile_Close (vfile);
}
return m;
}
// Write motion to memory VFile, then to the FILE *
static geBoolean Model_WriteMotionToFile (geMotion *pMotion, FILE *f)
{
geVFile_MemoryContext MemContext;
geVFile *MemSys;
// geVFile *MemFile;
geBoolean rslt = GE_FALSE;
// context is initially NULL for create
MemContext.Data = NULL;
MemContext.DataLength = 0;
MemSys = geVFile_OpenNewSystem (NULL, GE_VFILE_TYPE_MEMORY, NULL, &MemContext, GE_VFILE_OPEN_CREATE);
if (MemSys != NULL)
{
// write to the memory file
rslt = geMotion_WriteToFile (pMotion, MemSys);
if (rslt != GE_FALSE)
{
// UpdateContext returns the memory pointer and size
geVFile_UpdateContext (MemSys, &MemContext, sizeof (MemContext));
// now write this to the FILE *
rslt = (fwrite (MemContext.Data, MemContext.DataLength, 1, f) == 1);
}
geVFile_Close (MemSys);
}
return rslt;
}
static geBoolean Model_Write
(
Model const *m,
FILE *f
)
{
int IsMotion;
geBoolean rslt;
assert (m != NULL);
assert (f != NULL);
rslt = GE_TRUE;
{
char Name[300];
Util_QuoteString (m->Name, Name);
if (fprintf (f, "Model %s\n", Name) < 0) return GE_FALSE;
}
if (fprintf (f, "\tModelId %d\n", m->Id) < 0) return GE_FALSE;
if (fprintf (f, "\tCurrentKeyTime %f\n", m->CurrentKeyTime) < 0) return GE_FALSE;
if (fprintf (f, "%s", "\tTransform\n") < 0) return GE_FALSE;
if (!TypeIO_WriteXForm3dText (f, &(m->XfmObjectToWorld))) return GE_FALSE;
/*
Determine if we should write motion information.
Write motion information only if:
The motion is non-null
*/
if (m->pMotion != NULL)
{
IsMotion = 1;
}
else
{
IsMotion = 0;
}
if (fprintf (f, "\tMotion %d\n", IsMotion) < 0) return GE_FALSE;
if (IsMotion != 0)
{
rslt = Model_WriteMotionToFile (m->pMotion, f);
// rslt = geMotion_WriteToFile (m->pMotion, f);
}
return rslt;
}
static Model *Model_CreateFromFile
(
Parse3dt *Parser,
int VersionMajor,
int VersionMinor,
const char **Expected
)
{
char ModelName[500];
int ModelId;
Model *m;
if ((VersionMajor == 1) && (VersionMinor < 19))
{
if (!Parse3dt_GetIdentifier (Parser, (*Expected = "Model"), ModelName)) return NULL;
}
else
{
if (!Parse3dt_GetLiteral (Parser, (*Expected = "Model"), ModelName)) return NULL;
}
if (!Parse3dt_GetInt (Parser, (*Expected = "ModelId"), &ModelId)) return NULL;
// create the model structure
m = Model_Create (ModelId, ModelName);
if (m != NULL)
{
FILE *f;
f = Scanner_GetFile (Parser->Scanner);
if ((VersionMajor > 1) ||
(VersionMajor == 1) && (VersionMinor >= 7))
{
if (!Parse3dt_GetFloat (Parser, (*Expected = "CurrentKeyTime"), &m->CurrentKeyTime)) goto ReadError;
if (!Parse3dt_GetXForm3d (Parser, (*Expected = "Transform"), &m->XfmObjectToWorld)) goto ReadError;
}
if ((VersionMajor > MODEL_MOTION_VERSION_MAJOR) ||
((VersionMajor == MODEL_MOTION_VERSION_MAJOR) &&
(VersionMinor >= MODEL_MOTION_VERSION_MINOR)))
{
int IsMotion;
if (!Parse3dt_GetInt (Parser, (*Expected = "Motion"), &IsMotion)) goto ReadError;
if (IsMotion != 0)
{
m->pMotion = Model_CreateMotionFromFile (f, Scanner_GetFileName (Parser->Scanner));
}
}
if ((VersionMajor == 1) && (VersionMinor < 10))
{
Model_Scale (m, Units_CentimetersToEngine (1.0f));
}
// calling GetPath will force a path to be created if one doesn't exist.
Model_GetPath (m);
{
int NumKeys;
NumKeys = Model_GetNumKeys (m);
if (NumKeys == 0)
{
// no keys, add one...
geXForm3d XfmIdentity;
geXForm3d_SetIdentity (&XfmIdentity);
Model_AddKeyframe (m, 0.0f, &XfmIdentity);
}
Model_SetRotationLock (m, (NumKeys > 1));
}
}
return m;
ReadError :
Model_Destroy (&m);
return NULL;
}
static geMotion *Model_CreateScaledMotion
(
Model const *pModel,
geFloat ScaleFactor
)
{
geMotion *pFixedMotion;
geMotion *pMotion;
geXForm3d XfmObjWorld;
assert (pModel != NULL);
pMotion = Model_GetMotion (pModel);
// get object-to-world transform that's used in transforming keyframes
// from object space to world space.
XfmObjWorld = *Model_GetObjectToWorldXfm (pModel);
geVec3d_Clear (&XfmObjWorld.Translation);
pFixedMotion = geMotion_Create (GE_TRUE);
if (pFixedMotion != NULL)
{
int Index;
gePath *pPath;
gePath *pNewPath;
int iKey;
pPath = geMotion_GetPath (pMotion, 0);
pNewPath = gePath_Create (
GE_PATH_INTERPOLATE_HERMITE, // translational
GE_PATH_INTERPOLATE_SLERP, // rotational
GE_FALSE); // looped flag
// add the path to the new motion.
geMotion_AddPath (pFixedMotion, pNewPath, MODEL_PATH_NAME, &Index);
// Get the transforms from the original path, scale them,
// and add them to the new path.
for (iKey = 0; iKey < gePath_GetKeyframeCount (pPath, GE_PATH_TRANSLATION_CHANNEL); ++iKey)
{
geXForm3d XfmXlate;
geXForm3d XfmRotate;
geFloat Time;
// get the rotation element
gePath_GetKeyframe (pPath, iKey, GE_PATH_ROTATION_CHANNEL, &Time, &XfmRotate);
// get the translation element
gePath_GetKeyframe (pPath, iKey, GE_PATH_TRANSLATION_CHANNEL, &Time, &XfmXlate);
geVec3d_Scale (&XfmXlate.Translation, ScaleFactor, &XfmRotate.Translation);
// Make the transform relative to the model's current orientation
{
geXForm3d XfmWork;
geXForm3d_GetTranspose (&XfmObjWorld, &XfmWork);
geXForm3d_Multiply (&XfmRotate, &XfmWork, &XfmRotate);
geXForm3d_Multiply (&XfmObjWorld, &XfmRotate, &XfmRotate);
}
gePath_InsertKeyframe (pNewPath, GE_PATH_ALL_CHANNELS, Time, &XfmRotate);
}
}
return pFixedMotion;
}
void Model_Scale
(
Model *pModel,
geFloat ScaleFactor
)
{
geMotion *pNewMotion;
assert (pModel != NULL);
assert (ScaleFactor > 0);
if (pModel->pMotion != NULL)
{
pNewMotion = Model_CreateScaledMotion (pModel, ScaleFactor);
if (pNewMotion != NULL)
{
geMotion_Destroy (&pModel->pMotion);
pModel->pMotion = pNewMotion;
}
}
geVec3d_Scale (&pModel->XfmObjectToWorld.Translation, ScaleFactor, &pModel->XfmObjectToWorld.Translation);
}
typedef struct
{
geBoolean SuppressHidden;
geBoolean VisDetail;
int ModelId;
int BrushCount;
BrushList *pList;
} Model_BrushEnumData;
static geBoolean Model_BrushCountCallback
(
Brush *b,
void *pVoid
)
{
Model_BrushEnumData *pData;
pData = (Model_BrushEnumData *)pVoid;
if ((Brush_GetModelId (b) == pData->ModelId) && (!Brush_IsSubtract(b)) &&
((pData->SuppressHidden == GE_FALSE) || (Brush_IsVisible (b))))
{
Brush *pBrush;
// add it to the list
pBrush = Brush_Clone (b);
if (pBrush != NULL)
{
BrushList_Append (pData->pList, pBrush);
++(pData->BrushCount);
}
}
return GE_TRUE;
}
static geBoolean Model_WriteToMap
(
Model const *m,
FILE *f,
BrushList const *pBList,
geBoolean SuppressHidden,
geBoolean VisDetail
)
{
Brush const *b;
BrushIterator bi;
geXForm3d XfmCurrentSave;
geXForm3d XfmZero;
Model_BrushEnumData EnumData;
assert (m != NULL);
assert (f != NULL);
assert (pBList != NULL);
// get transforms from current to key 0
Model_GetKeyframe (m, m->CurrentKeyTime, &XfmCurrentSave);
Model_GetKeyframe (m, 0.0f, &XfmZero);
// count number of brushes in this model
EnumData.SuppressHidden = SuppressHidden;
EnumData.VisDetail = VisDetail;
EnumData.ModelId = m->Id;
EnumData.BrushCount = 0;
EnumData.pList = BrushList_Create ();
if (EnumData.pList == NULL)
{
return GE_FALSE;
}
// Build a list of brushes that belong to this model.
// Count them at the same time.
BrushList_EnumCSGBrushes (pBList, &EnumData, Model_BrushCountCallback);
// Transform the brushes to the model's origin
Model_TransformFromTo (m, &XfmCurrentSave, &XfmZero, EnumData.pList);
// write brush count
TypeIO_WriteInt (f, EnumData.BrushCount);
// write the individual brushes from the temporary list
b = BrushList_GetFirst (EnumData.pList, &bi);
while (b != NULL)
{
Brush_WriteToMap (b, f, VisDetail);
b = BrushList_GetNext (&bi);
}
// destroy model brush list...
BrushList_Destroy (&EnumData.pList);
// If the model has an animation path, write that to the file.
{
gePath *pPath;
geBoolean HasMotion;
pPath = Model_GetPath ((Model *)m);
HasMotion = ((pPath != NULL) && (gePath_GetKeyframeCount (pPath, GE_PATH_TRANSLATION_CHANNEL) > 1));
// write motion flag
TypeIO_WriteInt (f, HasMotion);
if (HasMotion)
{
geMotion *NewMotion;
/*
The editor stores the motion's keys relative to the model's current orientation.
The utilities expect the keys to be relative to the world's origin with no rotation.
So I need to rotate the keys to match the model's default orientation.
*/
NewMotion = geMotion_Create (GE_TRUE);
if (NewMotion != NULL)
{
int Index;
gePath *pPath;
gePath *NewPath;
int iKey;
NewPath = gePath_Create (
GE_PATH_INTERPOLATE_HERMITE, // translational
GE_PATH_INTERPOLATE_SLERP, // rotational
GE_FALSE); // looped flag
geMotion_AddPath (NewMotion, NewPath, "FixedPath", &Index);
// paths are reference counted, so need to destroy it...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -