Initial commit

This commit is contained in:
2024-10-11 22:21:32 +02:00
commit 1bdd01e9b2
38 changed files with 11363 additions and 0 deletions

285
animation/animator.jai Normal file
View File

@@ -0,0 +1,285 @@
Animation_State_Type :: enum {
SINGLE;
BLEND_TREE_1D;
}
Animation_Point :: struct {
position: float;
index: s32;
time: float;
multiplier: float = 1.0;
looping: bool;
}
Animation_Transition :: struct {
time: float;
duration: float;
from_index: s32;
to_index: s32;
}
Animation_State :: struct {
type: Animation_State_Type;
union {
single : struct {
time: float;
multiplier: float;
looping: bool;
index: s32;
}
blend_tree : struct {
value: float; // Range is 0.0 - 1.0
points: [..] Animation_Point;
}
}
}
Animator :: struct {
playing: bool;
state_index: s32;
states: [..] Animation_State;
playing_transition: bool;
transition : Animation_Transition;
}
sample_animation_node :: (animation: Animation, f0: s32, f1: s32, t: float, node_index: s32) -> position: Vector3, rotation: Quaternion, scale: Vector3 {
if node_index >= animation.nodes.count return .{0,0,0}, .{0,0,0,1}, .{1,1,1};
node_anim := animation.nodes[node_index];
pos : Vector3 = ---;
rot : Quaternion = ---;
scale : Vector3 = ---;
if node_anim.position.count == 0 {
pos = node_anim.const_position;
} else {
pos = lerp(node_anim.position[f0], node_anim.position[f1], t);
}
if node_anim.rotation.count == 0 {
rot = node_anim.const_rotation;
} else {
rot = slerp(node_anim.rotation[f0], node_anim.rotation[f1], t);
}
if node_anim.scale.count == 0 {
scale = node_anim.const_scale;
} else {
scale = lerp(node_anim.scale[f0], node_anim.scale[f1], t);
}
return pos, rot, scale;
}
sample_animation :: (e: *Entity, animation: Animation, time: float, weight: float) {
frame_time := time * animation.framerate;
// Sample!
f0 := min(cast(s32)frame_time + 0, animation.num_frames - 1);
f1 := min(cast(s32)frame_time + 1, animation.num_frames - 1);
t := cast(float)min(frame_time - cast(float)f0, 1.0);
for * e.renderable.nodes {
pos, rot, scale := sample_animation_node(animation, f0, f1, t, cast(s32)it_index);
it.transform.position += pos * weight;
// Make sure that we interpolate with the shortest path
if dot(it.transform.orientation, rot) < 0.0 {
rot = -rot;
}
it.transform.orientation += rot * weight;
it.transform.scale += scale * weight;
}
}
animation_is_done :: (e: *Entity) -> bool {
assert(cast(bool)(e.flags & Entity_Flags.ANIMATED));
animator := e.animator;
if animator.playing_transition return false;
state := animator.states[animator.state_index];
if state.type == {
case .SINGLE;
animation := e.renderable.model.animations[state.single.index];
return state.single.time >= animation.duration - 0.0001;
case .BLEND_TREE_1D;
}
return false;
}
animation_time :: (e: *Entity) -> float {
assert(cast(bool)(e.flags & Entity_Flags.ANIMATED));
animator := e.animator;
if animator.playing_transition return 0.0;
state := animator.states[animator.state_index];
if state.type == {
case .SINGLE;
animation := e.renderable.model.animations[state.single.index];
return state.single.time;
case .BLEND_TREE_1D;
}
return 0.0;
}
animation_time_normalized :: (e: *Entity) -> float {
assert(cast(bool)(e.flags & Entity_Flags.ANIMATED));
animator := e.animator;
if animator.playing_transition return 0.0;
state := animator.states[animator.state_index];
if state.type == {
case .SINGLE;
animation := e.renderable.model.animations[state.single.index];
return state.single.time / xx animation.duration;
case .BLEND_TREE_1D;
}
return 0.0;
}
sample_animation_state :: (e: *Entity, index: s32, weight: float) {
state := *e.animator.states[index];
if state.type == {
case .SINGLE;
{
animation := *e.renderable.model.animations[state.single.index];
sample_animation(e, animation, min(state.single.time, cast(float)animation.duration - 0.0001), weight);
}
case .BLEND_TREE_1D;
{
p0 : s32;
p1 : s32;
found := false;
current_value := state.blend_tree.value;
for index: 0..state.blend_tree.points.count-1 {
p := *state.blend_tree.points[index];
if current_value >= p.position && index != state.blend_tree.points.count-1 {
p0 = xx index;
p1 = p0 + 1;
}
}
p0_position := state.blend_tree.points[p0].position;
p1_position := state.blend_tree.points[p1].position;
position := state.blend_tree.value - p0_position;
range := p1_position - p0_position;
alpha := position / range;
anim0 := e.renderable.model.animations[state.blend_tree.points[p0].index];
anim1 := e.renderable.model.animations[state.blend_tree.points[p1].index];
time0 := state.blend_tree.points[p0].time;
time1 := state.blend_tree.points[p1].time;
sample_animation(e, anim0, time0, (1.0 - alpha) * weight);
sample_animation(e, anim1, time1, alpha * weight);
}
}
}
transition_to_animation_state :: (e: *Entity, index: s32, transition_duration: float) {
assert(cast(bool)(e.flags & Entity_Flags.ANIMATED));
e.animator.playing_transition = true;
e.animator.transition.time = 0.0;
e.animator.transition.duration = transition_duration;
e.animator.transition.from_index = e.animator.state_index;
e.animator.transition.to_index = index;
// @Incomplete: Missing blend tree
e.animator.states[index].single.time = 0.0;
}
update_animation_state :: (e: *Entity, index: s32, dt: float) {
state := *e.animator.states[index];
if state.type == {
case .SINGLE;
{
state.single.time += dt * state.single.multiplier;
animation := *e.renderable.model.animations[state.single.index];
if state.single.time > animation.duration {
if state.single.looping {
state.single.time = cast(float)(state.single.time - animation.duration);
} else {
state.single.time = xx animation.duration;
}
}
}
case .BLEND_TREE_1D;
{
p0 : s32;
p1 : s32;
found := false;
for *p: state.blend_tree.points {
p.time += dt * p.multiplier;
animation := *e.renderable.model.animations[p.index];
if p.time > animation.duration {
if p.looping {
p.time = cast(float)(p.time - animation.duration);
} else {
p.time = xx animation.duration - 0.0001; // Is this correct?
}
}
}
}
}
}
update_animator :: (e: *Entity, animator: *Animator, dt: float) {
if animator.playing {
// Reset all transforms
for * e.renderable.nodes {
it.transform.position = .{0,0,0};
it.transform.scale = .{0,0,0};
it.transform.orientation = .{0,0,0,0};
it.has_sampled_animation = false;
}
if animator.playing_transition {
update_animation_state(e, animator.transition.from_index, dt);
update_animation_state(e, animator.transition.to_index, dt);
animator.transition.time += dt;
alpha := animator.transition.time / animator.transition.duration;
clamped := clamp(alpha, 0.0, 1.0);
sample_animation_state(e, animator.transition.from_index, 1.0 - clamped);
sample_animation_state(e, animator.transition.to_index, clamped);
if alpha >= 1.0 {
animator.playing_transition = false;
animator.state_index = animator.transition.to_index;
}
} else {
update_animation_state(e, animator.state_index, dt);
sample_animation_state(e, animator.state_index, 1.0);
}
}
}