//================================================================================================
// DESCRIPTION:	3D file loading common code
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Nov-19 02:06:09
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================================================

#include <E3D/SkinMesh.h>
#include <E3D/3DIO.h>

#define _ObjectsALLOC_INCREMENT  256

#define  CfPointSetCUSTOM_POINT_ATTRIBUTES     0x00000001
#define  CfMeshCUSTOM_VERTEX_ATTRIBUTES        0x00000002
#define  CfPolyGroupCUSTOM_VERTEX_ATTRIBUTES   0x00000004
#define  CfMaterialANIMATED_TEXTURE            0x00000008
#define  CfMeshTEXTUREMAPPERS                  0x00000010
#define  CfVertexGeometryTEXTUREMAPPERS        0x00000020

// Keep everything in a struct, so saving 3D files is thread safe
//
struct E3dLoader_S
{
	uint32_t                VersionMajorMinor;

	const uint8_t*          Buffer;
	EListA(, E3dGeometry*,  Geometries);
	EListA(, E3dPolyGroup*, PolyGroups);
	EListA(, E3dNode*,      Nodes);
	EListA(, E3dNode*,      RootNodes);
	EListA(, E3dMaterial*,  Materials);
	EListA(, E3dMesh*,      SkinnedMeshes);
};


//========================================================================
// Free the contents of an E3dLoader
// Geometries, PolyGroups, Nodes, forward-referencing proxies etc.
//========================================================================
static void _3DLoader_FreeContents(E3dLoader* P3DLoader)
{
	EList_FreeAChk(P3DLoader->Geometries);
	EList_ObjectsFreeA(P3DLoader->PolyGroups);
	EList_ObjectsFreeA(P3DLoader->Nodes);
	EList_ObjectsFreeA(P3DLoader->RootNodes);
	EList_ObjectsFreeA(P3DLoader->Materials);
	EList_FreeAChk(P3DLoader->SkinnedMeshes);
}


static inline void _ObjectAddByID(EResContext* PContext, const EIndex PIncrement, EObject* PObject, const EIndex PID)
{
	if(PID >= PContext->AllObjects.Count)
	{
		const EIndex  LNewBlocks = PID / PIncrement + 1, LNewSize = LNewBlocks * PIncrement;

		if(PContext->AllObjects.Count > 0)
		{
			const EIndex  LDiff = LNewSize - PContext->AllObjects.Count;

			ECoreObject**  LListNew = PContext->AllObjects.Items;EResizeNT(ECoreObject*, LNewSize, LListNew);
			_ELst_Remap(PContext->AllObjects.Items, LListNew, PContext->AllObjects.Count);
			memset(LListNew + PContext->AllObjects.Count, 0, sizeof(ECoreObject*) * LDiff);
			PContext->AllObjects.Items = LListNew;
		}
		else
		{
			ENewNClr(LNewSize, PContext->AllObjects.Items);
		}
		PContext->AllObjects.Count = PContext->AllObjects.CountAllocated = LNewSize;
	}
	E_Assign(PObject, PContext->AllObjects.Items[PID]);
}


static const uint8_t* _GetNodeHrc(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t* PPtr, const EBool PHardWiredResources, int* PBaseNodeIndex,
 int* PMaxNodeNum, const EBool PAddToScene, int* PResultRet)
{
	E3dNode*        LRootNode = NULL;
	int             LNNodesInHierarchy;
	const uint8_t*  LPtr = PPtr;

// Number of Nodes in this hierarchy
//
	EM_GetInt32I_be(LNNodesInHierarchy, LPtr);

if(E3dDebugNODE)
{
	printf("%s() %s:%d   %sat %08llx%s LNNodesInHierarchy %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal,
	 LNNodesInHierarchy);
	fflush(stdout);
}

	if(LNNodesInHierarchy > 0)
	{
		*PMaxNodeNum += LNNodesInHierarchy;

		{
			EIndex  LNodeC = 0;for(;LNodeC < LNNodesInHierarchy;LNodeC++)
			{
				EBool  LOldJoint = FALSE;

				_GetObjectFlagsAndID(LPtr, LNodeID);

/*
	int  LNodeID = -1;
	{
		const uint8_t  LObjectFlags = *LPtr++, LObjectIndexSize = LObjectFlags & 0x0F;switch(LObjectIndexSize)
		{
			case 1:  LNodeID = *LPtr++;break;
			case 2:  EM_GetInt16I_be(LNodeID, LPtr);break;
			case 4:  EM_GetInt32I_be(LNodeID, LPtr);break;
			case 8:  EM_GetInt64I_be(LNodeID, LPtr);break;
		}
//printf("%s() %s:%d  %d %d  (%s)\n", __FUNCTION__, __FILE__, __LINE__, LObjectFlags, LNodeID, (char*)LIPtr);fflush(stdout);
	}
*/


#ifdef CfOLD
				if(_CDS_FileVersion < 928) { int  LNodeIndex;EM_GetInt32I_be(LNodeIndex, LPtr);(void)LNodeIndex; }
#endif

				char*  LName;
				if(_CDS_FileVersion >= 909) LName = _Cf_GetString(&LPtr);
				else LName = _Cf_GetStringOld(&LPtr);

				E3dNode*  LNode;E_NewC(E3dModel_class, LNode);

				if(LNode == NULL) { *PResultRet = E3dfCOULD_NOT_CREATE_NODE;return(NULL); }

				EObject_SetName(LNode, LName, FALSE);

#ifdef CfOLD
				if(PHardWiredResources)
				{
					EBool  LVisible, LBranchExpanded, LOutputsEnabled, LBranchOutputsEnabled;
					uint8_t  LCh = *LPtr++;
					if(LCh & 0x01) LVisible = TRUE;else LVisible = FALSE;
					if(_CDS_FileVersion >= 905) { if(LCh & 0x02) LBranchExpanded = TRUE;else LBranchExpanded = FALSE; }
					else LBranchExpanded = TRUE;

					if(_CDS_FileVersion >= 905) { if(LCh & 0x02) LBranchExpanded = TRUE;else LBranchExpanded = FALSE; }
					else LBranchExpanded = TRUE;

					if(_CDS_FileVersion >= 923)
					{
						if(LCh & 0x04) LOutputsEnabled       = TRUE;else LOutputsEnabled       = FALSE;
						if(LCh & 0x08) LBranchOutputsEnabled = TRUE;else LBranchOutputsEnabled = FALSE;
					}
					else
					{
						LOutputsEnabled = TRUE;LBranchOutputsEnabled = TRUE;
					}

					const uint8_t  LSelection = *LPtr++;
					uint32_t       LSubCameras = E3dCameraALL;if(_CDS_FileVersion >= 925) EM_GetUInt32I_be(LSubCameras, LPtr);

					const uint8_t  LType = *LPtr++;

					LNode->Visible = LVisible;
					LNode->SchemObject.BranchExpanded = LBranchExpanded;
					LNode->Selection = LSelection;

					LNode->OutputsEnabled       = LOutputsEnabled;
					LNode->BranchOutputsEnabled = LBranchOutputsEnabled;
					LNode->SubCameras = LSubCameras;

					switch(LType)
					{
						case CfModelJOINT:  E3dNodeComponent_Add(LNode, E3dJointNodeInfo_class);LOldJoint = TRUE;break;
						default:            break;
					}
				}

				if(_CDS_FileVersion >= 928) { if(LNodeID >= 0) _ObjectAddByID(PContext, _ObjectsALLOC_INCREMENT, (EObject*)LNode, LNodeID); }
				else
				{
					EIndex  LIdx;(void)LIdx;EList_ObjectAddAChk_s(PContext->AllObjects, _ObjectsALLOC_INCREMENT, LNode,  LIdx);
				}
#else  // CfOLD
				if(LNodeID >= 0) _ObjectAddByID(PContext, _ObjectsALLOC_INCREMENT, (EObject*)LNode, LNodeID);

if(E3dDebugNODE)
{
	PContext->Verbose = 2;
	printf("%s() %s:%d  <%s>  Resources at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, LName, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);fflush(stdout);
}
					const int  LLen = EObject_ResourcesFromBinaryImage((ECoreObject*)LNode, PContext, P3DLoader->Buffer, LPtr,  &PContext->ObjectProxies);if(LLen >= 0)
					{
						LPtr += LLen;
					}
if(E3dDebugNODE)
{
	printf("%s() %s:%d  <%s>  Resources end at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, LName, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);fflush(stdout);
	PContext->Verbose = 0;
	printf("%s() %s:%d  <%s>  at %08llx\n", __FUNCTION__, __FILE__, __LINE__, LName, (long long)(LPtr - P3DLoader->Buffer));fflush(stdout);
}
#endif  // CfOLD


if(E3dDebugOBJECT) { printf("%s() %s:%d  PContext->AllObjects.Count %lld\n", __FUNCTION__, __FILE__, __LINE__, (long long)PContext->AllObjects.Count);fflush(stdout); }

				if(LNodeC == 0)
				{
					LRootNode = LNode;
					if(PAddToScene) EList_ObjectAddA(P3DLoader->RootNodes, 16, LNode);
				}

				EList_ObjectAddA(P3DLoader->Nodes, 16, LNode);
if(E3dDebugNODE)
{
	printf("%s() %s:%d  Node %p  P3DLoader->Nodes_Count %lld\n", __FUNCTION__, __FILE__, __LINE__, P3DLoader->Nodes, (long long)P3DLoader->Nodes_Count);fflush(stdout);
}

if(E3dDebugNODE) { printf("%s() %s:%d  [%p:%s]\n", __FUNCTION__, __FILE__, __LINE__, LNode, LNode->Name);fflush(stdout); }
				const char	LRotationOrder = *LPtr++;

// Node's Material
//
				if(_CDS_FileVersion < 909) { int  LIdx;EM_GetInt32I_be(LIdx, LPtr);(void)LIdx; }

				{
					E3dNode**  LParentP = &LNode->Parent;

// Parent index
//
					int  LParentIndex;EM_GetInt32I_be(LParentIndex, LPtr);
					if(LParentIndex < -1 || LParentIndex >= *PMaxNodeNum)
					{
						*PResultRet = E3dfBAD_PARENT_INDEX;
//printf("%s() Bad parent index: %d %d\n", __FUNCTION__, LParentIndex, *PMaxNodeNum);fflush(stdout);
						return(LPtr);
					}
					*(int*)LParentP = LParentIndex;

					if(_CDS_FileVersion < 909)
					{
						E3dNode**  LChildP = &LNode->Child;
						E3dNode**  LNextSiblingP = &LNode->NextSibling;

						int  LChildIndex;EM_GetInt32I_be(LChildIndex, LPtr);
						if(LChildIndex < -1 || LChildIndex >= *PMaxNodeNum) { *PResultRet = E3dfBAD_CHILD_INDEX;return(LPtr); }
						*(int*)LChildP = LChildIndex;

						int  LNextSiblingIndex;EM_GetInt32I_be(LNextSiblingIndex, LPtr);
						if(LNextSiblingIndex < -1 || LNextSiblingIndex >= *PMaxNodeNum) { *PResultRet = E3dfBAD_NEXTSIBLING_INDEX;return(LPtr); }
						*(int*)LNextSiblingP = LNextSiblingIndex;
					}
				}
//printf("\n");fflush(stdout);

				if(EObject_IsSubClass(LNode, E3dModel))
				{
					E3dModel*  LModel = (E3dModel*)LNode;E3dTransformSet*  LTransformSet = &LModel->TransformSet;
					int        LON;EM_GetInt32I_be(LON, LPtr);  // Number of Objects

					if(_CDS_FileVersion >= 809)
					{
if(E3dDebugNODE)
{
	printf("%s() %s:%d  Model [%s%s%s] Object indices: at %s%08llx%s  LON %d\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, LNode->Name, ECodeNormal,
	 ECodeCyan, (long long)(LPtr - P3DLoader->Buffer - 4), ECodeNormal, LON);
	fflush(stdout);
}
						int  LOC = 0;for(;LOC < LON;LOC++)
						{
							int  LObjectIndex;EM_GetInt32I_be(LObjectIndex, LPtr);

if(_CDS_FileVersion > 907 && _CDS_FileVersion < 928) LObjectIndex++;

							if(LObjectIndex >= 0 && LObjectIndex < PContext->AllObjects.Count)
							{
								ECoreObject*  LObject = PContext->AllObjects.Items[LObjectIndex];

//if(EObject_IsSubClass(LObject, E3dCamera)) { printf("%s() %s:%d <%s>\n", __FUNCTION__, __FILE__, __LINE__, LObject->Name);fflush(stdout); }
								if(LObject)
								{
if(E3dDebugNODE)
{
	printf("%s() %s:%d  index %d:(%s)<%p:%s>\n",  __FUNCTION__, __FILE__, __LINE__,  LObjectIndex, LObject->Class->Name, LObject, LObject && LObject->Name ? LObject->Name : "");
	fflush(stdout);
}
									if(EObject_IsSubClass(LObject, E3dObject)) E3dModel_ObjectAppend(LModel, (E3dObject*)LObject, FALSE);
								}
								else
								{
printf("%s() %s:%d  %sObject %d is NULL%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, (int)LObjectIndex, ECodeNormal);fflush(stdout);
								}
							}
							else
							{
printf("%s() %s:%d  %sObject index %lld is outside of the valid range (0...%lld)%s\n",  __FUNCTION__, __FILE__, __LINE__,
 ECodeYellow, (long long)LObjectIndex, (long long)(PContext->AllObjects.Count - 1), ECodeNormal);fflush(stdout);
							}
						}
					}
					else
					{
						int  LOC = 0;for(;LOC < LON;LOC++)
						{
							int  LObjectIndex;EM_GetInt32I_be(LObjectIndex, LPtr);
							E3dModel_ObjectAppend(LModel, (E3dObject*)(P3DLoader->Geometries[LObjectIndex]), FALSE);
						}
					}


					if(_CDS_FileVersion >= 916)
					{
// Version 916 and up, supports arbitrary transform lists on Models
//
						int  LNTransforms;EM_GetInt32I_be(LNTransforms, LPtr);
if(E3dDebugNODE)
{
	printf("%s() %s:%d  Model [%s%s%s] Transforms: at %s%08llx%s  %d\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, LNode->Name, ECodeNormal,
	 ECodeCyan, (long long)(LPtr - P3DLoader->Buffer - 4), ECodeNormal, LNTransforms);fflush(stdout);
}
						EIndex  LI = 0;for(;LI < LNTransforms;LI++)
						{
							_GetObjectFlagsAndID(LPtr, LTransformID);

							{
								int                LClassIndex;EM_GetInt16I_be(LClassIndex, LPtr);
								EResClassEntry*    LClassEntry = PContext->Classes.Items[LClassIndex];
								const ECoreClass*  LClass = LClassEntry->Class;
								E3dTransform*      LTransform;E_NewC(LClass, LTransform);EList_ObjectAdd(LTransformSet->Transforms, LTransform);

if(E3dDebugNODE)
{
	printf("%s() %s:%d  Model [%s%s%s] Transform: at %s%08llx%s  %lld  ClassIndex  %d (%s)\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, LNode->Name, ECodeNormal,
	 ECodeCyan, (long long)(LPtr - P3DLoader->Buffer - 2), ECodeNormal, (long long)LI, LClassIndex, LClass->Name);fflush(stdout);
}
								const EIndex       LBinaryImageLen = ERes_FromBinaryImage(PContext, P3DLoader->Buffer, LPtr,  LClassEntry->AllResources, &LClass->Resources,  LTransform,
								 &PContext->ObjectProxies);

								if(LBinaryImageLen > 0) LPtr += LBinaryImageLen;
								else break;    // Error decoding resources, end the for() loop

								if(LTransformID >= 0) _ObjectAddByID(PContext, _ObjectsALLOC_INCREMENT, (EObject*)LTransform, LTransformID);

if(0 && E3dDebugNODE)
{
	printf("%s() %s:%d  PContext->AllObjects.Count %lld\n", __FUNCTION__, __FILE__, __LINE__, (long long)PContext->AllObjects.Count);fflush(stdout);
}
							}
						}
					}

if(E3dDebugNODE)
{
	printf("%s() %s:%d  Model [%s%s%s] TransformSet: at %s%08llx%s\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, LNode->Name, ECodeNormal,
	 ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);
	fflush(stdout);
}
					LModel->TransformSet.EulerRotation.Order = LRotationOrder;

					EM_GetFloat32I_be(LModel->TransformSet.Translation.X, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.Translation.Y, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.Translation.Z, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.Scaling.X, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.Scaling.Y, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.Scaling.Z, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.EulerRotation.Angles.X, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.EulerRotation.Angles.Y, LPtr);
					EM_GetFloat32I_be(LModel->TransformSet.EulerRotation.Angles.Z, LPtr);
				}

				if(LOldJoint)
				{
					E3dJointNodeInfo*  LJointNodeInfo = (E3dJointNodeInfo*)E3dNodeComponent_ByClass(LNode, E3dJointNodeInfo_class, FALSE);

// Read joint-orient Matrix
//
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M00], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M01], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M02], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M03], LPtr);

					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M10], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M11], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M12], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M13], LPtr);

					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M20], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M21], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M22], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M23], LPtr);

					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M30], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M31], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M32], LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->JointOrientMatrix[M33], LPtr);

/*
printf("%s() [%s]\n", __FUNCTION__, LJointNode->Name);fflush(stdout);
E3dMatrix_Print4x4(LJointNode->JointOrientMatrix);

E3dMatrix_LoadIdentity(LJointNode->JointOrientMatrix);
*/

					EM_GetFloat32I_be(LJointNodeInfo->AngleXMin, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngleXMax, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngleYMin, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngleYMax, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngleZMin, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngleZMax, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngularDampening.X, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngularDampening.Y, LPtr);
					EM_GetFloat32I_be(LJointNodeInfo->AngularDampening.Z, LPtr);
				}


// Read Components
//
				{
					EIndex  LLength;EM_GetInt32I_be(LLength, LPtr);

//printf("NodeInfo on [%s] %d bytes\n", LModel->Name, LLength);fflush(stdout);
// Components total length
//
					if(LLength)
					{
						const uint8_t*  LIPtr = LPtr;
						const EIndex    LStart = LIPtr - P3DLoader->Buffer;
						const uint8_t*  LEndPtr = LPtr + LLength;

if(E3dDebugNODE)
{
	printf("%s() %s:%d  Model [%s%s%s] NodeComponent at %s%08llx%s  length: %08llx end %08llx\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, LNode->Name, ECodeNormal,
	 ECodeCyan, (long long)(LIPtr - P3DLoader->Buffer), ECodeNormal,  (long long)LLength, (long long)(LPtr + LLength - P3DLoader->Buffer));fflush(stdout);
}
						for(;LIPtr < LEndPtr;)
						{
// ETEMPORARY - InterestPoint changed to AimPoint (used not just for Cameras any more, but now, any Model can have an Aim constraint)
//
							E3dNodeComponentClass*	LClass;
							EBool  LOldInterestPoint = FALSE;

							_GetObjectFlagsAndID(LIPtr, LNodeComponentID);

//						if(_CDS_FileVersion < 924 && (EStr_Equal((char*)LIPtr, "InterestPoint") || EStr_Equal((char*)LIPtr, "AimPoint")))
							if(_CDS_FileVersion < 924 && EStr_Equal((char*)LIPtr, "InterestPoint"))
							{
								LClass = E3dNodeComponent_ClassByName("AimConstraint");
								LOldInterestPoint = TRUE;
//printf("%s() %s:%d  [%s] Old NodeComponentClass %s%s%s\n", __FUNCTION__, __FILE__, __LINE__, LNode->Name, ECodeYellow, (char*)LIPtr, ECodeNormal);fflush(stdout);
							}
							else LClass = E3dNodeComponent_ClassByName((char*)LIPtr);

if(E3dDebugNODE)
{
	printf("%s() %s:%d  at %s%08llx%s  NodeComponent (%s%s%s)\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LIPtr - P3DLoader->Buffer), ECodeNormal,
	 ECodeGreen, LIPtr, ECodeNormal);fflush(stdout);
}
							if(LClass)
							{
								E3dNodeComponent*  LInfo = E3dNodeComponent_Add(LNode, LClass);
								const size_t       LNameLen = strlen((char*)LIPtr) + 1;

								LIPtr += LNameLen;

if(E3dDebugNODE)
{
	printf("%s() %s:%d  %sNodeComponentClass%s (%s)\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LClass->Name);fflush(stdout);
}
								if(!LClass->ClassActive) { printf("Manager for NodeComponent class '%s' is not loaded.\n", LClass->Name);fflush(stdout); }

								if(LInfo && LIPtr < LEndPtr)
								{
//printf("%s() %s:%d 924+\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
									EIndex  LBinaryImageLen = 0;

									if(LOldInterestPoint)
									{
// Add a temporary NodeComponent to LNode, so we can read and resolve Object references.
// This will be converted and removed before we return to the application.
//
										E3dOldInterestPoint*  LInterestPoint = (E3dOldInterestPoint*)E3dNodeComponent_Add(LNode, _E3dOldInterestPointNodeComponent_class);if(LInterestPoint)
										{
//PContext->Verbose = 2;
											LBinaryImageLen = ERes_FromBinaryImage(PContext,  P3DLoader->Buffer, LIPtr,  NULL, &_E3dOldInterestPointNodeComponent_class->Resources,  LInterestPoint,
											 &PContext->ObjectProxies);
//PContext->Verbose = 0;

if(0)
{
	printf("%s() %s:%d  [%p:%s] Components (%s) at %08llx len %08x\n", __FUNCTION__, __FILE__, __LINE__, LNode, LNode->Name, LClass->Name, (long long)LStart, (int)LBinaryImageLen);
	fflush(stdout);
}
										}

										LInfo->InternalFlags &= ~(EComponentREMOVABLE|EComponentEDITABLE);
										E3dConstraintNodeComponent*  LConstraintInfo = (E3dConstraintNodeComponent*)LInfo;
										LConstraintInfo->ConstraintFlags |= E3dConstraintDRIVING;
									}
									else
									{
										if(LNodeComponentID >= 0) _ObjectAddByID(PContext, _ObjectsALLOC_INCREMENT, (EObject*)LInfo, LNodeComponentID);

										LBinaryImageLen = EObject_ResourcesFromBinaryImage((ECoreObject*)LInfo, PContext,  P3DLoader->Buffer, LIPtr,  &PContext->ObjectProxies);

										if(LBinaryImageLen < 0)
										{
											printf("%s() %s:%d  at %s%08llx%s %s[%s//%s]%s error loading: %s%s%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)LStart, ECodeNormal,
											 ECodeYellow, LNode->Name ? LNode->Name : "", LClass->Name, ECodeNormal, ECodeYellow, EResult_ToString(LBinaryImageLen), ECodeNormal);fflush(stdout);
										}
										else if(0)
										{
											printf("%s() %s:%d  at %s%08llx%s len %08llx [%s] Components (%s)\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)LStart, ECodeNormal,
											 (long long)LBinaryImageLen, LNode->Name, LClass->Name);
											fflush(stdout);
										}
									}

									if(LBinaryImageLen > 0)
									{
										LIPtr += LBinaryImageLen;
if(E3dDebugNODE)
{
	printf("%s() %s:%d  [%s] Components (%s) ends at %08llx len %08llx\n",  __FUNCTION__, __FILE__, __LINE__,  LNode->Name, LClass->Name, (long long)(LIPtr - P3DLoader->Buffer),
	 (long long)LBinaryImageLen);fflush(stdout);
}
									}
									else break;  // Error decoding resources, end the for() loop
								}
							}
							else break;  // If we couldn't find the class, we can't decode the resources, so we have to terminate the loop here
						}

						LPtr += LLength;
					}
				}
if(E3dDebugNODE)
{
	printf("\n");fflush(stdout);
}
			}
		}

if(E3dDebugNODE)
{
	if(0)
	{
		printf("%s() %s:%d  LRootNode [%s] hierarchy links at %s%08llx%s\n",  __FUNCTION__, __FILE__, __LINE__,  LRootNode->Name,
		 ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);
		fflush(stdout);
	}
	printf("%s() %s:%d  [%s] hierarchy links at %s%08llx%s  LNNodesInHierarchy %lld\n",  __FUNCTION__, __FILE__, __LINE__,  LRootNode->Name,
	 ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, (long long)LNNodesInHierarchy);
	fflush(stdout);
}

// Now, convert hierarchy link indices to pointers
//
		{
			EIndex  LNodeC = 0;for(;LNodeC < LNNodesInHierarchy;LNodeC++)
			{
				E3dNode*   LNode = P3DLoader->Nodes[*PBaseNodeIndex + LNodeC];
				E3dNode**  LParentP = &LNode->Parent;

				int        LIndex = *(int*)LParentP;
				if(LIndex != -1) { LNode->Parent = P3DLoader->Nodes[LIndex]; }   // E3dNODE_LINKREF
				else LNode->Parent = NULL;

				if(_CDS_FileVersion < 909)
				{
					E3dNode**  LChildP = &LNode->Child;
					E3dNode**  LNextSiblingP = &LNode->NextSibling;

// Keep this like this! Don't move LNode->Child = NULL to before the if! Some compilers have issues because of the pointer aliasing here.
//
					LIndex = *(int*)LChildP;
					if(LIndex != -1) E_AssignToMemberNoChkNoFree(P3DLoader->Nodes[LIndex], LNode,Child);  // E3dNODE_LINKREF
					else LNode->Child = NULL;

					LIndex = *(int*)LNextSiblingP;
					if(LIndex != -1) E_AssignToMemberNoChkNoFree(P3DLoader->Nodes[LIndex], LNode,NextSibling);  // E3dNODE_LINKREF
					else LNode->NextSibling = NULL;
				}

				LNode->Prev = NULL;
				LNode->Next = NULL;
				LNode->PrevSibling = NULL;
			}
		}

// Create Child and NextSibling links from Parent links
//
		if(_CDS_FileVersion >= 909)
		{
			E3dNodeHrc_UpdateChildAndNextSiblingLinks(P3DLoader->Nodes + *PBaseNodeIndex, LNNodesInHierarchy);
		}

		*PBaseNodeIndex += LNNodesInHierarchy;
	}

	if(LRootNode)
	{
		E3dNodeHrc_UpdateConnections(LRootNode);  // Call this, so we can use the Next field...
		E3dNodeHrc_UpdateMatrices(LRootNode, TRUE);
	}
	else
	{
//printf("%s() %s:%d  %d\n", __FUNCTION__, __FILE__, __LINE__, P3DLoader->RootNodes_Count);fflush(stdout);
		*PResultRet = E3dfCOULD_NOT_READ_NODE_HIERARCHY;return(LPtr);
	}

	return(LPtr);
}


const uint8_t* _GetObjectComponents(EResContext* PContext, E3dLoader* P3DLoader, E3dObject* PObject,  const uint8_t* PBuffer, const uint8_t* PPtr)
{
// Read runtime-typed objects
//

// Total length
//
//const EBool  LOldWithNoTotalLen = _CDS_FileVersion < 915;	// E3dFIXME : this is a guess. Find the actual version when we started outputting TotalLen
	const EBool  LOldWithNoTotalLen = FALSE;
	EIndex       LLength;EM_GetInt32I_be(LLength, PPtr);

	if(LOldWithNoTotalLen || LLength)
	{
		const uint8_t*  LPtr = PPtr;
		const uint8_t*  LEndPtr = PPtr + LLength;

if(E3dDebugOBJECT)
{
	#if DebugPOINTERS
		printf("%s() %s:%d  <%s> ObjectComponents found at %s%08llx%s  len: %08llx (til %08llx)\n",  __FUNCTION__, __FILE__, __LINE__,  PObject->Name,
		 ECodeCyan, (long long)(LIPtr - P3DLoader->Buffer), ECodeNormal, (long long)LLength, (long long)(LIPtr - P3DLoader->Buffer + LLength));fflush(stdout);
	#else
		printf(" %s() %s:%d  at %s%08llx%s  ObjectComponents len: %08llx (til %08llx)\n",  __FUNCTION__, __FILE__, __LINE__,
		 ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, (long long)LLength, (long long)(LPtr - P3DLoader->Buffer + LLength));fflush(stdout);
	#endif
}
		for(;LPtr < LEndPtr;)
		{
if(E3dDebugOBJECT) { printf(" %s() %s:%d  at %s%08llx%s  %02x\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - PBuffer), ECodeNormal, *LPtr);fflush(stdout); }

			_GetObjectFlagsAndID(LPtr, LObjectComponentID);

if(E3dDebugOBJECT) { printf(" %s() %s:%d  at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - PBuffer), ECodeNormal);fflush(stdout); }

			E3dObjectComponentClass*  LClass = E3dObjectComponent_ClassByName((char*)LPtr);if(LClass)
			{
				const size_t  LNameLen = strlen(LClass->Name) + 1;

if(E3dDebugOBJECT)
{
	#if DebugPOINTERS
		printf(" %s() %s:%d  at %s%08llx%s  ObjectComponentClass %p:(%s)\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal,
		 LClass, LClass->Name);fflush(stdout);
	#else
		printf(" %s() %s:%d  at %s%08llx%s  ObjectComponentClass (%s)\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal,
		 LClass->Name);fflush(stdout);
	#endif
}

				LPtr += LNameLen;
				if(!LClass->ClassActive) { printf("Manager for ObjectComponent class '%s' is not loaded.\n", LClass->Name);fflush(stdout); }


				E3dObjectComponent*  LInfo = NULL;
				if(LClass == E3dSkinMeshInfo_class) LInfo = E3dObjectComponent_ByClass(PObject, E3dSkinMeshInfo_class, FALSE);

//printf("%s() %s:%d (%s) %p\n", __FUNCTION__, __FILE__, __LINE__, LClass->Name, LInfo);fflush(stdout);

				if(LInfo == NULL) LInfo = E3dObjectComponent_Add(PObject, LClass);
				if(LClass->Resources.Count > 0 && LPtr < LEndPtr)
				{
//printf("ObjectComponentClass '%s' has resources\n", LClass->Name);fflush(stdout);
					if(LInfo)
					{
//printf("Component: %p  Class %p:'%s'\n", LInfo, LInfo->Class, LInfo->Class->Name);fflush(stdout);

						PContext->UnrecognizedResources_Count = 0;
						if(LObjectComponentID >= 0) _ObjectAddByID(PContext, _ObjectsALLOC_INCREMENT, (EObject*)LInfo, LObjectComponentID);

//PContext->Verbose = 1;
						const int  LBinaryImageLen = EObject_ResourcesFromBinaryImage((ECoreObject*)LInfo, PContext,  P3DLoader->Buffer, LPtr,  &PContext->ObjectProxies);
//PContext->Verbose = 0;

if(PContext->UnrecognizedResources_Count)
{
	printf("%s() %s:%d  Unrecognized Resource in <%s>\n", __FUNCTION__, __FILE__, __LINE__, PObject->Name);fflush(stdout);
}

						if(LBinaryImageLen > 0) LPtr += LBinaryImageLen;
						else break;    // Error decoding resources, end the for() loop

//printf("Component: %p  Class %p:'%s'\n", LInfo, LInfo->Class, LInfo->Class->Name);fflush(stdout);

//printf("<%s> ObjectComponent: '%s'  length: %d (til %08x)\n", PObject->Name, LClass->Name, LBinaryImageLen, LEndPtr - P3DLoader->Buffer);fflush(stdout);
					}
				}
			}
			else break;  // If we couldn't find the class, we can't safely decode the resources, so we have to terminate the loop here
		}

		if(LOldWithNoTotalLen) PPtr = LPtr;
		else PPtr += LLength;
	}

	return(PPtr);
}


#ifdef CfOLD
static E3dLight* _LightLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t** PPtrP, char* PName)
{
	const uint8_t*  LPtr = *PPtrP;

	E3dLight*  LLight;E_New(E3dLight, LLight);
	if(LLight == NULL) return(0);

	EObject_SetName(LLight, PName, FALSE);

	if(_CDS_FileVersion >= 908)
	{
		int  LLength;

		EM_GetInt32I_be(LLength, LPtr);

		ERes_FromBinaryImage(PContext, P3DLoader->Buffer, LPtr,  NULL, &E3dLight_class->Resources, LLight,  &PContext->ObjectProxies);
		LPtr += LLength;
	}
	else
	{
		int  LType;EM_GetInt32I_be(LType, LPtr);

		LLight->Type = LType;

		{
			float  LR, LG, LB;
			EM_GetFloat32I_be(LR, LPtr);
			EM_GetFloat32I_be(LG, LPtr);
			EM_GetFloat32I_be(LB, LPtr);
			EcColor_SetRGB(&LLight->Color,  LR, LG, LB);
		}

		EM_GetFloat32I_be(LLight->AttenuationConstant, LPtr);
		EM_GetFloat32I_be(LLight->AttenuationLinear, LPtr);
		EM_GetFloat32I_be(LLight->AttenuationQuadratic, LPtr);
		EM_GetFloat32I_be(LLight->ShadowBrightness, LPtr);
	}

	*PPtrP = LPtr;
	return(LLight);
}
#endif


static EIndex _VertexGeometry_TextureMappersLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t* PPtr,  E3dVertexGeometry* PVertexGeometry)
{
	const uint8_t*  LPtr = PPtr;

// Total length
//
	int  LLength;EM_GetInt32I_be(LLength, LPtr);if(LLength)
	{
//printf("%s() %s:%d  <%s%s%s> Mesh TextureMappers  Length %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, PMesh->Name, ECodeNormal, (long long)LLength);fflush(stdout);
		const uint8_t*  LIPtr = LPtr;
		const uint8_t*  LEndPtr = LPtr + LLength;

		for(;LIPtr < LEndPtr;)
		{
//printf("%s() %s:%d    %sGREEN%s  TextureMapper P%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (char*)LIPtr);fflush(stdout);
			E3dTextureMapperClass*  LClass = E3dTextureMapper_ClassByName((char*)LIPtr);if(LClass)
			{
				const size_t  LNameLen = strlen(LClass->Name) + 1;

//printf("%s() %s:%d    %sGREEN%s  TextureMapper P%s found\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (char*)LIPtr);fflush(stdout);

				LIPtr += LNameLen;
				if(!LClass->ClassActive) { printf("Manager for TextureMapper class '%s' is not loaded.\n", LClass->Name);fflush(stdout); }

				E3dTextureMapper*  LMapper = E3dTextureMapper_New(LClass);if(LMapper)
				{
					ELst_ObjectAdd_om_s(PVertexGeometry,TextureMappers, LMapper);
//printf("%s() %s:%d    %sMapper%s %s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LClass->Name);fflush(stdout);
				}

				if(LIPtr < LEndPtr)
				{
					if(LClass->Resources.Count > 0)
					{
//printf("%s() %s:%d    %sHas Resources%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);
						EResClassEntry*  LClassEntry = EList_ClassEntryFind_s(PContext->Classes, (ECoreClass*)LClass);

if(E3dDebugPOLYGROUP) { printf("%s() %s:%d  %p %s %p\n", __FUNCTION__, __FILE__, __LINE__, LClass, LClass->Name, LClassEntry);fflush(stdout); }

						const int  LBinaryImageLen = ERes_FromBinaryImage(PContext, P3DLoader->Buffer, LIPtr,  LClassEntry ? LClassEntry->AllResources : NULL, &LClass->Resources,
						 LMapper,  &PContext->ObjectProxies);
if(0)
{
	printf("Class: %s at%s%08llx%s  length: %d (til %s%08llx%s)\n", LClass->Name,  ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeCyan, LBinaryImageLen,
	 ECodeCyan, (long long)(LEndPtr - P3DLoader->Buffer), ECodeCyan);
	fflush(stdout);
}

//printf("%s() %s:%d    %sGREEN%s  %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LBinaryImageLen);fflush(stdout);
						if(LBinaryImageLen > 0) LIPtr += LBinaryImageLen;
						else break;    // Error decoding resources, end the for() loop
					}

				}
//printf("%s() %s:%d  %sGREEN%s  Geo %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, PPolyGroup->Geometry);fflush(stdout);
//				if(LClass->Update) LClass->Update(LMapper,  PMesh->Vertices, PMesh->Vertices.Count,  PMesh->VertexSize, (EObject*)PPolyGroup);
//				E3dTextureMapper_SetTiling(LMapper);
			}
			else break;  // If we couldn't find the class, we can't safely decode the Resources, so we have to terminate the loop here
		}

		LPtr += LLength;
	}

	return(LPtr - PPtr);
}


static const uint8_t* _PointSetLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t* PPtr, const uint32_t PFeatureFlags, E3dPointSet* PPointSet)
{
	const uint8_t*  LPtr = PPtr;

// Index of Material
//
	int  LIndex;EM_GetInt32I_be(LIndex, LPtr);
	if(LIndex < -1 || LIndex >= P3DLoader->Materials_Count) return(NULL);	// Error
	if(LIndex == -1 || P3DLoader->Materials == NULL || LIndex >= P3DLoader->Materials_Count) PPointSet->Material = NULL;
	else
	{
		E_AssignToMember(P3DLoader->Materials[LIndex], PPointSet,Material);
	}

// Load TextureMappers
//
	if(PFeatureFlags & CfVertexGeometryTEXTUREMAPPERS)
	{
		LPtr += _VertexGeometry_TextureMappersLoad(PContext, P3DLoader, LPtr,  (E3dVertexGeometry*)PPointSet);
	}

	EIndex  LNumOfPoints;EM_GetInt32I_be(LNumOfPoints, LPtr);

	if(PFeatureFlags & CfPointSetCUSTOM_POINT_ATTRIBUTES)
	{
		uint16_t  LNCustomVertexAttributes = 0;EM_GetInt16I_be(LNCustomVertexAttributes, LPtr);
		E3dVertexSize*  LVAOffsets = NULL;

//printf("%s() %s:%d  %sPFeatureFlags%s  %08x  %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, PFeatureFlags, LNCustomVertexAttributes);fflush(stdout);

		if(LNCustomVertexAttributes > 0)
		{
if(E3dDebugPOINTSET)
{
	printf("%s() %s:%d  <%s%s%s> LNCustomVertexAttributes %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, PPointSet->Name, ECodeNormal, (long long)LNCustomVertexAttributes);
	fflush(stdout);
}
			ENewN(LNCustomVertexAttributes, LVAOffsets);
			EIndex  LVAI = 0;for(;LVAI < LNCustomVertexAttributes;LVAI++)
			{
				char*     LSemantic = _Cf_GetString(&LPtr);const char*  LSemanticBuiltIn = E3dVertexNode_SemanticGetBuiltIn(LSemantic);
				char*     LName = _Cf_GetString(&LPtr);
				uint16_t  LType;EM_GetInt16I_be(LType, LPtr);
				uint16_t  LCount;EM_GetInt16I_be(LCount, LPtr);

				E3dPointSet_PointAttributeAdd(PPointSet,  LSemanticBuiltIn, LName, LType, LCount,  NULL);

				LVAOffsets[LVAI] = EResource_OffsetByIndex(&PPointSet->VertexAttributes,  LVAI + E3dVertexGeometry_DefaultVertexAttributes.Count);

if(E3dDebugPOINTSET)
{
	printf("%s() %s:%d  %s%s%s %s  %d %d  %d\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, LSemantic ? (char*)LSemantic : "-", ECodeNormal, LName ? LName : "-",
	 LType, LCount, LVAOffsets[LVAI]);
	fflush(stdout);
}
				EFreeChkClr(LSemantic);
				EFreeChkClr(LName);
			}
if(E3dDebugPOINTSET) { printf("\n");fflush(stdout); }
		}

		if(LNumOfPoints > 0)
		{
			const E3dVertexSize  LStride = PPointSet->VertexSize;
			E3dVertex*           LPoint = E3dVertices_New(LStride, LNumOfPoints, FALSE);

			PPointSet->Vertices.Items = LPoint;
			PPointSet->Vertices.Count = PPointSet->Vertices.CountAllocated = LNumOfPoints;

			const EResource*     LVAStart = PPointSet->VertexAttributes.Items + E3dVertexGeometry_DefaultVertexAttributes.Count;

if(E3dDebugPOINTSET) { printf("%s() %s:%d  <%s%s%s>\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, PPointSet->Name, ECodeNormal);fflush(stdout); }

			EIndex  LPI = 0;for(;LPI < LNumOfPoints;LPI++, EStruct_Inc(LPoint, LStride))
			{
				EM_GetFloat32I_be(LPoint->Position.X, LPtr);
				EM_GetFloat32I_be(LPoint->Position.Y, LPtr);
				EM_GetFloat32I_be(LPoint->Position.Z, LPtr);

				{
					EIndex  LVAI = 0;const EResource*  LVA = LVAStart;for(;LVAI < LNCustomVertexAttributes;LVAI++, LVA++)
					{
if(E3dDebugPOINTSET) { printf(" %s%s%s %s:", ECodeGreen, LVA->Semantic ? (char*)LVA->Semantic : "-", ECodeNormal,  LVA->Name ? LVA->Name : "-");fflush(stdout); }

						const E3dVertexSize  LOffset = LVAOffsets[LVAI];
						switch(LVA->Type)
						{
							case EResUINT8:
								{
									uint8_t*  LArray = (uint8_t*)LPoint + LOffset;
									const EIndex  LN = LVA->Ext.Count;
if(E3dDebugPOINTSET) { EIndex  LI = 0;for(;LI < LN;LI++) { LArray[LI] = *LPtr++;printf(" %d", LArray[LI]); } }
else
{
									memcpy(LArray, LPtr, LN);LPtr += LN;
}
								}
							break;

							case EResFLOAT32:
								{
									float*  LArray = (float*)((uint8_t*)LPoint + LOffset);
									const EIndex  LN = LVA->Ext.Count;
if(E3dDebugPOINTSET) { EIndex  LI = 0;for(;LI < LN;LI++) { EM_GetFloat32I_be(LArray[LI], LPtr);printf(" %f", LArray[LI]); } }
else
{
									EIndex  LI = 0;for(;LI < LN;LI++) EM_GetFloat32I_be(LArray[LI], LPtr);
}
								}
							break;

							case EResFLOAT32_2:
								{
									float*  LArray = (float*)((uint8_t*)LPoint + LOffset);
									EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 2)
									{ EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr); }
								}
							break;

							case EResFLOAT32_3:
								{
									float*  LArray = (float*)((uint8_t*)LPoint + LOffset);
									EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 3)
									{ EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr);EM_GetFloat32I_be(LArray[LI + 2], LPtr); }
								}
							break;

							case EResFLOAT32_4:
								{
									float*  LArray = (float*)((uint8_t*)LPoint + LOffset);
									EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 4)
									{ EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr);EM_GetFloat32I_be(LArray[LI + 2], LPtr);EM_GetFloat32I_be(LArray[LI + 3], LPtr); }
								}
							break;

							default:
								{
char  s[512];printf("%s() %s:%d  %sIMPLEMENT ME%s  %s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal, ERes_TypeToString(LVA->Type, s, 511));fflush(stdout);
								}
							break;
						}
					}
if(E3dDebugPOINTSET) { if(LNCustomVertexAttributes > 0) { printf("\n");fflush(stdout); } }
				}
			}
		}
		EFreeChkClr(LVAOffsets);
	}
	else
	{
// Old versions had a built-in color point attribute
//
		{ EResource*  LColorResource = NULL;E3dPointSet_PointAttributeAdd(PPointSet,  E3dVtxAttribute_Color, "Color", EResFLOAT32, 4,  &LColorResource); }
		const EIndex  LColorOffset = EResource_OffsetByType(&PPointSet->VertexAttributes,  E3dVtxAttribute_Color, "Color", EResFLOAT32, 4, 0,  NULL);

		if(LNumOfPoints)
		{
			const E3dVertexSize  LStride = PPointSet->VertexSize;
			E3dVertex*           LPoint = E3dVertices_New(LStride, LNumOfPoints, FALSE);

			PPointSet->Vertices.Items = LPoint;
			PPointSet->Vertices.Count = PPointSet->Vertices.CountAllocated = LNumOfPoints;

			EIndex  LI = 0;for(;LI < LNumOfPoints;LI++, EStruct_Inc(LPoint, LStride))
			{
				EM_GetFloat32I_be(LPoint->Position.X, LPtr);
				EM_GetFloat32I_be(LPoint->Position.Y, LPtr);
				EM_GetFloat32I_be(LPoint->Position.Z, LPtr);

				{
					float*  LPointColor = (float*)((uint8_t*)LPoint + LColorOffset);E_Set4_a(LPointColor, 0.2, 1.0, 0.2, 1.0);
					EM_GetFloat32I_be(LPointColor[0], LPtr);
					EM_GetFloat32I_be(LPointColor[1], LPtr);
					EM_GetFloat32I_be(LPointColor[2], LPtr);
					EM_GetFloat32I_be(LPointColor[3], LPtr);
				}
			}
		}
	}

//printf("Mtl %d  NP %d\n", LIndex, LNumOfPoints);fflush(stdout);

	return(LPtr);
}


#ifdef CfOLD
static inline int _SplineSegmentType(const uint16_t PI)
{
	switch(PI)
	{
		case CfSegmentBEZIER:   return(E3dSplineBEZIER);
		case CfSegmentLINEAR:   return(E3dSplineLINEAR);
		case CfSegmentARC:      return(E3dSplineARC);
		case CfSegmentHERMITE:  return(E3dSplineHERMITE);
		default:                return(E3dSplineLINEAR);
	}
}
#endif


static const uint8_t* _GetSplineCVs(const uint8_t* PPtr, const int PSplineType, E3dSplineCV** PCVsP, EIndex* PNumOfCVsP)
{
	int  LNumOfCVs;EM_GetInt32I_be(LNumOfCVs, PPtr);

	if(_CDS_FileVersion >= 927)
	{
		uint16_t  LCVContentFlags;EM_GetUInt16I_be(LCVContentFlags, PPtr);

		if(LNumOfCVs)
		{
			E3dSplineCV*  LCVs = E3dSplineCVs_New(PSplineType, LNumOfCVs);if(LCVs)
			{
				E3dSplineCV*	LCV = LCVs;

				*PCVsP = LCVs;

				EIndex  LI = 0;for(;LI < LNumOfCVs;LI++, LCV++)
				{
#ifdef CfOLD
					uint16_t  LIdx;EM_GetUInt16I_be(LIdx, PPtr);LCV->Segment.Shape = _SplineSegmentType(LIdx);
#else
					EM_GetUInt16I_be(LCV->Segment.Shape, PPtr);
#endif
					if(LCVContentFlags & E3dCVsPOSITION)
					{
						EM_GetFloat32I_be(LCV->Position.X, PPtr);
						EM_GetFloat32I_be(LCV->Position.Y, PPtr);
						EM_GetFloat32I_be(LCV->Position.Z, PPtr);
					}

					if(LCVContentFlags & E3dCVsPREVIOUS)
					{
						EM_GetFloat32I_be(LCV->Previous.X, PPtr);
						EM_GetFloat32I_be(LCV->Previous.Y, PPtr);
						EM_GetFloat32I_be(LCV->Previous.Z, PPtr);
					}
					else LCV->Previous = LCV->Position;

					if(LCVContentFlags & E3dCVsNEXT)
					{
						EM_GetFloat32I_be(LCV->Next.X, PPtr);
						EM_GetFloat32I_be(LCV->Next.Y, PPtr);
						EM_GetFloat32I_be(LCV->Next.Z, PPtr);
					}
					else LCV->Next = LCV->Position;

					if(LCVContentFlags & E3dCVsNORMAL)
					{
						EM_GetFloat32I_be(LCV->Normal.X, PPtr);
						EM_GetFloat32I_be(LCV->Normal.Y, PPtr);
						EM_GetFloat32I_be(LCV->Normal.Z, PPtr);
					}

					if(LCVContentFlags & E3dCVsCV_FLAGS)
					{
						EM_GetInt16I_be(LCV->CVFlags, PPtr);
					}

					if(LCVContentFlags & E3dCVsSEGMENT_CONSTRAINT)
					{
						EM_GetInt16I_be(LCV->Constraint, PPtr);
					}

					if(LCVContentFlags & E3dCVsSEGMENT_FLAGS)
					{
						EM_GetInt16I_be(LCV->Segment.Flags, PPtr);
					}

					if(LCVContentFlags & E3dCVsSEGMENT_NUMOFLINSTEPS)
					{
						EM_GetInt32I_be(LCV->Segment.NumOfLinSteps, PPtr);
					}

					if(LCVContentFlags & E3dCVsSEGMENT_ARCRADIUS)
					{
						EM_GetFloat32I_be(LCV->Segment.Radius, PPtr);
					}
				}
			}
		}
		else
		{
			printf("%s() %s:%d  %sWARNING%s: Spline with 0 CVs!\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
		}
	}
#ifdef CfOLD
	else
	{
		if(LNumOfCVs)
		{
			E3dSplineCV*  LCVs = E3dSplineCVs_New(PSplineType, LNumOfCVs);if(LCVs)
			{
				E3dSplineCV*  LCV = LCVs;

				*PCVsP = LCVs;

				switch(PSplineType)
				{
					caseE3dSplineHAS_TANGENTS:
						{
							EIndex  LI = 0;for(;LI < LNumOfCVs;LI++, LCV++)
							{
								if(_CDS_FileVersion >= 904)
								{
									int  LIdx;EM_GetInt32I_be(LIdx, PPtr);LCV->Segment.Shape = _SplineSegmentType(LIdx);
								}

								EM_GetFloat32I_be(LCV->Previous.X, PPtr);
								EM_GetFloat32I_be(LCV->Previous.Y, PPtr);
								EM_GetFloat32I_be(LCV->Previous.Z, PPtr);

								EM_GetFloat32I_be(LCV->Position.X, PPtr);
								EM_GetFloat32I_be(LCV->Position.Y, PPtr);
								EM_GetFloat32I_be(LCV->Position.Z, PPtr);

								EM_GetFloat32I_be(LCV->Next.X, PPtr);
								EM_GetFloat32I_be(LCV->Next.Y, PPtr);
								EM_GetFloat32I_be(LCV->Next.Z, PPtr);

								if(_CDS_FileVersion >= 922)
								{
									EM_GetFloat32I_be(LCV->Normal.X, PPtr);
									EM_GetFloat32I_be(LCV->Normal.Y, PPtr);
									EM_GetFloat32I_be(LCV->Normal.Z, PPtr);
								}

								if(_CDS_FileVersion >= 914)
								{
									EM_GetInt16I_be(LCV->Constraint, PPtr);
									EM_GetInt16I_be(LCV->CVFlags, PPtr);
									EM_GetInt16I_be(LCV->Segment.Flags, PPtr);
									EM_GetInt32I_be(LCV->Segment.NumOfLinSteps, PPtr);
								}
								else if(_CDS_FileVersion >= 912)
								{
									EM_GetInt16I_be(LCV->Constraint, PPtr);
									EM_GetInt16I_be(LCV->CVFlags, PPtr);
									EM_GetInt32I_be(LCV->Segment.NumOfLinSteps, PPtr);
								}
								else
								{
									EM_GetInt32I_be(LCV->Constraint, PPtr);
									LCV->Segment.Flags = E3dSegmentDEFAULT;

									if(_CDS_FileVersion < 904)
									{
										int  LType;EM_GetInt32I_be(LType, PPtr);switch(LType)
										{
											case CfSegmentBEZIER:   LCV->Segment.Shape = E3dSplineBEZIER;break;
											case CfSegmentLINEAR:   LCV->Segment.Shape = E3dSplineLINEAR;break;
											case CfSegmentARC:      LCV->Segment.Shape = E3dSplineARC;break;
											case CfSegmentHERMITE:  LCV->Segment.Shape = E3dSplineHERMITE;break;
											default:                LCV->Segment.Shape = E3dSplineLINEAR;break;
										}
									}

									EM_GetInt32I_be(LCV->Segment.NumOfLinSteps, PPtr);
								}
							}
						}
					break;

					default:
						{
							int  LI = 0;for(;LI < LNumOfCVs;LI++, LCV++)
							{
								if(_CDS_FileVersion >= 912)
								{
									int  LType;EM_GetInt32I_be(LType, PPtr);switch(LType)
									{
									 case CfSegmentBEZIER:   LCV->Segment.Shape = E3dSplineBEZIER;break;
									 case CfSegmentLINEAR:   LCV->Segment.Shape = E3dSplineLINEAR;break;
									 case CfSegmentARC:      LCV->Segment.Shape = E3dSplineARC;break;
									 case CfSegmentHERMITE:  LCV->Segment.Shape = E3dSplineHERMITE;break;
									 default:                LCV->Segment.Shape = E3dSplineLINEAR;break;
									}
								}

								EM_GetFloat32I_be(LCV->Position.X, PPtr);
								EM_GetFloat32I_be(LCV->Position.Y, PPtr);
								EM_GetFloat32I_be(LCV->Position.Z, PPtr);

								if(_CDS_FileVersion >= 922)
								{
									EM_GetFloat32I_be(LCV->Normal.X, PPtr);
									EM_GetFloat32I_be(LCV->Normal.Y, PPtr);
									EM_GetFloat32I_be(LCV->Normal.Z, PPtr);
								}

								if(_CDS_FileVersion >= 912)
								{
									EM_GetInt16I_be(LCV->CVFlags, PPtr);
									if(_CDS_FileVersion >= 920) EM_GetInt16I_be(LCV->Segment.Flags, PPtr);
									EM_GetInt32I_be(LCV->Segment.NumOfLinSteps, PPtr);
								}
								else
								{
									LCV->Segment.Flags = E3dSegmentDEFAULT;
									EM_GetInt32I_be(LCV->Segment.NumOfLinSteps, PPtr);
								}
							}
						}
					break;
				}
			}
		}
		else
		{
			printf("%s() %s:%d  %sWARNING%s: Spline with 0 CVs!\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
		}
	}
#endif  // CfOLD

	*PNumOfCVsP = LNumOfCVs;

	return(PPtr);
}


static const uint8_t* _SplineLoad(const uint8_t* PPtr, E3dSpline* PSpline)
{
	if(PSpline == NULL) return(PPtr);

#ifdef CfOLD
	int  LType;EM_GetInt32I_be(LType, PPtr);

	switch(LType)
	{
		case CDS_SPL_LINEAR:    LType = E3dSplineLINEAR;break;
		case CDS_SPL_BEZIER:    LType = E3dSplineBEZIER;break;
		case CDS_SPL_BSPLINE:   LType = E3dSplineBSPLINE;break;
		case CDS_SPL_NURBS:     LType = E3dSplineNURBS;break;
		case CDS_SPL_CARDINAL:  LType = E3dSplineCARDINAL;break;
		case CDS_SPL_HERMITE:   LType = E3dSplineHERMITE;break;
	}
#else
	uint16_t  LType;EM_GetInt16I_be(LType, PPtr);
#endif

	{
		PSpline->SplineType = LType;

		if(_CDS_FileVersion >= 915)
		{
			EM_GetInt32I_be(PSpline->NumOfLinSteps, PPtr);
			EM_GetInt32I_be(PSpline->NumOfStepsForLength, PPtr);

			const uint8_t  LCh = *(PPtr++);

			if(LCh & 0x01) PSpline->Closed = TRUE;
			else PSpline->Closed = FALSE;
			if(LCh & 0x02) PSpline->UniformLinSteps = TRUE;
			else PSpline->UniformLinSteps = FALSE;

			PPtr = _GetSplineCVs(PPtr, LType, &PSpline->CVs.Items, &PSpline->CVs.Count);PSpline->CVs.CountAllocated = PSpline->CVs.Count;
		}
#ifdef CfOLD
		else
		{
			int  LNumOfCVs;EM_GetInt32I_be(LNumOfCVs, PPtr);

			PSpline->CVs.Count = PSpline->CVs.CountAllocated = LNumOfCVs;
			EM_GetInt32I_be(PSpline->NumOfLinSteps, PPtr);

			const uint8_t  LCh = *(PPtr++);

			if(LCh & 0x01) PSpline->Closed = TRUE;
			else PSpline->Closed = FALSE;
			if(LCh & 0x02) PSpline->UniformLinSteps = TRUE;
			else PSpline->UniformLinSteps = FALSE;

			int  LCVArrayIndex;EM_GetInt32I_be(LCVArrayIndex, PPtr);

			if((LCVArrayIndex < 0) || (LCVArrayIndex >= _SplineCVArrays_Count))
			{
				return(PPtr);
			}

			PSpline->CVs.Items = _SplineCVArrays[LCVArrayIndex].CVs;
		}
#endif  // CfOLD
	}

	return(PPtr);
}


#define _GetFloat32IC(v, buffer) EM_GetFloat32I_be(v, buffer);if(!EFinite(v)) v = 0.0

#define _M_GetVertexNode(mVertexNode) do\
{\
	LNVertices++;\
\
	if(LVertexNormal)\
	{\
		EM_GetFloat32I_be(LVertexNormal->X, LPtr);EM_GetFloat32I_be(LVertexNormal->Y, LPtr);EM_GetFloat32I_be(LVertexNormal->Z, LPtr);EStruct_Inc(LVertexNormal, LVertexNodeSize);\
	}\
\
	if(LColorResource)\
	{\
		const int  LOffset = (EPointerInt)LColorResource->Address;\
		switch(LColorResource->Type)\
		{\
			case EResFLOAT32:\
				{\
					float*  lPtr = (float*)((uint8_t*)LVertexNode + LOffset);\
					_GetFloat32IC(lPtr[0], LPtr);\
					_GetFloat32IC(lPtr[1], LPtr);\
					_GetFloat32IC(lPtr[2], LPtr);\
					_GetFloat32IC(lPtr[3], LPtr);\
				}\
			break;\
\
			case EResUINT8:\
				{\
					uint8_t*  lPtr = (uint8_t*)LVertexNode + LOffset;\
					memcpy(lPtr, LPtr, 4);\
				}\
			break;\
		}\
	}\
	if(LST0)      { E3dST_Init(LST0);_GetFloat32IC(LST0->S, LPtr);_GetFloat32IC(LST0->T, LPtr);EStruct_Inc(LST0, LVertexNodeSize); }\
	if(LST1)      { E3dST_Init(LST1);_GetFloat32IC(LST1->S, LPtr);_GetFloat32IC(LST1->T, LPtr);EStruct_Inc(LST1, LVertexNodeSize); }\
	if(LST2)      { E3dST_Init(LST2);_GetFloat32IC(LST2->S, LPtr);_GetFloat32IC(LST2->T, LPtr);EStruct_Inc(LST2, LVertexNodeSize); }\
	if(LST3)      { E3dST_Init(LST3);_GetFloat32IC(LST3->S, LPtr);_GetFloat32IC(LST3->T, LPtr);EStruct_Inc(LST3, LVertexNodeSize); }\
	if(LST4)      { E3dST_Init(LST4);_GetFloat32IC(LST4->S, LPtr);_GetFloat32IC(LST4->T, LPtr);EStruct_Inc(LST4, LVertexNodeSize); }\
	if(LST5)      { E3dST_Init(LST5);_GetFloat32IC(LST5->S, LPtr);_GetFloat32IC(LST5->T, LPtr);EStruct_Inc(LST5, LVertexNodeSize); }\
	if(LST6)      { E3dST_Init(LST6);_GetFloat32IC(LST6->S, LPtr);_GetFloat32IC(LST6->T, LPtr);EStruct_Inc(LST6, LVertexNodeSize); }\
	if(LST7)      { E3dST_Init(LST7);_GetFloat32IC(LST7->S, LPtr);_GetFloat32IC(LST7->T, LPtr);EStruct_Inc(LST7, LVertexNodeSize); }\
	if(LTangent)  { _GetFloat32IC(LTangent->X, LPtr);_GetFloat32IC(LTangent->Y, LPtr);_GetFloat32IC(LTangent->Z, LPtr);EStruct_Inc(LTangent, LVertexNodeSize); }\
	if(LUV)       { _GetFloat32IC(LUV->X, LPtr);_GetFloat32IC(LUV->Y, LPtr);EStruct_Inc(LUV, LVertexNodeSize); }\
} while(0)

#if E3dDebugVERTEX
#define _PrintVertexAttrValue(pFV)      do { printf(" %f", pFV);fflush(stdout); } while(0)
#define _PrintVertexAttrValueNewLine()  do { printf("\n");fflush(stdout); } while(0)
#else
#define _PrintVertexAttrValue(pFV)
#define _PrintVertexAttrValueNewLine()
#endif

#define _VertexNodeLoad(pVertexNode) do\
{\
	EIndex  lVAI = 0;const EResource*  lVA = LVAs;for(;lVAI < LNCustomVertexAttributes;lVAI++, lVA++)\
	{\
		const E3dVertexSize  lOffset = LVAOffsets[lVAI];\
		switch(lVA->Type)\
		{\
			case EResFLOAT32:\
				{\
if(0)\
{\
	printf("%s() %s:%d      %s%s %s%s %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, (char*)lVA->Semantic, lVA->Name, ECodeNormal, (long long)lVA->Ext.Count);fflush(stdout);\
}\
					float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
					EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI++) { EM_GetFloat32I_be(LArray[lI], LPtr);_PrintVertexAttrValue(LArray[lI]); }\
				}\
			break;\
\
			case EResFLOAT64:\
				{\
if(0)\
{\
	printf("%s() %s:%d      %s%s %s%s %lld\n",  __FUNCTION__, __FILE__, __LINE__, ECodeGreen, (char*)lVA->Semantic, lVA->Name, ECodeNormal, (long long)lVA->Ext.Count);\
	fflush(stdout);\
}\
					float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
					EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI++) { EM_GetFloat32I_be(LArray[lI], LPtr);_PrintVertexAttrValue(LArray[lI]); }\
				}\
			break;\
\
			case EResFLOAT32_2:\
				{\
					float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
					EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI += 2)\
					{ EM_GetFloat32I_be(LArray[lI], LPtr);EM_GetFloat32I_be(LArray[lI + 1], LPtr);_PrintVertexAttrValue(LArray[lI]);_PrintVertexAttrValue(LArray[lI + 1]); }\
				}\
			break;\
\
			case EResFLOAT32_3:\
				{\
					float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
					EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI += 3)\
					{ EM_GetFloat32I_be(LArray[lI], LPtr);EM_GetFloat32I_be(LArray[lI + 1], LPtr);EM_GetFloat32I_be(LArray[lI + 2], LPtr); }\
				}\
			break;\
\
			case EResFLOAT32_4:\
				{\
					float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
					EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI += 4)\
					{\
						EM_GetFloat32I_be(LArray[lI], LPtr);EM_GetFloat32I_be(LArray[lI + 1], LPtr);EM_GetFloat32I_be(LArray[lI + 2], LPtr);EM_GetFloat32I_be(LArray[lI + 3], LPtr);\
						_PrintVertexAttrValue(LArray[lI]);_PrintVertexAttrValue(LArray[lI + 1]);_PrintVertexAttrValue(LArray[lI + 2]);_PrintVertexAttrValue(LArray[lI + 3]);\
					}\
				}\
			break;\
\
			case EResSTRUCT:\
/*printf("%s() %s:%d      %sSTRUCT%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);*/\
				if(lVA->Semantic == E3dVtxAttribute_ST)\
				{\
/*printf("%s() %s:%d        %sST%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);*/\
					E3dST*  LST = (E3dST*)((uint8_t*)pVertexNode + lOffset);\
					EM_GetFloat32I_be(LST->S, LPtr);\
					EM_GetFloat32I_be(LST->T, LPtr);\
					LST->Flags = E3dVtxVISIBLE;\
/* Texture-space tangents are not saved for now, as they are usually re-generated. But, we want to initialize them to avoid potential rendering issues and Valgrind warnings. */\
					LST->Tangent = E3dCoord3_s(1.0, 0.0, 0.0);\
/*\
					EM_GetFloat32I_be(LST->Tangent.X, LPtr);\
					EM_GetFloat32I_be(LST->Tangent.Y, LPtr);\
					EM_GetFloat32I_be(LST->Tangent.Z, LPtr);\
*/\
					_PrintVertexAttrValue(LST->S);_PrintVertexAttrValue(LST->T);\
				}\
				else\
				{\
					char  s[256];printf("%s() %s:%d   %s%s%s:%s%s%s  %sIMPLEMENT ME%s Type 0x%04x (%s)\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, (char*)lVA->Semantic, ECodeNormal,\
					 ECodeGreen, lVA->Name, ECodeNormal,  ECodeYellow, ECodeNormal, lVA->Type, ERes_TypeToString(lVA->Type, s, 256));fflush(stdout);\
				}\
			break;\
\
			default:\
				{\
					char  s[256];printf("%s() %s:%d  %s%s%s:%s%s%s  %sIMPLEMENT ME%s Type 0x%04x (%s)\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, (char*)lVA->Semantic, ECodeNormal,\
					 ECodeGreen, lVA->Name, ECodeNormal,  ECodeYellow, ECodeNormal, lVA->Type, ERes_TypeToString(lVA->Type, s, 256));fflush(stdout);\
				}\
			break;\
		}\
		_PrintVertexAttrValueNewLine();\
	}\
} while(0)



//================================================================================
// Load core PolyGroup data such as primitive list and VertexAttribute info
//================================================================================
static const uint8_t* _PolyGroupCoreLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t PVertexIndexBits, const uint32_t PFeatureFlags,
 const uint8_t* PBuffer, const uint8_t* PPtr,  E3dVertexGeometry* PVertexGeometry,  E3dPolyGroup* PPolyGroup)
{
	const uint8_t*  LPtr = PPtr;

if(E3dDebugPOLYGROUP) { printf(" %s() %s:%d %p <%s>\n", __FUNCTION__, __FILE__, __LINE__, PPolyGroup, PPolyGroup->Name);fflush(stdout); }

	uint16_t        LNCustomVertexAttributes = 0;
	EListI_s(, EResource, LCustomVertexAttributes);
	EBool           LNeedVAConversion = FALSE;
	E3dVertexSize*  LVAOffsets = NULL;

	if(_CDS_FileVersion >= 911)
	{
//PContext->Verbose = 2;
//printf("%s() %s:%d <%s>\n", __FUNCTION__, __FILE__, __LINE__, PPolyGroup->Name);fflush(stdout);

//printf("%s() %s:%d  at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - P3DLoader->Buffer));fflush(stdout);
		const int  LLen = EObject_ResourcesFromBinaryImage((ECoreObject*)PPolyGroup, PContext, PBuffer, LPtr,  &PContext->ObjectProxies);

//PContext->Verbose = 0;

//PPolyGroup->VertexNormalType = E3dNormalPROCESS;

		if(LLen >= 0) LPtr += LLen;
		else
		{
printf("%s() %s:%d  %sERROR%s reading ObjectComponents for PolyGroup <<%s>>\n", __FUNCTION__, __FILE__, __LINE__, ECodeRed, ECodeNormal, PPolyGroup->Name);fflush(stdout);
		}
//printf("%s() %s:%d  at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - P3DLoader->Buffer));fflush(stdout);

		if(_CDS_FileVersion < 919)
		{
//printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
			PPolyGroup->EdgeFlags &= ~(E3dSELECTED_EDGES);  // Before v919 there was no way to save Edge selection etc. information...
		}

		int  LMaterialIndex = -1;
		if(PFeatureFlags & CfPolyGroupCUSTOM_VERTEX_ATTRIBUTES)
		{
			EM_GetInt32I_be(LMaterialIndex, LPtr);

			EM_GetInt16I_be(LNCustomVertexAttributes, LPtr);
			ENewNClr(LNCustomVertexAttributes, LCustomVertexAttributes.Items);
			LCustomVertexAttributes.Count = LNCustomVertexAttributes;

if(E3dDebugPOLYGROUP) { printf("%s() %s:%d  <%s%s%s> VertexAttributes %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, PPolyGroup->Name, ECodeNormal, LNCustomVertexAttributes);fflush(stdout); }
			ENewN(LNCustomVertexAttributes, LVAOffsets);
			EIndex  LVAI = 0;EResource*  LSrcVA = LCustomVertexAttributes.Items;for(;LVAI < LNCustomVertexAttributes;LVAI++, LSrcVA++)
			{
				char*     LSemantic = _Cf_GetString(&LPtr);
				char*     LName = _Cf_GetString(&LPtr);
				uint16_t  LType;EM_GetInt16I_be(LType, LPtr);(void)LType;
				uint16_t  LCount;EM_GetInt16I_be(LCount, LPtr);(void)LCount;

				const char*  LSemanticBuiltIn = E3dVertexNode_SemanticGetBuiltIn(LSemantic);
				if(E3dPolyGroup_VertexAttributeAdd(PPolyGroup,  LName, LSemanticBuiltIn, -1,  NULL) == E_SUCCESS)
				{
					LSrcVA->Type = LType;
					LSrcVA->Ext.Count = LCount;
					const EResource*  LDstVA = PPolyGroup->VertexAttributes.Items + LVAI;

// E3dPolyGroup_VertexAttributeAdd() sets the types of some built-ins to a default (e.g. Normals will be E3dCoord3), so LDstVA->Type may not match LType.
// This may happen if, say we load a file saved by a single-precision Eqx3D into a double-precision build or vice versa.
// We just need to convert those values on load.
// However, if the Count doesn't match, we'll bail.
//
					if(LDstVA->Ext.Count != LCount) goto VertexAttributeError;
					if(LDstVA->Type != LType) LNeedVAConversion = TRUE;

					LVAOffsets[LVAI] = sizeof(E3dVertexNodeCore) + EResource_OffsetByIndex(&PPolyGroup->VertexAttributes,  LVAI);

if(E3dDebugPOLYGROUP)
{
	char  s0[256], s1[256];
	printf("%s() %s:%d    LSemantic %s%s%s ~ LSemanticBuiltIn %s%p%s  Name %s  SrcType %s  SrcCount %lld   DstType %s  Count %lld  Offset %d\n",  __FUNCTION__, __FILE__, __LINE__,
	 ECodeGreen, (char*)LSemantic, ECodeNormal,  ECodeGreen, LSemanticBuiltIn, ECodeNormal,  LName,  ERes_TypeToString(LType, s0, 256), (long long)LCount,
	 ERes_TypeToString(LDstVA->Type, s1, 256), (long long)LDstVA->Ext.Count, LVAOffsets[LVAI]);
	fflush(stdout);
}

					if(LSemanticBuiltIn) EFreeClr(LSemantic);
				}
				else
				{
VertexAttributeError:
					EFreeChkClr(LName);
					EFreeChkClr(LSemantic);

					EList_FreeChk_s(LCustomVertexAttributes);
					EFreeClr(LVAOffsets);
					return(LPtr);
				}

				EFreeChkClr(LName);
			}
if(E3dDebugMESH) { printf("\n");fflush(stdout); }
		}
		else
		{
// Old way VertexAttribute flags
//
			E3dVertexAttributeFlags  LVertexAttributeFlags;EM_GetInt32I_be(LVertexAttributeFlags, LPtr);

if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s  LVertexAttributeFlags %08x\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal,
	 LVertexAttributeFlags);
	fflush(stdout);
}

			if(_CDS_FileVersion >= 931)
			{
//E3dIMPLEMENTME
// We should save the vertex attribute list as a list of Resources.
// Name, Type, Semantic, Transformability (e.g. normal, tangents etc. are rotated, colors are not, we need to be able to specify this for custom attributes)
			}
			else LVertexAttributeFlags |= E3dVtxNodeCORE|E3dVtxNodeNORMAL;  // Files before 931 may not have all the core flags and the NORMAL flag, but they all had vertex normals

			LVertexAttributeFlags &= ~E3dVtxNodeEDGE_INDEX;

if(E3dDebugPOLYGROUP) { printf(" %s() %s:%d  %sLVertexAttributeFlags%s %08x\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal, LVertexAttributeFlags);fflush(stdout); }

			E3dPolyGroup_VertexAttributesSetFromFlags(PPolyGroup, LVertexAttributeFlags);

			EM_GetInt32I_be(LMaterialIndex, LPtr);
		}

if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s  Material index %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, LMaterialIndex);
	fflush(stdout);
}

		if(P3DLoader->Materials && LMaterialIndex >= 0 && LMaterialIndex < P3DLoader->Materials_Count)
		{
			E3dMaterial*  LMaterial = P3DLoader->Materials[LMaterialIndex];
			E_AssignToMember(LMaterial, PPolyGroup,Material);
		}
		else
		{
if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s  %sP3DLoader has no Materials!%s\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal,
	 ECodeYellow, ECodeNormal);
	fflush(stdout);
}
		}
	}
	else
	{
// Index of PolyGroup's Material
//
		{
			int  LIndex;EM_GetInt32I_be(LIndex, LPtr);

			if(LIndex < -1 || LIndex >= P3DLoader->Materials_Count) return(LPtr);
			if(LIndex == -1 || P3DLoader->Materials == NULL || LIndex >= P3DLoader->Materials_Count);
			else
			{
				E3dMaterial*  LMaterial = P3DLoader->Materials[LIndex];
				E_AssignToMember(LMaterial, PPolyGroup,Material);
			}
		}

		PPolyGroup->VertexNormalType = *LPtr++;
		uint8_t  LCFlags = *LPtr++;

if(E3dDebugPOLYGROUP)
{
	printf(" VertexNormalType %02x  Flags %08x  LCFlags %02x  SmoothingAngle %f\n", PPolyGroup->VertexNormalType, PPolyGroup->Flags, LCFlags, PPolyGroup->SmoothingAngle);
	fflush(stdout);
}

		LPtr++;  // Reserved
		LPtr++;  // Reserved

		EM_GetInt32I_be(PPolyGroup->Flags, LPtr);

		if(LCFlags & 0x01) PPolyGroup->DoubleSided = TRUE;
		else PPolyGroup->DoubleSided = FALSE;


		if(LCFlags & 0x02) PPolyGroup->Visible = TRUE;else PPolyGroup->Visible = FALSE;
		if(LCFlags & 0x04) PPolyGroup->Selected = TRUE;else PPolyGroup->Selected = FALSE;

		EM_GetFloat32I_be(PPolyGroup->SmoothingAngle, LPtr);

		E3dVertexAttributeFlags  LVertexAttributeFlags;EM_GetUInt32I_be(LVertexAttributeFlags, LPtr);
		LVertexAttributeFlags |= E3dVtxNodeNORMAL;  // Old files always had vertex normals, but did not indicate it with a flag

		E3dPolyGroup_VertexAttributesSetFromFlags(PPolyGroup, LVertexAttributeFlags);

if(E3dDebugPOLYGROUP) { printf("%s() %s:%d  %sLVertexAttributeFlags%s  %08x\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LVertexAttributeFlags);fflush(stdout); }
		LPtr += 12;  // Reserved
	}

	const E3dVertexNodeSize  LVertexNodeSize = PPolyGroup->VertexNodeSize;

// Load Polygons
	{
		int  LNPrims;EM_GetInt32I_be(LNPrims, LPtr);
		_TotalNumOfPolygons += LNPrims;

		if(LNPrims > 0)
		{
			E3dPolygon*  LPolygon = E3dPolygons_New(LNPrims);if(LPolygon == NULL)
			{
				EList_FreeChk_s(LCustomVertexAttributes);
				return(LPtr);
			}
			PPolyGroup->Polygons.Items = LPolygon;
			PPolyGroup->Polygons.Count = PPolyGroup->Polygons.CountAllocated = LNPrims;

// If vertex normals are enabled, but VertexNormalType is 0, that's an invalid combination. Fix it to E3dNormalFROM_SMOOTHING_ANGLE
//
			if(PPolyGroup->Flags & E3dUSE_VERTEX_NORMALS)
			{
				if(PPolyGroup->VertexNormalType == 0) PPolyGroup->VertexNormalType = E3dNormalFROM_SMOOTHING_ANGLE;
			}

			if(PFeatureFlags & CfPolyGroupCUSTOM_VERTEX_ATTRIBUTES)
			{
// New way (.eqx file version 0.8.0 and above)
//
				const EResource*  LVAs = PPolyGroup->VertexAttributes.Items + E3dPolyGroup_DefaultVertexAttributes_Count;

				EIndex  LPI = 0;for(;LPI < LNPrims;LPI++)
				{
					EM_GetFloat32I_be(LPolygon->Normal.X, LPtr);
					EM_GetFloat32I_be(LPolygon->Normal.Y, LPtr);
					EM_GetFloat32I_be(LPolygon->Normal.Z, LPtr);

if(E3dDebugPOLYGON) { printf("%s() %s:%d   %sNormal%s  %f,%f,%f\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, XYZ_s(LPolygon->Normal));fflush(stdout); }
					EM_GetInt16I_be(LPolygon->Flags, LPtr);

					EIndex  LVN = 0;switch(PVertexIndexBits)
					{
						case  8:  LVN = *LPtr++;break;
						case 16:  EM_GetUInt16I_be(LVN, LPtr);break;
						case 32:  EM_GetUInt32I_be(LVN, LPtr);break;
						case 64:  EM_GetUInt64I_be(LVN, LPtr);break;
					}

					LPolygon->NumOfVertexNodes = LVN;
					if(E3dPolygon_VertexNodesNew(LPolygon, LVertexNodeSize, LPolygon->NumOfVertexNodes) == NULL)
					{
						EList_FreeChk_s(LCustomVertexAttributes);
						return(LPtr);
					}

if(E3dDebugPOLYGON)
{
	printf("%s() %s:%d  %sPoly%s %lld  Verts %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LPI, (long long)LVN);fflush(stdout);
}

					EIndex  LVI = 0, LNExteriorVertices = 0, LNVertices = 0;EBool  LInExterior = TRUE;E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					for(;LVI < LVN;LVI++, EStruct_Inc(LVertexNode, LVertexNodeSize))
					{
						EIndex  LIndex = 0;switch(PVertexIndexBits)
						{
							case 8:   LIndex = (int8_t)(*LPtr++);break;
							case 16:  EM_GetInt16I_be(LIndex, LPtr);break;
							case 32:  EM_GetInt32I_be(LIndex, LPtr);break;
							case 64:  EM_GetInt64I_be(LIndex, LPtr);break;
						}
						LVertexNode->VertexID = LIndex;

if(E3dDebugPOLYGON) { printf("%s() %s:%d    %sV%s %lld:%lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LVI, (long long)LIndex);fflush(stdout); }

						if(LIndex >= 0)
						{
							_VertexNodeLoad(LVertexNode);
							LNVertices++;
							if(LInExterior) LNExteriorVertices++;
						}
						else LInExterior = FALSE;
					}

if(E3dDebugPOLYGON) { printf("\n");fflush(stdout); }
					LPolygon->NumOfVertices = LNVertices;
					LPolygon->NumOfExteriorVertices = LNExteriorVertices;

					if(LPolygon->Flags & E3dPolygonHAS_TRIANGLES)
					{
						LPolygon->Flags &= ~E3dPolygonHAS_TRIANGLES;
						int  LN;EM_GetInt32I_be(LN, LPtr);  // Number of triangles

if(E3dDebugPOLYGON) { printf("%s() %s:%d  %sTris%s %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LN);fflush(stdout); }

						{
							ENewN(LN, LPolygon->ITriangles);E3dITriangle*  LITriangle = LPolygon->ITriangles;

							EIndex  LI = 0;for(;LI < LN;LI++, LITriangle++)
							{
								EM_GetInt32I_be(LITriangle->VertexNodes[0], LPtr);
								EM_GetInt32I_be(LITriangle->VertexNodes[1], LPtr);
								EM_GetInt32I_be(LITriangle->VertexNodes[2], LPtr);
							}
						}
					}

					if(LPolygon->NumOfVertexNodes < 3 || LPolygon->NumOfExteriorVertices < 3 || LPolygon->NumOfVertices < 3)
					{
						EFree(LPolygon->VertexNodes);
					}
					else LPolygon++;
				}

				PPolyGroup->Polygons.Count = LPolygon - PPolyGroup->Polygons.Items;

if(E3dDebugPOLYGROUP)
{
	printf("%s() %s:%d  %sPolys read%s %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal,  (long long)(LPolygon - PPolyGroup->Polygons.Items));fflush(stdout);
}
				EList_FreeChk_s(LCustomVertexAttributes);
			}
			else
			{
				const int         LNormalOffset  = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_Normal,  0);
				const EResource*  LColorResource = E3dPolyGroup_VertexAttributeResource(PPolyGroup, E3dVtxAttribute_Color, 0);
				const int         LST0Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      0);
				const int         LST1Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      1);
				const int         LST2Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      2);
				const int         LST3Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      3);
				const int         LST4Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      4);
				const int         LST5Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      5);
				const int         LST6Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      6);
				const int         LST7Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      7);
				const int         LUVOffset      = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_UV,      0);
				const int         LTangentOffset = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_Tangent, 0);

// Old way
if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s  NPolygons %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, LNPrims);
	fflush(stdout);
}
				EIndex  LPI = 0;for(;LPI < LNPrims;LPI++)
				{
					EM_GetFloat32I_be(LPolygon->Normal.X, LPtr);
					EM_GetFloat32I_be(LPolygon->Normal.Y, LPtr);
					EM_GetFloat32I_be(LPolygon->Normal.Z, LPtr);

					EM_GetInt16I_be(LPolygon->Flags, LPtr); 

					int  LVN = 0;switch(PVertexIndexBits)
					{
						case  8:  LVN = *LPtr++;break;
						case 16:  EM_GetUInt16I_be(LVN, LPtr);break;
						case 32:  EM_GetUInt32I_be(LVN, LPtr);;break;
					}

if(E3dDebugPOLYGON)
{
	printf(" %s() %s:%d  at %s%08llx%s LPI %lld  LVN %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal,
	 (long long)LPI, (long long)LVN);
	fflush(stdout);
}

					LPolygon->NumOfVertexNodes = LVN;
					if(E3dPolygon_VertexNodesNew(LPolygon, LVertexNodeSize, LPolygon->NumOfVertexNodes) == NULL) return(LPtr);

					int  LNExteriorVertices = 0;EBool  LInExterior = TRUE;
					EIndex          LNVertices = 0;
					E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;
					E3dCoord3*  LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dST*      LST0 = LST0Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST0Offset) : NULL;
					E3dST*      LST1 = LST1Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST1Offset) : NULL;
					E3dST*      LST2 = LST2Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST2Offset) : NULL;
					E3dST*      LST3 = LST3Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST3Offset) : NULL;
					E3dST*      LST4 = LST4Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST4Offset) : NULL;
					E3dST*      LST5 = LST5Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST5Offset) : NULL;
					E3dST*      LST6 = LST6Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST6Offset) : NULL;
					E3dST*      LST7 = LST7Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST7Offset) : NULL;
					E3dCoord3*  LTangent = LTangentOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LTangentOffset) : NULL;
					E3dCoord2*  LUV = LUVOffset >= 0 ? (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffset) : NULL;

if(0 && E3dDebugPOLYGON) { printf("%s() %s:%d  LPI %3lld : LVN %lld", __FUNCTION__, __FILE__, __LINE__, (long long)LPI, (long long)LVN);fflush(stdout); }

					int  LVI = 0;for(;LVI < LVN;LVI++, EStruct_Inc(LVertexNode, LVertexNodeSize))
					{
						EIndex  LIndex = 0;switch(PVertexIndexBits)
						{
							case 8:   LIndex = (int8_t)(*LPtr++);break;
							case 16:  EM_GetInt16I_be(LIndex, LPtr);break;
							case 32:  EM_GetInt32I_be(LIndex, LPtr);break;
						}
						LVertexNode->VertexID = LIndex;

if(E3dDebugPOLYGON)
{
	printf(" %s() %s:%d  at %s%08llx%s   LIndex %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, (long long)LIndex);
	fflush(stdout);
}

if(0 && E3dDebugPOLYGON) { printf(" %lld", (long long)LIndex);fflush(stdout); }
						if(LIndex >= 0)
						{
							_M_GetVertexNode(LVertexNode);

							if(LInExterior) LNExteriorVertices++;
						}
						else
						{
							LInExterior = FALSE;
							if(LVertexNormal) EStruct_Inc(LVertexNormal, LVertexNodeSize);
							if(LST0) EStruct_Inc(LST0, LVertexNodeSize);
							if(LST1) EStruct_Inc(LST1, LVertexNodeSize);
							if(LST2) EStruct_Inc(LST2, LVertexNodeSize);
							if(LST3) EStruct_Inc(LST3, LVertexNodeSize);
							if(LST4) EStruct_Inc(LST4, LVertexNodeSize);
							if(LST5) EStruct_Inc(LST5, LVertexNodeSize);
							if(LST6) EStruct_Inc(LST6, LVertexNodeSize);
							if(LST7) EStruct_Inc(LST7, LVertexNodeSize);
							if(LTangent) EStruct_Inc(LTangent, LVertexNodeSize);
							if(LUV) EStruct_Inc(LUV, LVertexNodeSize);
						}
					}

if(E3dDebugPOLYGON) { printf("\n");fflush(stdout); }

					LPolygon->NumOfVertices = LNVertices;
					LPolygon->NumOfExteriorVertices = LNExteriorVertices;

					if(LPolygon->Flags & E3dPolygonHAS_TRIANGLES)
					{
						LPolygon->Flags &= ~E3dPolygonHAS_TRIANGLES;
						int  LN;EM_GetInt32I_be(LN, LPtr);  // Number of triangles

						{
							ENewN(LN, LPolygon->ITriangles);E3dITriangle*  LITriangle = LPolygon->ITriangles;

							EIndex  LI = 0;for(;LI < LN;LI++, LITriangle++)
							{
								EM_GetInt32I_be(LITriangle->VertexNodes[0], LPtr);
								EM_GetInt32I_be(LITriangle->VertexNodes[1], LPtr);
								EM_GetInt32I_be(LITriangle->VertexNodes[2], LPtr);
							}
						}
					}

					if(LPolygon->NumOfVertexNodes < 3 || LPolygon->NumOfExteriorVertices < 3 || LPolygon->NumOfVertices < 3)
					{
						EFree(LPolygon->VertexNodes);
					}
					else LPolygon++;
				}

				PPolyGroup->Polygons.Count = LPolygon - PPolyGroup->Polygons.Items;
			}
		}
	}

// Load triangle strips
//
	{
		int  LNPrims;EM_GetInt32I_be(LNPrims, LPtr);

		_TriangleStrips_Count += LNPrims;
		if(LNPrims > 0)
		{
			if(PFeatureFlags & CfPolyGroupCUSTOM_VERTEX_ATTRIBUTES)
			{
if(E3dDebugTRIANGLE_STRIP)
{
	printf("%s() %s:%d    %sTriStrips%s %lld  PVertexIndexBits %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LNPrims, (long long)PVertexIndexBits);
	fflush(stdout);
}
// New way (.eqx file version 0.8.0 and above)
//
				const EResource*  LVAs = PPolyGroup->VertexAttributes.Items + E3dPolyGroup_DefaultVertexAttributes_Count;
				int  LPI = 0;for(;LPI < LNPrims;LPI++)
				{
					EIndex  LVN = 0;switch(PVertexIndexBits)
					{
						case  8:  LVN = *LPtr++;break;
						case 16:  EM_GetUInt16I_be(LVN, LPtr);break;
						case 32:  EM_GetUInt32I_be(LVN, LPtr);break;
						case 64:  EM_GetUInt64I_be(LVN, LPtr);break;
					}

if(E3dDebugTRIANGLE_STRIP)
{
	printf("%s() %s:%d    %sTriStrip%s %4lld  Verts %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LPI, (long long)LVN);fflush(stdout);
}

					E3dTriangleStrip*  LTriangleStrip = E3dMesh_PolyGroupAddTriangleStrip(NULL, PPolyGroup);if(LTriangleStrip == NULL) return(LPtr);

					E3dVertexNode*  LVertexNode = E3dTriangleStrip_VertexNodesNew(LTriangleStrip, LVertexNodeSize, (LVN + 1));if(LVertexNode == NULL) return(LPtr);

					LTriangleStrip->NumOfVertices = LVN;

					int  LVI = 0;for(;LVI < LVN;LVI++, EStruct_Inc(LVertexNode, LVertexNodeSize))
					{
						EIndex  LIndex = 0;switch(PVertexIndexBits)
						{
							case 8:   LIndex = (int8_t)(*LPtr++);break;
							case 16:  EM_GetInt16I_be(LIndex, LPtr);break;
							case 32:  EM_GetInt32I_be(LIndex, LPtr);break;
						}

if(E3dDebugTRIANGLE_STRIP)
{
	printf("%s() %s:%d      %sVertex%s %4lld  %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LVI, (long long)LIndex);fflush(stdout);
}
						LVertexNode->VertexID = LIndex;

						if(LIndex >= 0)
						{
							_VertexNodeLoad(LVertexNode);
						}
					}
				}
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
			}
			else
			{
				const int         LNormalOffset  = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_Normal,  0);
				const EResource*  LColorResource = E3dPolyGroup_VertexAttributeResource(PPolyGroup, E3dVtxAttribute_Color, 0);
				const int         LST0Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      0);
				const int         LST1Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      1);
				const int         LST2Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      2);
				const int         LST3Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      3);
				const int         LST4Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      4);
				const int         LST5Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      5);
				const int         LST6Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      6);
				const int         LST7Offset     = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_ST,      7);
				const int         LUVOffset      = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_UV,      0);
				const int         LTangentOffset = E3dPolyGroup_VertexAttributeOffset(PPolyGroup, E3dVtxAttribute_Tangent, 0);

// Old way
//
				int  LPI = 0;for(;LPI < LNPrims;LPI++)
				{
					unsigned int  LVN;EM_GetInt32I_be(LVN, LPtr);

					E3dTriangleStrip*  LTriangleStrip = E3dMesh_PolyGroupAddTriangleStrip(NULL, PPolyGroup);if(LTriangleStrip == NULL) return(LPtr);

					E3dVertexNode*  LVertexNode = E3dTriangleStrip_VertexNodesNew(LTriangleStrip, LVertexNodeSize, (LVN + 1));if(LVertexNode == NULL) return(LPtr);

					LTriangleStrip->NumOfVertices = LVN;
					EIndex          LNVertices = 0;(void)LNVertices;
					E3dCoord3*  LVertexNormal = LNormalOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LNormalOffset) : NULL;
					E3dST*      LST0 = LST0Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST0Offset) : NULL;
					E3dST*      LST1 = LST1Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST1Offset) : NULL;
					E3dST*      LST2 = LST2Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST2Offset) : NULL;
					E3dST*      LST3 = LST3Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST3Offset) : NULL;
					E3dST*      LST4 = LST4Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST4Offset) : NULL;
					E3dST*      LST5 = LST5Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST5Offset) : NULL;
					E3dST*      LST6 = LST6Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST6Offset) : NULL;
					E3dST*      LST7 = LST7Offset >= 0 ? (E3dST*)EOffsetToPtr(LVertexNode, LST7Offset) : NULL;
					E3dCoord3*  LTangent = LTangentOffset >= 0 ? (E3dCoord3*)EOffsetToPtr(LVertexNode, LTangentOffset) : NULL;
					E3dCoord2*  LUV = LUVOffset >= 0 ? (E3dCoord2*)EOffsetToPtr(LVertexNode, LUVOffset) : NULL;
					int  LVI = 0;for(;LVI < LVN;LVI++, EStruct_Inc(LVertexNode, LVertexNodeSize))
					{
						EIndex  LIndex = 0;switch(PVertexIndexBits)
						{
							case 8:   LIndex = (int8_t)(*LPtr++);break;
							case 16:  EM_GetInt16I_be(LIndex, LPtr);break;
							case 32:  EM_GetInt32I_be(LIndex, LPtr);break;
						}
						LVertexNode->VertexID = LIndex;

						if(LIndex >= 0)
						{
							_M_GetVertexNode(LVertexNode);
						}
						else
						{
							if(LST0) EStruct_Inc(LST0, LVertexNodeSize);
							if(LST1) EStruct_Inc(LST1, LVertexNodeSize);
							if(LST2) EStruct_Inc(LST2, LVertexNodeSize);
							if(LST3) EStruct_Inc(LST3, LVertexNodeSize);
							if(LST4) EStruct_Inc(LST4, LVertexNodeSize);
							if(LST5) EStruct_Inc(LST5, LVertexNodeSize);
							if(LST6) EStruct_Inc(LST6, LVertexNodeSize);
							if(LST7) EStruct_Inc(LST7, LVertexNodeSize);
							if(LTangent) EStruct_Inc(LTangent, LVertexNodeSize);
							if(LUV) EStruct_Inc(LUV, LVertexNodeSize);
						}
					}
				}
			}
		}
//printf("PPolyGroup->TriangleStrips_Count %d\n", PPolyGroup->TriangleStrips_Count);fflush(stdout);
	}

	EFreeChkClr(LVAOffsets);


if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s after reading Polygons & Strips\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);
	fflush(stdout);
}

// Load TextureMappers
//
	if(_CDS_FileVersion >= 801)
	{
		EBool  LCheckForTextureMapperRefs = FALSE;
// Total length
//
		int  LLength;EM_GetInt32I_be(LLength, LPtr);if(LLength)
		{
			LCheckForTextureMapperRefs = TRUE;
			const uint8_t*  LIPtr = LPtr;
			const uint8_t*  LEndPtr = LPtr + LLength;
			int             LTxI = 0;(void)LTxI;

			for(;LIPtr < LEndPtr;)
			{
				E3dTextureMapperClass*  LClass = E3dTextureMapper_ClassByName((char*)LIPtr);if(LClass)
				{
					const size_t  LNameLen = strlen(LClass->Name) + 1;

//printf("TextureMapper [%s] found\n", (char*)LIPtr);fflush(stdout);

					LIPtr += LNameLen;
					if(!LClass->ClassActive) { printf("Manager for TextureMapper class '%s' is not loaded.\n", LClass->Name);fflush(stdout); }

					E3dTextureMapper*  LMapper = E3dTextureMapper_New(LClass);if(LMapper)
					{
						ELst_ObjectAdd_om_s(PPolyGroup,TextureMappers, LMapper);
					}

					if(_CDS_FileVersion < 932)
					{
						EList_ObjectAdd_s(PPolyGroup->TextureMapperRefs, LMapper);
					}

					if(LIPtr < LEndPtr)
					{
						if(LClass->Resources.Count > 0)
						{
//printf("TextureMapper [%s] has resources  %p\n", LClass->Name, LMapper);fflush(stdout);
							EResClassEntry*  LClassEntry = EList_ClassEntryFind_s(PContext->Classes, (ECoreClass*)LClass);

if(E3dDebugPOLYGROUP) { printf("%s() %s:%d  %p %s %p\n", __FUNCTION__, __FILE__, __LINE__, LClass, LClass->Name, LClassEntry);fflush(stdout); }

							const int  LBinaryImageLen = ERes_FromBinaryImage(PContext, P3DLoader->Buffer, LIPtr,  LClassEntry ? LClassEntry->AllResources : NULL, &LClass->Resources,
							 LMapper,  &PContext->ObjectProxies);

if(0)
{
	printf("Class: %s at%s%08llx%s  length: %d (til %s%08llx%s)\n", LClass->Name, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeCyan, LBinaryImageLen, ECodeCyan,
	 (long long)(LEndPtr - P3DLoader->Buffer), ECodeCyan);
	fflush(stdout);
}
							if(LBinaryImageLen > 0) LIPtr += LBinaryImageLen;
							else break;    // Error decoding resources, end the for() loop

						}

						LTxI++;
					}
//printf("%s() %s:%d  %sGREEN%s  Geo %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, PPolyGroup->Geometry);fflush(stdout);
//					if(LClass->Update) LClass->Update(LMapper,  PMesh->Vertices, PMesh->Vertices.Count,  PMesh->VertexSize, (EObject*)PPolyGroup);
//					E3dTextureMapper_SetTiling(LMapper);
				}
				else break;  // If we couldn't find the class, we can't safely decode the Resources, so we have to terminate the loop here
			}

			LPtr += LLength;
		}

// Read TextureMapperRefs
//
		if(_CDS_FileVersion >= 932 && (LCheckForTextureMapperRefs || (PFeatureFlags & CfMeshTEXTUREMAPPERS)))
		{
//printf("%s() %s:%d  %sTextureMapperRefs%s  %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (long long)LN);fflush(stdout);
			int  LN;EM_GetInt16I_be(LN, LPtr);if(LN > 0)
			{
				ENewNClr(LN, PPolyGroup->TextureMapperRefs.Items);
				EIndex  LI = 0;for(;LI < LN;LI++)
				{
					uint16_t  LV;EM_GetUInt16I_be(LV, LPtr);
					if(LV & E3dTextureMapperFROM_MESH)
					{
						LV &= ~E3dTextureMapperFROM_MESH;
						if(PVertexGeometry && LV < PVertexGeometry->TextureMappers.Count) E_AssignToMember(PVertexGeometry->TextureMappers.Items[LV], PPolyGroup,TextureMapperRefs.Items[LI]);
					}
					else
					{
						if(LV < PPolyGroup->TextureMappers.Count) E_AssignToMember(PPolyGroup->TextureMappers.Items[LV], PPolyGroup,TextureMapperRefs.Items[LI]);
					}
				}
				PPolyGroup->TextureMapperRefs.Count = LN;
			}
		}

/*
		if(LMaterial && LMaterial->Textures)
		{
printf("%s() %s:%d  Material [%p:%s] has Textures, PolyGroup <<%p%s>> TextureMappers : %p\n",  __FUNCTION__, __FILE__, __LINE__,  LMaterial, LMaterial->Name, PPolyGroup,
 PPolyGroup->Name, PPolyGroup->TextureMappers);fflush(stdout);

			if(PPolyGroup->TextureMappers == NULL)
			{
printf("%s() %s:%d  Material [%s] has Textures, but PolyGroup <<%s>> has no TextureMappers\n",  __FUNCTION__, __FILE__, __LINE__,  LMaterial->Name, PPolyGroup->Name);
fflush(stdout);
			}
		}
*/
	}
	else
	{
// Old CDS file, create TextureMappers from the old, per-texture mapping info
//
		E3dMaterial*  LMaterial = PPolyGroup->Material;if(LMaterial)
		{
			if(LMaterial->Textures.Count > 0)
			{
				EIndex  LTxI = 0;const EIndex  LTxN = LMaterial->Textures.Count;for(;LTxI < LTxN;LTxI++)
				{
					E3d2DTexture*  LTexture = LMaterial->Textures.Items[LTxI];
					E3dTextureMapper*  LMapper = (E3dTextureMapper*)E_Copy(LTexture->MapperOld);

					ELst_ObjectAdd_om_s(PPolyGroup,TextureMappers, LMapper);
					EList_ObjectAdd_s(PPolyGroup->TextureMapperRefs, LMapper);
				}
			}
		}
	}

	return(LPtr);
}


//========================================
// Load a PolyGroup
//========================================
static int _PolyGroupLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t** PPtrP,  const uint8_t PVertexIndexBits, const uint32_t PFeatureFlags,
 E3dVertexGeometry* PVertexGeometry, E3dPolyGroup* PPolyGroup)
{
	const uint8_t*  LPtr = *PPtrP;

if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s before _PolyGroupCoreLoad\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);fflush(stdout);
}

	LPtr = _PolyGroupCoreLoad(PContext, P3DLoader, PVertexIndexBits, PFeatureFlags, P3DLoader->Buffer, LPtr,  PVertexGeometry, PPolyGroup);

if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s after _PolyGroupCoreLoad\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);fflush(stdout);
}

// Read Components and their Resources
//
	{
// Components total length
//
		int  LLength;EM_GetInt32I_be(LLength, LPtr);if(LLength)
		{
if(E3dDebugPOLYGROUP)
{
	printf(" %s() %s:%d  at %s%08llx%s  Length %08x\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, LLength);fflush(stdout);
}

			const uint8_t*  LIPtr = LPtr;const uint8_t*  LEndPtr = LPtr + LLength;for(;LIPtr < LEndPtr;)
			{
				_GetObjectFlagsAndID(LIPtr, LPolyGroupComponentID);

				E3dPolyGroupComponentClass*  LClass = E3dPolyGroupComponent_ClassByName((char*)LIPtr);if(LClass)
				{
					const size_t  LNameLen = strlen(LClass->Name) + 1;

//printf("PolyGroupComponentClass [%s] found\n", (char*)LIPtr);fflush(stdout);

					LIPtr += LNameLen;
					if(!LClass->ClassActive) { printf("Manager for PolyGroupComponent class '%s' is not loaded.\n", LClass->Name);fflush(stdout); }

					E3dPolyGroupComponent*  LInfo = E3dPolyGroupComponent_Add(PPolyGroup, LClass);
					if(LClass->Resources.Count > 0 && LIPtr < LEndPtr)
					{
//printf("PolyGroupComponentClass [%s] has resources  LLength = %08x\n", LClass->Name, LLength);fflush(stdout);
						if(LInfo)
						{
							if(LPolyGroupComponentID >= 0) _ObjectAddByID(PContext, _ObjectsALLOC_INCREMENT, (EObject*)LInfo, LPolyGroupComponentID);

//PContext->Verbose = 1;
							const int  LBinaryImageLen = EObject_ResourcesFromBinaryImage((ECoreObject*)LInfo, PContext,  P3DLoader->Buffer, LIPtr,  &PContext->ObjectProxies);
//PContext->Verbose = 0;

if(0)
{
	printf("[%s] PolyGroupInfo: %s at %s%08llx%s  length: %d (til %s%08llx%s)\n", PPolyGroup->Name, LClass->Name, ECodeCyan, (long long)(LIPtr - P3DLoader->Buffer), ECodeNormal,
	 LBinaryImageLen, ECodeCyan, (long long)(LEndPtr - P3DLoader->Buffer), ECodeNormal);
	fflush(stdout);
}

							if(LBinaryImageLen > 0) LIPtr += LBinaryImageLen;
							else break;    // Error decoding resources, end the for() loop
						}
					}
				}
				else break;  // If we couldn't find the class, we can't safely decode the resources, so we have to terminate the loop here
			}

			LPtr += LLength;
		}
	}
if(E3dDebugPOLYGROUP) { printf(" %s() %s:%d  at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);fflush(stdout); }

	*PPtrP = LPtr;
	return(E_SUCCESS);
}



static int _FaceLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t* PBuffer, const uint8_t** PPtrP, const uint32_t PFeatureFlags,  E3dFace* PFace)
{
	const uint8_t*  LPtr = *PPtrP;
	int             LResult = E_SUCCESS;
	const uint8_t   LVertexIndexBits = 32;

// Index of Material
//
	if(_CDS_FileVersion < 921)  // Starting with 921, the Face's Material index is saved with its PolyGroup
	{
		int  LIndex;EM_GetInt32I_be(LIndex, LPtr);
		if(LIndex < -1 || LIndex >= P3DLoader->Materials_Count) { LIndex = -1;LResult = E_CORRUPT_DATA; }  // Error

		if(PFace->PolyGroup)
		{
			if(LIndex == -1 || P3DLoader->Materials == NULL || LIndex >= P3DLoader->Materials_Count) PFace->PolyGroup->Material = NULL;
			else
			{
				E_AssignToMember(P3DLoader->Materials[LIndex], PFace,PolyGroup->Material);
			}
		}
	}

// Load TextureMappers
//
	if(PFeatureFlags & CfVertexGeometryTEXTUREMAPPERS)
	{
		LPtr += _VertexGeometry_TextureMappersLoad(PContext, P3DLoader, LPtr,  (E3dVertexGeometry*)PFace);
	}

	int  LHN;EM_GetInt32I_be(LHN, LPtr);

	if(_CDS_FileVersion >= 919)
	{
		{
			int  LIndex;EM_GetInt32I_be(LIndex, LPtr);

if(E3dDebugSPLINE)
{
	printf("  %s() %s:%d  Exterior Spline %lld %lld\n", __FUNCTION__, __FILE__, __LINE__, (long long)LIndex, (long long)PContext->AllObjects.Count);fflush(stdout);
}

			if(LIndex < -1 || LIndex >= PContext->AllObjects.Count)
			{
printf("%s() %s:%d  %sERROR%s Face <%s%s%s> exterior index invalid\n", __FUNCTION__, __FILE__, __LINE__, ECodeRed, ECodeNormal,  ECodeCyan, PFace->Name, ECodeNormal);
fflush(stdout);
				LResult = E_CORRUPT_DATA;  // Error: could not find Face's exterior Spline
			}
			else
			{
				ECoreObject*  LObject = PContext->AllObjects.Items[LIndex];
				E3dSpline*    LSpline = E_DynamicCast(LObject, E3dSpline);
				if(LSpline) E3dFace_SetExterior(PFace, LSpline);
				else LResult = E_CORRUPT_DATA;
			}
		}

		int  LHI = 0;for(;LHI < LHN;LHI++)
		{
			int  LIndex;EM_GetInt32I_be(LIndex, LPtr);
if(E3dDebugSPLINE)
{
	printf("  %s() %s:%d  Hole Spline %lld %lld\n", __FUNCTION__, __FILE__, __LINE__, (long long)LIndex, (long long)PContext->AllObjects.Count);fflush(stdout);
}
			if(LIndex < -1 || LIndex >= PContext->AllObjects.Count)
			{
printf("  %s() %s:%d  %sERROR%s Face hole index invalid  %lld of %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeRed, ECodeNormal,
 (long long)LIndex, (long long)PContext->AllObjects.Count);fflush(stdout);
				LResult = E_CORRUPT_DATA;  // Error: could not find Face's hole Spline
			}
			else
			{
				ECoreObject*  LObject = PContext->AllObjects.Items[LIndex];if(LObject)
				{
					E3dSpline*  LSpline = E_DynamicCast(LObject, E3dSpline);
					if(LSpline) E3dFace_AppendHole(PFace, LSpline);
					else
					{
printf("  %s() %s:%d  %sERROR%s Face <%s> hole is not a Spline (%s)\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeRed, ECodeNormal, PFace->Name, LObject->Class->Name);
fflush(stdout);
						LResult = E_CORRUPT_DATA;
					}
				}
				else
				{
printf("  %s() %s:%d  %sERROR%s Face <%s> hole is NULL at Object index %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeRed, ECodeNormal, PFace->Name, (long long)LIndex);
fflush(stdout);
					LResult = E_CORRUPT_DATA;
				}
			}
		}

// Tesselation is saved for Faces from version 921
//
		if(_CDS_FileVersion >= 921)
		{
			LPtr = _PolyGroupCoreLoad(PContext, P3DLoader, LVertexIndexBits, PFeatureFlags, PBuffer, LPtr,  (E3dVertexGeometry*)PFace, PFace->PolyGroup);
E3dPolyGroup_FreePolygons(PFace->PolyGroup);
			if(PFace->PolyGroup->Polygons.Count > 0)
			{
				E3dPolyGroup*  LFacePolyGroup = PFace->PolyGroup;
				E3dFace_CreateVertices(PFace);
printf("%s() %s:%d  <%s%s%s>  Verts %lld  Polys %lld  Poly0 Verts %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, PFace->Name, ECodeNormal,
 (long long)PFace->Vertices.Count, (long long)LFacePolyGroup->Polygons.Count, (long long)LFacePolyGroup->Polygons.Items[0].NumOfVertexNodes);fflush(stdout);
printf("  %s() %s:%d <%s> %lld\n", __FUNCTION__, __FILE__, __LINE__, PFace->Name, (long long)LFacePolyGroup->Polygons.Count);fflush(stdout);
				E3dPolyGroup_CreateRenderablePolygons(LFacePolyGroup,  PFace->Vertices.Items, PFace->Vertices.Count, PFace->VertexSize,  TRUE,  4);
			}
			else E3dFace_Tesselate(PFace);
		}
	}
	else
	{
		{
			const char  LCSelection = _CDS_FileVersion >= 907 ? *LPtr++ : 0;

			{
				E3dSpline*  LSpline;E_New(E3dSpline, LSpline);
				if(P3DLoader->VersionMajorMinor >= 0x00010002) E3dSpline_Load(&LPtr, LSpline);
				else LPtr = _SplineLoad(LPtr, LSpline);
				if(LSpline) { E3dFace_SetExterior(PFace, LSpline);LSpline->Selection = LCSelection; }
			}
		}

		int  LHI = 0;for(;LHI < LHN;LHI++)
		{
			const char  LCSelection = _CDS_FileVersion >= 907 ? *LPtr++ : 0;

			E3dSpline*  LSpline;E_New(E3dSpline, LSpline);
			if(P3DLoader->VersionMajorMinor >= 0x00010002) E3dSpline_Load(&LPtr, LSpline);
			else LPtr = _SplineLoad(LPtr, LSpline);
			if(LSpline) { E3dFace_AppendHole(PFace, LSpline);LSpline->Selection = LCSelection; }
		}
	}

	*PPtrP = LPtr;
	return(LResult);
}


static const uint8_t* _SkinLoad(E3dLoader* P3DLoader, E3dMesh* PMesh, const uint8_t* PPtr)
{
	E3dSkinMeshInfo*  LSkin = (E3dSkinMeshInfo*)E3dObjectComponent_ByClass((E3dObject*)PMesh, E3dSkinMeshInfo_class, FALSE);
	if(LSkin == NULL) LSkin = (E3dSkinMeshInfo*)E3dObjectComponent_Add((E3dObject*)PMesh, E3dSkinMeshInfo_class);

	const uint8_t*  LPtr = PPtr;
	int  LNumOfJoints;EM_GetInt32I_be(LNumOfJoints, LPtr);

//printf("%s()-00 %lld (at %s%08llx%s)\n", __FUNCTION__, (long long)LNumOfJoints, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ENormalCodde);fflush(stdout);
	if(LNumOfJoints)
	{
		EList_PointerAddA(P3DLoader->SkinnedMeshes, 8, PMesh);

		if(LSkin)
		{
			ENewN(LNumOfJoints, LSkin->Joints.Items);LSkin->Joints.Count = LSkin->Joints.CountAllocated = LNumOfJoints;
			EIndex  LI = 0;E3dSkinJoint*  LSkinJoint = LSkin->Joints.Items;for(LI = 0;LI < LNumOfJoints;LI++, LSkinJoint++)
			{
				E3dSkinJoint_Init(LSkinJoint);

				int  LIndex;EM_GetInt32I_be(LIndex, LPtr);
				LSkinJoint->Node = (E3dNode*)((EPointerInt)LIndex);

				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M00], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M01], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M02], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M03], LPtr);

				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M10], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M11], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M12], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M13], LPtr);

				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M20], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M21], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M22], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M23], LPtr);

				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M30], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M31], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M32], LPtr);
				EM_GetFloat32I_be(LSkinJoint->WorldToBindPoseMatrix[M33], LPtr);
			}
		}
		else
		{
			LPtr += LNumOfJoints * (sizeof(uint32_t) + sizeof(float) * 16);
		}
	}

	if(LNumOfJoints > 0)
	{
		const EIndex    LVN = PMesh->Vertices.Count;

		if(LSkin)
		{
			E3dSkinVertex*  LSkinVertex = E3dSkinVertices_New(LVN, TRUE);LSkin->SkinVertices = LSkinVertex;
			E3dSkinJoint*   LSkinJoints = LSkin->Joints.Items;
			EIndex  LVI = 0;for(;LVI < LVN;LVI++, LSkinVertex++)
			{
				EM_GetFloat32I_be(LSkinVertex->BindPosition.X, LPtr);
				EM_GetFloat32I_be(LSkinVertex->BindPosition.Y, LPtr);
				EM_GetFloat32I_be(LSkinVertex->BindPosition.Z, LPtr);

				int  LBoneN;EM_GetInt32I_be(LBoneN, LPtr);if(LBoneN)
				{
					E3dSkinJoint**	LBones;ENewN(LBoneN, LBones);
					if(LBones == NULL) { E_Delete(PMesh);return(NULL); }
					E3dWeight*		LWeights;ENewN(LBoneN, LWeights);if(LWeights == NULL) { EFree(LBones);return(NULL); }

					LSkinVertex->Bones = LBones;
					LSkinVertex->Weights = LWeights;
					LSkinVertex->NumOfBones = LBoneN;

					int  LBoneC = 0, LTJointN = 0;for(;LBoneC < LBoneN;LBoneC++)
					{
						int  LIndex;EM_GetInt32I_be(LIndex, LPtr);
						EM_GetFloat32I_be(LWeights[LTJointN], LPtr);

						{
							E3dSkinJoint*  LSkinJoint = LSkinJoints + LIndex;
							int  LJointIdx = (EPointerInt)LSkinJoint->Node;  // Not a pointer yet...

							if(LJointIdx > -1)
							{
								LBones[LTJointN++] = LSkinJoint;
							}
						}
					}
					LSkinVertex->NumOfBones = LTJointN;
				}
			}
		}
		else
		{
			EIndex  LVI = 0;for(;LVI < LVN;LVI++)
			{
				LPtr += sizeof(float) * 3;

				int  LBoneN;EM_GetInt32I_be(LBoneN, LPtr);if(LBoneN)
				{
					int  LBoneC = 0;for(;LBoneC < LBoneN;LBoneC++)
					{
						LPtr += sizeof(uint32_t);
						LPtr += sizeof(float);
					}
				}
			}
		}
	}
	else
	{
printf("%s() %s:%d  %s<%s> LSkin->Joints is NULL%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, PMesh->Name, ECodeNormal);fflush(stdout);
	}

	return(LPtr);
}


//========================================
// Load a Polygon Mesh
//========================================
static const uint8_t* _MeshLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t* PPtr, const uint32_t PFeatureFlags,  E3dMesh* PMesh)
{
	const uint8_t*  LPtr = PPtr;
	uint8_t         LVertexIndexBits = 0;
#ifdef CfOLD
	{
		LVertexIndexBits = 32;
		EM_GetInt32I_be(PMesh->Vertices.Count, LPtr);
		int  LIndex;EM_GetInt32I_be(LIndex, LPtr);
		if(PMesh->Vertices.Count == 0 || LIndex < 0 || LIndex >= _VertexArrays_Count) PMesh->Vertices.Items = NULL;
		else PMesh->Vertices.Items = _VertexArrays[LIndex].Vertices;
	}
#else
	{
		uint16_t  LNCustomVertexAttributes = 0;if(PFeatureFlags & CfMeshCUSTOM_VERTEX_ATTRIBUTES) EM_GetInt16I_be(LNCustomVertexAttributes, LPtr);
		E3dVertexSize*  LVAOffsets = NULL;

//printf("%s() %s:%d  %sPFeatureFlags%s  %08x  %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, PFeatureFlags, LNCustomVertexAttributes);fflush(stdout);

		if(LNCustomVertexAttributes > 0)
		{
if(E3dDebugMESH) { printf("%s() %s:%d  <%s%s%s> VertexAttributes\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, PMesh->Name, ECodeNormal);fflush(stdout); }
			ENewN(LNCustomVertexAttributes, LVAOffsets);
			EIndex  LVAI = 0;for(;LVAI < LNCustomVertexAttributes;LVAI++)
			{
				char*     LSemantic = _Cf_GetString(&LPtr);const char*  LSemanticBuiltIn = E3dVertexNode_SemanticGetBuiltIn(LSemantic);
				char*     LName = _Cf_GetString(&LPtr);
				uint16_t  LType;EM_GetInt16I_be(LType, LPtr);
				uint16_t  LCount;EM_GetInt16I_be(LCount, LPtr);

				E3dVertexGeometry_VertexAttributeAdd((E3dVertexGeometry*)PMesh,  LSemanticBuiltIn, LName, LType, LCount,  NULL);

				LVAOffsets[LVAI] = EResource_OffsetByIndex(&PMesh->VertexAttributes,  LVAI + E3dVertexGeometry_DefaultVertexAttributes.Count);

if(E3dDebugMESH)
{
	printf("%s() %s:%d  %s%s%s %s  %d %d  %d\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, LSemantic ? (char*)LSemantic : "-", ECodeNormal, LName ? LName : "-", LType,
	 LCount, LVAOffsets[LVAI]);
	fflush(stdout);
}
				EFreeChkClr(LSemantic);
				EFreeChkClr(LName);
			}
if(E3dDebugMESH) { printf("\n");fflush(stdout); }
		}

		LVertexIndexBits = *LPtr++;if(LVertexIndexBits > 0)
		{
if(E3dDebugMESH)
{
	printf(" %s() %s:%d  at %s%08llx%s  LVertexIndexBits %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, LVertexIndexBits);
	fflush(stdout);
}
			EIndex  LVN = 0;

			switch(LVertexIndexBits)
			{
				case 32:  EM_GetInt32I_be(LVN, LPtr);break;
				case 16:  EM_GetInt16I_be(LVN, LPtr);break;
				case  8:  LVN = *LPtr++;break;
				default:
printf("%s() %s:%d  %sInvalid LVertexIndexBits %d%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, LVertexIndexBits, ECodeNormal);fflush(stdout);
				break;
			}

			const uint8_t  LFloatBits = *LPtr++;

if(E3dDebugMESH)
{
	printf(" %s() %s:%d  %s01%s  LVN %lld  %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (long long)LVN, LFloatBits);fflush(stdout);
}
			E3dMesh_VerticesAdd(PMesh, LVN);
			E3dVertex*  LVertices = PMesh->Vertices.Items;if(LVertices)
			{
				const E3dVertexSize  LMeshVertexSize = PMesh->VertexSize;
				const EResource*     LVAs = PMesh->VertexAttributes.Items + E3dVertexGeometry_DefaultVertexAttributes.Count;
				switch(LFloatBits)
				{
					case 64:
						if(LNCustomVertexAttributes)
						{
							EIndex  LVI = 0;E3dVertex*  LVertex = LVertices;for(;LVI < LVN;LVI++, EStruct_Inc(LVertex, LMeshVertexSize))
							{
								EM_GetFloat64I_be(LVertex->Position.X, LPtr);
								EM_GetFloat64I_be(LVertex->Position.Y, LPtr);
								EM_GetFloat64I_be(LVertex->Position.Z, LPtr);
								EM_GetInt16I_be(LVertex->Flags, LPtr);

								{
									EIndex  LVAI = 0;const EResource*  LVA = LVAs;for(;LVAI < LNCustomVertexAttributes;LVAI++, LVA++)
									{
										const E3dVertexSize  LOffset = LVAOffsets[LVAI];
										if(LVA->Semantic == E3dVtxAttribute_ST)
										{
											E3dST*  LV = (E3dST*)((uint8_t*)LVertex + LOffset);
											EM_GetFloat64I_be(LV->S, LPtr);
											EM_GetFloat64I_be(LV->T, LPtr);
											LV->Tangent = E3dCoord3_s(0.0, 0.0, 0.0);
										}
										else switch(LVA->Type)
										{
											case EResFLOAT32:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI++) EM_GetFloat32I_be(LArray[LI], LPtr);
												}
											break;

											case EResFLOAT32_2:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 2)
													{ EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr); }
												}
											break;

											case EResFLOAT32_3:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 3)
													{ EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr);EM_GetFloat32I_be(LArray[LI + 2], LPtr); }
												}
											break;

											case EResFLOAT32_4:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 4)
													{
														EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr);
														EM_GetFloat32I_be(LArray[LI + 2], LPtr);EM_GetFloat32I_be(LArray[LI + 3], LPtr);
													}
												}
											break;

											default:
												{
char  s[512];printf("%s() %s:%d  %sIMPLEMENT ME%s  %s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal, ERes_TypeToString(LVA->Type, s, 511));fflush(stdout);
												}
											break;
										}
									}
								}
							}
						}
						else
						{
							EIndex  LVI = 0;E3dVertex*  LVertex = LVertices;for(;LVI < LVN;LVI++, EStruct_Inc(LVertex, LMeshVertexSize))
							{
								EM_GetFloat64I_be(LVertex->Position.X, LPtr);
								EM_GetFloat64I_be(LVertex->Position.Y, LPtr);
								EM_GetFloat64I_be(LVertex->Position.Z, LPtr);
								EM_GetInt16I_be(LVertex->Flags, LPtr);
							}
						}
					break;

					case 32:
						if(LNCustomVertexAttributes)
						{
							EIndex  LVI = 0;E3dVertex*  LVertex = LVertices;for(;LVI < LVN;LVI++, EStruct_Inc(LVertex, LMeshVertexSize))
							{
								EM_GetFloat32I_be(LVertex->Position.X, LPtr);
								EM_GetFloat32I_be(LVertex->Position.Y, LPtr);
								EM_GetFloat32I_be(LVertex->Position.Z, LPtr);
								EM_GetInt16I_be(LVertex->Flags, LPtr);

								{
									EIndex  LVAI = 0;const EResource*  LVA = LVAs;for(;LVAI < LNCustomVertexAttributes;LVAI++, LVA++)
									{
										const E3dVertexSize  LOffset = LVAOffsets[LVAI];

										if(LVA->Semantic == E3dVtxAttribute_ST)
										{
											E3dST*  LV = (E3dST*)((uint8_t*)LVertex + LOffset);
											EM_GetFloat32I_be(LV->S, LPtr);
											EM_GetFloat32I_be(LV->T, LPtr);
											LV->Tangent = E3dCoord3_s(0.0, 0.0, 0.0);
										}
										else switch(LVA->Type)
										{
											case EResFLOAT32:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI++) EM_GetFloat32I_be(LArray[LI], LPtr);
												}
											break;

											case EResFLOAT32_2:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 2) { EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr); }
												}
											break;

											case EResFLOAT32_3:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 3)
													{ EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr);EM_GetFloat32I_be(LArray[LI + 2], LPtr); }
												}
											break;

											case EResFLOAT32_4:
												{
													float*  LArray = (float*)((uint8_t*)LVertex + LOffset);
													EIndex  LI = 0;const EIndex  LN = LVA->Ext.Count;for(;LI < LN;LI += 4)
													{
														EM_GetFloat32I_be(LArray[LI], LPtr);EM_GetFloat32I_be(LArray[LI + 1], LPtr);
														EM_GetFloat32I_be(LArray[LI + 2], LPtr);EM_GetFloat32I_be(LArray[LI + 3], LPtr);
													}
												}
											break;

											default:
												{
char  s[512];printf("%s() %s:%d  %sIMPLEMENT ME%s  %s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal, ERes_TypeToString(LVA->Type, s, 511));fflush(stdout);
												}
											break;
										}
									}
								}
							}
						}
						else
						{
							EIndex  LVI = 0;E3dVertex*  LVertex = LVertices;for(;LVI < LVN;LVI++, EStruct_Inc(LVertex, LMeshVertexSize))
							{
								EM_GetFloat32I_be(LVertex->Position.X, LPtr);
								EM_GetFloat32I_be(LVertex->Position.Y, LPtr);
								EM_GetFloat32I_be(LVertex->Position.Z, LPtr);
								EM_GetInt16I_be(LVertex->Flags, LPtr);
							}
						}
					break;

					default:
						printf("%s() %s:%d FLOAT BITS = %d???\n", __FUNCTION__, __FILE__, __LINE__, LFloatBits);fflush(stdout);
					break;
				}
				PMesh->Vertices.Items = LVertices;PMesh->Vertices.Count = PMesh->Vertices.CountAllocated = LVN;
			}
		}

		EFreeChkClr(LVAOffsets);
	}
#endif

	if(PMesh->SaveEdges)
	{
		EIndex  LNumOfEdges;EM_GetInt32I_be(LNumOfEdges, LPtr);if(LNumOfEdges)
		{
			E3dEdge*  LEdge = E3dMesh_EdgesNew(PMesh, LNumOfEdges, TRUE);

			if(PMesh->SaveEdgeHardness)
			{
				EIndex  LI = 0;for(;LI < LNumOfEdges;LI++, LEdge++)
				{
					switch(LVertexIndexBits)
					{
						case 8:   LEdge->Start = *LPtr++;LEdge->End = *LPtr++;break;
						case 16:  EM_GetInt16I_be(LEdge->Start, LPtr);EM_GetInt16I_be(LEdge->End, LPtr);break;
						case 32:  EM_GetInt32I_be(LEdge->Start, LPtr);EM_GetInt32I_be(LEdge->End, LPtr);break;
					}
					const uint8_t  LCh = *LPtr++;
					if(LCh & 0x01) LEdge->Flags |= E3dEdgeVISIBLE; else LEdge->Flags &= ~E3dEdgeVISIBLE;
					if(LCh & 0x02) LEdge->Flags |= E3dEdgeSELECTED;else LEdge->Flags &= ~E3dEdgeSELECTED;
					EM_GetFloat32I_be(LEdge->Hardness, LPtr);
				}
			}
			else
			{
				EIndex  LI = 0;for(;LI < LNumOfEdges;LI++, LEdge++)
				{
					switch(LVertexIndexBits)
					{
						case 8:   LEdge->Start = *LPtr++;LEdge->End = *LPtr++;break;
						case 16:  EM_GetInt16I_be(LEdge->Start, LPtr);EM_GetInt16I_be(LEdge->End, LPtr);break;
						case 32:  EM_GetInt32I_be(LEdge->Start, LPtr);EM_GetInt32I_be(LEdge->End, LPtr);break;
					}
					const uint8_t  LCh = *LPtr++;
					if(LCh & 0x01) LEdge->Flags |= E3dEdgeVISIBLE; else LEdge->Flags &= ~E3dEdgeVISIBLE;
					if(LCh & 0x02) LEdge->Flags |= E3dEdgeSELECTED;else LEdge->Flags &= ~E3dEdgeSELECTED;
				}
			}
		}
	}


// Load TextureMappers
//
	if(PFeatureFlags & CfMeshTEXTUREMAPPERS)
	{
		LPtr += _VertexGeometry_TextureMappersLoad(PContext, P3DLoader, LPtr,  (E3dVertexGeometry*)PMesh);
	}


// Load PolyGroups
//


//printf(" %s() %s:%d  LPolyGroups_Count at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - P3DLoader->Buffer));fflush(stdout);
	int  LPolyGroups_Count;EM_GetInt32I_be(LPolyGroups_Count, LPtr);

if(E3dDebugMESH)
{
	printf(" %s() %s:%d  NumOfPolyGroups %d at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, LPolyGroups_Count, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal);
	fflush(stdout);
}

//printf(" %s() %s:%d  %s02%s  LPolyGroups_Count %lld\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, (long long)LPolyGroups_Count);fflush(stdout);
	if(LPolyGroups_Count)
	{
		EIndex  LI = 0;for(;LI < LPolyGroups_Count;LI++)
		{
#ifdef CfOLD
			E3dPolyGroup*  LPolyGroup;
			int  LIndex;EM_GetInt32I_be(LIndex, LPtr);

if(E3dDebugMESH) { printf("%s() %s:%d  index %d\n", __FUNCTION__, __FILE__, __LINE__, LIndex);fflush(stdout); }

			if(LIndex < 0 || LIndex >= P3DLoader->PolyGroups_Count) LPolyGroup = NULL;
			else LPolyGroup = P3DLoader->PolyGroups[LIndex];
#else
// Inlined PolyGroup
			E3dPolyGroup*  LPolyGroup;E_New(E3dPolyGroup, LPolyGroup);

			char*  LName = _Cf_GetString(&LPtr);
			EObject_SetName(LPolyGroup, LName, FALSE);

			_PolyGroupLoad(PContext, P3DLoader, &LPtr, LVertexIndexBits, PFeatureFlags,  (E3dVertexGeometry*)PMesh, LPolyGroup);
#endif

			if(LPolyGroup)
			{
// Check for bad VertexIDs etc.
//
if(E3dDebugMESH)
{
	printf(" %s() %s:%d  at %s%08llx%s <%s/%s>\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, PMesh->Name, LPolyGroup->Name);
	fflush(stdout);
}
				E3dMeshPolyGroup_Check(PMesh, LPolyGroup);
				E3dMesh_AppendPolyGroup(PMesh, LPolyGroup);

				if(PMesh->SaveEdges)
				{
					int  LEdges_Count;EM_GetInt32I_be(LEdges_Count, LPtr);

if(E3dDebugMESH)
{
	printf(" %s() %s:%d  at %s%08llx%s  LEdges_Count %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - P3DLoader->Buffer), ECodeNormal, LEdges_Count);
	fflush(stdout);
}

					EResizeNT(EIndex, LEdges_Count, LPolyGroup->Edges.Items);
					LPolyGroup->Edges.Count = LEdges_Count;
					int  LEI = 0;EIndex*  LEIdx = LPolyGroup->Edges.Items;for(;LEI < LEdges_Count;LEI++, LEIdx++)
					{
						EM_GetInt32I_be(*LEIdx, LPtr);
					}
				}
			}
#ifdef CfOLD
			else
			{
printf("%s() %s:%d  %d: NULL PolyGroup pointer in Mesh [%s]\n", __FUNCTION__, __FILE__, __LINE__, LIndex, PMesh->Name);fflush(stdout);
			}
#endif
		}

		if(PMesh->SaveEdges) E3dMesh_EdgeFlagsUpdate(PMesh);
		else E3dMesh_CreateEdgesFromPolygons(PMesh, NULL, FALSE, FALSE);

//		E3dMesh_RecomputePolygonNormals(PMesh);

		E3dMesh_TextureMappingUpdate(PMesh, TRUE);
	}

	return(LPtr);
}


//================================================
//================================================
static int _ObjectsLoad(EResContext* PContext, E3dLoader* P3DLoader, const uint8_t** PPtrP, const uint32_t PFeatureFlags)
{
	const uint8_t*  LPtr = *PPtrP;
	const uint8_t*  LBuffer = P3DLoader->Buffer;

	int  LNumOfObjects;EM_GetInt32I_be(LNumOfObjects, LPtr);

if(E3dDebugOBJECT) { printf("%s() %s:%d  Objects: %d\n", __FUNCTION__, __FILE__, __LINE__, LNumOfObjects);fflush(stdout); }

	if(LNumOfObjects < 1) { *PPtrP = LPtr;return(E_NO_DATA); }

//E_CallStackPrint(TRUE, 0);

	{
		EIndex  LObjI = 0;for(;LObjI < LNumOfObjects;LObjI++)
		{
			E3dObject*  LObject = NULL;
			char*       LName = NULL;

			if(_CDS_FileVersion >= 909)
			{
				const uint8_t*  LObjStartPtr = LPtr;

				_GetObjectFlagsAndID(LPtr, LObjectID);if(LObjectID >= 0 || _CDS_FileVersion < 9999)
				{
					uint16_t  LType;EM_GetInt16I_be(LType, LPtr);

					LName = _Cf_GetString(&LPtr);

if(E3dDebugOBJECT)
{
	printf("{\n %s() %s:%d  Object %d at %s%08llx%s ID %08x  Name <%s>  Class %d",  __FUNCTION__, __FILE__, __LINE__,  (int)LObjI,
	 ECodeCyan, (long long)(LObjStartPtr - P3DLoader->Buffer), ECodeNormal, LObjectID, LName, LType);
	fflush(stdout);
}
					const ECoreClass*  LClass = (LType >= 0 && LType < PContext->Classes.Count) ? PContext->Classes.Items[LType]->Class : NULL;if(LClass)
					{
						E_NewC(LClass, LObject);
						EObject_SetName(LObject, LName, FALSE);

//printf("%s() %s:%d  (%s) <%p:%s>\n", __FUNCTION__, __FILE__, __LINE__, LClass->Name, LObject, LName);fflush(stdout);

if(E3dDebugOBJECT)
{
	#if E3dDebugSHOW_POINTERS
		printf(" (%s) %p  RefCnt %d\n", LClass->Name, LObject, LObject->RefCnt);fflush(stdout);
	#else
		printf(" (%s)  RefCnt %d\n", LClass->Name, LObject->RefCnt);fflush(stdout);
	#endif
}

if(E3dDebugOBJECT) { printf(" %s() %s:%d  PContext->AllObjects index %lld\n", __FUNCTION__, __FILE__, __LINE__, (long long)PContext->AllObjects.Count);fflush(stdout); }

						if(_CDS_FileVersion >= 928) { if(LObjectID >= 0) _ObjectAddByID(PContext, _ObjectsALLOC_INCREMENT, (EObject*)LObject, LObjectID); }
						else { EIndex  LIdx;(void)LIdx;EList_ObjectAddAChk_s(PContext->AllObjects, _ObjectsALLOC_INCREMENT, LObject,  LIdx); }

if(E3dDebugOBJECT)
{
	printf(" %s() %s:%d  Resources at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal);fflush(stdout);
}

//PContext->Verbose = 2;
//printf("%s() %s:%d <%s>\n", __FUNCTION__, __FILE__, __LINE__, LName);fflush(stdout);
						const int  LLen = EObject_ResourcesFromBinaryImage((ECoreObject*)LObject, PContext, LBuffer, LPtr,  &PContext->ObjectProxies);
//PContext->Verbose = 0;

if(E3dDebugOBJECT) { printf(" %s() %s:%d  EResource context version : %d\n", __FUNCTION__, __FILE__, __LINE__, PContext->Version);fflush(stdout); }

						if(LLen >= 0)
						{
							LPtr += LLen;
if(E3dDebugOBJECT)
{
	printf(" %s() %s:%d  Resources ended at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal);fflush(stdout);
}
						}
						else
						{
printf("%s() %s:%d  (%s):%s at %08llx EObject_ResourcesFromBinaryImage() returned code %d (%s) reading file %sFAILED%s\n",  __FUNCTION__, __FILE__, __LINE__,
 LClass->Name, LName,  (long long)(LPtr - LBuffer), LLen, EResult_ToString(LLen), ECodeRed, ECodeNormal);
fflush(stdout);
// Skip the rest of the 'Obj=' chunk
//						goto Done;
							return(E_CORRUPT_DATA);
						}

						if(EObject_IsSubClass(LObject, E3dGeometry))
						{
if(E3dDebugOBJECT)
{
		#if DebugPOINTERS
			printf(" %s() %s:%d  at %s%08llx%s (%p) before calling _GeometryLoad()\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal, LPtr);
			fflush(stdout);
		#else
			printf(" %s() %s:%d  at %s%08llx%s before calling _GeometryLoad()\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal);fflush(stdout);
		#endif
}
							_GeometryLoad(PContext, P3DLoader, &LPtr, LName, PFeatureFlags,  LObject);
if(E3dDebugOBJECT)
{
		#if DebugPOINTERS
			printf(" %s() %s:%d  at %s%08llx%s (%p)\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal, LPtr);fflush(stdout);
		#else
			printf(" %s() %s:%d  at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal);fflush(stdout);
		#endif
}
						}
					}
					else
					{
if(E3dDebugOBJECT) { printf(" | Bad type ID %s%d%s <%s>\n", ECodeRed, LType, ECodeNormal, LName);fflush(stdout); }
						EFreeChk(LName);
//assert(0);
						return(E_CORRUPT_DATA);
					}
				}
			}
#ifdef CfOLD
			else
			{
				if(_CDS_FileVersion >= 900) LName = _Cf_GetStringOld(&LPtr);

				int  LType;EM_GetInt32I_be(LType, LPtr);

				switch(LType)
				{
					case CfObjGEOMETRY:
						LObject = (E3dObject*)_GeometryLoad(PContext, P3DLoader, &LPtr, LName, PFeatureFlags,  NULL);
					break;

					case CfObjLIGHT:
						LObject = (E3dObject*)_LightLoad(PContext, P3DLoader, &LPtr, LName);
					break;

					default:
						LObject = NULL;
						EFreeChkClr(LName);
					break;
				}
			}
#endif

			{ EIndex  LIdx;(void)LIdx;EList_ObjectAddAChk_s(PContext->AllObjects, _ObjectsALLOC_INCREMENT, LObject,  LIdx); }
if(E3dDebugOBJECT) { printf(" %s() %s:%d  PContext->AllObjects.Count %lld\n", __FUNCTION__, __FILE__, __LINE__, (long long)PContext->AllObjects.Count);fflush(stdout); }

			if(LObject)
			{
				if(_CDS_FileVersion >= 903)
				{
if(E3dDebugOBJECT)
{
	#if DebugPOINTERS
		printf("%s() %s:%d  Components at %s%08llx%s (%p)\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal, LPtr);fflush(stdout);
	#else
		printf(" %s() %s:%d  Components at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal);fflush(stdout);
	#endif
}
					if(LObject) LPtr = _GetObjectComponents(PContext, P3DLoader, LObject,  LBuffer, LPtr);
				}
if(E3dDebugOBJECT) { printf(" %s() %s:%d  end at %s%08llx%s\n}\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LBuffer), ECodeNormal);fflush(stdout); }
			}
		}
	}

//Done:;

// We didn't have the Objects before, so we just stored the indices in the LODs arrays...
// Now convert those indices into actual Object pointers
//
	EIndex  LObjI = 0;const EIndex  LObjN = PContext->AllObjects.Count;for(;LObjI < LObjN;LObjI++)
	{
		ECoreObject*  LObject = PContext->AllObjects.Items[LObjI];if(LObject)
		{
			if(EObject_IsSubClass(LObject, E3dGeometry))
			{
				E3dGeometry*   LGeometry = (E3dGeometry*)LObject;
				E3dGeometry**  LLODs = LGeometry->LODs;

				int  LODC = 0;const int  LODN = LGeometry->LODs_Count;for(;LODC < LODN;LODC++)
				{
					const int  LIndex = (EPointerInt)LLODs[LODC];
					if(LIndex >= 0 && LIndex < LObjN) E_AssignNoChkNoFree(PContext->AllObjects.Items[LIndex], LLODs[LODC]);
					else LLODs[LODC] = NULL;
				}
			}
		}
	}

	*PPtrP = LPtr;
	return(E_SUCCESS);
}


// Texture mapping types
//
enum
{
	CfTXT_FREEHAND = 0,     // No mapping, STs were edited by hand, or computed by the creator of the object
	CfTXT_XY,               // Linear
	CfTXT_XZ,
	CfTXT_YZ,
	CfTXT_PERSPECTIVE,
	CfTXT_UV,
	CfTXT_CYLINDRICAL,
	CfTXT_SPHERICAL,
	CfTXT_ENVIRONMENT_MAP,  // Needs a fish-eye image...
	CfTXT_CUBE_MAP_REFLECTION,
	CfTXT_CUBE_MAP_REFRACTION
};


static E3dTextureMapperClass* _TextureMapperIdToClass(const int PId)
{
	switch(PId)
	{
		case CfTXT_FREEHAND:             return(E3dTextureMapperFreehand_class);
		case CfTXT_XY:                   return(E3dTextureMapperXY_class);
		case CfTXT_XZ:                   return(E3dTextureMapperXZ_class);
		case CfTXT_YZ:                   return(E3dTextureMapperYZ_class);
		case CfTXT_UV:                   return(E3dTextureMapperUV_class);
		case CfTXT_CYLINDRICAL:          return(E3dTextureMapperCylindrical_class);
		case CfTXT_SPHERICAL:            return(E3dTextureMapperSpherical_class);
		case CfTXT_ENVIRONMENT_MAP:      return(E3dTextureMapperEnvMap_class);
		case CfTXT_CUBE_MAP_REFLECTION:  return(E3dTextureMapperCubeMapReflection_class);
		case CfTXT_CUBE_MAP_REFRACTION:  return(E3dTextureMapperCubeMapRefraction_class);
		default:                          return(NULL);
	}
}


const uint8_t* _BlendersRead(E3d2DTexture* PTexture, const uint8_t* PPtr)
{
	int  LIdx;const EIndex  LBN = PTexture->Blenders_Count;EM_GetInt32I_be(LIdx, PPtr);  // Blenders_Count

	EIndex  LBC = 0;E3dTextureBlender*  LBlender = PTexture->Blenders;for(;LBC < LIdx;LBC++, LBlender++)
	{
		if(LBC < LBN)
		{
			EM_GetFloat32I_be(LBlender->Factor, PPtr);
			EM_GetUInt32I_be(LBlender->Channels, PPtr);
		}
		else PPtr += 8;
	}

	return(PPtr);
}


static const EIndex _MaterialTexturesLoad(EResContext* PContext, E3dLoader* P3DLoader, E3dMaterial* PMaterial, const uint32_t PFeatureFlags, const uint8_t** PPtr)
{
	const uint8_t*         LPtr = *PPtr;
	const EResClassEntry*  LTextureClassEntry = EList_ClassEntryFind_s(PContext->Classes, (ECoreClass*)E3d2DTexture_class);

	EIndex  LTxN;EM_GetInt32I_be(LTxN, LPtr);

	EIndex  LTxI = 0;for(;LTxI < LTxN;LTxI++)
	{
		E3d2DTexture*  LTexture = E3dMaterial_AddTexture(PMaterial);if(LTexture)
		{
			if(PFeatureFlags & CfMaterialANIMATED_TEXTURE)
			{
				_GetObjectFlagsAndID(LPtr, LID);
				if(LID >= 0) _ObjectAddByID(PContext, 64, (EObject*)LTexture, LID);
			}

			if(_CDS_FileVersion >= 910)
			{
				{
					const int  LResult = ERes_FromBinaryImage(PContext, P3DLoader->Buffer, LPtr,  LTextureClassEntry ? LTextureClassEntry->AllResources : NULL, &E3dTexture_resources,
					  LTexture,  NULL);
					if(LResult >= 0) LPtr += LResult;
					else
					{
						printf("%s() %s:%d  %sMaterial [%s] Texture loading failed%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, PMaterial->Name, ECodeNormal);fflush(stdout);
						return(LResult);
					}
				}

				if(_CDS_FileVersion >= 926)
				{
					const char*               LTextureShaderClassName = (char*)LPtr;
					E3d2DTextureShaderClass*  LTextureShaderClass = E3d2DTextureShader_ClassByName(LTextureShaderClassName);
					LPtr += strlen((const char*)LPtr) + 1;
					if(LTextureShaderClass)
					{
						const EResClassEntry*  LTextureShaderClassEntry = EList_ClassEntryFind_s(PContext->Classes, (ECoreClass*)LTextureShaderClass);
						LTexture->Shader = E3d2DTextureShader_New(LTextureShaderClass);
if(0)
{
	printf("%s() %s:%d  %sTextureShaderClass%s %s %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LTextureShaderClass->Name, LTextureShaderClassEntry);
	fflush(stdout);
}
//PContext->Verbose = 2;
						const EIndex  LBinaryImageLen = ERes_FromBinaryImage(PContext, P3DLoader->Buffer, LPtr,  LTextureShaderClassEntry ? LTextureShaderClassEntry->AllResources : NULL,
						 &LTextureShaderClass->Resources,  LTexture->Shader,  NULL);
//PContext->Verbose = 0;
						if(LBinaryImageLen >= 0) LPtr += LBinaryImageLen;
						else
						{
							printf("%s() %s:%d  %sMaterial [%s] TextureShader loading failed%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, PMaterial->Name, ECodeNormal);fflush(stdout);
							return(LBinaryImageLen);
						}
					}
				}

				LPtr = _BlendersRead(LTexture, LPtr);

				if(PFeatureFlags & CfMaterialANIMATED_TEXTURE)
				{
					int32_t  LNImageFrames = 0;EM_GetUInt32I_be(LNImageFrames, LPtr);
					if(LNImageFrames > 0)
					{
						ENewNClr(LNImageFrames, LTexture->ImageFrames);
						int  LI = 0;for(;LI < LNImageFrames;LI++)
						{
							E3dImage*  LE3dImage;E_New(E3dImage, LE3dImage);
							EM_GetFloat64I_be(LE3dImage->TimeStamp, LPtr);
							const size_t  LL = strlen((char*)LPtr);
							LE3dImage->FileName = EStrDup((char*)LPtr);LPtr += LL + 1;
							E_AssignToMember(LE3dImage, LTexture,ImageFrames[LI]);
						}
						LTexture->ImageFrames_Count = LTexture->ImageFrames_CountAllocated = LNImageFrames;
					}
				}
			}
			else
			{
				if(_CDS_FileVersion >= 708)
				{
					const uint8_t  LCh = *LPtr++;
					if(LCh & 0x01) LTexture->UseMIPMaps = TRUE;
					if(LCh & 0x04) LTexture->AnisotropicFiltering = TRUE;
					if(LCh & 0x02) LTexture->ImageAsNormalMap = TRUE;
					LTexture->MagFilter = *LPtr++;

					if(_CDS_FileVersion >= 906) LTexture->NormalMapType = *LPtr++;
					else LPtr++;
					LPtr++;
				}

				if(_CDS_FileVersion < 801)
				{
					int  LIdx;(void)LIdx;

					EM_GetInt32I_be(LIdx, LPtr);  // Texture SizeX / SizeY
					EM_GetInt32I_be(LIdx, LPtr);

// Used to be NumOfComponents
//
					LPtr += 4;

					int  LMapperClassID;EM_GetInt32I_be(LMapperClassID, LPtr);
					{
						E3dTextureMapper*  LMapper = E3dTextureMapper_New(_TextureMapperIdToClass(LMapperClassID));

						EM_GetFloat32I_be(LMapper->STScaling.X, LPtr);
						EM_GetFloat32I_be(LMapper->STOffset.X, LPtr);
						EM_GetInt32I_be(LMapper->STCount.X, LPtr);
						EM_GetFloat32I_be(LMapper->STScaling.Y, LPtr);
						EM_GetFloat32I_be(LMapper->STOffset.Y, LPtr);
						EM_GetInt32I_be(LMapper->STCount.Y, LPtr);

// If there's no Mapping (free-hand ST), turn on repeats
//
						if(LMapperClassID == CfTXT_FREEHAND)
						{
							LMapper->STCount.X = 2;
							LMapper->STCount.Y = 2;
						}

						EM_GetFloat32I_be(LMapper->EulerRotation.Angles.X, LPtr);
						EM_GetFloat32I_be(LMapper->EulerRotation.Angles.Y, LPtr);
						EM_GetFloat32I_be(LMapper->EulerRotation.Angles.Z, LPtr);
						E_AssignToMember(LMapper, LTexture,MapperOld);
					}
				}

				E3dTextureBlender*  LBlender;
				EIndex              LBC;const EIndex  LBN = LTexture->Blenders_Count;
				float               LFV;

				if(_CDS_FileVersion <= 905)
				{
					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Ambient"))  LBlender->Factor = LFV;

					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Diffuse"))  LBlender->Factor = LFV;

					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Specular")) LBlender->Factor = LFV;

					if(_CDS_FileVersion >= 806)
					{
						EM_GetFloat32I_be(LFV, LPtr);
						for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Emission")) LBlender->Factor = LFV;
					}

					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Reflective")) LBlender->Factor = LFV;

					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Transparent")) LBlender->Factor = LFV;

					EM_GetFloat32I_be(LTexture->Bump.Factor, LPtr);

					LPtr += 4;	// OBSOLETE: used to be LTexture->ReflectionMapFactor
				}
				else
				{
					LPtr = _BlendersRead(LTexture, LPtr);

					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Emission"))    LBlender->Factor = LFV;

					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Reflective"))  LBlender->Factor = LFV;

					EM_GetFloat32I_be(LFV, LPtr);
					for(LBC = 0, LBlender = LTexture->Blenders;LBC < LBN;LBC++, LBlender++) if(EStr_Equal(LBlender->ShaderResource->Name, "Transparent")) LBlender->Factor = LFV;

					EM_GetFloat32I_be(LTexture->Bump.Factor, LPtr);
				}

				if(_CDS_FileVersion >= 909) LTexture->FileName = _Cf_GetString(&LPtr);
				else LTexture->FileName = _Cf_GetStringOld(&LPtr);
			}
		}
	}

	*PPtr = LPtr;

	return(E_SUCCESS);
}


static void _SkinnedMeshesBoneIndicesToPointers(E3dLoader* P3DLoader)
{
	int  LI = 0;const int  LN = P3DLoader->SkinnedMeshes_Count;for(;LI < LN;LI++)
	{
		E3dMesh*          LMesh = P3DLoader->SkinnedMeshes[LI];
		E3dSkinMeshInfo*  LSkin = (E3dSkinMeshInfo*)E3dObjectComponent_ByClass((E3dObject*)LMesh, E3dSkinMeshInfo_class, FALSE);if(LSkin)
		{
			E3dSkinJoint*     LSkinJoints = LSkin->Joints.Items;

			EIndex  LJC = 0;const EIndex  LJN = LSkin->Joints.Count;E3dSkinJoint*  LSkinJoint = LSkinJoints;for(;LJC < LJN;LJC++, LSkinJoint++)
			{
				const int  LIndex = (EPointerInt)LSkinJoint->Node;

// Keep this like this! Don't move LSkinJoint->Node = NULL before the if! Some compilers have issues because of the aliasing here.
//
				if(LIndex > -1 && LIndex <= P3DLoader->Nodes_Count) E_AssignNoChkNoFree(P3DLoader->Nodes[LIndex], LSkinJoint->Node);
				else LSkinJoint->Node = NULL;
			}
		}
	}
}


#if E3dDebugREF
#define _Dbg_TargetPrint()\
if(LTargetIsObject)\
{\
	if(LTarget->Resource->Type & EResDYNAMIC)\
	{\
/* It's an Object array */\
		printf(" {Objects[]}");fflush(stdout);\
	}\
	else if(LTargetObject->Class)\
	{\
		printf(" [%s%p%s:(%s)%s%s%s]", ECodeCyan, LTarget->Object, ECodeNormal, LTargetObject->Class->Name ? LTargetObject->Class->Name : "",\
		 ECodeGreen, LTargetObject->Name ? LTargetObject->Name : "", ECodeNormal);\
		fflush(stdout);\
		printf(".%s%s%s (%p)", ECodeCyan, LTarget->Resource->Name, ECodeNormal, LTarget->ObjectRef);fflush(stdout);\
	}\
	else { printf("{}");fflush(stdout); }\
}\
else\
{\
	printf("[%s%p%s:{struct}]", ECodeCyan, LTarget->Object, ECodeNormal);fflush(stdout);\
}\
if(LTC < LTN - 1) printf(", ")
#else
#define _Dbg_TargetPrint()
#endif // E3dDebugREF


static int _ProxyResolve(EObjectProxy* PProxy, EResContext* PContext,  const EIndex* PObjectIndices, const EIndex PNObjectIndices)
{
	if(PProxy->Targets)
	{
		const EPointerInt  LIndex = (EPointerInt)PProxy->Object;
		EPointerInt        LIndex1 = LIndex;

		if(LIndex >= -1 && LIndex < PNObjectIndices)
		{
			ECoreObject*  LObject;

			if(LIndex == -1) LObject = NULL;  // -1 means NULL pointer. This is a perfectly valid value. An EResOBJECT type field of a struct may just be NULL.
			else
			{
				LIndex1 = PObjectIndices[LIndex];
#if E3dDebugREF
//printf("%s() %s:%d  %3lld -> %3lld", __FUNCTION__, __FILE__, __LINE__, (long long)LIndex, (long long)LIndex1);fflush(stdout);
#endif // E3dDebugREF
				if(LIndex1 >= 0 && LIndex1 < PContext->AllObjects.Count)
				{
					LObject = PContext->AllObjects.Items[LIndex1];
#if E3dDebugREF
//printf(" (%s) |%s|\n", LObject ? LObject->Class->Name : "", LObject ? LObject->Name : "");fflush(stdout);
#endif // E3dDebugREF
				}
				else
				{
#if E3dDebugREF
//printf("\n");fflush(stdout);
printf("%s() %s:%d Object not found for index %lld\n", __FUNCTION__, __FILE__, __LINE__, (long long)LIndex);fflush(stdout);
#endif // E3dDebugREF
					return(E_NOT_FOUND);
				}
			}


			{
#if E3dDebugREF
{
const char*  LObjectName      = LObject && LObject->Name  ? LObject->Name        : "";const size_t  LNL = strlen(LObjectName);
const char*  LObjectClassName = LObject && LObject->Class ? LObject->Class->Name : "";const size_t  LCL = strlen(LObjectClassName);
char*        LStr = EMalloc(2 + 64 + 4 + 64 + LCL + 2 + LNL + 8);
sprintf(LStr, " %3lld -> %3lld [%s%p%s:(%s)%s%s%s]", (long long)LIndex, (long long)LIndex1, ECodeCyan, LObject, ECodeNormal,
 LObjectClassName, ECodeGreen, LObjectName, ECodeNormal);
EStr_StringPadToLength(LStr, 72);
printf("%s assign to:", LStr);fflush(stdout);
EFree(LStr);
}
#endif // E3dDebugREF

				int  LTC = 0;const int  LTN = PProxy->Targets_Count;ETargetSlot*  LTarget = PProxy->Targets;for(;LTC < LTN;LTC++, LTarget++)
				{
#if E3dDebugREF || EObjectMEMORY_DEBUG
					ECoreObject*  LTargetObject = LTarget->Object;

// This is a hacky test, because LTarget->Object may not be an Object, so it may not have Name and Class fields.
// But, it's only used for debugging.
//
//const EBool  LTargetIsObject = LTarget->Resource && (LTarget->Resource->Type & (~(EResDYNAMIC|EResCOUNT|EResCOUNT_FUNC))) == EResOBJECT;if(LTargetIsObject)
const EBool  LTargetIsObject = E3d_IsObject(LTargetObject);
#endif
					_Dbg_TargetPrint();

#if EObjectMEMORY_DEBUG
					{
						if(LTargetIsObject)
						{
//						EObject_NamedResourceAssignToMember(LObject, LTargetObject,LTarget->Resource->Name);
							E_AssignToProxy(LObject, LTarget->ObjectRef[0], LTargetObject,LTarget->Resource->Name);
						}
						else E_Assign(LObject, LTarget->ObjectRef[0]);
					}
#else
					E_Assign(LObject, LTarget->ObjectRef[0]);
#endif
				}
			}
#if E3dDebugREF
printf("\n");fflush(stdout);
#endif // E3dDebugREF
		}
		else
		{
if(1)
{
	printf("%s() %s:%d  Proxy Object index %lld is out of range (0 .. %lld)  meant to be assigned to:",  __FUNCTION__, __FILE__, __LINE__,  (long long)LIndex,
	 (long long)PNObjectIndices);
	fflush(stdout);
}

			int  LTC = 0;const int  LTN = PProxy->Targets_Count;ETargetSlot*  LTarget = PProxy->Targets;for(;LTC < LTN;LTC++, LTarget++)
			{
//printf(" [%s%p%s:%s]", ECodeCyan, LTarget->Object, ECodeNormal, "");fflush(stdout);
#if E3dDebugREF
				ECoreObject*  LTargetObject = LTarget->Object;
				const EBool  LTargetIsObject = E3d_IsObject(LTargetObject);
#endif
				_Dbg_TargetPrint();
			}
printf("\n");fflush(stdout);
		}

		EList_FreeA(PProxy->Targets);
	}
	return(E_SUCCESS);
}


static E3dAnimation* _SceneFindResidentAnimationByClass(E3dScene* PScene, const E3dAnimationClass* PClass)
{
	EIndex  LI = 0;const EIndex  LN = PScene->GlobalAnimations.Count;for(;LI < LN;LI++)
	{
		E3dAnimation*  LAnimation = PScene->GlobalAnimations.Items[LI];if(!LAnimation->Removable)
		{
			if(LAnimation->Class == PClass) return(LAnimation);
		}
	}
	return(NULL);
}
