mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
595 lines
16 KiB
C++
595 lines
16 KiB
C++
#include "stdafx.h"
|
|
#include "SEAsset.h"
|
|
|
|
#include "File.h"
|
|
#include "BinaryWriter.h"
|
|
|
|
namespace Assets::Exporters
|
|
{
|
|
constexpr static const char* SEModelMagic = "SEModel";
|
|
constexpr static const char* SEAnimMagic = "SEAnim";
|
|
|
|
// Specifies the data present for this file container
|
|
enum class SEModelDataPresenceFlags : uint8_t
|
|
{
|
|
// Whether or not this model contains a bone block
|
|
SEMODEL_PRESENCE_BONE = 1 << 0,
|
|
// Whether or not this model contains submesh blocks
|
|
SEMODEL_PRESENCE_MESH = 1 << 1,
|
|
// Whether or not this model contains inline material blocks
|
|
SEMODEL_PRESENCE_MATERIALS = 1 << 2,
|
|
// The file contains a custom data block
|
|
SEMODEL_PRESENCE_CUSTOM = 1 << 7,
|
|
};
|
|
|
|
// Specifies the data present for bones
|
|
enum class SEModelBonePresenceFlags : uint8_t
|
|
{
|
|
// Whether or not bones contain global-space matricies
|
|
SEMODEL_PRESENCE_GLOBAL_MATRIX = 1 << 0,
|
|
// Whether or not bones contain local-space matricies
|
|
SEMODEL_PRESENCE_LOCAL_MATRIX = 1 << 1,
|
|
// Whether or not bones contain scales
|
|
SEMODEL_PRESENCE_SCALES = 1 << 2,
|
|
};
|
|
|
|
// Specifies the data present for meshes
|
|
enum class SEModelMeshPresenceFlags : uint8_t
|
|
{
|
|
// Whether or not meshes contain at least 1 uv map
|
|
SEMODEL_PRESENCE_UVSET = 1 << 0,
|
|
// Whether or not meshes contain vertex normals
|
|
SEMODEL_PRESENCE_NORMALS = 1 << 1,
|
|
// Whether or not meshes contain vertex colors (RGBA)
|
|
SEMODEL_PRESENCE_COLOR = 1 << 2,
|
|
// Whether or not meshes contain at least 1 weighted skin
|
|
SEMODEL_PRESENCE_WEIGHTS = 1 << 3,
|
|
};
|
|
|
|
// Specifies how the data is interpreted by the importer
|
|
enum class SEAnimAnimationType : uint8_t
|
|
{
|
|
// Animation translations are set to this exact value each frame
|
|
SEANIM_ABSOLUTE = 0,
|
|
// This animation is applied to existing animation data in the scene
|
|
SEANIM_ADDITIVE = 1,
|
|
// Animation translations are based on rest position in scene
|
|
SEANIM_RELATIVE = 2
|
|
};
|
|
|
|
// Specifies the data present for each frame of every bone (Internal use only, matches specification v1.0.1)
|
|
enum class SEAnimDataPresenceFlags : uint8_t
|
|
{
|
|
// Animation has bone location keyframes
|
|
SEANIM_BONE_LOC = 1 << 0,
|
|
// Animation has bone rotation keyframes
|
|
SEANIM_BONE_ROT = 1 << 1,
|
|
// Animation has bone scale keyframes
|
|
SEANIM_BONE_SCALE = 1 << 2,
|
|
// The file contains notetrack data
|
|
SEANIM_PRESENCE_NOTE = 1 << 6,
|
|
// The file contains a custom data block
|
|
SEANIM_PRESENCE_CUSTOM = 1 << 7,
|
|
};
|
|
|
|
bool SEAsset::ExportAnimation(const Animation& Animation, const string& Path)
|
|
{
|
|
auto Writer = IO::BinaryWriter(IO::File::Create(Path));
|
|
|
|
Writer.Write((void*)SEAnimMagic, 0, strlen(SEAnimMagic)); // Magic
|
|
Writer.Write<uint16_t>(0x1); // Version
|
|
Writer.Write<uint16_t>(0x1C); // Header size
|
|
|
|
// We need to iterate all curves and find the must used type
|
|
// When we do, we'll default to that, and inject modifiers for the rest...
|
|
uint32_t Slots[3] = { 0, 0, 0 };
|
|
|
|
// This is a traditional map of bones for the format
|
|
List<string> Bones;
|
|
|
|
{
|
|
Dictionary<string, uint32_t> SEBones;
|
|
|
|
for (auto& Kvp : Animation.Curves)
|
|
{
|
|
for (auto& Curve : Kvp.Value())
|
|
{
|
|
Slots[(uint32_t)Curve.Mode]++;
|
|
}
|
|
|
|
if (Kvp.Value().Count() > 0)
|
|
SEBones.Add(Kvp.Key(), 0);
|
|
}
|
|
|
|
for (auto& Kvp : SEBones)
|
|
Bones.EmplaceBack(Kvp.Key());
|
|
}
|
|
|
|
auto AnimationType = AnimationCurveMode::Absolute;
|
|
|
|
uint32_t LargestSize = 0;
|
|
for (uint32_t i = 0; i < 3; i++)
|
|
{
|
|
if (Slots[i] > LargestSize)
|
|
{
|
|
LargestSize = Slots[i];
|
|
AnimationType = (AnimationCurveMode)i;
|
|
}
|
|
}
|
|
|
|
switch (AnimationType)
|
|
{
|
|
case AnimationCurveMode::Absolute:
|
|
Writer.Write<uint8_t>((uint8_t)SEAnimAnimationType::SEANIM_ABSOLUTE);
|
|
break;
|
|
case AnimationCurveMode::Additive:
|
|
Writer.Write<uint8_t>((uint8_t)SEAnimAnimationType::SEANIM_ADDITIVE);
|
|
break;
|
|
case AnimationCurveMode::Relative:
|
|
Writer.Write<uint8_t>((uint8_t)SEAnimAnimationType::SEANIM_RELATIVE);
|
|
break;
|
|
}
|
|
|
|
Dictionary<uint32_t, AnimationCurveMode> BoneTypeModifiers;
|
|
|
|
for (uint32_t i = 0; i < Bones.Count(); i++)
|
|
{
|
|
auto& Bone = Bones[i];
|
|
|
|
if (Animation.Curves.ContainsKey(Bone))
|
|
{
|
|
auto& Curves = Animation.Curves[Bone];
|
|
|
|
for (auto& Curve : Curves)
|
|
{
|
|
if (Curve.Mode != AnimationType)
|
|
BoneTypeModifiers.Add(i, Curve.Mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
Writer.Write<uint8_t>(Animation.Looping ? (1 << 0) : 0);
|
|
|
|
auto DataPresentFlags = 0;
|
|
DataPresentFlags |= (uint8_t)SEAnimDataPresenceFlags::SEANIM_BONE_LOC;
|
|
DataPresentFlags |= (uint8_t)SEAnimDataPresenceFlags::SEANIM_BONE_ROT;
|
|
DataPresentFlags |= (uint8_t)SEAnimDataPresenceFlags::SEANIM_BONE_SCALE;
|
|
|
|
if (Animation.Notificiations.Count() > 0)
|
|
DataPresentFlags |= (uint8_t)SEAnimDataPresenceFlags::SEANIM_PRESENCE_NOTE;
|
|
|
|
Writer.Write<uint8_t>((uint8_t)DataPresentFlags);
|
|
|
|
Writer.Write<uint8_t>(0); // Data property flags
|
|
Writer.Write<uint16_t>(0); // Reserved bytes
|
|
|
|
Writer.Write<float>(Animation.FrameRate);
|
|
|
|
auto FrameCount = Animation.FrameCount(true); // We must use legacy mode for SEAnims
|
|
auto BoneCount = Bones.Count();
|
|
auto NotificationCount = Animation.NotificationCount(); // Same as SEAnims
|
|
|
|
Writer.Write<uint32_t>(FrameCount);
|
|
Writer.Write<uint32_t>(BoneCount);
|
|
Writer.Write<uint8_t>((uint8_t)BoneTypeModifiers.Count());
|
|
|
|
uint8_t Reserved[] = { 0, 0, 0 };
|
|
Writer.Write(&Reserved[0], 0, 3);
|
|
|
|
Writer.Write<uint32_t>(NotificationCount);
|
|
|
|
for (auto& Bone : Bones)
|
|
Writer.WriteCString(Bone);
|
|
|
|
for (auto& BoneTypeKvp : BoneTypeModifiers)
|
|
{
|
|
if (BoneCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)BoneTypeKvp.Key());
|
|
else if (BoneCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)BoneTypeKvp.Key());
|
|
else
|
|
Writer.Write<uint32_t>((uint32_t)BoneTypeKvp.Key());
|
|
|
|
switch (BoneTypeKvp.Value())
|
|
{
|
|
case AnimationCurveMode::Absolute:
|
|
Writer.Write<uint8_t>((uint8_t)SEAnimAnimationType::SEANIM_ABSOLUTE);
|
|
break;
|
|
case AnimationCurveMode::Relative:
|
|
Writer.Write<uint8_t>((uint8_t)SEAnimAnimationType::SEANIM_RELATIVE);
|
|
break;
|
|
case AnimationCurveMode::Additive:
|
|
Writer.Write<uint8_t>((uint8_t)SEAnimAnimationType::SEANIM_ADDITIVE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < BoneCount; i++)
|
|
{
|
|
Writer.Write<uint8_t>(0); // Flags
|
|
|
|
auto& BoneCurves = Animation.Curves[Bones[i]];
|
|
|
|
int32_t TrackSlots[7] = { -1, -1, -1, -1, -1, -1, -1 };
|
|
|
|
uint32_t PositionTrackCount = 0;
|
|
uint32_t RotationTrackCount = 0;
|
|
uint32_t ScaleTrackCount = 0;
|
|
|
|
for (uint32_t i = 0; i < BoneCurves.Count(); i++)
|
|
{
|
|
auto& Curve = BoneCurves[i];
|
|
if (Curve.Property == CurveProperty::RotateQuaternion)
|
|
{
|
|
TrackSlots[(uint32_t)Curve.Property - 1] = i;
|
|
RotationTrackCount = max(RotationTrackCount, Curve.Keyframes.Count());
|
|
}
|
|
else if (Curve.Property >= CurveProperty::TranslateX && Curve.Property <= CurveProperty::TranslateZ)
|
|
{
|
|
TrackSlots[(uint32_t)Curve.Property - 4] = i;
|
|
PositionTrackCount = max(PositionTrackCount, Curve.Keyframes.Count());
|
|
}
|
|
else if (Curve.Property >= CurveProperty::ScaleX && Curve.Property <= CurveProperty::ScaleZ)
|
|
{
|
|
TrackSlots[(uint32_t)Curve.Property - 4] = i;
|
|
ScaleTrackCount = max(ScaleTrackCount, Curve.Keyframes.Count());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Positions
|
|
//
|
|
|
|
if (FrameCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)PositionTrackCount);
|
|
else if (FrameCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)PositionTrackCount);
|
|
else
|
|
Writer.Write<uint32_t>(PositionTrackCount);
|
|
|
|
for (uint32_t i = 0; i < PositionTrackCount; i++)
|
|
{
|
|
uint32_t Frame = 0;
|
|
Vector3 Key{};
|
|
|
|
if (TrackSlots[1] > -1)
|
|
{
|
|
Frame = BoneCurves[TrackSlots[1]].Keyframes[i].Frame.Integer32;
|
|
Key.X = BoneCurves[TrackSlots[1]].Keyframes[i].Value.Float;
|
|
}
|
|
if (TrackSlots[2] > -1)
|
|
{
|
|
Frame = BoneCurves[TrackSlots[2]].Keyframes[i].Frame.Integer32;
|
|
Key.Y = BoneCurves[TrackSlots[2]].Keyframes[i].Value.Float;
|
|
}
|
|
if (TrackSlots[3] > -1)
|
|
{
|
|
Frame = BoneCurves[TrackSlots[3]].Keyframes[i].Frame.Integer32;
|
|
Key.Z = BoneCurves[TrackSlots[3]].Keyframes[i].Value.Float;
|
|
}
|
|
|
|
if (FrameCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)Frame);
|
|
else if (FrameCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)Frame);
|
|
else
|
|
Writer.Write<uint32_t>(Frame);
|
|
|
|
Writer.Write<Vector3>(Key);
|
|
}
|
|
|
|
//
|
|
// Rotations
|
|
//
|
|
|
|
if (FrameCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)RotationTrackCount);
|
|
else if (FrameCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)RotationTrackCount);
|
|
else
|
|
Writer.Write<uint32_t>(RotationTrackCount);
|
|
|
|
for (uint32_t i = 0; i < RotationTrackCount; i++)
|
|
{
|
|
uint32_t Frame = 0;
|
|
Quaternion Key{0, 0, 0, 1};
|
|
|
|
if (TrackSlots[0] > -1)
|
|
{
|
|
Frame = BoneCurves[TrackSlots[0]].Keyframes[i].Frame.Integer32;
|
|
Key = BoneCurves[TrackSlots[0]].Keyframes[i].Value.Vector4;
|
|
}
|
|
|
|
if (FrameCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)Frame);
|
|
else if (FrameCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)Frame);
|
|
else
|
|
Writer.Write<uint32_t>(Frame);
|
|
|
|
Writer.Write<Quaternion>(Key);
|
|
}
|
|
|
|
//
|
|
// Scales
|
|
//
|
|
|
|
if (FrameCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)ScaleTrackCount);
|
|
else if (FrameCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)ScaleTrackCount);
|
|
else
|
|
Writer.Write<uint32_t>(ScaleTrackCount);
|
|
|
|
for (uint32_t i = 0; i < ScaleTrackCount; i++)
|
|
{
|
|
uint32_t Frame = 0;
|
|
Vector3 Key{};
|
|
|
|
if (TrackSlots[4] > -1)
|
|
{
|
|
Frame = BoneCurves[TrackSlots[4]].Keyframes[i].Frame.Integer32;
|
|
Key.X = BoneCurves[TrackSlots[4]].Keyframes[i].Value.Float;
|
|
}
|
|
if (TrackSlots[5] > -1)
|
|
{
|
|
Frame = BoneCurves[TrackSlots[5]].Keyframes[i].Frame.Integer32;
|
|
Key.Y = BoneCurves[TrackSlots[5]].Keyframes[i].Value.Float;
|
|
}
|
|
if (TrackSlots[6] > -1)
|
|
{
|
|
Frame = BoneCurves[TrackSlots[6]].Keyframes[i].Frame.Integer32;
|
|
Key.Z = BoneCurves[TrackSlots[6]].Keyframes[i].Value.Float;
|
|
}
|
|
|
|
if (FrameCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)Frame);
|
|
else if (FrameCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)Frame);
|
|
else
|
|
Writer.Write<uint32_t>(Frame);
|
|
|
|
Writer.Write<Vector3>(Key);
|
|
}
|
|
}
|
|
|
|
for (auto& NoteTrack : Animation.Notificiations)
|
|
{
|
|
for (auto& Key : NoteTrack.Value())
|
|
{
|
|
if (FrameCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)Key);
|
|
else if (FrameCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)Key);
|
|
else
|
|
Writer.Write<uint32_t>(Key);
|
|
|
|
Writer.WriteCString(NoteTrack.Key());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SEAsset::ExportModel(const Model& Model, const string& Path)
|
|
{
|
|
auto Writer = IO::BinaryWriter(IO::File::Create(Path));
|
|
|
|
Writer.Write((void*)SEModelMagic, 0, strlen(SEModelMagic)); // Magic
|
|
Writer.Write<uint16_t>(0x1); // Version
|
|
Writer.Write<uint16_t>(0x14); // Header size
|
|
|
|
auto FileDpFlags = 0;
|
|
|
|
if (!Model.Bones.Empty())
|
|
FileDpFlags |= (uint8_t)SEModelDataPresenceFlags::SEMODEL_PRESENCE_BONE;
|
|
if (!Model.Meshes.Empty())
|
|
FileDpFlags |= (uint8_t)SEModelDataPresenceFlags::SEMODEL_PRESENCE_MESH;
|
|
if (!Model.Materials.Empty())
|
|
FileDpFlags |= (uint8_t)SEModelDataPresenceFlags::SEMODEL_PRESENCE_MATERIALS;
|
|
|
|
bool HasGlobalMatrix = false;
|
|
bool HasLocalMatrix = false;
|
|
bool HasScales = false;
|
|
|
|
for (auto& Bone : Model.Bones)
|
|
{
|
|
if (Bone.GetFlag(BoneFlags::HasGlobalSpaceMatrices))
|
|
HasGlobalMatrix = true;
|
|
if (Bone.GetFlag(BoneFlags::HasLocalSpaceMatrices))
|
|
HasLocalMatrix = true;
|
|
if (Bone.GetFlag(BoneFlags::HasScale))
|
|
HasScales = true;
|
|
|
|
if (HasGlobalMatrix && HasLocalMatrix && HasScales)
|
|
break;
|
|
}
|
|
|
|
auto BoneDpFlags = 0;
|
|
|
|
if (HasGlobalMatrix)
|
|
BoneDpFlags |= (uint8_t)SEModelBonePresenceFlags::SEMODEL_PRESENCE_GLOBAL_MATRIX;
|
|
if (HasLocalMatrix)
|
|
BoneDpFlags |= (uint8_t)SEModelBonePresenceFlags::SEMODEL_PRESENCE_LOCAL_MATRIX;
|
|
if (HasScales)
|
|
BoneDpFlags |= (uint8_t)SEModelBonePresenceFlags::SEMODEL_PRESENCE_SCALES;
|
|
|
|
auto MeshDpFlags = 0;
|
|
|
|
MeshDpFlags |= (uint8_t)SEModelMeshPresenceFlags::SEMODEL_PRESENCE_NORMALS;
|
|
MeshDpFlags |= (uint8_t)SEModelMeshPresenceFlags::SEMODEL_PRESENCE_COLOR;
|
|
MeshDpFlags |= (uint8_t)SEModelMeshPresenceFlags::SEMODEL_PRESENCE_UVSET;
|
|
|
|
bool HasBones = false;
|
|
if (!Model.Bones.Empty())
|
|
{
|
|
MeshDpFlags |= (uint8_t)SEModelMeshPresenceFlags::SEMODEL_PRESENCE_WEIGHTS;
|
|
HasBones = true;
|
|
}
|
|
|
|
Writer.Write<uint8_t>(FileDpFlags);
|
|
Writer.Write<uint8_t>(BoneDpFlags);
|
|
Writer.Write<uint8_t>(MeshDpFlags);
|
|
|
|
auto BoneCount = Model.Bones.Count();
|
|
auto MeshCount = Model.Meshes.Count();
|
|
auto MaterialCount = Model.Materials.Count();
|
|
|
|
Writer.Write<uint32_t>(BoneCount);
|
|
Writer.Write<uint32_t>(MeshCount);
|
|
Writer.Write<uint32_t>(MaterialCount);
|
|
|
|
uint8_t Reserved[3]{};
|
|
Writer.Write(Reserved, 0, 3);
|
|
|
|
for (auto& Bone : Model.Bones)
|
|
Writer.WriteCString(Bone.Name());
|
|
|
|
for (auto& Bone : Model.Bones)
|
|
{
|
|
Writer.Write<uint8_t>(0); // Bone Flags
|
|
Writer.Write<int32_t>(Bone.Parent()); // Parent
|
|
|
|
if (HasGlobalMatrix)
|
|
{
|
|
Writer.Write<Vector3>(Bone.GlobalPosition());
|
|
Writer.Write<Quaternion>(Bone.GlobalRotation());
|
|
}
|
|
|
|
if (HasLocalMatrix)
|
|
{
|
|
Writer.Write<Vector3>(Bone.LocalPosition());
|
|
Writer.Write<Quaternion>(Bone.LocalRotation());
|
|
}
|
|
|
|
if (HasScales)
|
|
Writer.Write<Vector3>(Bone.Scale());
|
|
}
|
|
|
|
for (auto& Mesh : Model.Meshes)
|
|
{
|
|
Writer.Write<uint8_t>(0); // Mesh Flags
|
|
|
|
auto VertexCount = Mesh.Vertices.Count();
|
|
auto FaceCount = Mesh.Faces.Count();
|
|
auto UVLayerCount = Mesh.Vertices.UVLayerCount();
|
|
auto MaxSkinCount = Mesh.Vertices.WeightCount();
|
|
|
|
Writer.Write<uint8_t>(UVLayerCount);
|
|
Writer.Write<uint8_t>(MaxSkinCount);
|
|
Writer.Write<uint32_t>(VertexCount);
|
|
Writer.Write<uint32_t>(FaceCount);
|
|
|
|
for (auto& Vertex : Mesh.Vertices)
|
|
Writer.Write<Vector3>(Vertex.Position());
|
|
|
|
for (auto& Vertex : Mesh.Vertices)
|
|
{
|
|
for (uint32_t i = 0; i < UVLayerCount; i++)
|
|
Writer.Write<Vector2>(Vertex.UVLayers(i));
|
|
}
|
|
|
|
for (auto& Vertex : Mesh.Vertices)
|
|
Writer.Write<Vector3>(Vertex.Normal());
|
|
|
|
for (auto& Vertex : Mesh.Vertices)
|
|
Writer.Write<VertexColor>(Vertex.Color());
|
|
|
|
if (MaxSkinCount > 0 && HasBones)
|
|
{
|
|
for (auto& Vertex : Mesh.Vertices)
|
|
{
|
|
for (uint32_t i = 0; i < MaxSkinCount; i++)
|
|
{
|
|
auto& Weight = Vertex.Weights(i);
|
|
|
|
if (BoneCount <= 0xFF)
|
|
Writer.Write<uint8_t>((uint8_t)Weight.Bone);
|
|
else if (BoneCount <= 0xFFFF)
|
|
Writer.Write<uint16_t>((uint16_t)Weight.Bone);
|
|
else
|
|
Writer.Write<uint32_t>(Weight.Bone);
|
|
|
|
Writer.Write<float>(Weight.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& Face : Mesh.Faces)
|
|
{
|
|
if (VertexCount <= 0xFF)
|
|
{
|
|
Writer.Write<uint8_t>((uint8_t)Face[0]);
|
|
Writer.Write<uint8_t>((uint8_t)Face[1]);
|
|
Writer.Write<uint8_t>((uint8_t)Face[2]);
|
|
}
|
|
else if (VertexCount <= 0xFFFF)
|
|
{
|
|
Writer.Write<uint16_t>((uint16_t)Face[0]);
|
|
Writer.Write<uint16_t>((uint16_t)Face[1]);
|
|
Writer.Write<uint16_t>((uint16_t)Face[2]);
|
|
}
|
|
else
|
|
{
|
|
Writer.Write<uint32_t>(Face[0]);
|
|
Writer.Write<uint32_t>(Face[1]);
|
|
Writer.Write<uint32_t>(Face[2]);
|
|
}
|
|
}
|
|
|
|
for (auto& MaterialIndex : Mesh.MaterialIndices)
|
|
Writer.Write<int32_t>(MaterialIndex);
|
|
}
|
|
|
|
for (auto& Material : Model.Materials)
|
|
{
|
|
Writer.WriteCString(Material.Name);
|
|
Writer.Write<bool>(true);
|
|
|
|
if (Material.Slots.ContainsKey(MaterialSlotType::Albedo))
|
|
Writer.WriteCString(Material.Slots[MaterialSlotType::Albedo].first);
|
|
else if (Material.Slots.ContainsKey(MaterialSlotType::Diffuse))
|
|
Writer.WriteCString(Material.Slots[MaterialSlotType::Diffuse].first);
|
|
else
|
|
Writer.Write<uint8_t>(0);
|
|
|
|
if (Material.Slots.ContainsKey(MaterialSlotType::Normal))
|
|
Writer.WriteCString(Material.Slots[MaterialSlotType::Normal].first);
|
|
else
|
|
Writer.Write<uint8_t>(0);
|
|
|
|
if (Material.Slots.ContainsKey(MaterialSlotType::Specular))
|
|
Writer.WriteCString(Material.Slots[MaterialSlotType::Specular].first);
|
|
else
|
|
Writer.Write<uint8_t>(0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
imstring SEAsset::ModelExtension()
|
|
{
|
|
return ".semodel";
|
|
}
|
|
|
|
imstring SEAsset::AnimationExtension()
|
|
{
|
|
return ".seanim";
|
|
}
|
|
|
|
ExporterScale SEAsset::ExportScale()
|
|
{
|
|
return ExporterScale::Default;
|
|
}
|
|
|
|
bool SEAsset::SupportsAnimations()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool SEAsset::SupportsModels()
|
|
{
|
|
return true;
|
|
}
|
|
}
|