❄️🎁⛄ The Winter Games Tournament is Live! 🎄🎅❄️
Jump to content

Recommended Posts

Posted

A small change was needed for LOD distances. The current version is now 301. A Vec3 was added in the node properties. The old per-LOD value will be ignored.

If you don't know what the LOD distances should be, just fill it with NAN values.

See G3DModelLoader::LoadNode() below.

The original version 300 models will continue to load with no changes.

#include "UltraEngine.h"

using namespace UltraEngine;

namespace UltraEngine::Core
{
	G3DModelLoader::G3DModelLoader()
	{
		extensions = { L"mdl" };
	}

	String G3DModelLoader::ReadText(shared_ptr<Stream> stream)
	{
		int len = stream->ReadInt();
		auto pos = stream->GetPosition();
		String s;
		if (len)
		{
			s = stream->ReadString(len);
			stream->Seek(pos + len);
		}
		return s;
	}
	
	bool G3DModelLoader::Reload(shared_ptr<Stream> stream, shared_ptr<Object> o, const LoadFlags flags)
	{
		auto modelbase = o->As<ModelBase>();
		if (modelbase == NULL) return false;
		modelbase->model = CreateModel(NULL);
		auto model = modelbase->model->As<Model>();

		auto start = stream->GetPosition();
		if (stream->ReadString(12) != "Ultra Model") return false;
		Assert(stream->GetPosition() == start + 12);
		this->version = stream->ReadInt();
		if (version != 300 and version != 301)
		{
			Print("Error: MDL version " + String(version) + " not supported");
			return false;
		}

		return LoadNode(stream, model, flags);
	}

	bool G3DModelLoader::LoadNode(shared_ptr<Stream> stream, shared_ptr<Model> model, const LoadFlags flags)
	{
		Vec3 pos, scale, lodrange;
		Vec4 color;
		Quat rot;
		String s;
		if (stream->ReadString(4) != "NODE")
		{
			Print("Error: Expected NODE tag");
			return false;
		}

		model->name = ReadText(stream);
		model->properties = ParseJson(ReadText(stream));
		ParseJson(ReadText(stream));

		pos.x = stream->ReadFloat(); pos.y = stream->ReadFloat(); pos.z = stream->ReadFloat();
		rot.x = stream->ReadFloat(); rot.y = stream->ReadFloat(); rot.z = stream->ReadFloat(); rot.w = stream->ReadFloat();
		scale.x = stream->ReadFloat(); scale.y = stream->ReadFloat(); scale.z = stream->ReadFloat();
		color.x = stream->ReadFloat(); color.y = stream->ReadFloat(); color.z = stream->ReadFloat(); color.a = stream->ReadFloat();
		if (version > 300)
		{
			lodrange.x = stream->ReadFloat(); lodrange.y = stream->ReadFloat(); lodrange.z = stream->ReadFloat();
		}
		model->SetPosition(pos);
		model->SetRotation(rot);
		model->SetScale(scale);
		model->SetColor(color);
		if (version > 300)
		{
			auto range = model->GetLodDistance();
			if (not isnan(lodrange.x)) range.x = lodrange.x;
			if (not isnan(lodrange.y)) range.y = lodrange.y;
			if (not isnan(lodrange.z)) range.z = lodrange.z;
			model->SetLodDistance(range.x, range.y, range.z);
		}

		int countlods = stream->ReadInt();
		for (int level = 0; level < countlods; ++level)
		{
			if (not LoadLod(stream, model, level, flags)) return false;
		}

		// Skeleton
		if (stream->ReadString(4) != "SKEL")
		{
			Print("Error: Expected SKEL tag");
			return false;
		}
		int bones = stream->ReadInt();
		if (bones)
		{
			if (model->GetParent())
			{
				Print("Error: Skeleton can only appear in the model root node");
				return false;
			}
			if (bones < 0)
			{
				Print("Error: Skeleton bones must be more than zero");
				return false;
			}
			auto skeleton = CreateSkeleton(nullptr);
			skeleton->root = std::make_shared<Bone>(nullptr, skeleton);
			skeleton->bones.resize(bones);
			if (not LoadBone(stream, skeleton, skeleton->root, 0, flags)) return false;
			skeleton->root->UpdateMatrix();
			skeleton->bones[0] = skeleton->root;
			skeleton->UpdateSkinning();
			for (int n = 0; n < skeleton->bones.size(); ++n)
			{
				if (skeleton->bones[n] == NULL) continue;
				auto bone = skeleton->bones[n];
				bone->inversebindmatrix = bone->matrix.Inverse();
				bone->animbone->inversebindmatrix = bone->inversebindmatrix;
				bone->Finalize();
			}
			model->SetSkeleton(skeleton);
		}

		//Attachment
		if (stream->ReadString(4) != "ATCH")
		{
			Print("Error: Expected ATCH tag");
			return false;
		}
		int attachmentboneid = stream->ReadInt();
		if (attachmentboneid != -1)
		{
			stream->Seek(stream->GetPosition() + 64);
		}

		// Animations
		if (stream->ReadString(4) != "ASET")
		{
			Print("Error: Expected ASET tag");
			return false;
		}
		int animations = stream->ReadInt();
		if (animations)
		{
			if (model->GetParent())
			{
				Print("Error: Animations can only appear in the model root node");
				return false;
			}
			for (int anim = 0; anim < animations; ++anim)
			{
				if (stream->ReadString(4) != "ANIM")
				{
					Print("Error: Expected ANIM tag");
					return false;
				}

				auto seq = std::make_shared<Sequence>();
				model->skeleton->root->animations.push_back(seq);
				model->skeleton->root->animbone->animations.push_back(seq);

				WString animname = ReadText(stream);
				float speed = stream->ReadFloat();
				int keyframes = stream->ReadInt();
				float duration = float(keyframes) / 60.0f * speed;

				int bones = stream->ReadInt();
				for (int b = 0; b < bones; ++b)
				{
					if (stream->ReadString(4) != "BONE")
					{
						Print("Error: Expected BONE tag");
						return false;
					}
					int keyflags = stream->ReadInt();
					auto bone = model->skeleton->bones[b];
					bone->animations.resize(animations);
					bone->animations[anim] = std::make_shared<Sequence>();
					bone->animations[anim]->name = animname;
					bone->animations[anim]->speed = speed;
					bone->animations[anim]->keyframes.reserve(keyframes);
					bone->animations[anim]->duration = duration;
					KeyFrame key;
					for (int k = 0; k < keyframes; ++k)
					{
						if ((1 & keyflags) != 0)
						{
							key.position.x = stream->ReadFloat();
							key.position.y = stream->ReadFloat();
							key.position.z = stream->ReadFloat();
						}
						if ((2 & keyflags) != 0)
						{
							key.rotation.x = stream->ReadFloat();
							key.rotation.y = stream->ReadFloat();
							key.rotation.z = stream->ReadFloat();
							key.rotation.w = stream->ReadFloat();
						}
						if ((4 & keyflags) != 0)
						{
							key.scale = stream->ReadFloat();
							stream->ReadFloat();
							stream->ReadFloat();
						}
						bone->animations[anim]->keyframes.push_back(key);
					}
					bone->animbone->animations = bone->animations;
				}
			}
		}

		// Collider
		if (stream->ReadString(4) != "PHYS")
		{
			auto pos = stream->GetPosition() - 4;
			Print("Error: Expected PHYS tag at position " + String(pos));
			return false;
		}
		int colliderdatasize = stream->ReadInt();
		auto colliderstartposition = stream->GetPosition();
		if (colliderdatasize)
		{
			Vec3 position, scale, euler;
			Quat rotation;
			std::vector<shared_ptr<Collider> > parts;
			int partcount = stream->ReadInt();
			for (int n = 0; n < partcount; ++n)
			{
				if (stream->ReadString(4) != "PART")
				{
					auto pos = stream->GetPosition() - 4;
					Print("Error: Expected PART tag at position " + String(pos));
					return false;
				}
				shared_ptr<Collider> part;
				auto tag = stream->ReadString(4);
				if (tag == "HULL")
				{
					float tol = stream->ReadFloat();// tolerance
					std::vector<Vec3> points;
					Vec3 p;
					int count = stream->ReadInt();
					for (int n = 0; n < count; ++n)
					{
						p.x = stream->ReadFloat();
						p.y = stream->ReadFloat();
						p.z = stream->ReadFloat();
						points.push_back(p);
					}
					part = CreateConvexHullCollider(points, 0.0f);
					if (part) part->tolerance = tol;
				}
				else if (tag == "MESH")
				{
					int opt = stream->ReadInt();// optimize flag
					int count = stream->ReadInt();
					int vcount;
					std::vector<Vec3> face;
					Vec3 p;
					std::vector<std::vector<Vec3> > meshfaces;
					for (int n = 0; n < count; ++n)
					{
						vcount = stream->ReadInt();
						face.clear();
						for (int v = 0; v < vcount; ++v)
						{
							p.x = stream->ReadFloat();
							p.y = stream->ReadFloat();
							p.z = stream->ReadFloat();
							face.push_back(p);
						}
						meshfaces.push_back(face);
					}
					part = CreateMeshCollider(meshfaces, false);
					if (part) part->optimizemesh = opt;
				}
				else
				{
					position.x = stream->ReadFloat(); position.y = stream->ReadFloat(); position.z = stream->ReadFloat();
					rotation.x = stream->ReadFloat(); rotation.y = stream->ReadFloat(); rotation.z = stream->ReadFloat(); rotation.w = stream->ReadFloat();
					scale.x = stream->ReadFloat(); scale.y = stream->ReadFloat(); scale.z = stream->ReadFloat();
					if (tag == "BOX_")
					{
						part = CreateBoxCollider(scale, position, rotation.Euler());						
					}
					else if (tag == "CYLI")
					{
						part = CreateCylinderCollider(scale.x, scale.y, position, rotation.Euler());
					}
					else if (tag == "CONE")
					{
						part = CreateConeCollider(scale.x, scale.y, position, rotation.Euler());
					}
					else if (tag == "CCYL")
					{
						part = CreateChamferCylinderCollider(scale.x, scale.y, position, rotation.Euler());
					}
					else if (tag == "CAPS")
					{
						part = CreateCapsuleCollider(scale.x, scale.y, position, rotation.Euler());
					}
					else if (tag == "SPHE")
					{
						part = CreateSphereCollider(scale.x, position);
					}
					else
					{
						Print("Error: Unknown collider type \"" + tag + "\"");
						return false;
					}
				}
				if (part) parts.push_back(part);
			}
			if (parts.size())
			{
				if (parts.size() == 1)
				{
					model->SetCollider(parts[0]);
				}
				else
				{
					auto c = CreateCompoundCollider(parts);
					model->SetCollider(c);
				}
			}
			stream->Seek(colliderstartposition + colliderdatasize);
		}

		// Load children
		if (stream->ReadString(4) != "KIDS")
		{
			Print("Error: Expected KIDS tag");
			return false;
		}
		int countkids = stream->ReadInt();
		for (int n = 0; n < countkids; ++n)
		{
			auto child = CreateModel(NULL);
			child->SetParent(model);
			if (not LoadNode(stream, child, flags)) return false;
		}

		model->UpdateBounds();
		return true;
	}

	bool G3DModelLoader::LoadLod(shared_ptr<Stream> stream, shared_ptr<Model> model, const int level, const LoadFlags flags)
	{
		if (stream->ReadString(4) != "LOD_")
		{
			Print("Error: Expected LOD_ tag");
			return false;
		}

		if (level >= model->lods.size()) model->AddLod();
		float loddistance = stream->ReadFloat();
		if (version < 301)
		{
			if (loddistance > 0.0f and model->lods.size() > 0 and model->lods.size() <= 4)
			{
				auto lods = model->GetLodDistance();
				int i = int(model->lods.size()) - 2;
				lods[i] = loddistance;
				model->SetLodDistance(lods.x, lods.y, lods.z);
			}
		}
		int countmeshes = stream->ReadInt();
		for (int m = 0; m < countmeshes; ++m)
		{
			if (not LoadMesh(stream, model, level, flags)) return false;
		}
		return true;
	}

	bool G3DModelLoader::LoadMesh(shared_ptr<Stream> stream, shared_ptr<Model> model, const int level, const LoadFlags flags)
	{
		if (stream->ReadString(4) != "MESH")
		{
			Print("Error: Expected MESH tag");
			return false;
		}
		
		MeshPrimitives type = MeshPrimitives(stream->ReadInt());
		if (type < 1 or type > 4)
		{
			Print("Error: Mesh type must be between one and four");
			return false;
		}

		auto mesh = model->AddMesh(type, level);

		mesh->name = ReadText(stream);

		WString mtlpath = ReadText(stream);
		if (not mtlpath.empty())
		{
			if (mtlpath.Left(2) == "./" and not stream->path.empty())
			{
				mtlpath = ExtractDir(stream->path) + "/" + mtlpath;
			}
			auto mtl = LoadMaterial(mtlpath, flags);
			if (mtl) mesh->SetMaterial(mtl);
		}

		int vertexstride = stream->ReadInt();
		if (vertexstride != 88)
		{
			Print("Vertext stride must be 84");
			return false;
		}
		int vertexcount = stream->ReadInt();

		mesh->m_vertices.resize(vertexcount);
		for (int v = 0; v < vertexcount; ++v)
		{
			mesh->m_vertices[v].position.x = stream->ReadFloat();
			mesh->m_vertices[v].position.y = stream->ReadFloat();
			mesh->m_vertices[v].position.z = stream->ReadFloat();

			mesh->m_vertices[v].normal.x = stream->ReadFloat();
			mesh->m_vertices[v].normal.y = stream->ReadFloat();
			mesh->m_vertices[v].normal.z = stream->ReadFloat();
			
			mesh->m_vertices[v].texcoords.x = stream->ReadFloat();
			mesh->m_vertices[v].texcoords.y = stream->ReadFloat();
			mesh->m_vertices[v].texcoords.z = stream->ReadFloat();
			mesh->m_vertices[v].texcoords.w = stream->ReadFloat();
			
			mesh->m_vertices[v].color.r = float(stream->ReadByte()) / 255.0f;
			mesh->m_vertices[v].color.g = float(stream->ReadByte()) / 255.0f;
			mesh->m_vertices[v].color.b = float(stream->ReadByte()) / 255.0f;
			mesh->m_vertices[v].color.a = float(stream->ReadByte()) / 255.0f;
			
			mesh->m_vertices[v].displacement = stream->ReadFloat();
			
			mesh->m_vertices[v].tangent.x = stream->ReadFloat();
			mesh->m_vertices[v].tangent.y = stream->ReadFloat();
			mesh->m_vertices[v].tangent.z = stream->ReadFloat();
			
			mesh->m_vertices[v].bitangent.x = stream->ReadFloat();
			mesh->m_vertices[v].bitangent.y = stream->ReadFloat();
			mesh->m_vertices[v].bitangent.z = stream->ReadFloat();
			
			mesh->m_vertices[v].boneindices[0] = stream->ReadShort();
			mesh->m_vertices[v].boneindices[1] = stream->ReadShort();
			mesh->m_vertices[v].boneindices[2] = stream->ReadShort();
			mesh->m_vertices[v].boneindices[3] = stream->ReadShort();

			mesh->m_vertices[v].boneweights.x = float(stream->ReadByte()) / 255.0f;
			mesh->m_vertices[v].boneweights.y = float(stream->ReadByte()) / 255.0f;
			mesh->m_vertices[v].boneweights.z = float(stream->ReadByte()) / 255.0f;
			mesh->m_vertices[v].boneweights.w = float(stream->ReadByte()) / 255.0f;

			//SubD normals
			stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat();
		}

		Vec4 zero;
		for (const auto& vertex : mesh->vertices)
		{
			if (vertex.boneweights != zero)
			{
				mesh->SetSkinned(true);
				break;
			}
		}

		int indicesize = stream->ReadInt();
		int indicecount = stream->ReadInt();

		uint32_t index;		
		switch (indicesize)
		{
		case 2:
			mesh->m_indices.reserve(indicecount);
			for (int i = 0; i < indicecount; ++i) mesh->AddIndice(stream->ReadShort());
			break;
		case 4:
			mesh->m_indices.resize(indicecount);
			stream->Read(mesh->m_indices.data(), indicecount * sizeof(mesh->indices[0]));
			break;
		default:
			return false;
		}

		//Primitives flags
		if (stream->ReadString(4) != "PRIM")
		{
			Print("Error: Expected PRIM tag");
			return false;
		}
		int primcount = stream->ReadInt();
		if (primcount)
		{
			if (primcount != indicecount / type)
			{
				Print("Error: Primitives count must be equal to the number of primitives in the mesh, or zero");
				return false;
			}
			stream->Seek(stream->GetPosition() + primcount);
		}

		//Vertex morphs
		if (stream->ReadString(4) != "MSET")
		{
			Print("Error: Expected MORP tag");
			return false;
		}
		int morphcount = stream->ReadInt();
		for (int m = 0; m < morphcount; ++m)
		{
			if (stream->ReadString(4) != "MORP")
			{
				Print("Error: Expected MORP tag");
				return false;
			}

			if (stream->ReadInt() != 48) return false;

			for (int v = 0; v < vertexcount; ++v)
			{
				// Position
				stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat();

				// Normal
				stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat();

				// Tangent
				stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat();

				// Bitangent
				stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat();
			}
		}

		// Pick structure cache
		if (stream->ReadString(4) != "PICK")
		{
			Print("Error: Expected PICK tag");
			return false;
		}
		int pickcachesize = stream->ReadInt();
		if (pickcachesize) stream->Seek(stream->GetPosition() + pickcachesize);

		mesh->UpdateBounds();
		return true;
	}

	bool G3DModelLoader::LoadBone(shared_ptr<Stream> stream, shared_ptr<Skeleton> skeleton, shared_ptr<Bone> bone, const int animcount, const LoadFlags flags)
	{
		if (stream->ReadString(4) != "BONE")
		{
			Print("Error: Expected BONE tag");
			return false;
		}

		bone->m_id = stream->ReadInt();
		bone->animbone->id = bone->id;

		//Print(bone->m_id);
		
		if (bone->id >= skeleton->bones.size()) skeleton->bones.resize(bone->id + 1);
		skeleton->bones[bone->id] = bone;
		if (bone->id >= skeleton->animskeleton->bones.size()) skeleton->animskeleton->bones.resize(bone->id + 1);
		skeleton->animskeleton->bones[bone->id] = bone->animbone;

		bone->name = ReadText(stream);

		bone->position.x = stream->ReadFloat(); bone->position.y = stream->ReadFloat(); bone->position.z = stream->ReadFloat();
		
		bone->quaternion.x = stream->ReadFloat(); bone->quaternion.y = stream->ReadFloat(); bone->quaternion.z = stream->ReadFloat(); bone->quaternion.w = stream->ReadFloat();

		bone->scale = stream->ReadFloat(); stream->ReadFloat(); stream->ReadFloat();// scale y and z not supported

		/*int count = stream->ReadInt();
		if (bone->GetParent() and count != animcount)
		{
			Print("Error: Bone animation count must match that of the root node");
			return false;
		}

		int i = stream->GetPosition();

		for (int anim = 0; anim < count; ++anim)
		{
			auto seq = std::make_shared<Sequence>();
			bone->animations.push_back(seq);
			bone->animbone->animations.push_back(seq);

			if (stream->ReadString(4) != "ANIM")
			{
				Print("Error: Expected ANIM tag");
				return false;
			}
			
			bone->animations[anim]->name = ReadText(stream);
			bone->animations[anim]->speed = stream->ReadFloat();
			int keyflags = stream->ReadInt();
			int keyframes = stream->ReadInt();
			if (not keyflags or not keyframes) continue;
			bone->animations[anim]->duration = float(keyframes) / 60.0f * bone->animations[anim]->speed;

			KeyFrame key;
			bone->animations[anim]->keyframes.reserve(keyframes);
			for (int k = 0; k < keyframes; ++k)
			{
				if ((1 & keyflags) != 0)
				{
					key.position.x = stream->ReadFloat();
					key.position.y = stream->ReadFloat();
					key.position.z = stream->ReadFloat();
				}
				if ((2 & keyflags) != 0)
				{
					key.rotation.x = stream->ReadFloat();
					key.rotation.y = stream->ReadFloat();
					key.rotation.z = stream->ReadFloat();
					key.rotation.w = stream->ReadFloat();
				}
				if ((4 & keyflags) != 0)
				{
					key.scale = stream->ReadFloat();
					stream->ReadFloat();
					stream->ReadFloat();
				}
				bone->animations[anim]->keyframes.push_back(key);
			}			
		}
		bone->animbone->animations = bone->animations;
		*/

		if (stream->ReadString(4) != "KIDS")
		{
			Print("Error: Expected KIDS tag");
			return false;
		}
		int childcount = stream->ReadInt();
		for (int n = 0; n < childcount; ++n)
		{
			auto child = std::make_shared<Bone>(bone, skeleton);
			bone->kids.push_back(child);
			if (not LoadBone(stream, skeleton, child, bone->animations.size(), flags))
			{
				return false;
			}
		}
		
		return true;
	}
}

 

  • Thanks 1

Let's build cool stuff and have fun. :)

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...