diff --git a/VisualC/HatchGameEngine.vcxproj b/VisualC/HatchGameEngine.vcxproj index b15d4e6b..66a3d728 100644 --- a/VisualC/HatchGameEngine.vcxproj +++ b/VisualC/HatchGameEngine.vcxproj @@ -245,7 +245,10 @@ CD .. + + + diff --git a/VisualC/HatchGameEngine.vcxproj.filters b/VisualC/HatchGameEngine.vcxproj.filters index 808e77d1..602081df 100644 --- a/VisualC/HatchGameEngine.vcxproj.filters +++ b/VisualC/HatchGameEngine.vcxproj.filters @@ -285,9 +285,18 @@ Source Files + + Source Files + Source Files + + Source Files + + + Source Files + Source Files diff --git a/source/Engine/Bytecode/StandardLibrary.cpp b/source/Engine/Bytecode/StandardLibrary.cpp index d84669db..2f0526e9 100644 --- a/source/Engine/Bytecode/StandardLibrary.cpp +++ b/source/Engine/Bytecode/StandardLibrary.cpp @@ -7363,7 +7363,7 @@ VMValue Model_GetAnimationIndex(int argCount, VMValue* args, Uint32 threadID) { } /*** * Model.GetFrameCount - * \desc Returns how many frames exist in the model. + * \desc Returns how many frames exist in the model. (Deprecated; use instead.) * \param model (Integer): The model index to check. * \return The frame count. Will always return 0 for skeletal-animated models. * \ns Model @@ -7371,7 +7371,7 @@ VMValue Model_GetAnimationIndex(int argCount, VMValue* args, Uint32 threadID) { VMValue Model_GetFrameCount(int argCount, VMValue* args, Uint32 threadID) { CHECK_ARGCOUNT(1); IModel* model = GET_ARG(0, GetModel); - return INTEGER_VAL((int)model->FrameCount); + return INTEGER_VAL((int)model->Meshes[0]->FrameCount); } /*** * Model.GetAnimationLength diff --git a/source/Engine/Rendering/FaceInfo.cpp b/source/Engine/Rendering/FaceInfo.cpp index eb2484f7..306b2e95 100644 --- a/source/Engine/Rendering/FaceInfo.cpp +++ b/source/Engine/Rendering/FaceInfo.cpp @@ -28,14 +28,14 @@ PUBLIC void FaceInfo::SetMaterial(Material* material) { UseMaterial = true; MaterialInfo.Texture = NULL; - Image* image = material->ImagePtr; + Image* image = material->TextureDiffuse; if (image && image->TexturePtr) MaterialInfo.Texture = (Texture*)image->TexturePtr; for (unsigned i = 0; i < 4; i++) { - MaterialInfo.Specular[i] = material->Specular[i] * 0x100; - MaterialInfo.Ambient[i] = material->Ambient[i] * 0x100; - MaterialInfo.Diffuse[i] = material->Diffuse[i] * 0x100; + MaterialInfo.Specular[i] = material->ColorSpecular[i] * 0x100; + MaterialInfo.Ambient[i] = material->ColorAmbient[i] * 0x100; + MaterialInfo.Diffuse[i] = material->ColorDiffuse[i] * 0x100; } } diff --git a/source/Engine/Rendering/Material.cpp b/source/Engine/Rendering/Material.cpp index 1b7cb4a1..522ba883 100644 --- a/source/Engine/Rendering/Material.cpp +++ b/source/Engine/Rendering/Material.cpp @@ -4,44 +4,54 @@ class Material { public: - float Diffuse[4]; - float Specular[4]; - float Ambient[4]; - float Emissive[4]; - - float Shininess; - float ShininessStrength; - float Opacity; - float IndexOfRefraction; - - Image* ImagePtr; + char* Name = nullptr; + + float ColorDiffuse[4]; + float ColorSpecular[4]; + float ColorAmbient[4]; + float ColorEmissive[4]; + + float Shininess = 0.0f; + float ShininessStrength = 1.0f; + float Opacity = 1.0f; + + char* TextureDiffuseName = nullptr; + char* TextureSpecularName = nullptr; + char* TextureAmbientName = nullptr; + char* TextureEmissiveName = nullptr; + + Image* TextureDiffuse = nullptr; + Image* TextureSpecular = nullptr; + Image* TextureAmbient = nullptr; + Image* TextureEmissive = nullptr; }; #endif #include +#include + PUBLIC Material::Material() { - for (int i = 0; i < 3; i++) { - Diffuse[i] = - Specular[i] = - Ambient[i] = - Emissive[i] = 0.0f; + for (int i = 0; i < 4; i++) { + ColorDiffuse[i] = + ColorSpecular[i] = + ColorAmbient[i] = + ColorEmissive[i] = 1.0f; } - - Diffuse[3] = - Specular[3] = - Ambient[3] = - Emissive[3] = 1.0f; - - Shininess = 0.0f; - ShininessStrength = 1.0f; - Opacity = 1.0f; - - ImagePtr = nullptr; } PUBLIC void Material::Dispose() { - delete ImagePtr; + delete TextureDiffuse; + delete TextureSpecular; + delete TextureAmbient; + delete TextureEmissive; + + Memory::Free(Name); + + Memory::Free(TextureDiffuseName); + Memory::Free(TextureSpecularName); + Memory::Free(TextureAmbientName); + Memory::Free(TextureEmissiveName); } PUBLIC Material::~Material() { diff --git a/source/Engine/Rendering/Mesh.h b/source/Engine/Rendering/Mesh.h index d81ba426..4cb83302 100644 --- a/source/Engine/Rendering/Mesh.h +++ b/source/Engine/Rendering/Mesh.h @@ -107,20 +107,20 @@ struct NodeAnim { } }; -struct ModelAnim { - char* Name; +struct ModelAnim; + +struct SkeletalAnim { + ModelAnim* ParentAnim; vector Channels; HashMap* NodeLookup; - Uint32 Length; Uint32 DurationInFrames; + double BaseDuration; double TicksPerSecond; - ~ModelAnim() { - Memory::Free(Name); - + ~SkeletalAnim() { for (size_t i = 0; i < Channels.size(); i++) delete Channels[i]; @@ -128,6 +128,21 @@ struct ModelAnim { } }; +struct ModelAnim { + char* Name = nullptr; + + Uint32 StartFrame = 0; + Uint32 Length = 0; + + SkeletalAnim* Skeletal = nullptr; + + ~ModelAnim() { + Memory::Free(Name); + + delete Skeletal; + } +}; + struct Skeleton { MeshBone** Bones; size_t NumBones; diff --git a/source/Engine/Rendering/ModelRenderer.cpp b/source/Engine/Rendering/ModelRenderer.cpp index 0e0f97ef..cf183117 100644 --- a/source/Engine/Rendering/ModelRenderer.cpp +++ b/source/Engine/Rendering/ModelRenderer.cpp @@ -127,13 +127,17 @@ PRIVATE void ModelRenderer::DrawMesh(IModel* model, Mesh* mesh, Skeleton* skelet DrawMesh(model, mesh, positionBuffer, normalBuffer, uvBuffer, mvpMatrix); } -PRIVATE void ModelRenderer::DrawMesh(IModel* model, Mesh* mesh, Uint32 frame, Matrix4x4& mvpMatrix) { +PRIVATE void ModelRenderer::DrawMesh(IModel* model, Mesh* mesh, Uint16 animation, Uint32 frame, Matrix4x4& mvpMatrix) { Vector3* positionBuffer = mesh->PositionBuffer; Vector3* normalBuffer = mesh->NormalBuffer; Vector2* uvBuffer = mesh->UVBuffer; - if (model->UseVertexAnimation && mesh->FrameCount) { - model->DoVertexFrameInterpolation(mesh, frame, &positionBuffer, &normalBuffer, &uvBuffer); + if (model->UseVertexAnimation) { + ModelAnim* anim = nullptr; + if (animation < model->AnimationCount) + anim = model->Animations[animation]; + + model->DoVertexFrameInterpolation(mesh, anim, frame, &positionBuffer, &normalBuffer, &uvBuffer); } DrawMesh(model, mesh, positionBuffer, normalBuffer, uvBuffer, mvpMatrix); @@ -399,7 +403,7 @@ PRIVATE void ModelRenderer::DrawNode(IModel* model, ModelNode* node, Matrix4x4* DrawNode(model, node->Children[i], world); } -PUBLIC void ModelRenderer::DrawModel(IModel* model, Uint32 frame) { +PRIVATE void ModelRenderer::DrawModelInternal(IModel* model, Uint16 animation, Uint32 frame) { if (DoProjection) Graphics::CalculateMVPMatrix(&MVPMatrix, ModelMatrix, ViewMatrix, ProjectionMatrix); else @@ -414,23 +418,26 @@ PUBLIC void ModelRenderer::DrawModel(IModel* model, Uint32 frame) { else { // Just render every mesh directly for (size_t i = 0; i < model->MeshCount; i++) - DrawMesh(model, model->Meshes[i], frame, MVPMatrix); + DrawMesh(model, model->Meshes[i], animation, frame, MVPMatrix); } } PUBLIC void ModelRenderer::DrawModel(IModel* model, Uint16 animation, Uint32 frame) { + Uint16 numAnims = model->AnimationCount; + if (numAnims > 0) { + if (animation >= numAnims) + animation = numAnims - 1; + } + else + animation = 0; + if (!model->UseVertexAnimation) { if (ArmaturePtr == nullptr) ArmaturePtr = model->BaseArmature; - Uint16 numAnims = model->AnimationCount; - if (numAnims > 0) { - if (animation >= numAnims) - animation = numAnims - 1; - + if (numAnims > 0) model->Animate(ArmaturePtr, model->Animations[animation], frame); - } } - DrawModel(model, frame); + DrawModelInternal(model, animation, frame); } diff --git a/source/Engine/Rendering/PolygonRenderer.cpp b/source/Engine/Rendering/PolygonRenderer.cpp index 8f53b518..2014b164 100644 --- a/source/Engine/Rendering/PolygonRenderer.cpp +++ b/source/Engine/Rendering/PolygonRenderer.cpp @@ -382,7 +382,7 @@ PUBLIC void PolygonRenderer::DrawModelSkinned(IModel* model, Uint16 armature) { rend.ClipFaces = DoProjection; rend.ArmaturePtr = model->ArmatureList[armature]; rend.SetMatrices(ModelMatrix, ViewMatrix, ProjectionMatrix, NormalMatrix); - rend.DrawModel(model, 0); + rend.DrawModel(model, 0, 0); } PUBLIC void PolygonRenderer::DrawVertexBuffer() { Matrix4x4 mvpMatrix; diff --git a/source/Engine/Rendering/Texture.cpp b/source/Engine/Rendering/Texture.cpp index 13b81aee..5e4df7f2 100644 --- a/source/Engine/Rendering/Texture.cpp +++ b/source/Engine/Rendering/Texture.cpp @@ -82,7 +82,7 @@ PUBLIC bool Texture::ConvertToPalette(Uint32 *palColors, unsigned numPa Uint32 color = pixels[i]; if (color & 0xFF000000) { if (!colorsHash->GetIfExists(color, &nearestColor)) { - Uint32 rgb[3]; + Uint8 rgb[3]; ColorUtils::SeparateRGB(color, rgb); nearestColor = ColorUtils::NearestColor(rgb[0], rgb[1], rgb[2], palColors, numPaletteColors); colorsHash->Put(color, nearestColor); diff --git a/source/Engine/ResourceTypes/IModel.cpp b/source/Engine/ResourceTypes/IModel.cpp index d4b0ac6c..e5790989 100644 --- a/source/Engine/ResourceTypes/IModel.cpp +++ b/source/Engine/ResourceTypes/IModel.cpp @@ -12,7 +12,6 @@ class IModel { size_t MeshCount; size_t VertexCount; - size_t FrameCount; size_t VertexIndexCount; Uint8 VertexPerFace; @@ -41,15 +40,15 @@ class IModel { #include #include #include -#include +#include #include +#include #include #include #include PUBLIC IModel::IModel() { VertexCount = 0; - FrameCount = 0; Meshes = nullptr; MeshCount = 0; @@ -86,7 +85,9 @@ PUBLIC bool IModel::Load(Stream* stream, const char* filename) { Clock::Start(); - if (MD3Model::IsMagic(stream)) + if (HatchModel::IsMagic(stream)) + success = HatchModel::Convert(this, stream, filename); + else if (MD3Model::IsMagic(stream)) success = MD3Model::Convert(this, stream, filename); else if (RSDKModel::IsMagic(stream)) success = RSDKModel::Convert(this, stream); @@ -158,7 +159,7 @@ PUBLIC bool IModel::HasBones() { return BaseArmature->NumSkeletons > 0; } -PUBLIC void IModel::AnimateNode(ModelNode* node, ModelAnim* animation, Uint32 frame, Matrix4x4* parentMatrix) { +PUBLIC void IModel::AnimateNode(ModelNode* node, SkeletalAnim* animation, Uint32 frame, Matrix4x4* parentMatrix) { NodeAnim* nodeAnim = animation->NodeLookup->Get(node->Name); if (nodeAnim) UpdateChannel(node->LocalTransform, nodeAnim, frame); @@ -173,7 +174,7 @@ PUBLIC void IModel::Pose() { BaseArmature->RootNode->Transform(); } -PUBLIC void IModel::Pose(Armature* armature, ModelAnim* animation, Uint32 frame) { +PUBLIC void IModel::Pose(Armature* armature, SkeletalAnim* animation, Uint32 frame) { Matrix4x4 identity; Matrix4x4::Identity(&identity); @@ -283,9 +284,23 @@ PUBLIC Sint64 IModel::GetInBetween(Uint32 frame) { return inbetween; } -PUBLIC void IModel::DoVertexFrameInterpolation(Mesh* mesh, Uint32 frame, Vector3** positionBuffer, Vector3** normalBuffer, Vector2** uvBuffer) { - Uint32 keyframe = GetKeyFrame(frame) % mesh->FrameCount; - Uint32 nextKeyframe = (keyframe + 1) % mesh->FrameCount; +PUBLIC void IModel::DoVertexFrameInterpolation(Mesh* mesh, ModelAnim* animation, Uint32 frame, Vector3** positionBuffer, Vector3** normalBuffer, Vector2** uvBuffer) { + Uint32 startFrame = 0; + Uint32 animLength; + + if (animation) { + animLength = animation->Length % (mesh->FrameCount + 1); + if (!animLength) + animLength = 1; + startFrame = animation->StartFrame % animLength; + } + else if (mesh->FrameCount) + animLength = mesh->FrameCount; + else + animLength = 1; + + Uint32 keyframe = startFrame + (GetKeyFrame(frame) % animLength); + Uint32 nextKeyframe = startFrame + ((keyframe + 1) % animLength); Sint64 inbetween = GetInBetween(frame); if (inbetween == 0 || inbetween == 0x10000) { @@ -362,11 +377,26 @@ PRIVATE void IModel::UpdateChannel(Matrix4x4* out, NodeAnim* channel, Uint32 fra } PUBLIC void IModel::Animate(Armature* armature, ModelAnim* animation, Uint32 frame) { - Pose(armature, animation, frame); + if (animation->Skeletal == nullptr) + return; + + if (armature == nullptr) + armature = BaseArmature; + + Pose(armature, animation->Skeletal, frame); armature->UpdateSkeletons(); } +PUBLIC void IModel::Animate(Uint16 animation, Uint32 frame) { + if (AnimationCount > 0) { + if (animation >= AnimationCount) + animation = AnimationCount - 1; + + Animate(nullptr, Animations[animation], frame); + } +} + PUBLIC int IModel::GetAnimationIndex(const char* animationName) { if (!AnimationCount) return -1; diff --git a/source/Engine/ResourceTypes/ModelFormats/HatchModel.cpp b/source/Engine/ResourceTypes/ModelFormats/HatchModel.cpp new file mode 100644 index 00000000..5af0dde2 --- /dev/null +++ b/source/Engine/ResourceTypes/ModelFormats/HatchModel.cpp @@ -0,0 +1,919 @@ +#if INTERFACE +#include +#include +class HatchModel { +public: + static Sint32 Version; + + static Uint32 NumVertexStore; + static Uint32 NumNormalStore; + static Uint32 NumTexCoordStore; + static Uint32 NumColorStore; + + static Vector3* VertexStore; + static Vector3* NormalStore; + static Vector2* TexCoordStore; + static Uint32* ColorStore; +}; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HATCH_MODEL_MAGIC 0x484D444C // HMDL + +Sint32 HatchModel::Version; + +Uint32 HatchModel::NumVertexStore; +Uint32 HatchModel::NumNormalStore; +Uint32 HatchModel::NumTexCoordStore; +Uint32 HatchModel::NumColorStore; + +Vector3* HatchModel::VertexStore; +Vector3* HatchModel::NormalStore; +Vector2* HatchModel::TexCoordStore; +Uint32* HatchModel::ColorStore; + +PUBLIC STATIC bool HatchModel::IsMagic(Stream* stream) { + Uint32 magic = stream->ReadUInt32BE(); + + stream->Skip(-4); + + return magic == HATCH_MODEL_MAGIC; +} + +PUBLIC STATIC void HatchModel::ReadMaterialInfo(Stream* stream, Uint8 *destColors, char **texName) { + *texName = stream->ReadString(); + + Uint32 colorIndex = stream->ReadUInt32(); + + Uint32 color = GetStoredColor(colorIndex); + + ColorUtils::Separate(color, destColors); +} + +PRIVATE STATIC Material* HatchModel::ReadMaterial(Stream* stream, const char *parentDirectory) { + Material* material = new Material(); + + material->Name = stream->ReadString(); + + Uint8 flags = stream->ReadByte(); + + // Read diffuse + if (flags & 1) { + char *diffuseTexture; + Uint8 diffuseColor[4]; + + ReadMaterialInfo(stream, diffuseColor, &diffuseTexture); + + ColorUtils::Separate(material->ColorDiffuse, diffuseColor); + + material->TextureDiffuse = IModel::LoadMaterialImage(diffuseTexture, parentDirectory); + if (material->TextureDiffuse) + material->TextureDiffuseName = StringUtils::Duplicate(material->TextureDiffuse->Filename); + } + + // Read specular + if (flags & 2) { + char *specularTexture; + Uint8 specularColor[4]; + + ReadMaterialInfo(stream, specularColor, &specularTexture); + + ColorUtils::Separate(material->ColorSpecular, specularColor); + + material->TextureSpecular = IModel::LoadMaterialImage(specularTexture, parentDirectory); + if (material->TextureSpecular) + material->TextureSpecularName = StringUtils::Duplicate(material->TextureSpecular->Filename); + } + + // Read ambient + if (flags & 4) { + char *ambientTexture; + Uint8 ambientColor[4]; + + ReadMaterialInfo(stream, ambientColor, &ambientTexture); + + ColorUtils::Separate(material->ColorAmbient, ambientColor); + + material->TextureAmbient = IModel::LoadMaterialImage(ambientTexture, parentDirectory); + if (material->TextureAmbient) + material->TextureAmbientName = StringUtils::Duplicate(material->TextureAmbient->Filename); + } + + // Read emissive + if (flags & 8) { + char *emissiveTexture; + Uint8 emissiveColor[4]; + + ReadMaterialInfo(stream, emissiveColor, &emissiveTexture); + + ColorUtils::Separate(material->ColorEmissive, emissiveColor); + + material->TextureEmissive = IModel::LoadMaterialImage(emissiveTexture, parentDirectory); + if (material->TextureEmissive) + material->TextureEmissiveName = StringUtils::Duplicate(material->TextureEmissive->Filename); + } + + // Read shininess + if (flags & 16) { + Uint8 val = stream->ReadByte(); + material->Shininess = (float)val / 255; + val = stream->ReadByte(); + material->ShininessStrength = (float)val / 255; + } + + // Read opacity + if (flags & 32) { + Uint8 val = stream->ReadByte(); + material->Opacity = (float)val / 255; + } + + return material; +} + +PRIVATE STATIC void HatchModel::ReadVertexStore(Stream* stream) { + NumVertexStore = stream->ReadUInt32(); + if (!NumVertexStore) + return; + + VertexStore = (Vector3*)Memory::Malloc(NumVertexStore * sizeof(Vector3)); + + Vector3* vert = VertexStore; + for (Uint32 i = 0; i < NumVertexStore; i++) { + vert->X = stream->ReadInt64(); + vert->Y = stream->ReadInt64(); + vert->Z = stream->ReadInt64(); + vert++; + } +} + +PRIVATE STATIC void HatchModel::ReadNormalStore(Stream* stream) { + NumNormalStore = stream->ReadUInt32(); + if (!NumNormalStore) + return; + + NormalStore = (Vector3*)Memory::Malloc(NumNormalStore * sizeof(Vector3)); + + Vector3* norm = NormalStore; + for (Uint32 i = 0; i < NumNormalStore; i++) { + norm->X = stream->ReadInt64(); + norm->Y = stream->ReadInt64(); + norm->Z = stream->ReadInt64(); + norm++; + } +} + +PRIVATE STATIC void HatchModel::ReadTexCoordStore(Stream* stream) { + NumTexCoordStore = stream->ReadUInt32(); + if (!NumTexCoordStore) + return; + + TexCoordStore = (Vector2*)Memory::Malloc(NumTexCoordStore * sizeof(Vector2)); + + Vector2* uvs = TexCoordStore; + for (Uint32 i = 0; i < NumTexCoordStore; i++) { + uvs->X = stream->ReadInt64(); + uvs->Y = stream->ReadInt64(); + uvs++; + } +} + +PRIVATE STATIC void HatchModel::ReadColorStore(Stream* stream) { + NumColorStore = stream->ReadUInt32(); + if (!NumColorStore) + return; + + ColorStore = (Uint32*)Memory::Malloc(NumColorStore * sizeof(Uint32)); + + Uint32* colors = ColorStore; + for (Uint32 i = 0; i < NumColorStore; i++) { + Uint8 r = stream->ReadByte(); + Uint8 g = stream->ReadByte(); + Uint8 b = stream->ReadByte(); + Uint8 a = stream->ReadByte(); + *colors = ColorUtils::ToRGB(r, g, b, a); + colors++; + } +} + +PRIVATE STATIC Vector3 HatchModel::GetStoredVertex(Uint32 idx) { + if (idx < 0 || idx >= NumVertexStore) + return {}; + + return VertexStore[idx]; +} + +PRIVATE STATIC Vector3 HatchModel::GetStoredNormal(Uint32 idx) { + if (idx < 0 || idx >= NumNormalStore) + return {}; + + return NormalStore[idx]; +} + +PRIVATE STATIC Vector2 HatchModel::GetStoredTexCoord(Uint32 idx) { + if (idx < 0 || idx >= NumTexCoordStore) + return {}; + + return TexCoordStore[idx]; +} + +PRIVATE STATIC Uint32 HatchModel::GetStoredColor(Uint32 idx) { + if (idx < 0 || idx >= NumColorStore) + return 0; + + return ColorStore[idx]; +} + +PRIVATE STATIC void HatchModel::ReadVertexIndices(Sint32* indices, Sint32 triangleCount, Stream* stream) { + for (Sint32 i = 0; i < triangleCount; i++) { + *indices++ = stream->ReadUInt32(); + *indices++ = stream->ReadUInt32(); + *indices++ = stream->ReadUInt32(); + } + + *indices = -1; +} + +PRIVATE STATIC Mesh* HatchModel::ReadMesh(IModel* model, Stream* stream) { + // Read mesh data + Mesh* mesh = new Mesh; + mesh->Name = stream->ReadString(); + mesh->VertexFlag = VertexType_Position; + + Uint8 flags = stream->ReadByte(); + if (flags & 1) + mesh->VertexFlag |= VertexType_Normal; + if (flags & 2) + mesh->VertexFlag |= VertexType_UV; + if (flags & 4) + mesh->VertexFlag |= VertexType_Color; + if (flags & 8) + mesh->MaterialIndex = stream->ReadByte(); + + Uint32 vertexCount = stream->ReadUInt32(); + if (!vertexCount) { + Log::Print(Log::LOG_ERROR, "Mesh \"%s\" has no vertices!", mesh->Name); + return nullptr; + } + + Uint32 triangleCount = stream->ReadUInt32(); + if (!triangleCount) { + Log::Print(Log::LOG_ERROR, "Mesh \"%s\" has no triangles!", mesh->Name); + return nullptr; + } + + Uint16 frameCount = stream->ReadUInt16(); + if (!frameCount) { + Log::Print(Log::LOG_ERROR, "Mesh \"%s\" has no frames!", mesh->Name); + return nullptr; + } + + mesh->VertexCount = vertexCount; + mesh->FrameCount = frameCount; + + mesh->PositionBuffer = (Vector3*)Memory::Malloc(vertexCount * frameCount * sizeof(Vector3)); + + // Read vertices + Vector3* vert = mesh->PositionBuffer; + for (Sint32 i = 0; i < frameCount; i++) { + for (Sint32 j = 0; j < vertexCount; j++) + *vert++ = GetStoredVertex(stream->ReadUInt32()); + } + + // Read normals + if (mesh->VertexFlag & VertexType_Normal) { + mesh->NormalBuffer = (Vector3*)Memory::Malloc(vertexCount * frameCount * sizeof(Vector3)); + + Vector3* norm = mesh->NormalBuffer; + for (Sint32 i = 0; i < frameCount; i++) + for (Sint32 j = 0; j < vertexCount; j++) + *norm++ = GetStoredNormal(stream->ReadUInt32()); + } + + // Read UVs + if (mesh->VertexFlag & VertexType_UV) { + mesh->UVBuffer = (Vector2*)Memory::Malloc(vertexCount * frameCount * sizeof(Vector2)); + + Vector2* uv = mesh->UVBuffer; + for (Sint32 i = 0; i < frameCount; i++) + for (Sint32 j = 0; j < vertexCount; j++) + *uv++ = GetStoredTexCoord(stream->ReadUInt32()); + } + + // Read colors + if (mesh->VertexFlag & VertexType_Color) { + mesh->ColorBuffer = (Uint32*)Memory::Malloc(vertexCount * frameCount * sizeof(Uint32)); + + Uint32* colors = mesh->ColorBuffer; + for (Sint32 i = 0; i < frameCount; i++) + for (Sint32 j = 0; j < vertexCount; j++) + *colors++ = GetStoredColor(stream->ReadUInt32()); + } + + // Read vertex indices + mesh->VertexIndexCount = triangleCount * 3; + model->VertexIndexCount += mesh->VertexIndexCount; + + mesh->VertexIndexBuffer = (Sint32*)Memory::Malloc((mesh->VertexIndexCount + 1) * sizeof(Sint32)); + + ReadVertexIndices(mesh->VertexIndexBuffer, triangleCount, stream); + + return mesh; +} + +PUBLIC STATIC bool HatchModel::Convert(IModel* model, Stream* stream, const char* path) { + bool success = false; + + if (stream->ReadUInt32BE() != HATCH_MODEL_MAGIC) { + Log::Print(Log::LOG_ERROR, "Model not of Hatch type!"); + return false; + } + + Version = stream->ReadByte(); + + if (Version > 0) { + Log::Print(Log::LOG_ERROR, "Unsupported Hatch model version!"); + return false; + } + + Uint16 meshCount = stream->ReadUInt16(); + if (!meshCount) { + Log::Print(Log::LOG_ERROR, "Model has no meshes!"); + return false; + } + + Uint8 materialCount; + Uint16 animCount; + + Uint32 vertexDataOffset = stream->ReadUInt32(); + Uint32 normalDataOffset = stream->ReadUInt32(); + Uint32 uvDataOffset = stream->ReadUInt32(); + Uint32 colorDataOffset = stream->ReadUInt32(); + Uint32 meshDataOffset = stream->ReadUInt32(); + Uint32 materialDataOffset = stream->ReadUInt32(); + Uint32 animDataOffset = stream->ReadUInt32(); + + // Read vertex storage + stream->Seek(vertexDataOffset); + ReadVertexStore(stream); + + // Read normal storage + stream->Seek(normalDataOffset); + ReadNormalStore(stream); + + // Read UV storage + stream->Seek(uvDataOffset); + ReadTexCoordStore(stream); + + // Read color storage + stream->Seek(colorDataOffset); + ReadColorStore(stream); + + // Create model + model->Meshes = new Mesh*[meshCount]; + model->MeshCount = meshCount; + model->VertexCount = 0; + model->VertexPerFace = 3; + model->UseVertexAnimation = true; + + // Read materials + stream->Seek(materialDataOffset); + + materialCount = stream->ReadByte(); + + if (materialCount) { + model->MaterialCount = materialCount; + model->Materials = new Material*[materialCount]; + + const char *parentDirectory = StringUtils::GetPath(path); + + for (Uint8 i = 0; i < materialCount; i++) { + Material* material = ReadMaterial(stream, parentDirectory); + if (material == nullptr) { + for (unsigned j = 0; j <= i; j++) { + delete model->Materials[j]; + delete model->Materials; + model->Materials = nullptr; + } + + goto fail; + } + + model->Materials[i] = material; + } + } + + // Read animations + stream->Seek(animDataOffset); + + animCount = stream->ReadUInt16(); + + if (animCount) { + model->AnimationCount = animCount; + model->Animations = new ModelAnim*[animCount]; + + for (Uint16 i = 0; i < animCount; i++) { + ModelAnim* anim = new ModelAnim; + anim->Name = stream->ReadString(); + anim->StartFrame = stream->ReadUInt32(); + anim->Length = stream->ReadUInt32(); + model->Animations[i] = anim; + } + } + + // Read meshes + stream->Seek(meshDataOffset); + + for (Uint16 i = 0; i < meshCount; i++) { + Mesh* mesh = ReadMesh(model, stream); + if (mesh == nullptr) { + for (unsigned j = 0; j <= i; j++) { + delete model->Meshes[j]; + delete model->Meshes; + model->Meshes = nullptr; + } + + goto fail; + } + + model->Meshes[i] = mesh; + model->VertexCount += mesh->VertexCount; + } + + success = true; + +fail: + Memory::Free(VertexStore); + Memory::Free(NormalStore); + Memory::Free(TexCoordStore); + Memory::Free(ColorStore); + + VertexStore = nullptr; + NormalStore = nullptr; + TexCoordStore = nullptr; + ColorStore = nullptr; + + return success; +} + +static HashMap* vertexIDs; +static HashMap* normalIDs; +static HashMap* texCoordIDs; +static HashMap* colorIDs; + +static vector vertexList; +static vector normalList; +static vector texCoordList; +static vector colorList; + +PRIVATE STATIC void HatchModel::WriteMesh(Mesh* mesh, Stream* stream) { + Uint8 flags = 0; + if (mesh->VertexFlag & VertexType_Normal) + flags |= 1; + if (mesh->VertexFlag & VertexType_UV) + flags |= 2; + if (mesh->VertexFlag & VertexType_Color) + flags |= 4; + if (mesh->MaterialIndex != -1) + flags |= 8; + + stream->WriteString(mesh->Name); + stream->WriteByte(flags); + + if (flags & 8) + stream->WriteByte(mesh->MaterialIndex); + + Uint32 vertexCount = mesh->VertexCount; + Uint16 frameCount = mesh->FrameCount; + + stream->WriteUInt32(vertexCount); + stream->WriteUInt32(mesh->VertexIndexCount / 3); + stream->WriteUInt16(frameCount); + + // Write vertices + Vector3* vert = mesh->PositionBuffer; + for (Sint32 j = 0; j < frameCount; j++) { + for (Sint32 k = 0; k < vertexCount; k++) { + Uint32 key = vertexIDs->HashFunction(vert, sizeof(*vert)); + if (vertexIDs->Exists(key)) + stream->WriteUInt32(vertexIDs->Get(key)); + else { + vertexIDs->Put(key, vertexList.size()); + stream->WriteUInt32(vertexList.size()); + vertexList.push_back(*vert); + } + vert++; + } + } + + // Write normals + if (flags & 1) { + Vector3* norm = mesh->NormalBuffer; + for (Sint32 j = 0; j < frameCount; j++) { + for (Sint32 k = 0; k < vertexCount; k++) { + Uint32 key = normalIDs->HashFunction(norm, sizeof(*norm)); + if (normalIDs->Exists(key)) + stream->WriteUInt32(normalIDs->Get(key)); + else { + normalIDs->Put(key, normalList.size()); + stream->WriteUInt32(normalList.size()); + normalList.push_back(*norm); + } + norm++; + } + } + } + + // Write texture coordinates + if (flags & 2) { + Vector2* uv = mesh->UVBuffer; + for (Sint32 j = 0; j < frameCount; j++) { + for (Sint32 k = 0; k < vertexCount; k++) { + Uint32 key = texCoordIDs->HashFunction(uv, sizeof(*uv)); + if (texCoordIDs->Exists(key)) + stream->WriteUInt32(texCoordIDs->Get(key)); + else { + texCoordIDs->Put(key, texCoordList.size()); + stream->WriteUInt32(texCoordList.size()); + texCoordList.push_back(*uv); + } + uv++; + } + } + } + + // Write colors + if (flags & 4) { + Uint32* color = mesh->ColorBuffer; + for (Sint32 j = 0; j < frameCount; j++) { + for (Sint32 k = 0; k < vertexCount; k++) { + HatchModel::WriteColorIndex(color, stream); + color++; + } + } + } + + // Write vertex indices + for (Uint32 j = 0; j < mesh->VertexIndexCount; j++) + stream->WriteUInt32(mesh->VertexIndexBuffer[j]); +} + +PRIVATE STATIC char* HatchModel::GetMaterialTextureName(const char* name, const char* parentDirectory) { + const char* result = strstr(name, parentDirectory); + if (result) { + size_t offset = strlen(parentDirectory); + while (name[offset] == '/') + offset++; + return StringUtils::Create(name + offset); + } + + return StringUtils::Duplicate(name); +} + +PRIVATE STATIC void HatchModel::WriteColorIndex(Uint32* color, Stream* stream) { + Uint32 key = colorIDs->HashFunction(color, sizeof(*color)); + if (colorIDs->Exists(key)) + stream->WriteUInt32(colorIDs->Get(key)); + else { + colorIDs->Put(key, colorList.size()); + stream->WriteUInt32(colorList.size()); + colorList.push_back(*color); + } +} + +PRIVATE STATIC void HatchModel::WriteMaterial(Material* material, Stream* stream, const char* parentDirectory) { + stream->WriteString(material->Name); + + Uint8 flags = 0; + + if ((material->ColorDiffuse[0] != 1.0 + && material->ColorDiffuse[1] != 1.0 + && material->ColorDiffuse[2] != 1.0 + && material->ColorDiffuse[3] != 1.0) + || material->TextureDiffuseName) + flags |= 1; + + if ((material->ColorSpecular[0] != 1.0 + && material->ColorSpecular[1] != 1.0 + && material->ColorSpecular[2] != 1.0 + && material->ColorSpecular[3] != 1.0) + || material->TextureSpecularName) + flags |= 2; + + if ((material->ColorAmbient[0] != 1.0 + && material->ColorAmbient[1] != 1.0 + && material->ColorAmbient[2] != 1.0 + && material->ColorAmbient[3] != 1.0) + || material->TextureAmbientName) + flags |= 4; + + if ((material->ColorEmissive[0] != 1.0 + && material->ColorEmissive[1] != 1.0 + && material->ColorEmissive[2] != 1.0 + && material->ColorEmissive[3] != 1.0) + || material->TextureEmissiveName) + flags |= 8; + + if (material->Shininess != 0.0 || material->ShininessStrength != 1.0) + flags |= 16; + + if (material->Opacity != 1.0) + flags |= 32; + + Log::Print(Log::LOG_VERBOSE, "Material: %s", material->Name); + Log::Print(Log::LOG_VERBOSE, " - Flags: %d", flags); + + stream->WriteByte(flags); + + // Write diffuse + if (flags & 1) { + if (material->TextureDiffuseName) { + char* name = GetMaterialTextureName(material->TextureDiffuseName, parentDirectory); + + stream->WriteString(name); + + Log::Print(Log::LOG_VERBOSE, " - Diffuse texture: %s", name); + + Memory::Free(name); + } + else + stream->WriteByte('\0'); + + Uint32 color = ColorUtils::ToRGBA(material->ColorDiffuse); + + HatchModel::WriteColorIndex(&color, stream); + + Log::Print(Log::LOG_VERBOSE, " - Diffuse color: %f %f %f %f", + material->ColorDiffuse[0], + material->ColorDiffuse[1], + material->ColorDiffuse[2], + material->ColorDiffuse[3]); + } + + // Write specular + if (flags & 2) { + if (material->TextureSpecularName) { + char* name = GetMaterialTextureName(material->TextureSpecularName, parentDirectory); + + stream->WriteString(name); + + Log::Print(Log::LOG_VERBOSE, " - Specular texture: %s", name); + + Memory::Free(name); + } + else + stream->WriteByte('\0'); + + Uint32 color = ColorUtils::ToRGBA(material->ColorSpecular); + + HatchModel::WriteColorIndex(&color, stream); + + Log::Print(Log::LOG_VERBOSE, " - Specular color: %f %f %f %f", + material->ColorSpecular[0], + material->ColorSpecular[1], + material->ColorSpecular[2], + material->ColorSpecular[3]); + } + + // Write ambient + if (flags & 4) { + if (material->TextureAmbientName) { + char* name = GetMaterialTextureName(material->TextureAmbientName, parentDirectory); + + stream->WriteString(name); + + Log::Print(Log::LOG_VERBOSE, " - Ambient texture: %s", name); + + Memory::Free(name); + } + else + stream->WriteByte('\0'); + + Uint32 color = ColorUtils::ToRGBA(material->ColorAmbient); + + HatchModel::WriteColorIndex(&color, stream); + + Log::Print(Log::LOG_VERBOSE, " - Ambient color: %f %f %f %f", + material->ColorAmbient[0], + material->ColorAmbient[1], + material->ColorAmbient[2], + material->ColorAmbient[3]); + } + + // Write emissive + if (flags & 8) { + if (material->TextureEmissiveName) { + char* name = GetMaterialTextureName(material->TextureEmissiveName, parentDirectory); + + stream->WriteString(name); + + Log::Print(Log::LOG_VERBOSE, " - Emissive texture: %s", name); + + Memory::Free(name); + } + else + stream->WriteByte('\0'); + + Uint32 color = ColorUtils::ToRGBA(material->ColorEmissive); + + HatchModel::WriteColorIndex(&color, stream); + + Log::Print(Log::LOG_VERBOSE, " - Emissive color: %f %f %f %f", + material->ColorEmissive[0], + material->ColorEmissive[1], + material->ColorEmissive[2], + material->ColorEmissive[3]); + } + + // Write shininess + if (flags & 16) { + stream->WriteByte(material->Shininess * 0xFF); + stream->WriteByte(material->ShininessStrength * 0xFF); + + Log::Print(Log::LOG_VERBOSE, " - Shininess: %f", material->Shininess); + Log::Print(Log::LOG_VERBOSE, " - Shininess strength: %f", material->ShininessStrength); + } + + // Write opacity + if (flags & 32) { + stream->WriteByte(material->Opacity * 0xFF); + + Log::Print(Log::LOG_VERBOSE, " - Opacity: %f", material->Opacity); + } +} + +PUBLIC STATIC bool HatchModel::Save(IModel* model, const char* filename) { + Stream* stream = FileStream::New(filename, FileStream::WRITE_ACCESS); + if (!stream) { + Log::Print(Log::LOG_ERROR, "Couldn't open \"%s\"!", filename); + return false; + } + + stream->WriteUInt32BE(HATCH_MODEL_MAGIC); + stream->WriteByte(0); + + stream->WriteUInt16(model->MeshCount); + + Uint32 vertexDataOffsetPos = stream->Position(); stream->WriteUInt32(0x00000000); + Uint32 normalDataOffsetPos = stream->Position(); stream->WriteUInt32(0x00000000); + Uint32 uvDataOffsetPos = stream->Position(); stream->WriteUInt32(0x00000000); + Uint32 colorDataOffsetPos = stream->Position(); stream->WriteUInt32(0x00000000); + Uint32 meshDataOffsetPos = stream->Position(); stream->WriteUInt32(0x00000000); + Uint32 materialDataOffsetPos = stream->Position(); stream->WriteUInt32(0x00000000); + Uint32 animDataOffsetPos = stream->Position(); stream->WriteUInt32(0x00000000); + + vertexIDs = new HashMap(NULL, 1024); + normalIDs = new HashMap(NULL, 1024); + texCoordIDs = new HashMap(NULL, 1024); + colorIDs = new HashMap(NULL, 1024); + + vertexList.clear(); + normalList.clear(); + texCoordList.clear(); + colorList.clear(); + + // Write meshes + size_t lastPos = stream->Position(); + stream->Seek(meshDataOffsetPos); + stream->WriteUInt32(lastPos); + stream->Seek(lastPos); + + Log::Print(Log::LOG_VERBOSE, "Mesh count: %d (%08X)", model->MeshCount, lastPos); + + for (size_t i = 0; i < model->MeshCount; i++) { + WriteMesh(model->Meshes[i], stream); + } + + // Write materials + lastPos = stream->Position(); + stream->Seek(materialDataOffsetPos); + stream->WriteUInt32(lastPos); + stream->Seek(lastPos); + + size_t numMaterials = model->MaterialCount; + + stream->WriteByte(numMaterials); + + Log::Print(Log::LOG_VERBOSE, "Material count: %d (%08X)", numMaterials, lastPos); + + const char *parentDirectory = StringUtils::GetPath(filename); + if (StringUtils::StartsWith(parentDirectory, "./")) + parentDirectory += 2; + if (StringUtils::StartsWith(parentDirectory, "Resources/")) + parentDirectory += 10; + + for (size_t i = 0; i < numMaterials; i++) { + WriteMaterial(model->Materials[i], stream, parentDirectory); + } + + // Write animations + lastPos = stream->Position(); + stream->Seek(animDataOffsetPos); + stream->WriteUInt32(lastPos); + stream->Seek(lastPos); + + stream->WriteByte(model->AnimationCount); + + Log::Print(Log::LOG_VERBOSE, "Animation count: %d (%08X)", model->AnimationCount, lastPos); + + for (size_t i = 0; i < model->AnimationCount; i++) { + ModelAnim* anim = model->Animations[i]; + stream->WriteString(anim->Name); + stream->WriteUInt32(anim->StartFrame); + stream->WriteUInt32(anim->Length); + } + + // Write stored vertices + lastPos = stream->Position(); + stream->Seek(vertexDataOffsetPos); + stream->WriteUInt32(lastPos); + stream->Seek(lastPos); + + size_t numVertices = vertexList.size(); + + stream->WriteUInt32(numVertices); + + Log::Print(Log::LOG_VERBOSE, "Vertex list size: %d (%08X)", numVertices, lastPos); + + for (size_t i = 0; i < numVertices; i++) { + Vector3 v = vertexList[i]; + stream->WriteInt64(v.X); + stream->WriteInt64(v.Y); + stream->WriteInt64(v.Z); + } + + // Write stored normals + lastPos = stream->Position(); + stream->Seek(normalDataOffsetPos); + stream->WriteUInt32(lastPos); + stream->Seek(lastPos); + + size_t numNormals = normalList.size(); + + stream->WriteUInt32(numNormals); + + Log::Print(Log::LOG_VERBOSE, "Normal list size: %d (%08X)", numNormals, lastPos); + + for (size_t i = 0; i < numNormals; i++) { + Vector3 n = normalList[i]; + stream->WriteInt64(n.X); + stream->WriteInt64(n.Y); + stream->WriteInt64(n.Z); + } + + // Write stored texture coordinates + lastPos = stream->Position(); + stream->Seek(uvDataOffsetPos); + stream->WriteUInt32(lastPos); + stream->Seek(lastPos); + + size_t numTexCoords = texCoordList.size(); + + stream->WriteUInt32(numTexCoords); + + Log::Print(Log::LOG_VERBOSE, "Texture coordinate list size: %d (%08X)", numTexCoords, lastPos); + + for (size_t i = 0; i < numTexCoords; i++) { + Vector2 t = texCoordList[i]; + stream->WriteInt64(t.X); + stream->WriteInt64(t.Y); + } + + // Write stored colors + lastPos = stream->Position(); + stream->Seek(colorDataOffsetPos); + stream->WriteUInt32(lastPos); + stream->Seek(lastPos); + + size_t numColors = colorList.size(); + + stream->WriteUInt32(numColors); + + Log::Print(Log::LOG_VERBOSE, "Color list size: %d (%08X)", numColors, lastPos); + + for (size_t i = 0; i < numColors; i++) { + Uint32 color = colorList[i]; + Uint8 rgba[4]; + ColorUtils::Separate(color, rgba); + stream->WriteByte(rgba[0]); + stream->WriteByte(rgba[1]); + stream->WriteByte(rgba[2]); + stream->WriteByte(rgba[3]); + } + + stream->Close(); + + delete vertexIDs; + delete normalIDs; + delete texCoordIDs; + delete colorIDs; + + return true; +} diff --git a/source/Engine/ResourceTypes/ModelFormats/Importer.cpp b/source/Engine/ResourceTypes/ModelFormats/Importer.cpp index df5d846f..52eb2790 100644 --- a/source/Engine/ResourceTypes/ModelFormats/Importer.cpp +++ b/source/Engine/ResourceTypes/ModelFormats/Importer.cpp @@ -54,7 +54,7 @@ static void CopyColors(float dest[4], aiColor4D& src) { } static char* GetString(aiString src) { - return StringUtils::Duplicate(src.C_Str()); + return StringUtils::Create(src.C_Str()); } static Matrix4x4* CopyMatrix(aiMatrix4x4 mat) { @@ -109,6 +109,7 @@ PRIVATE STATIC Mesh* ModelImporter::LoadMesh(IModel* imodel, struct aiMesh* ames Mesh* mesh = new Mesh; mesh->Name = GetString(amesh->mName); mesh->VertexCount = numVertices; + mesh->FrameCount = 1; mesh->VertexIndexCount = numFaces * 3; mesh->VertexIndexBuffer = (Sint32*)Memory::Malloc((mesh->VertexIndexCount + 1) * sizeof(Sint32)); @@ -184,28 +185,52 @@ PRIVATE STATIC Mesh* ModelImporter::LoadMesh(IModel* imodel, struct aiMesh* ames return mesh; } -PRIVATE STATIC Material* ModelImporter::LoadMaterial(IModel* imodel, struct aiMaterial* mat) { +PRIVATE STATIC Material* ModelImporter::LoadMaterial(IModel* imodel, struct aiMaterial* mat, unsigned i) { Material* material = new Material(); + aiString matName; + aiString texDiffuse; + aiString texSpecular; + aiString texAmbient; + aiString texEmissive; + aiColor4D colorDiffuse; aiColor4D colorSpecular; aiColor4D colorAmbient; aiColor4D colorEmissive; + ai_real shininess, shininessStrength, opacity; + unsigned int n = 1; + if (mat->Get(AI_MATKEY_NAME, matName) == AI_SUCCESS) + material->Name = GetString(matName); + else { + char temp[20]; + + snprintf(temp, sizeof temp, "Material %d", i); + + material->Name = StringUtils::Duplicate(temp); + } + if (mat->GetTexture(aiTextureType_DIFFUSE, 0, &texDiffuse) == AI_SUCCESS) - material->ImagePtr = IModel::LoadMaterialImage(texDiffuse.data, ModelImporter::ParentDirectory); + material->TextureDiffuse = IModel::LoadMaterialImage(texDiffuse.data, ModelImporter::ParentDirectory); + if (mat->GetTexture(aiTextureType_SPECULAR, 0, &texSpecular) == AI_SUCCESS) + material->TextureSpecular = IModel::LoadMaterialImage(texSpecular.data, ModelImporter::ParentDirectory); + if (mat->GetTexture(aiTextureType_AMBIENT, 0, &texAmbient) == AI_SUCCESS) + material->TextureAmbient = IModel::LoadMaterialImage(texAmbient.data, ModelImporter::ParentDirectory); + if (mat->GetTexture(aiTextureType_EMISSIVE, 0, &texEmissive) == AI_SUCCESS) + material->TextureEmissive = IModel::LoadMaterialImage(texEmissive.data, ModelImporter::ParentDirectory); if (aiGetMaterialColor(mat, AI_MATKEY_COLOR_DIFFUSE, &colorDiffuse) == AI_SUCCESS) - CopyColors(material->Diffuse, colorDiffuse); + CopyColors(material->ColorDiffuse, colorDiffuse); if (aiGetMaterialColor(mat, AI_MATKEY_COLOR_SPECULAR, &colorSpecular) == AI_SUCCESS) - CopyColors(material->Specular, colorSpecular); + CopyColors(material->ColorSpecular, colorSpecular); if (aiGetMaterialColor(mat, AI_MATKEY_COLOR_AMBIENT, &colorAmbient) == AI_SUCCESS) - CopyColors(material->Ambient, colorAmbient); + CopyColors(material->ColorAmbient, colorAmbient); if (aiGetMaterialColor(mat, AI_MATKEY_COLOR_EMISSIVE, &colorEmissive) == AI_SUCCESS) - CopyColors(material->Emissive, colorEmissive); + CopyColors(material->ColorEmissive, colorEmissive); if (aiGetMaterialFloatArray(mat, AI_MATKEY_SHININESS, &shininess, &n) == AI_SUCCESS) material->Shininess = shininess; @@ -286,9 +311,8 @@ PRIVATE STATIC Skeleton* ModelImporter::LoadBones(IModel* imodel, Mesh* mesh, st return skeleton; } -PRIVATE STATIC ModelAnim* ModelImporter::LoadAnimation(IModel* imodel, struct aiAnimation* aanim) { - ModelAnim* anim = new ModelAnim; - anim->Name = GetString(aanim->mName); +PRIVATE STATIC SkeletalAnim* ModelImporter::LoadAnimation(IModel* imodel, ModelAnim* parentAnim, struct aiAnimation* aanim) { + SkeletalAnim* anim = new SkeletalAnim; anim->Channels.resize(aanim->mNumChannels); anim->NodeLookup = new HashMap(NULL, 256); // Might be enough @@ -299,11 +323,13 @@ PRIVATE STATIC ModelAnim* ModelImporter::LoadAnimation(IModel* imodel, struct ai double durationInSeconds = baseDuration / ticksPerSecond; - anim->Length = (int)baseDuration; anim->DurationInFrames = (int)(durationInSeconds * 60); anim->BaseDuration = baseDuration; anim->TicksPerSecond = ticksPerSecond; + parentAnim->StartFrame = 0; + parentAnim->Length = (int)baseDuration; + for (size_t i = 0; i < aanim->mNumChannels; i++) { struct aiNodeAnim* channel = aanim->mChannels[i]; NodeAnim* nodeAnim = new NodeAnim; @@ -373,7 +399,7 @@ PRIVATE STATIC bool ModelImporter::DoConversion(const struct aiScene* scene, IMo imodel->Materials = new Material*[imodel->MaterialCount]; for (size_t i = 0; i < imodel->MaterialCount; i++) - imodel->Materials[i] = LoadMaterial(imodel, scene->mMaterials[i]); + imodel->Materials[i] = LoadMaterial(imodel, scene->mMaterials[i], i); } // Load meshes @@ -430,14 +456,22 @@ PRIVATE STATIC bool ModelImporter::DoConversion(const struct aiScene* scene, IMo } // Load animations - // FIXME: Doesn't seem to be working with COLLADA scenes for some reason. - // Might be an issue in assimp? if (scene->HasAnimations()) { imodel->AnimationCount = scene->mNumAnimations; imodel->Animations = new ModelAnim*[imodel->AnimationCount]; - for (size_t i = 0; i < imodel->AnimationCount; i++) - imodel->Animations[i] = LoadAnimation(imodel, scene->mAnimations[i]); + for (size_t i = 0; i < imodel->AnimationCount; i++) { + struct aiAnimation* aanim = scene->mAnimations[i]; + + ModelAnim* parentAnim = new ModelAnim; + parentAnim->Name = GetString(aanim->mName); + + SkeletalAnim *skAnim = LoadAnimation(imodel, parentAnim, aanim); + skAnim->ParentAnim = parentAnim; + parentAnim->Skeletal = skAnim; + + imodel->Animations[i] = parentAnim; + } } return true; diff --git a/source/Engine/ResourceTypes/ModelFormats/MD3Model.cpp b/source/Engine/ResourceTypes/ModelFormats/MD3Model.cpp index d3a1510c..55aa91c5 100644 --- a/source/Engine/ResourceTypes/ModelFormats/MD3Model.cpp +++ b/source/Engine/ResourceTypes/ModelFormats/MD3Model.cpp @@ -4,6 +4,7 @@ class MD3Model { public: static Sint32 Version; + static bool UseUVKeyframes; static Sint32 DataEndOffset; static vector MaterialNames; }; @@ -18,7 +19,8 @@ class MD3Model { #define MD3_MODEL_MAGIC 0x49445033 // IDP3 -#define MD3_VERSION 15 +#define MD3_BASE_VERSION 15 +#define MD3_EXTRA_VERSION 2038 #define MD3_XYZ_SCALE (1.0 / 64) @@ -29,6 +31,7 @@ class MD3Model { #define MAX_QPATH 64 Sint32 MD3Model::Version; +bool MD3Model::UseUVKeyframes; Sint32 MD3Model::DataEndOffset; vector MD3Model::MaterialNames; @@ -40,7 +43,7 @@ PUBLIC STATIC bool MD3Model::IsMagic(Stream* stream) { return magic == MD3_MODEL_MAGIC; } -PUBLIC STATIC void MD3Model::DecodeNormal(Uint16 index, float& x, float& y, float& z) { +PRIVATE STATIC void MD3Model::DecodeNormal(Uint16 index, float& x, float& y, float& z) { unsigned latIdx = (index >> 8) & 0xFF; unsigned lngIdx = index & 0xFF; @@ -52,7 +55,7 @@ PUBLIC STATIC void MD3Model::DecodeNormal(Uint16 index, float& x, float& y, floa z = cosf(lng); } -PUBLIC STATIC void MD3Model::ReadShader(Stream* stream) { +PRIVATE STATIC void MD3Model::ReadShader(Stream* stream) { char name[MAX_QPATH + 1]; memset(name, '\0', sizeof name); stream->ReadBytes(name, MAX_QPATH); @@ -62,7 +65,7 @@ PUBLIC STATIC void MD3Model::ReadShader(Stream* stream) { MaterialNames.push_back(shaderName); } -PUBLIC STATIC void MD3Model::ReadVerticesAndNormals(Vector3* vert, Vector3* norm, Sint32 vertexCount, Stream* stream) { +PRIVATE STATIC void MD3Model::ReadVerticesAndNormals(Vector3* vert, Vector3* norm, Sint32 vertexCount, Stream* stream) { for (Sint32 i = 0; i < vertexCount; i++) { // MD3 coordinate system is usually Z up and Y forward (I think.) vert->X = stream->ReadInt16() * FP16_TO(MD3_XYZ_SCALE); @@ -80,7 +83,7 @@ PUBLIC STATIC void MD3Model::ReadVerticesAndNormals(Vector3* vert, Vector3* norm } } -PUBLIC STATIC void MD3Model::ReadUVs(Vector2* uvs, Sint32 vertexCount, Stream* stream) { +PRIVATE STATIC void MD3Model::ReadUVs(Vector2* uvs, Sint32 vertexCount, Stream* stream) { for (Sint32 i = 0; i < vertexCount; i++) { uvs->X = (int)(stream->ReadFloat() * 0x10000); uvs->Y = (int)(stream->ReadFloat() * 0x10000); @@ -88,7 +91,7 @@ PUBLIC STATIC void MD3Model::ReadUVs(Vector2* uvs, Sint32 vertexCount, Stream* s } } -PUBLIC STATIC void MD3Model::ReadVertexIndices(Sint32* indices, Sint32 triangleCount, Stream* stream) { +PRIVATE STATIC void MD3Model::ReadVertexIndices(Sint32* indices, Sint32 triangleCount, Stream* stream) { for (Sint32 i = 0; i < triangleCount; i++) { *indices++ = stream->ReadUInt32(); *indices++ = stream->ReadUInt32(); @@ -98,7 +101,7 @@ PUBLIC STATIC void MD3Model::ReadVertexIndices(Sint32* indices, Sint32 triangleC *indices = -1; } -PUBLIC STATIC Mesh* MD3Model::ReadSurface(IModel* model, Stream* stream, size_t surfaceDataOffset) { +PRIVATE STATIC Mesh* MD3Model::ReadSurface(IModel* model, Stream* stream, size_t surfaceDataOffset) { Sint32 magic = stream->ReadUInt32BE(); if (magic != MD3_MODEL_MAGIC) { Log::Print(Log::LOG_ERROR, "Invalid magic for MD3 surface!"); @@ -162,16 +165,24 @@ PUBLIC STATIC Mesh* MD3Model::ReadSurface(IModel* model, Stream* stream, size_t stream->Seek(surfaceDataOffset + stDataOffset); - ReadUVs(uv, vertexCount, stream); - - // Copy the values to other frames - for (Sint32 i = 0; i < vertexCount; i++) { - Vector2* uvA = &mesh->UVBuffer[i]; - - for (Sint32 j = 1; j < frameCount; j++) { - Vector2* uvB = &mesh->UVBuffer[i + (vertexCount * j)]; - uvB->X = uvA->X; - uvB->Y = uvA->X; + if (UseUVKeyframes) { + for (Sint32 i = 0; i < frameCount; i++) { + ReadUVs(uv, vertexCount, stream); + uv += vertexCount; + } + } + else { + ReadUVs(uv, vertexCount, stream); + + // Copy the values to other frames + for (Sint32 i = 0; i < vertexCount; i++) { + Vector2* uvA = &mesh->UVBuffer[i]; + + for (Sint32 j = 1; j < frameCount; j++) { + Vector2* uvB = &mesh->UVBuffer[i + (vertexCount * j)]; + uvB->X = uvA->X; + uvB->Y = uvA->X; + } } } @@ -209,11 +220,13 @@ PUBLIC STATIC bool MD3Model::Convert(IModel* model, Stream* stream, const char* Version = stream->ReadInt32(); - if (Version != MD3_VERSION) { - Log::Print(Log::LOG_ERROR, "Unsupported MD3 model version!"); + if (Version != MD3_BASE_VERSION && Version != MD3_EXTRA_VERSION) { + Log::Print(Log::LOG_ERROR, "Unknown MD3 model version!"); return false; } + UseUVKeyframes = Version == MD3_EXTRA_VERSION; + char name[MAX_QPATH + 1]; memset(name, '\0', sizeof name); stream->ReadBytes(name, MAX_QPATH); @@ -267,21 +280,30 @@ PUBLIC STATIC bool MD3Model::Convert(IModel* model, Stream* stream, const char* size_t offset = surfaceDataOffset; + Sint32 maxFrameCount = frameCount; + for (Sint32 i = 0; i < surfCount; i++) { Mesh* mesh = ReadSurface(model, stream, offset); - if (mesh == nullptr) + if (mesh == nullptr) { + for (signed j = 0; j <= i; j++) { + delete model->Meshes[j]; + delete model->Meshes; + model->Meshes = nullptr; + } return false; + } model->Meshes[i] = mesh; model->VertexCount += mesh->VertexCount; + if (mesh->FrameCount > maxFrameCount) + maxFrameCount = mesh->FrameCount; + offset += DataEndOffset; stream->Seek(offset); } - model->FrameCount = model->Meshes[0]->FrameCount; - // Add materials size_t numMaterials = MaterialNames.size(); if (numMaterials) { @@ -292,12 +314,21 @@ PUBLIC STATIC bool MD3Model::Convert(IModel* model, Stream* stream, const char* for (size_t i = 0; i < numMaterials; i++) { Material* material = new Material(); - material->ImagePtr = IModel::LoadMaterialImage(MaterialNames[i], parentDirectory); + material->Name = StringUtils::Create(MaterialNames[i]); + material->TextureDiffuse = IModel::LoadMaterialImage(MaterialNames[i], parentDirectory); + if (material->TextureDiffuse) + material->TextureDiffuseName = StringUtils::Duplicate(material->TextureDiffuse->Filename); model->Materials[i] = material; } MaterialNames.clear(); } + model->AnimationCount = 1; + model->Animations = new ModelAnim*[1]; + model->Animations[0] = new ModelAnim; + model->Animations[0]->Name = StringUtils::Duplicate("Vertex Animation"); + model->Animations[0]->Length = maxFrameCount; + return true; } diff --git a/source/Engine/ResourceTypes/ModelFormats/RSDKModel.cpp b/source/Engine/ResourceTypes/ModelFormats/RSDKModel.cpp index 564e75d8..accd781f 100644 --- a/source/Engine/ResourceTypes/ModelFormats/RSDKModel.cpp +++ b/source/Engine/ResourceTypes/ModelFormats/RSDKModel.cpp @@ -33,40 +33,42 @@ PUBLIC STATIC bool RSDKModel::Convert(IModel* model, Stream* stream) { Uint8 vertexFlag = stream->ReadByte(); model->VertexPerFace = stream->ReadByte(); - model->VertexCount = stream->ReadUInt16(); - model->FrameCount = stream->ReadUInt16(); + + Uint16 vertexCount = stream->ReadUInt16(); + Uint16 frameCount = stream->ReadUInt16(); // We only need one mesh for RSDK models Mesh* mesh = new Mesh; model->Meshes = new Mesh*[1]; model->Meshes[0] = mesh; model->MeshCount = 1; + model->VertexCount = vertexCount; - mesh->VertexCount = model->VertexCount; - mesh->FrameCount = model->FrameCount; + mesh->VertexCount = vertexCount; + mesh->FrameCount = frameCount; mesh->VertexFlag = vertexFlag; - mesh->PositionBuffer = (Vector3*)Memory::Malloc(model->VertexCount * model->FrameCount * sizeof(Vector3)); + mesh->PositionBuffer = (Vector3*)Memory::Malloc(mesh->VertexCount * frameCount * sizeof(Vector3)); if (vertexFlag & VertexType_Normal) - mesh->NormalBuffer = (Vector3*)Memory::Malloc(model->VertexCount * model->FrameCount * sizeof(Vector3)); + mesh->NormalBuffer = (Vector3*)Memory::Malloc(mesh->VertexCount * frameCount * sizeof(Vector3)); if (vertexFlag & VertexType_UV) - mesh->UVBuffer = (Vector2*)Memory::Malloc(model->VertexCount * model->FrameCount * sizeof(Vector2)); + mesh->UVBuffer = (Vector2*)Memory::Malloc(mesh->VertexCount * frameCount * sizeof(Vector2)); if (vertexFlag & VertexType_Color) - mesh->ColorBuffer = (Uint32*)Memory::Malloc(model->VertexCount * model->FrameCount * sizeof(Uint32)); + mesh->ColorBuffer = (Uint32*)Memory::Malloc(mesh->VertexCount * frameCount * sizeof(Uint32)); // Read UVs if (vertexFlag & VertexType_UV) { int uvX, uvY; - for (size_t i = 0; i < model->VertexCount; i++) { + for (size_t i = 0; i < mesh->VertexCount; i++) { Vector2* uv = &mesh->UVBuffer[i]; uv->X = uvX = (int)(stream->ReadFloat() * 0x10000); uv->Y = uvY = (int)(stream->ReadFloat() * 0x10000); // Copy the values to other frames - for (size_t f = 1; f < model->FrameCount; f++) { - uv += model->VertexCount; + for (size_t f = 1; f < frameCount; f++) { + uv += mesh->VertexCount; uv->X = uvX; uv->Y = uvY; } @@ -75,12 +77,12 @@ PUBLIC STATIC bool RSDKModel::Convert(IModel* model, Stream* stream) { // Read Colors if (vertexFlag & VertexType_Color) { Uint32* colorPtr, color; - for (size_t i = 0; i < model->VertexCount; i++) { + for (size_t i = 0; i < mesh->VertexCount; i++) { colorPtr = &mesh->ColorBuffer[i]; *colorPtr = color = stream->ReadUInt32(); // Copy the value to other frames - for (size_t f = 1; f < model->FrameCount; f++) { - colorPtr += model->VertexCount; + for (size_t f = 1; f < frameCount; f++) { + colorPtr += mesh->VertexCount; *colorPtr = color; } } @@ -92,6 +94,12 @@ PUBLIC STATIC bool RSDKModel::Convert(IModel* model, Stream* stream) { model->GlobalInverseMatrix = nullptr; model->UseVertexAnimation = true; + model->AnimationCount = 1; + model->Animations = new ModelAnim*[1]; + model->Animations[0] = new ModelAnim; + model->Animations[0]->Name = StringUtils::Duplicate("Vertex Animation"); + model->Animations[0]->Length = frameCount; + model->VertexIndexCount = stream->ReadInt16(); mesh->VertexIndexCount = model->VertexIndexCount; mesh->VertexIndexBuffer = (Sint32*)Memory::Malloc((model->VertexIndexCount + 1) * sizeof(Sint32)); @@ -103,7 +111,7 @@ PUBLIC STATIC bool RSDKModel::Convert(IModel* model, Stream* stream) { if (vertexFlag & VertexType_Normal) { Vector3* vert = mesh->PositionBuffer; Vector3* norm = mesh->NormalBuffer; - size_t totalVertexCount = model->VertexCount * model->FrameCount; + size_t totalVertexCount = mesh->VertexCount * frameCount; for (size_t v = 0; v < totalVertexCount; v++) { vert->X = (int)(stream->ReadFloat() * 0x10000); vert->Y = (int)(stream->ReadFloat() * 0x10000); @@ -118,7 +126,7 @@ PUBLIC STATIC bool RSDKModel::Convert(IModel* model, Stream* stream) { } else { Vector3* vert = mesh->PositionBuffer; - size_t totalVertexCount = model->VertexCount * model->FrameCount; + size_t totalVertexCount = mesh->VertexCount * frameCount; for (size_t v = 0; v < totalVertexCount; v++) { vert->X = (int)(stream->ReadFloat() * 0x10000); vert->Y = (int)(stream->ReadFloat() * 0x10000); diff --git a/source/Engine/Utilities/ColorUtils.cpp b/source/Engine/Utilities/ColorUtils.cpp index f0b91d3c..c6db53c5 100644 --- a/source/Engine/Utilities/ColorUtils.cpp +++ b/source/Engine/Utilities/ColorUtils.cpp @@ -17,6 +17,14 @@ PUBLIC STATIC Uint32 ColorUtils::ToRGB(int r, int g, int b) { return 0xFF000000U | r << 16 | g << 8 | b; } +PUBLIC STATIC Uint32 ColorUtils::ToRGB(int r, int g, int b, int a) { + CLAMP_VAL(r, 0x00, 0xFF); + CLAMP_VAL(g, 0x00, 0xFF); + CLAMP_VAL(b, 0x00, 0xFF); + CLAMP_VAL(a, 0x00, 0xFF); + + return a << 24 | r << 16 | g << 8 | b; +} PUBLIC STATIC Uint32 ColorUtils::ToRGB(float r, float g, float b) { int colR = (int)(r * 0xFF); int colG = (int)(g * 0xFF); @@ -24,9 +32,20 @@ PUBLIC STATIC Uint32 ColorUtils::ToRGB(float r, float g, float b) { return ColorUtils::ToRGB(colR, colG, colB); } +PUBLIC STATIC Uint32 ColorUtils::ToRGB(float r, float g, float b, float a) { + int colR = (int)(r * 0xFF); + int colG = (int)(g * 0xFF); + int colB = (int)(b * 0xFF); + int colA = (int)(a * 0xFF); + + return ColorUtils::ToRGB(colR, colG, colB, colA); +} PUBLIC STATIC Uint32 ColorUtils::ToRGB(float *r) { return ToRGB(r[0], r[1], r[2]); } +PUBLIC STATIC Uint32 ColorUtils::ToRGBA(float *r) { + return ToRGB(r[0], r[1], r[2], r[3]); +} PUBLIC STATIC void ColorUtils::SeparateRGB(Uint32 color, float* dest) { dest[0] = (color >> 16 & 0xFF) / 255.f; dest[1] = (color >> 8 & 0xFF) / 255.f; @@ -36,15 +55,24 @@ PUBLIC STATIC void ColorUtils::Separate(Uint32 color, float* dest) { dest[3] = (color >> 24 & 0xFF) / 255.f; SeparateRGB(color, dest); } -PUBLIC STATIC void ColorUtils::SeparateRGB(Uint32 color, Uint32* dest) { +PUBLIC STATIC void ColorUtils::SeparateRGB(Uint32 color, Uint8* dest) { dest[0] = (color >> 16) & 0xFF; dest[1] = (color >> 8) & 0xFF; dest[2] = color & 0xFF; } -PUBLIC STATIC void ColorUtils::Separate(Uint32 color, Uint32* dest) { +PUBLIC STATIC void ColorUtils::Separate(Uint32 color, Uint8* dest) { dest[3] = (color >> 24) & 0xFF; SeparateRGB(color, dest); } +PUBLIC STATIC void ColorUtils::SeparateRGB(float* color, Uint8* dest) { + dest[0] = color[0] * 0xFF; + dest[1] = color[1] * 0xFF; + dest[2] = color[2] * 0xFF; +} +PUBLIC STATIC void ColorUtils::Separate(float* color, Uint8* dest) { + dest[2] = color[3] * 0xFF; + SeparateRGB(color, dest); +} PUBLIC STATIC Uint32 ColorUtils::Tint(Uint32 color, Uint32 colorMult) { Uint32 dR = (colorMult >> 16) & 0xFF; Uint32 dG = (colorMult >> 8) & 0xFF;