mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Updated AnimationClip, Model Loader now loads Nodes into ECS
- SandboxApp: Started basic skinned mesh render system
This commit is contained in:
@@ -4,6 +4,7 @@ using cgltf;
|
|||||||
using GlitchyEngine.Math;
|
using GlitchyEngine.Math;
|
||||||
using GlitchyEngine.Renderer;
|
using GlitchyEngine.Renderer;
|
||||||
using GlitchyEngine.Renderer.Animation;
|
using GlitchyEngine.Renderer.Animation;
|
||||||
|
using GlitchyEngine.World;
|
||||||
|
|
||||||
namespace GlitchyEngine.Content
|
namespace GlitchyEngine.Content
|
||||||
{
|
{
|
||||||
@@ -11,10 +12,11 @@ namespace GlitchyEngine.Content
|
|||||||
{
|
{
|
||||||
static readonly Matrix RightToLeftHand = .Scaling(1, 1, -1);
|
static readonly Matrix RightToLeftHand = .Scaling(1, 1, -1);
|
||||||
|
|
||||||
public static void LoadModel(String filename, GraphicsContext context, Effect validationEffect, List<(Matrix Transform, GeometryBinding Model)> output, out Skeleton skeleton, out AnimationClip clip)
|
public static void LoadModel(String filename, GraphicsContext context, Effect validationEffect, Material material, EcsWorld world,
|
||||||
|
List<(Matrix Transform, GeometryBinding Model)> output, out Skeleton skeleton, out List<AnimationClip> clips)
|
||||||
{
|
{
|
||||||
skeleton = null;
|
skeleton = null;
|
||||||
clip = null;
|
removeMe___Clips = null;
|
||||||
|
|
||||||
CGLTF.Options options = .();
|
CGLTF.Options options = .();
|
||||||
CGLTF.Data* data;
|
CGLTF.Data* data;
|
||||||
@@ -26,6 +28,14 @@ namespace GlitchyEngine.Content
|
|||||||
|
|
||||||
Log.EngineLogger.Assert(result == .Success, "Failed to load buffers");
|
Log.EngineLogger.Assert(result == .Success, "Failed to load buffers");
|
||||||
|
|
||||||
|
for(var node in data.Scenes[0].Nodes)
|
||||||
|
{
|
||||||
|
NodesToEntities(data, node, null, world, context, validationEffect, material);
|
||||||
|
}
|
||||||
|
|
||||||
|
clips = removeMe___Clips;
|
||||||
|
|
||||||
|
|
||||||
for(var node in data.Nodes)
|
for(var node in data.Nodes)
|
||||||
{
|
{
|
||||||
if(node.Mesh != null)
|
if(node.Mesh != null)
|
||||||
@@ -45,14 +55,119 @@ namespace GlitchyEngine.Content
|
|||||||
|
|
||||||
if(node.Skin != null)
|
if(node.Skin != null)
|
||||||
{
|
{
|
||||||
|
AnimationClip clip;
|
||||||
(skeleton, clip) = ExtractBonestuff(node.Skin, data);
|
(skeleton, clip) = ExtractBonestuff(node.Skin, data);
|
||||||
|
clips.Add(clip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO make awesome stuff */
|
|
||||||
CGLTF.Free(data);
|
CGLTF.Free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<AnimationClip> removeMe___Clips;
|
||||||
|
|
||||||
|
private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, Entity? parentEntity, EcsWorld world, GraphicsContext context, Effect validationEffect, Material material)
|
||||||
|
{
|
||||||
|
Entity entity = world.NewEntity();
|
||||||
|
|
||||||
|
if(parentEntity.HasValue)
|
||||||
|
{
|
||||||
|
var childParent = world.AssignComponent<ParentComponent>(entity);
|
||||||
|
childParent.Entity = parentEntity.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childTransform = world.AssignComponent<TransformComponent>(entity);
|
||||||
|
|
||||||
|
if(node.HasMatrix)
|
||||||
|
{
|
||||||
|
childTransform.LocalTransform = *(Matrix*)&node.Matrix;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(node.HasTranslation)
|
||||||
|
childTransform.Position = *(Vector3*)&node.Translation;
|
||||||
|
else
|
||||||
|
childTransform.Position = .Zero;
|
||||||
|
|
||||||
|
if(node.HasRotation)
|
||||||
|
childTransform.Rotation = *(Quaternion*)&node.Rotation;
|
||||||
|
else
|
||||||
|
childTransform.Rotation = .Identity;
|
||||||
|
|
||||||
|
if(node.HasScale)
|
||||||
|
childTransform.Scale = *(Vector3*)&node.Scale;
|
||||||
|
else
|
||||||
|
childTransform.Scale = .(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parentEntity == null)
|
||||||
|
childTransform.Scale *= .(1, 1, -1);
|
||||||
|
|
||||||
|
Skeleton skeleton = null;
|
||||||
|
|
||||||
|
if(node.Skin != null)
|
||||||
|
{
|
||||||
|
skeleton = ExtractSkeleton(node.Skin);
|
||||||
|
|
||||||
|
removeMe___Clips = new List<AnimationClip>();
|
||||||
|
LoadAnimationClips(data, node.Skin, skeleton, removeMe___Clips);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node.Mesh != null)
|
||||||
|
{
|
||||||
|
// If we have only one primitive, add it directly to the entity
|
||||||
|
if(node.Mesh.Primitives.Length == 1)
|
||||||
|
{
|
||||||
|
var mesh = world.AssignComponent<MeshComponent>(entity);
|
||||||
|
mesh.Mesh = ModelLoader.PrimitiveToGeoBinding(context, node.Mesh.Primitives[0], validationEffect);
|
||||||
|
|
||||||
|
if(skeleton == null)
|
||||||
|
{
|
||||||
|
var meshRenderer = world.AssignComponent<MeshRendererComponent>(entity);
|
||||||
|
meshRenderer.Material = material;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(entity);
|
||||||
|
meshRenderer.Material = material;
|
||||||
|
meshRenderer.Skeleton = skeleton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// otherwise one child-entity per primitive
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(var primitive in node.Mesh.Primitives)
|
||||||
|
{
|
||||||
|
Entity meshEntity = world.NewEntity();
|
||||||
|
|
||||||
|
var meshParent = world.AssignComponent<ParentComponent>(meshEntity);
|
||||||
|
meshParent.Entity = entity;
|
||||||
|
|
||||||
|
var mesh = world.AssignComponent<MeshComponent>(meshEntity);
|
||||||
|
mesh.Mesh = ModelLoader.PrimitiveToGeoBinding(context, node.Mesh.Primitives[0], validationEffect);
|
||||||
|
|
||||||
|
if(skeleton == null)
|
||||||
|
{
|
||||||
|
var meshRenderer = world.AssignComponent<MeshRendererComponent>(entity);
|
||||||
|
meshRenderer.Material = material;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(entity);
|
||||||
|
meshRenderer.Material = material;
|
||||||
|
meshRenderer.Skeleton = skeleton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var child in node.Children)
|
||||||
|
{
|
||||||
|
NodesToEntities(data, child, entity, world, context, validationEffect, material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static GeometryBinding PrimitiveToGeoBinding(GraphicsContext context, CGLTF.Primitive primitive, Effect validationEffect)
|
public static GeometryBinding PrimitiveToGeoBinding(GraphicsContext context, CGLTF.Primitive primitive, Effect validationEffect)
|
||||||
{
|
{
|
||||||
GeometryBinding binding = new GeometryBinding(context);
|
GeometryBinding binding = new GeometryBinding(context);
|
||||||
@@ -95,6 +210,9 @@ namespace GlitchyEngine.Content
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasNormals = false;
|
||||||
|
bool hasTangents = false;
|
||||||
|
|
||||||
// vertices
|
// vertices
|
||||||
{
|
{
|
||||||
List<VertexElement> elements = scope .(primitive.Attributes.Length);
|
List<VertexElement> elements = scope .(primitive.Attributes.Length);
|
||||||
@@ -103,6 +221,19 @@ namespace GlitchyEngine.Content
|
|||||||
|
|
||||||
for(var attribute in primitive.Attributes)
|
for(var attribute in primitive.Attributes)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
StringView attributeName = StringView(attribute.Name);
|
||||||
|
|
||||||
|
if(attributeName.Equals("NORMAL"))
|
||||||
|
{
|
||||||
|
hasNormals = true;
|
||||||
|
}
|
||||||
|
else if(attributeName.Equals("TANGENT"))
|
||||||
|
{
|
||||||
|
hasTangents = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get Input Element format
|
// Get Input Element format
|
||||||
Format format = FormatFromVectorComponent(attribute.Data.Type, attribute.Data.ComponentType);
|
Format format = FormatFromVectorComponent(attribute.Data.Type, attribute.Data.ComponentType);
|
||||||
Log.EngineLogger.AssertDebug(format != .Unknown, "Vertex element format must not be \"Unknown.\"");
|
Log.EngineLogger.AssertDebug(format != .Unknown, "Vertex element format must not be \"Unknown.\"");
|
||||||
@@ -147,6 +278,7 @@ namespace GlitchyEngine.Content
|
|||||||
|
|
||||||
StringView strView = .(attribute.Name);
|
StringView strView = .(attribute.Name);
|
||||||
|
|
||||||
|
// Remove number from end of name
|
||||||
while((*(strView.EndPtr - 1)).IsDigit)
|
while((*(strView.EndPtr - 1)).IsDigit)
|
||||||
{
|
{
|
||||||
strView.Length--;
|
strView.Length--;
|
||||||
@@ -156,36 +288,254 @@ namespace GlitchyEngine.Content
|
|||||||
elements.Add(element);
|
elements.Add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate normals if missing
|
||||||
|
if(!hasNormals)
|
||||||
|
{
|
||||||
|
CGLTF.Accessor* positions = null;
|
||||||
|
|
||||||
|
// Find position accessor
|
||||||
|
for(var attribute in primitive.Attributes)
|
||||||
|
{
|
||||||
|
if(StringView(attribute.Name).Equals("POSITION"))
|
||||||
|
{
|
||||||
|
positions = attribute.Data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(positions != null, "The model appears to have no position data?!");
|
||||||
|
|
||||||
|
Vector3[] normals = new Vector3[positions.Count];
|
||||||
|
|
||||||
|
if(primitive.Indices != null)
|
||||||
|
GenerateNormals(primitive.Indices, positions, normals);
|
||||||
|
else
|
||||||
|
GenerateNormals(positions, normals);
|
||||||
|
|
||||||
|
VertexBuffer vertexBuffer = new VertexBuffer(context, (uint32)sizeof(Vector3), (uint32)normals.Count, .Immutable);
|
||||||
|
vertexBuffer.SetData<Vector3>(normals);
|
||||||
|
|
||||||
|
bindings.Add(vertexBuffer.Binding);
|
||||||
|
|
||||||
|
uint32 bindingSlot = (.)bindings.Count - 1;
|
||||||
|
|
||||||
|
binding.SetVertexBufferSlot(vertexBuffer.Binding, (.)bindingSlot);
|
||||||
|
VertexElement element = .(.R32G32B32_Float, "NORMAL", false, 0, bindingSlot);
|
||||||
|
elements.Add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: validate vertex layout somewhere else
|
||||||
|
|
||||||
VertexElement[] vertexElements = new VertexElement[elements.Count];
|
VertexElement[] vertexElements = new VertexElement[elements.Count];
|
||||||
for(int i < elements.Count)
|
for(int i < elements.Count)
|
||||||
{
|
{
|
||||||
vertexElements[i] = elements[i];
|
vertexElements[i] = elements[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: validate vertex layout somewhere else
|
|
||||||
|
|
||||||
VertexLayout layout = new VertexLayout(context, vertexElements, true, validationEffect.VertexShader);
|
VertexLayout layout = new VertexLayout(context, vertexElements, true, validationEffect.VertexShader);
|
||||||
binding.SetVertexLayout(layout..ReleaseRefNoDelete());
|
binding.SetVertexLayout(layout..ReleaseRefNoDelete());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: calculate normals if missing
|
|
||||||
// TODO: calculate tangents if missing (MikkTSpace algorithm)
|
// TODO: calculate tangents if missing (MikkTSpace algorithm)
|
||||||
// Note: Bitangent = cross(normal, tangent.xyz) * tangent.w
|
// Note: Bitangent = cross(normal, tangent.xyz) * tangent.w
|
||||||
|
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates the normals for one triangle
|
||||||
|
static mixin GenerateTriangleNormals(int index0, int index1, int index2, CGLTF.Accessor* positions, Vector3[] normals)
|
||||||
|
{
|
||||||
|
Vector3 position0 = GetEntry<Vector3>(positions, (.)index0);
|
||||||
|
Vector3 position1 = GetEntry<Vector3>(positions, (.)index1);
|
||||||
|
Vector3 position2 = GetEntry<Vector3>(positions, (.)index2);
|
||||||
|
|
||||||
|
ref Vector3 normal0 = ref normals[(.)index0];
|
||||||
|
ref Vector3 normal1 = ref normals[(.)index1];
|
||||||
|
ref Vector3 normal2 = ref normals[(.)index2];
|
||||||
|
|
||||||
|
Vector3 e0 = position1 - position0;
|
||||||
|
Vector3 e1 = position2 - position0;
|
||||||
|
|
||||||
|
Vector3 normal = Vector3.Cross(e0, e1);
|
||||||
|
|
||||||
|
normal0 += normal;
|
||||||
|
normal1 += normal;
|
||||||
|
normal2 += normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
static void NormalizeNormals(Vector3[] normals)
|
||||||
|
{
|
||||||
|
for(int i < normals.Count)
|
||||||
|
{
|
||||||
|
normals[i].Normalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates normals for the given model using indexed geometry
|
||||||
|
static void GenerateNormals(CGLTF.Accessor* indices, CGLTF.Accessor* positions, Vector3[] normals)
|
||||||
|
{
|
||||||
|
/// Enumerate triangle-wise
|
||||||
|
for(uint t = 0; t < indices.Count; t += 3)
|
||||||
|
{
|
||||||
|
int index0 = (.)CGLTF.AccessorReadIndex(indices, t);
|
||||||
|
int index1 = (.)CGLTF.AccessorReadIndex(indices, t + 1);
|
||||||
|
int index2 = (.)CGLTF.AccessorReadIndex(indices, t + 2);
|
||||||
|
|
||||||
|
GenerateTriangleNormals!(index0, index1, index2, positions, normals);
|
||||||
|
}
|
||||||
|
|
||||||
|
NormalizeNormals(normals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates normals for the given model using nonindexed geometry
|
||||||
|
static void GenerateNormals(CGLTF.Accessor* positions, Vector3[] normals)
|
||||||
|
{
|
||||||
|
/// Enumerate triangle-wise
|
||||||
|
for(int i = 0; i < (.)positions.Count; i += 3)
|
||||||
|
{
|
||||||
|
GenerateTriangleNormals!(i, i + 1, i + 2, positions, normals);
|
||||||
|
}
|
||||||
|
|
||||||
|
NormalizeNormals(normals);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Skeleton ExtractSkeleton(CGLTF.Skin* skin)
|
||||||
|
{
|
||||||
|
Skeleton skeleton = new Skeleton();
|
||||||
|
skeleton.Joints = new Joint[skin.Joints.Length];
|
||||||
|
|
||||||
|
for(int i < skin.Joints.Length)
|
||||||
|
{
|
||||||
|
ref Joint joint = ref skeleton.Joints[i];
|
||||||
|
|
||||||
|
joint.InverseBindPose = GetEntry<Matrix>(skin.InverseBindMatrices, i);
|
||||||
|
|
||||||
|
joint.Name = new String(skin.Joints[i].Name);
|
||||||
|
|
||||||
|
int parentId = skin.Joints.IndexOf(skin.Joints[i].Parent);
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(parentId < uint8.MaxValue, scope $"A skeleton must not have more than {uint8.MaxValue - 1} bones.");
|
||||||
|
|
||||||
|
if(parentId == -1)
|
||||||
|
joint.ParentID = uint8.MaxValue;
|
||||||
|
else
|
||||||
|
joint.ParentID = (uint8)parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AnimationBelongsToSkeleton(CGLTF.Animation animation, CGLTF.Skin* skin)
|
||||||
|
{
|
||||||
|
for(var channel in animation.Channels)
|
||||||
|
{
|
||||||
|
if(skin.Joints.IndexOf(channel.TargetNode) == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LoadAnimationClips(CGLTF.Data* data, CGLTF.Skin* skin, Skeleton skeleton, List<AnimationClip> clips)
|
||||||
|
{
|
||||||
|
for(var animation in data.Animations)
|
||||||
|
{
|
||||||
|
if(!AnimationBelongsToSkeleton(animation, skin))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
AnimationClip clip = new AnimationClip(skeleton);
|
||||||
|
clips.Add(clip);
|
||||||
|
clip.IsLooping = true;
|
||||||
|
|
||||||
|
for(var channel in animation.Channels)
|
||||||
|
{
|
||||||
|
int nodeIndex = skin.Joints.IndexOf(channel.TargetNode);
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(nodeIndex != -1);
|
||||||
|
|
||||||
|
ref JointAnimation jointAnimation = ref clip.JointAnimations[nodeIndex];
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Count == channel.Sampler.Output.Count);
|
||||||
|
|
||||||
|
int samples = (int)channel.Sampler.Input.Count;
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Input.ComponentType == .R_32f);
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Type == .Scalar);
|
||||||
|
|
||||||
|
InterpolationMode interpolationMode;
|
||||||
|
|
||||||
|
switch(channel.Sampler.Interpolation)
|
||||||
|
{
|
||||||
|
case .Step:
|
||||||
|
interpolationMode = .Step;
|
||||||
|
case .Linear:
|
||||||
|
interpolationMode = .Linear;
|
||||||
|
case .CubicSpline:
|
||||||
|
interpolationMode = .CubicSpline;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(channel.TargetPath)
|
||||||
|
{
|
||||||
|
case .Translation:
|
||||||
|
jointAnimation.TranslationChannel = new .(samples, interpolationMode);
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
|
||||||
|
|
||||||
|
for(int i < samples)
|
||||||
|
{
|
||||||
|
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
|
||||||
|
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
|
||||||
|
|
||||||
|
jointAnimation.TranslationChannel.TimeStamps[i] = timeStamp;
|
||||||
|
jointAnimation.TranslationChannel.Values[i] = sample;
|
||||||
|
}
|
||||||
|
case .Rotation:
|
||||||
|
jointAnimation.RotationChannel = new .(samples, interpolationMode);
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec4);
|
||||||
|
|
||||||
|
for(int i < samples)
|
||||||
|
{
|
||||||
|
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
|
||||||
|
Quaternion sample = GetEntry<Quaternion>(channel.Sampler.Output, i);
|
||||||
|
|
||||||
|
jointAnimation.RotationChannel.TimeStamps[i] = timeStamp;
|
||||||
|
jointAnimation.RotationChannel.Values[i] = sample;
|
||||||
|
}
|
||||||
|
case .Scale:
|
||||||
|
jointAnimation.ScaleChannel = new .(samples, interpolationMode);
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
|
||||||
|
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
|
||||||
|
|
||||||
|
for(int i < samples)
|
||||||
|
{
|
||||||
|
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
|
||||||
|
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
|
||||||
|
|
||||||
|
jointAnimation.ScaleChannel.TimeStamps[i] = timeStamp;
|
||||||
|
jointAnimation.ScaleChannel.Values[i] = sample;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Log.EngineLogger.Error($"Unknown channel target path \"{channel.TargetPath}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
clip.Duration = Math.Max(clip.Duration, jointAnimation.Duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static (Skeleton, AnimationClip) ExtractBonestuff(CGLTF.Skin* skin, CGLTF.Data* data)
|
static (Skeleton, AnimationClip) ExtractBonestuff(CGLTF.Skin* skin, CGLTF.Data* data)
|
||||||
{
|
{
|
||||||
Skeleton skeleton = new Skeleton();
|
Skeleton skeleton = new Skeleton();
|
||||||
skeleton.Joints = new Joint[skin.Joints.Length];
|
skeleton.Joints = new Joint[skin.Joints.Length];
|
||||||
|
|
||||||
AnimationClip testClip = new AnimationClip();
|
AnimationClip testClip = new AnimationClip(skeleton);
|
||||||
testClip.Skeleton = skeleton;
|
|
||||||
testClip.FramesPerSecond = 0;
|
|
||||||
testClip.JointAnimations = new JointAnimation[skeleton.Joints.Count];
|
|
||||||
//testClip.Samples = new AnimationSample[1];
|
|
||||||
//testClip.Samples[0].JointPose = new JointPose[skeleton.Joints.Count];
|
|
||||||
testClip.IsLooping = true;
|
testClip.IsLooping = true;
|
||||||
|
|
||||||
for(int i < skin.Joints.Length)
|
for(int i < skin.Joints.Length)
|
||||||
@@ -323,6 +673,16 @@ namespace GlitchyEngine.Content
|
|||||||
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 4);
|
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 4);
|
||||||
case typeof(Matrix):
|
case typeof(Matrix):
|
||||||
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 16);
|
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 16);
|
||||||
|
case default:
|
||||||
|
uint8* data = (uint8*)accessor.BufferView.Buffer.Data;
|
||||||
|
|
||||||
|
data += accessor.BufferView.Offset;
|
||||||
|
|
||||||
|
data += accessor.Offset;
|
||||||
|
|
||||||
|
data += accessor.Stride * (uint)index;
|
||||||
|
|
||||||
|
result = *(T*)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -67,23 +67,22 @@ namespace GlitchyEngine.Renderer.Animation
|
|||||||
class AnimationClip : RefCounted
|
class AnimationClip : RefCounted
|
||||||
{
|
{
|
||||||
private Skeleton _skeleton ~ _.ReleaseRef();
|
private Skeleton _skeleton ~ _.ReleaseRef();
|
||||||
public float FramesPerSecond;
|
//public float FramesPerSecond;
|
||||||
public JointAnimation[] JointAnimations;
|
public JointAnimation[] JointAnimations;
|
||||||
public bool IsLooping;
|
public bool IsLooping;
|
||||||
public float Duration;
|
public float Duration;
|
||||||
|
|
||||||
public Skeleton Skeleton
|
public Skeleton Skeleton => _skeleton;
|
||||||
{
|
|
||||||
get => _skeleton;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if(_skeleton == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_skeleton?.ReleaseRef();
|
[AllowAppend]
|
||||||
_skeleton = value;
|
public this(Skeleton skeleton)
|
||||||
_skeleton?.AddRef();
|
{
|
||||||
}
|
var jointAnimations = append JointAnimation[skeleton.Joints.Count];
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(skeleton != null);
|
||||||
|
|
||||||
|
_skeleton = skeleton..AddRef();
|
||||||
|
JointAnimations = jointAnimations;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check
|
// TODO: check
|
||||||
@@ -93,8 +92,6 @@ namespace GlitchyEngine.Renderer.Animation
|
|||||||
{
|
{
|
||||||
jointAnimation.Dispose();
|
jointAnimation.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete JointAnimations;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+69
-68
@@ -84,6 +84,8 @@ namespace Sandbox
|
|||||||
[AllowAppend]
|
[AllowAppend]
|
||||||
public this() : base("Example")
|
public this() : base("Example")
|
||||||
{
|
{
|
||||||
|
Application.Get().Window.IsVSync = false;
|
||||||
|
|
||||||
_context = Application.Get().Window.Context..AddRef();
|
_context = Application.Get().Window.Context..AddRef();
|
||||||
|
|
||||||
var effectLibrary = Application.Get().EffectLibrary;
|
var effectLibrary = Application.Get().EffectLibrary;
|
||||||
@@ -217,9 +219,11 @@ namespace Sandbox
|
|||||||
|
|
||||||
List<(Matrix Transform, GeometryBinding Model)> _modelTest = new .() ~ UnloadModelTest!();
|
List<(Matrix Transform, GeometryBinding Model)> _modelTest = new .() ~ UnloadModelTest!();
|
||||||
|
|
||||||
Skeleton Skeleton ~ delete _;
|
Skeleton Skeleton;
|
||||||
AnimationClip Clip ~ delete _;
|
List<AnimationClip> Clips ~ delete _;
|
||||||
|
|
||||||
AnimationPlayer AnimationPlayer ~ delete _;
|
AnimationPlayer AnimationPlayer ~ delete _;
|
||||||
|
Material animationMat;
|
||||||
|
|
||||||
mixin UnloadModelTest()
|
mixin UnloadModelTest()
|
||||||
{
|
{
|
||||||
@@ -234,18 +238,44 @@ namespace Sandbox
|
|||||||
void TestLoadModel()
|
void TestLoadModel()
|
||||||
{
|
{
|
||||||
var testEffect = Application.Get().EffectLibrary.Get("testShader");
|
var testEffect = Application.Get().EffectLibrary.Get("testShader");
|
||||||
|
var materialTestMaterial = new Material(testEffect);
|
||||||
|
animationMat = materialTestMaterial;
|
||||||
|
|
||||||
|
Matrix[] matrices = scope Matrix[255];
|
||||||
|
Matrix3x3[] matrices2 = scope Matrix3x3[255];
|
||||||
|
|
||||||
|
for(int i < 255)
|
||||||
|
{
|
||||||
|
matrices[i] = .Identity;
|
||||||
|
matrices2[i] = .Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
materialTestMaterial.SetVariable("SkinningMatrices", matrices);
|
||||||
|
materialTestMaterial.SetVariable("InvTransSkinningMatrices", matrices2);
|
||||||
|
|
||||||
|
materialTestMaterial.SetVariable("BaseColor", Color.White);
|
||||||
|
materialTestMaterial.SetVariable("LightDir", Vector3(1, 1, -0.5f).Normalized());
|
||||||
|
|
||||||
//ModelLoader.LoadModel("content\\Models\\Test\\axisTest.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
//ModelLoader.LoadModel("content\\Models\\Test\\axisTest.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
||||||
//ModelLoader.LoadModel("content\\Models\\RiggedSimple\\RiggedSimple.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
//ModelLoader.LoadModel("content\\Models\\RiggedSimple\\RiggedSimple.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
||||||
//ModelLoader.LoadModel("content\\Models\\Fox\\Fox_2.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
//ModelLoader.LoadModel("content\\Models\\Fox\\Fox.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
||||||
|
//ModelLoader.LoadModel("content\\Models\\Fox\\Fox_2.glb", _context, testEffect, materialTestMaterial, _world, _modelTest, out Skeleton, out Clips);
|
||||||
//ModelLoader.LoadModel("content\\Models\\DancingCylinder\\DancingCylinder.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
//ModelLoader.LoadModel("content\\Models\\DancingCylinder\\DancingCylinder.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
||||||
//ModelLoader.LoadModel("content\\Models\\Figure\\Figure.gltf", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
ModelLoader.LoadModel("content\\Models\\Figure\\Figure.gltf", _context, testEffect, materialTestMaterial, _world, _modelTest, out Skeleton, out Clips);
|
||||||
ModelLoader.LoadModel("content\\Models\\RiggedFigure\\RiggedFigure.glb", _context, testEffect, _modelTest, out Skeleton, out Clip);
|
//ModelLoader.LoadModel("content\\Models\\RiggedFigure\\RiggedFigure.glb", _context, testEffect, materialTestMaterial, _world, _modelTest, out Skeleton, out Clips);
|
||||||
|
|
||||||
|
materialTestMaterial.ReleaseRef();
|
||||||
testEffect.ReleaseRef();
|
testEffect.ReleaseRef();
|
||||||
|
|
||||||
if(Skeleton != null && Clip != null)
|
if(Clips != null && Clips.Count > 0)
|
||||||
AnimationPlayer = new AnimationPlayer(Skeleton, Clip);
|
AnimationPlayer = new AnimationPlayer(Skeleton, Clips.Back);
|
||||||
|
|
||||||
|
for(var (entity, transform, mesh, meshRenderer) in _world.Enumerate<TransformComponent, MeshComponent, SkinnedMeshRendererComponent>())
|
||||||
|
{
|
||||||
|
var animation = _world.AssignComponent<AnimationComponent>(entity);
|
||||||
|
|
||||||
|
animation.AnimationClip = Clips[0];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CGLTF.Options options = .();
|
CGLTF.Options options = .();
|
||||||
@@ -282,7 +312,9 @@ namespace Sandbox
|
|||||||
_world.Register<ParentComponent>();
|
_world.Register<ParentComponent>();
|
||||||
_world.Register<MeshComponent>();
|
_world.Register<MeshComponent>();
|
||||||
_world.Register<MeshRendererComponent>();
|
_world.Register<MeshRendererComponent>();
|
||||||
|
_world.Register<SkinnedMeshRendererComponent>();
|
||||||
_world.Register<CameraComponent>();
|
_world.Register<CameraComponent>();
|
||||||
|
_world.Register<AnimationComponent>();
|
||||||
|
|
||||||
var basicEffect = Application.Get().EffectLibrary.Get("basicShader");
|
var basicEffect = Application.Get().EffectLibrary.Get("basicShader");
|
||||||
|
|
||||||
@@ -292,7 +324,7 @@ namespace Sandbox
|
|||||||
testMaterial2.SetVariable("BaseColor", _squareColor1);
|
testMaterial2.SetVariable("BaseColor", _squareColor1);
|
||||||
|
|
||||||
basicEffect.ReleaseRef();
|
basicEffect.ReleaseRef();
|
||||||
|
/*
|
||||||
Entity[20][20] entities;
|
Entity[20][20] entities;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -343,12 +375,12 @@ namespace Sandbox
|
|||||||
var parent = _world.AssignComponent<ParentComponent>(entities[x][y]);
|
var parent = _world.AssignComponent<ParentComponent>(entities[x][y]);
|
||||||
parent.Entity = crazyParent;
|
parent.Entity = crazyParent;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
float f = 0.0f;
|
float f = 0.0f;
|
||||||
|
|
||||||
bool playAnimation = false;
|
bool playAnimation = false;
|
||||||
bool drawModel = false;
|
|
||||||
|
|
||||||
public override void Update(GameTime gameTime)
|
public override void Update(GameTime gameTime)
|
||||||
{
|
{
|
||||||
@@ -372,13 +404,25 @@ namespace Sandbox
|
|||||||
|
|
||||||
var basicEffect = Application.Get().EffectLibrary.Get("basicShader");
|
var basicEffect = Application.Get().EffectLibrary.Get("basicShader");
|
||||||
|
|
||||||
// Model test
|
_context.SetRasterizerState(_rasterizerState);
|
||||||
|
|
||||||
|
TransformSystem.Update(_world);
|
||||||
|
|
||||||
|
for(var (entity, transform, mesh, meshRenderer) in _world.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
|
||||||
{
|
{
|
||||||
//AnimationPlayer.CurrentClip.Samples[0].JointPose[1].Rotation = .(1, 0, 0, 1)..Normalize();
|
Renderer.Submit(mesh.Mesh, meshRenderer.Material, transform.WorldTransform);
|
||||||
|
}
|
||||||
|
|
||||||
Matrix scaling = .Scaling(1f, 1.0f, -1.0f);
|
for(var (entity, transform, mesh, meshRenderer, animation) in _world.Enumerate<TransformComponent, MeshComponent, SkinnedMeshRendererComponent, AnimationComponent>())
|
||||||
|
{
|
||||||
|
var material = meshRenderer.Material;
|
||||||
|
|
||||||
var testEffect = Application.Get().EffectLibrary.Get("testShader");
|
//var testEffect = Application.Get().EffectLibrary.Get("testShader");
|
||||||
|
|
||||||
|
if(AnimationPlayer == null)
|
||||||
|
{
|
||||||
|
AnimationPlayer = new AnimationPlayer(meshRenderer.Skeleton, Clips[0]);
|
||||||
|
}
|
||||||
|
|
||||||
if(AnimationPlayer != null)
|
if(AnimationPlayer != null)
|
||||||
{
|
{
|
||||||
@@ -417,66 +461,25 @@ namespace Sandbox
|
|||||||
|
|
||||||
Vector3 end = parentMatrix.Translation;
|
Vector3 end = parentMatrix.Translation;
|
||||||
|
|
||||||
Renderer.DrawLine(start, end, .Black, scaling);
|
Renderer.DrawLine(start, end, .Black, transform.WorldTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer.DrawLine(.Zero, Vector3(0.1f, 0, 0), .Red, scaling * mat);
|
Renderer.DrawLine(.Zero, Vector3(0.1f, 0, 0), .Red, transform.WorldTransform * mat);
|
||||||
Renderer.DrawLine(.Zero, Vector3(0, 0.1f, 0), .Lime, scaling * mat);
|
Renderer.DrawLine(.Zero, Vector3(0, 0.1f, 0), .Lime, transform.WorldTransform * mat);
|
||||||
Renderer.DrawLine(.Zero, Vector3(0, 0, 0.1f), .Blue, scaling * mat);
|
Renderer.DrawLine(.Zero, Vector3(0, 0, 0.1f), .Blue, transform.WorldTransform * mat);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
testEffect.Variables["SkinningMatrices"].SetData(AnimationPlayer.SkinningMatricies);
|
material.SetVariable("SkinningMatrices", AnimationPlayer.SkinningMatricies);
|
||||||
testEffect.Variables["InvTransSkinningMatrices"].SetData(AnimationPlayer.InvTransSkinningMatricies);
|
material.SetVariable("InvTransSkinningMatrices", AnimationPlayer.InvTransSkinningMatricies);
|
||||||
|
|
||||||
testEffect.Variables["BaseColor"].SetData(Color.White);
|
|
||||||
testEffect.Variables["LightDir"].SetData(Vector3(1, 1, -0.5f).Normalized());
|
|
||||||
|
|
||||||
|
// TODO: non engine
|
||||||
|
material.SetVariable("BaseColor", Color.White);
|
||||||
|
material.SetVariable("LightDir", Vector3(1, 1, -0.5f).Normalized());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(drawModel)
|
Renderer.Submit(mesh.Mesh, material, transform.WorldTransform);
|
||||||
{
|
|
||||||
for(var entry in _modelTest)
|
|
||||||
{
|
|
||||||
Renderer.Submit(entry.Model, testEffect, .Identity * scaling);//entry.Transform
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testEffect.ReleaseRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
_context.SetRasterizerState(_rasterizerState);
|
|
||||||
|
|
||||||
//var crazyTransform = _world.GetComponent<TransformComponent>(evenCrazierParent);
|
|
||||||
//crazyTransform.Rotation += .((float)gameTime.FrameTime.TotalSeconds / 2, 0, 0);
|
|
||||||
|
|
||||||
|
|
||||||
var crazyTransform = _world.GetComponent<TransformComponent>(crazyParent);
|
|
||||||
if(Input.IsKeyPressed(Key.Six))
|
|
||||||
{
|
|
||||||
crazyTransform.RotationEuler += .((float)gameTime.FrameTime.TotalSeconds, 0, 0);
|
|
||||||
}
|
|
||||||
if(Input.IsKeyPressed(Key.Seven))
|
|
||||||
{
|
|
||||||
crazyTransform.RotationEuler += .(0, (float)gameTime.FrameTime.TotalSeconds, 0);
|
|
||||||
}
|
|
||||||
if(Input.IsKeyPressed(Key.Eight))
|
|
||||||
{
|
|
||||||
Vector3 vec = crazyTransform.RotationEuler;
|
|
||||||
vec += .(0, 0, (float)gameTime.FrameTime.TotalSeconds);
|
|
||||||
crazyTransform.RotationEuler = vec;
|
|
||||||
}
|
|
||||||
if(Input.IsKeyPressed(Key.Nine))
|
|
||||||
{
|
|
||||||
crazyTransform.RotationEuler = .(0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TransformSystem.Update(_world);
|
|
||||||
|
|
||||||
for(var (entity, transform, mesh, meshRenderer) in _world.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
|
|
||||||
{
|
|
||||||
Renderer.Submit(mesh.Mesh, meshRenderer.Material, transform.WorldTransform);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
basicEffect.Variables["BaseColor"].SetData(_squareColor1);
|
basicEffect.Variables["BaseColor"].SetData(_squareColor1);
|
||||||
@@ -517,18 +520,16 @@ namespace Sandbox
|
|||||||
|
|
||||||
if(AnimationPlayer != null)
|
if(AnimationPlayer != null)
|
||||||
{
|
{
|
||||||
ImGui.DragFloat("Timestamp", &AnimationPlayer.TimeStamp, 0.01f, -Clip.Duration, Clip.Duration);
|
ImGui.DragFloat("Timestamp", &AnimationPlayer.TimeStamp, 0.01f, -AnimationPlayer.CurrentClip.Duration, AnimationPlayer.CurrentClip.Duration);
|
||||||
|
|
||||||
while(AnimationPlayer.TimeStamp < 0)
|
while(AnimationPlayer.TimeStamp < 0)
|
||||||
{
|
{
|
||||||
AnimationPlayer.TimeStamp += Clip.Duration;
|
AnimationPlayer.TimeStamp += AnimationPlayer.CurrentClip.Duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Checkbox("Play", &playAnimation);
|
ImGui.Checkbox("Play", &playAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Checkbox("Draw Model", &drawModel);
|
|
||||||
|
|
||||||
ImGui.End();
|
ImGui.End();
|
||||||
|
|
||||||
ImGui.Begin("Test");
|
ImGui.Begin("Test");
|
||||||
|
|||||||
Reference in New Issue
Block a user