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;