Browse Source

world: add SpriteAnimationPlayer

Part-of: #276
Daniele Bartolini 11 tháng trước cách đây
mục cha
commit
fc19420972

+ 30 - 24
src/world/animation_state_machine.cpp

@@ -29,13 +29,18 @@ static StateMachineInstance make_instance(u32 i)
 	StateMachineInstance inst = { i }; return inst;
 }
 
-AnimationStateMachine::AnimationStateMachine(Allocator &a, ResourceManager &rm, UnitManager &um)
+AnimationStateMachine::AnimationStateMachine(Allocator &a
+	, ResourceManager &rm
+	, UnitManager &um
+	, SpriteAnimationPlayer &sprite_player
+	)
 	: _marker(ANIMATION_STATE_MACHINE_MARKER)
 	, _resource_manager(&rm)
 	, _unit_manager(&um)
 	, _map(a)
 	, _machines(a)
 	, _events(a)
+	, _sprite_animation_player(&sprite_player)
 {
 	_unit_destroy_callback.destroy = unit_destroyed_callback_bridge;
 	_unit_destroy_callback.user_data = this;
@@ -60,9 +65,9 @@ StateMachineInstance AnimationStateMachine::create(UnitId unit, const AnimationS
 	m.unit          = unit;
 	m.time          = 0.0f;
 	m.time_total    = 0.0f;
-	m.num_frames    = 0;
-	m.frames        = NULL;
-	m.resource      = NULL;
+	m.anim_type     = smr->animation_type;
+	m.anim_resource = NULL;
+	m.anim_id       = UINT32_MAX;
 	m.state         = state_machine::initial_state(smr);
 	m.state_next    = NULL;
 	m.state_machine = smr;
@@ -178,22 +183,29 @@ void AnimationStateMachine::update(float dt)
 		expression_language::run(&byte_code[mi.state->speed_bytecode], variables, stack);
 		const f32 speed = stack.size > 0 ? stack_data[stack.size - 1] : 1.0f;
 
-		// Advance animation
-		const SpriteAnimationResource *sar = (SpriteAnimationResource *)_resource_manager->get(RESOURCE_TYPE_SPRITE_ANIMATION, name);
-		if (mi.resource != sar) {
-			mi.time       = 0.0f;
-			mi.time_total = sar->total_time;
-			mi.num_frames = sar->num_frames;
-			mi.frames     = sprite_animation_resource::frames(sar);
-			mi.resource   = sar;
-		}
-
-		if (!mi.resource)
+		// Advance animation.
+		const void *anim_resource = _resource_manager->get(mi.anim_type, name);
+		if (!anim_resource)
 			continue;
 
-		const f32 frame_ratio     = mi.time / mi.time_total;
-		const u32 frame_unclamped = u32(frame_ratio * f32(mi.num_frames));
-		const u32 frame_index     = min(frame_unclamped, mi.num_frames - 1);
+		if (mi.anim_resource != anim_resource) {
+			mi.anim_resource = anim_resource;
+			if (mi.anim_type == RESOURCE_TYPE_SPRITE_ANIMATION) {
+				sprite_animation_player::destroy(*_sprite_animation_player, mi.anim_id);
+				mi.anim_id = sprite_animation_player::create(*_sprite_animation_player, (const SpriteAnimationResource *)anim_resource);
+				mi.time = 0.0f;
+				mi.time_total = ((const SpriteAnimationResource *)anim_resource)->total_time;
+			}
+		}
+
+		if (mi.anim_type == RESOURCE_TYPE_SPRITE_ANIMATION) {
+			sprite_animation_player::evaluate(*_sprite_animation_player
+				, mi.anim_id
+				, mi.time
+				, mi.unit
+				, _events
+				);
+		}
 
 		mi.time += dt*speed;
 
@@ -218,12 +230,6 @@ void AnimationStateMachine::update(float dt)
 				}
 			}
 		}
-
-		// Emit events
-		SpriteFrameChangeEvent ev;
-		ev.unit      = mi.unit;
-		ev.frame_num = mi.frames[frame_index];
-		event_stream::write(_events, 0, ev);
 	}
 }
 

+ 10 - 10
src/world/animation_state_machine.h

@@ -9,16 +9,11 @@
 #include "resource/state_machine_resource.h"
 #include "resource/types.h"
 #include "world/event_stream.h"
+#include "world/sprite_animation_player.h"
 #include "world/types.h"
 
 namespace crown
 {
-struct SpriteFrameChangeEvent
-{
-	UnitId unit;
-	u32 frame_num;
-};
-
 struct AnimationStateMachine
 {
 	struct Machine
@@ -26,9 +21,9 @@ struct AnimationStateMachine
 		UnitId unit;
 		f32 time_total;
 		f32 time;
-		u32 num_frames;
-		const u32 *frames;
-		const SpriteAnimationResource *resource;
+		StringId64 anim_type;
+		const void *anim_resource;
+		AnimationId anim_id;
 		const State *state;
 		const State *state_next;
 		const StateMachineResource *state_machine;
@@ -42,9 +37,14 @@ struct AnimationStateMachine
 	Array<Machine> _machines;
 	EventStream _events;
 	UnitDestroyCallback _unit_destroy_callback;
+	SpriteAnimationPlayer *_sprite_animation_player;
 
 	///
-	AnimationStateMachine(Allocator &a, ResourceManager &rm, UnitManager &um);
+	AnimationStateMachine(Allocator &a
+		, ResourceManager &rm
+		, UnitManager &um
+		, SpriteAnimationPlayer &sprite_player
+		);
 
 	///
 	~AnimationStateMachine();

+ 80 - 0
src/world/sprite_animation_player.cpp

@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "world/event_stream.inl"
+#include "world/sprite_animation_player.h"
+
+namespace crown
+{
+namespace sprite_animation_player
+{
+	AnimationId create(SpriteAnimationPlayer &p, const SpriteAnimationResource *animation_resource)
+	{
+		SpriteAnimationPlayer::Index &index = p._indices[p._freelist_dequeue];
+		p._freelist_dequeue = index.next;
+		index.id += ANIMATION_ID_ADD;
+		index.index = array::size(p._animations);
+
+		SpriteAnimationPlayer::Animation a;
+		a.id         = index.id;
+		a.num_frames = animation_resource->num_frames;
+		a.time_total = animation_resource->total_time;
+		a.frames     = sprite_animation_resource::frames(animation_resource);
+		a.resource   = animation_resource;
+		array::push_back(p._animations, a);
+
+		return a.id;
+	}
+
+	void destroy(SpriteAnimationPlayer &p, AnimationId anim_id)
+	{
+		SpriteAnimationPlayer::Index &index = p._indices[anim_id & ANIMATION_INDEX_MASK];
+
+		SpriteAnimationPlayer::Animation &a = p._animations[array::size(p._animations) - 1];
+		array::pop_back(p._animations);
+		p._indices[a.id & ANIMATION_INDEX_MASK].index = index.index;
+
+		index.index = UINT32_MAX;
+		p._indices[p._freelist_enqueue].next = anim_id & ANIMATION_INDEX_MASK;
+		p._freelist_enqueue = anim_id & ANIMATION_INDEX_MASK;
+	}
+
+	bool has(SpriteAnimationPlayer &p, AnimationId anim_id)
+	{
+		SpriteAnimationPlayer::Index &index = p._indices[anim_id & ANIMATION_INDEX_MASK];
+		return index.index != UINT32_MAX && index.id == anim_id;
+	}
+
+	void evaluate(SpriteAnimationPlayer &p, AnimationId anim_id, f32 time, UnitId unit, EventStream &events)
+	{
+		SpriteAnimationPlayer::Index &index = p._indices[anim_id & ANIMATION_INDEX_MASK];
+		SpriteAnimationPlayer::Animation &a = p._animations[index.index];
+
+		const f32 frame_ratio     = time / a.time_total;
+		const u32 frame_unclamped = u32(frame_ratio * f32(a.num_frames));
+		const u32 frame_index     = min(frame_unclamped, a.num_frames - 1);
+
+		SpriteFrameChangeEvent ev;
+		ev.unit      = unit;
+		ev.frame_num = a.frames[frame_index];
+		event_stream::write(events, 0, ev);
+	}
+
+} // namespace sprite_animation_player
+
+SpriteAnimationPlayer::SpriteAnimationPlayer(Allocator &a)
+	: _animations(a)
+{
+	for (u32 i = 0; i < countof(_indices); ++i) {
+		_indices[i].id = i;
+		_indices[i].next = i + 1;
+		_indices[i].index = UINT32_MAX;
+	}
+
+	_freelist_dequeue = 0;
+	_freelist_enqueue = countof(_indices) - 1;
+}
+
+} // namespace crown

+ 67 - 0
src/world/sprite_animation_player.h

@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include "resource/sprite_resource.h"
+#include "world/event_stream.h"
+#include "world/types.h"
+
+namespace crown
+{
+struct SpriteFrameChangeEvent
+{
+	UnitId unit;
+	u32 frame_num;
+};
+
+#define MAX_ANIMATIONS       1024
+#define ANIMATION_INDEX_MASK (MAX_ANIMATIONS - 1)
+#define ANIMATION_ID_ADD     MAX_ANIMATIONS
+
+struct SpriteAnimationPlayer
+{
+	struct Index
+	{
+		AnimationId id;
+		u32 index;      ///< Index into _animations.
+		u32 next;       ///< Next free index slot.
+	};
+
+	struct Animation
+	{
+		AnimationId id;
+		u32 num_frames;
+		f32 time_total;
+		const u32 *frames;
+		const SpriteAnimationResource *resource;
+	};
+
+	u32 _freelist_dequeue;
+	u32 _freelist_enqueue;
+	Index _indices[MAX_ANIMATIONS];
+	Array<Animation> _animations;
+
+	///
+	explicit SpriteAnimationPlayer(Allocator &a);
+};
+
+namespace sprite_animation_player
+{
+	///
+	AnimationId create(SpriteAnimationPlayer &p, const SpriteAnimationResource *animation_resource);
+
+	///
+	void destroy(SpriteAnimationPlayer &p, AnimationId anim_id);
+
+	///
+	bool has(SpriteAnimationPlayer &p, AnimationId anim_id);
+
+	///
+	void evaluate(SpriteAnimationPlayer &p, AnimationId anim_id, f32 time, UnitId unit, EventStream &events);
+
+} // namespace sprite_animation_player
+
+} // namespace crown

+ 2 - 0
src/world/types.h

@@ -477,4 +477,6 @@ struct PhysicsTransformEvent
 	Matrix4x4 world;
 };
 
+typedef u32 AnimationId;
+
 } // namespace crown

+ 3 - 1
src/world/world.cpp

@@ -68,7 +68,8 @@ World::World(Allocator &a
 	_physics_world = CE_NEW(*_allocator, PhysicsWorld)(*_allocator, rm, um, *_lines);
 	_sound_world   = CE_NEW(*_allocator, SoundWorld)(*_allocator);
 	_script_world  = CE_NEW(*_allocator, ScriptWorld)(*_allocator, um, rm, env, *this);
-	_animation_state_machine = CE_NEW(*_allocator, AnimationStateMachine)(*_allocator, rm, um);
+	_sprite_animation_player = CE_NEW(*_allocator, SpriteAnimationPlayer)(*_allocator);
+	_animation_state_machine = CE_NEW(*_allocator, AnimationStateMachine)(*_allocator, rm, um, *_sprite_animation_player);
 
 	_gui_buffer.create();
 
@@ -96,6 +97,7 @@ World::~World()
 
 	// Destroy subsystems
 	CE_DELETE(*_allocator, _animation_state_machine);
+	CE_DELETE(*_allocator, _sprite_animation_player);
 	CE_DELETE(*_allocator, _script_world);
 	CE_DELETE(*_allocator, _sound_world);
 	CE_DELETE(*_allocator, _physics_world);

+ 2 - 0
src/world/world.h

@@ -13,6 +13,7 @@
 #include "resource/types.h"
 #include "world/event_stream.h"
 #include "world/gui.h"
+#include "world/sprite_animation_player.h"
 #include "world/types.h"
 
 namespace crown
@@ -50,6 +51,7 @@ struct World
 	PhysicsWorld *_physics_world;
 	SoundWorld *_sound_world;
 	ScriptWorld *_script_world;
+	SpriteAnimationPlayer *_sprite_animation_player;
 	AnimationStateMachine *_animation_state_machine;
 
 	Array<UnitId> _units;