⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 model.c

📁 3D游戏场景编辑器
💻 C
📖 第 1 页 / 共 3 页
字号:
	{
		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 + -