diff options
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/test/CollisionTest.cpp | 390 | ||||
| -rw-r--r-- | src/test/InputTest.cpp | 32 | ||||
| -rw-r--r-- | src/test/ParticleTest.cpp | 6 | ||||
| -rw-r--r-- | src/test/PhysicsTest.cpp | 10 | ||||
| -rw-r--r-- | src/test/Profiling.cpp | 243 | ||||
| -rw-r--r-- | src/test/RenderSystemTest.cpp | 129 | 
7 files changed, 741 insertions, 71 deletions
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 43f564a..0d98490 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,5 +1,6 @@  target_sources(test_main PUBLIC  	main.cpp +	CollisionTest.cpp  	PhysicsTest.cpp  	ScriptTest.cpp  	ParticleTest.cpp @@ -15,5 +16,6 @@ target_sources(test_main PUBLIC  	InputTest.cpp  	ScriptEventTest.cpp  	ScriptSceneTest.cpp +	Profiling.cpp  	ScriptECSTest.cpp  ) diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp new file mode 100644 index 0000000..dd45eb6 --- /dev/null +++ b/src/test/CollisionTest.cpp @@ -0,0 +1,390 @@ +#include "api/BoxCollider.h" +#include "manager/Mediator.h" +#include <cmath> +#include <cstddef> +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/api/Event.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.h> +#include <crepe/manager/Mediator.h> +#include <crepe/system/CollisionSystem.h> +#include <crepe/system/ScriptSystem.h> +#include <crepe/types.h> +#include <crepe/util/Log.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; +using namespace testing; + +class CollisionHandler : public Script { +public: +	int box_id; +	function<void(const CollisionEvent & ev)> test_fn = [](const CollisionEvent & ev) {}; + +	CollisionHandler(int box_id) { this->box_id = box_id; } + +	bool on_collision(const CollisionEvent & ev) { +		//Log::logf("Box {} script on_collision()", box_id); +		test_fn(ev); +		return true; +	} + +	void init() { +		subscribe<CollisionEvent>( +			[this](const CollisionEvent & ev) -> bool { return this->on_collision(ev); }); +	} +	void update() { +		// Retrieve component from the same GameObject this script is on +	} +}; + +class CollisionTest : public Test { +public: +	Mediator m; +	ComponentManager mgr{m}; +	CollisionSystem collision_sys{m}; +	ScriptSystem script_sys{m}; + +	GameObject world = mgr.new_object("world", "", {50, 50}); +	GameObject game_object1 = mgr.new_object("object1", "", {50, 50}); +	GameObject game_object2 = mgr.new_object("object2", "", {50, 30}); + +	CollisionHandler * script_object1_ref = nullptr; +	CollisionHandler * script_object2_ref = nullptr; + +	void SetUp() override { +		world.add_component<Rigidbody>(Rigidbody::Data{ +			// TODO: remove unrelated properties: +			.body_type = Rigidbody::BodyType::STATIC, +			.offset = {0, 0}, +		}); +		// Create a box with an inner size of 10x10 units +		world.add_component<BoxCollider>(vec2{0, -100}, vec2{100, 100}); // Top +		world.add_component<BoxCollider>(vec2{0, 100}, vec2{100, 100}); // Bottom +		world.add_component<BoxCollider>(vec2{-100, 0}, vec2{100, 100}); // Left +		world.add_component<BoxCollider>(vec2{100, 0}, vec2{100, 100}); // right + +		game_object1.add_component<Rigidbody>(Rigidbody::Data{ +			.mass = 1, +			.gravity_scale = 0.01, +			.body_type = Rigidbody::BodyType::DYNAMIC, +			.linear_velocity = {0, 0}, +			.constraints = {0, 0, 0}, +			.elastisity_coefficient = 1, +			.offset = {0, 0}, +			.collision_layers = {0}, +		}); +		game_object1.add_component<BoxCollider>(vec2{0, 0}, vec2{10, 10}); +		BehaviorScript & script_object1 +			= game_object1.add_component<BehaviorScript>().set_script<CollisionHandler>(1); +		script_object1_ref = static_cast<CollisionHandler *>(script_object1.script.get()); +		ASSERT_NE(script_object1_ref, nullptr); + +		game_object2.add_component<Rigidbody>(Rigidbody::Data{ +			.mass = 1, +			.gravity_scale = 0.01, +			.body_type = Rigidbody::BodyType::DYNAMIC, +			.linear_velocity = {0, 0}, +			.constraints = {0, 0, 0}, +			.elastisity_coefficient = 1, +			.offset = {0, 0}, +			.collision_layers = {0}, +		}); +		game_object2.add_component<BoxCollider>(vec2{0, 0}, vec2{10, 10}); +		BehaviorScript & script_object2 +			= game_object2.add_component<BehaviorScript>().set_script<CollisionHandler>(2); +		script_object2_ref = static_cast<CollisionHandler *>(script_object2.script.get()); +		ASSERT_NE(script_object2_ref, nullptr); + +		// Ensure Script::init() is called on all BehaviorScript instances +		script_sys.update(); +	} +}; + +TEST_F(CollisionTest, collision_example) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 2); +	}; +	EXPECT_FALSE(collision_happend); +	collision_sys.update(); +	EXPECT_FALSE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_both_no_velocity) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, 10); +		EXPECT_EQ(ev.info.resolution.y, 10); +		EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 2); +		EXPECT_EQ(ev.info.resolution.x, 10); +		EXPECT_EQ(ev.info.resolution.y, 10); +		EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {50, 30}; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_x_direction_no_velocity) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, -5); +		EXPECT_EQ(ev.info.resolution.y, 0); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::X_DIRECTION); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 2); +		EXPECT_EQ(ev.info.resolution.x, 5); +		EXPECT_EQ(ev.info.resolution.y, 0); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::X_DIRECTION); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {45, 30}; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_y_direction_no_velocity) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, 0); +		EXPECT_EQ(ev.info.resolution.y, -5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::Y_DIRECTION); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 2); +		EXPECT_EQ(ev.info.resolution.x, 0); +		EXPECT_EQ(ev.info.resolution.y, 5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::Y_DIRECTION); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {50, 25}; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_both) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, 10); +		EXPECT_EQ(ev.info.resolution.y, 10); +		EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 2); +		EXPECT_EQ(ev.info.resolution.x, 10); +		EXPECT_EQ(ev.info.resolution.y, 10); +		EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {50, 30}; +	Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); +	rg1.data.linear_velocity = {10, 10}; +	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); +	rg2.data.linear_velocity = {10, 10}; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_x_direction) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, -5); +		EXPECT_EQ(ev.info.resolution.y, -5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::X_DIRECTION); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 2); +		EXPECT_EQ(ev.info.resolution.x, 5); +		EXPECT_EQ(ev.info.resolution.y, 5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::X_DIRECTION); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {45, 30}; +	Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); +	rg1.data.linear_velocity = {10, 10}; +	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); +	rg2.data.linear_velocity = {10, 10}; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_dynamic_y_direction) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, -5); +		EXPECT_EQ(ev.info.resolution.y, -5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::Y_DIRECTION); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 2); +		EXPECT_EQ(ev.info.resolution.x, 5); +		EXPECT_EQ(ev.info.resolution.y, 5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::Y_DIRECTION); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {50, 25}; +	Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); +	rg1.data.linear_velocity = {10, 10}; +	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); +	rg2.data.linear_velocity = {10, 10}; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_static_both) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, 10); +		EXPECT_EQ(ev.info.resolution.y, 10); +		EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		// is static should not be called +		FAIL(); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {50, 30}; +	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); +	rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_static_x_direction) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, -5); +		EXPECT_EQ(ev.info.resolution.y, -5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::X_DIRECTION); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		// is static should not be called +		FAIL(); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {45, 30}; +	Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); +	rg1.data.linear_velocity = {10, 10}; +	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); +	rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_static_y_direction) { +	bool collision_happend = false; +	script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.resolution.x, -5); +		EXPECT_EQ(ev.info.resolution.y, -5); +		EXPECT_EQ(ev.info.resolution_direction, +				  crepe::CollisionSystem::Direction::Y_DIRECTION); +	}; +	script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +		// is static should not be called +		FAIL(); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {50, 25}; +	Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); +	rg1.data.linear_velocity = {10, 10}; +	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); +	rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} + +TEST_F(CollisionTest, collision_box_box_static_multiple) { //todo check visually +	bool collision_happend = false; +	float offset_value = 0; +	float resolution = 0; +	script_object1_ref->test_fn = [&](const CollisionEvent & ev) { +		collision_happend = true; +		EXPECT_EQ(ev.info.this_collider.game_object_id, 1); +		EXPECT_EQ(ev.info.this_collider.offset.x, offset_value); +		EXPECT_EQ(ev.info.resolution.x, resolution); +	}; +	script_object2_ref->test_fn = [&](const CollisionEvent & ev) { +		// is static should not be called +		FAIL(); +	}; +	EXPECT_FALSE(collision_happend); +	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +	tf.position = {45, 30}; +	Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); +	rg1.data.linear_velocity = {10, 10}; +	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); +	rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; +	BoxCollider & bxc = this->mgr.get_components_by_id<BoxCollider>(1).front().get(); +	bxc.offset = {5, 0}; +	this->game_object1.add_component<BoxCollider>(vec2{-5, 0}, vec2{10, 10}); +	offset_value = 5; +	resolution = 10; +	collision_sys.update(); +	offset_value = -5; +	resolution = 10; +	tf.position = {55, 30}; +	collision_sys.update(); +	EXPECT_TRUE(collision_happend); +} diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp index cb9833f..a7c0157 100644 --- a/src/test/InputTest.cpp +++ b/src/test/InputTest.cpp @@ -60,8 +60,8 @@ protected:  TEST_F(InputTest, MouseDown) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	bool mouse_triggered = false;  	EventHandler<MousePressEvent> on_mouse_down = [&](const MousePressEvent & event) { @@ -90,8 +90,8 @@ TEST_F(InputTest, MouseDown) {  TEST_F(InputTest, MouseUp) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	bool function_triggered = false;  	EventHandler<MouseReleaseEvent> on_mouse_release = [&](const MouseReleaseEvent & e) { @@ -118,8 +118,8 @@ TEST_F(InputTest, MouseUp) {  TEST_F(InputTest, MouseMove) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	bool function_triggered = false;  	EventHandler<MouseMoveEvent> on_mouse_move = [&](const MouseMoveEvent & e) { @@ -148,8 +148,8 @@ TEST_F(InputTest, MouseMove) {  TEST_F(InputTest, KeyDown) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	bool function_triggered = false; @@ -179,8 +179,8 @@ TEST_F(InputTest, KeyDown) {  TEST_F(InputTest, KeyUp) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	bool function_triggered = false;  	EventHandler<KeyReleaseEvent> on_key_release = [&](const KeyReleaseEvent & event) { @@ -203,8 +203,8 @@ TEST_F(InputTest, KeyUp) {  TEST_F(InputTest, MouseClick) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	bool on_click_triggered = false;  	EventHandler<MouseClickEvent> on_mouse_click = [&](const MouseClickEvent & event) { @@ -224,8 +224,8 @@ TEST_F(InputTest, MouseClick) {  TEST_F(InputTest, testButtonClick) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	GameObject button_obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1);  	bool button_clicked = false; @@ -251,8 +251,8 @@ TEST_F(InputTest, testButtonClick) {  TEST_F(InputTest, testButtonHover) {  	GameObject obj = mgr.new_object("camera", "camera", vec2{0, 0}, 0, 1); -	auto & camera = obj.add_component<Camera>(Color::BLACK, ivec2{0, 0}, vec2{500, 500}, 0.0, -											  vec2{0, 0}); +	auto & camera = obj.add_component<Camera>( +		ivec2{100, 100}, vec2{100, 100}, Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	camera.active = true;  	GameObject button_obj = mgr.new_object("body", "person", vec2{0, 0}, 0, 1);  	bool button_clicked = false; diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index a659fe5..1409c4f 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -32,7 +32,11 @@ public:  			Color color(0, 0, 0, 0);  			auto s1 = Texture("asset/texture/img.png");  			Sprite & test_sprite = game_object.add_component<Sprite>( -				s1, color, Sprite::FlipSettings{true, true}, 1, 1, 100); +				s1, Sprite::Data{ +						.color = color, +						.flip = Sprite::FlipSettings{true, true}, +						.size = {10, 10}, +					});  			game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{  				.position = {0, 0}, diff --git a/src/test/PhysicsTest.cpp b/src/test/PhysicsTest.cpp index 43af8e4..43d2931 100644 --- a/src/test/PhysicsTest.cpp +++ b/src/test/PhysicsTest.cpp @@ -30,8 +30,6 @@ public:  				.max_linear_velocity = vec2{10, 10},  				.max_angular_velocity = 10,  				.constraints = {0, 0}, -				.use_gravity = true, -				.bounce = false,  			});  		}  		transforms = mgr.get_components_by_id<Transform>(0); @@ -107,16 +105,16 @@ TEST_F(PhysicsTest, movement) {  	EXPECT_EQ(transform.position.y, 1);  	EXPECT_EQ(transform.rotation, 1); -	rigidbody.data.linear_damping.x = 0.5; -	rigidbody.data.linear_damping.y = 0.5; -	rigidbody.data.angular_damping = 0.5; +	rigidbody.data.linear_velocity_coefficient.x = 0.5; +	rigidbody.data.linear_velocity_coefficient.y = 0.5; +	rigidbody.data.angular_velocity_coefficient = 0.5;  	system.update();  	EXPECT_EQ(rigidbody.data.linear_velocity.x, 0.5);  	EXPECT_EQ(rigidbody.data.linear_velocity.y, 0.5);  	EXPECT_EQ(rigidbody.data.angular_velocity, 0.5);  	rigidbody.data.constraints = {1, 1, 0}; -	rigidbody.data.angular_damping = 0; +	rigidbody.data.angular_velocity_coefficient = 0;  	rigidbody.data.max_angular_velocity = 1000;  	rigidbody.data.angular_velocity = 360;  	system.update(); diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp new file mode 100644 index 0000000..c753bca --- /dev/null +++ b/src/test/Profiling.cpp @@ -0,0 +1,243 @@ +#include "manager/Mediator.h" +#include "system/ParticleSystem.h" +#include "system/PhysicsSystem.h" +#include "system/RenderSystem.h" +#include <chrono> +#include <cmath> +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/api/Event.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/ParticleEmitter.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> +#include <crepe/manager/ComponentManager.h> +#include <crepe/manager/EventManager.h> +#include <crepe/system/CollisionSystem.h> +#include <crepe/system/ScriptSystem.h> +#include <crepe/types.h> +#include <crepe/util/Log.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; +using namespace testing; + +class TestScript : public Script { +	bool oncollision(const CollisionEvent & test) { +		Log::logf("Box {} script on_collision()", test.info.this_collider.game_object_id); +		return true; +	} +	void init() { +		subscribe<CollisionEvent>( +			[this](const CollisionEvent & ev) -> bool { return this->oncollision(ev); }); +	} +	void update() { +		// Retrieve component from the same GameObject this script is on +	} +}; + +class DISABLED_ProfilingTest : public Test { +public: +	// Config for test +	// Minimum amount to let test pass +	const int min_gameobject_count = 100; +	// Maximum amount to stop test +	const int max_gameobject_count = 150; +	// Amount of times a test runs to calculate average +	const int average = 5; +	// Maximum duration to stop test +	const std::chrono::microseconds duration = 16000us; + +	Mediator m; +	ComponentManager mgr{m}; +	// Add system used for profling tests +	CollisionSystem collision_sys{m}; +	PhysicsSystem physics_sys{m}; +	ParticleSystem particle_sys{m}; +	RenderSystem render_sys{m}; +	ScriptSystem script_sys{m}; + +	// Test data +	std::map<std::string, std::chrono::microseconds> timings; +	int game_object_count = 0; +	std::chrono::microseconds total_time = 0us; + +	void SetUp() override { + +		GameObject do_not_use = mgr.new_object("DO_NOT_USE", "", {0, 0}); +		do_not_use.add_component<Camera>(ivec2{1080, 720}, vec2{2000, 2000}, +										 Camera::Data{ +											 .bg_color = Color::WHITE, +											 .zoom = 1.0f, +										 }); +		// initialize systems here: +		//calls init +		script_sys.update(); +		//creates window +		render_sys.update(); +	} + +	// Helper function to time an update call and store its duration +	template <typename Func> +	std::chrono::microseconds time_function(const std::string & name, Func && func) { +		auto start = std::chrono::steady_clock::now(); +		func(); +		auto end = std::chrono::steady_clock::now(); +		std::chrono::microseconds duration +			= std::chrono::duration_cast<std::chrono::microseconds>(end - start); +		timings[name] += duration; +		return duration; +	} + +	// Run and profile all systems, return the total time in milliseconds +	std::chrono::microseconds run_all_systems() { +		std::chrono::microseconds total_microseconds = 0us; +		total_microseconds += time_function("PhysicsSystem", [&]() { physics_sys.update(); }); +		total_microseconds +			+= time_function("CollisionSystem", [&]() { collision_sys.update(); }); +		total_microseconds +			+= time_function("ParticleSystem", [&]() { particle_sys.update(); }); +		total_microseconds += time_function("RenderSystem", [&]() { render_sys.update(); }); +		return total_microseconds; +	} + +	// Print timings of all functions +	void log_timings() const { +		std::string result = "\nFunction timings:\n"; + +		for (const auto & [name, duration] : timings) { +			result += name + " took " + std::to_string(duration.count() / 1000.0 / average) +					  + " ms (" + std::to_string(duration.count() / average) + " µs).\n"; +		} + +		result += "Total time: " + std::to_string(this->total_time.count() / 1000.0 / average) +				  + " ms (" + std::to_string(this->total_time.count() / average) + " µs)\n"; + +		result += "Amount of gameobjects: " + std::to_string(game_object_count) + "\n"; + +		GTEST_LOG_(INFO) << result; +	} + +	void clear_timings() { +		for (auto & [key, value] : timings) { +			value = std::chrono::microseconds(0); +		} +	} +}; + +TEST_F(DISABLED_ProfilingTest, Profiling_1) { +	while (this->total_time / this->average < this->duration) { + +		{ +			//define gameobject used for testing +			GameObject gameobject = mgr.new_object("gameobject", "", {0, 0}); +		} + +		this->game_object_count++; + +		this->total_time = 0us; +		clear_timings(); + +		for (int amount = 0; amount < this->average; amount++) { +			this->total_time += run_all_systems(); +		} + +		if (this->game_object_count >= this->max_gameobject_count) break; +	} +	log_timings(); +	EXPECT_GE(this->game_object_count, this->min_gameobject_count); +} + +TEST_F(DISABLED_ProfilingTest, Profiling_2) { +	while (this->total_time / this->average < this->duration) { + +		{ +			//define gameobject used for testing +			GameObject gameobject = mgr.new_object( +				"gameobject", "", {static_cast<float>(game_object_count * 2), 0}); +			gameobject.add_component<Rigidbody>(Rigidbody::Data{ +				.gravity_scale = 0.0, +				.body_type = Rigidbody::BodyType::STATIC, +			}); +			gameobject.add_component<BoxCollider>(vec2{0, 0}, vec2{1, 1}); + +			gameobject.add_component<BehaviorScript>().set_script<TestScript>(); +			auto img = Texture("asset/texture/square.png"); +			Sprite & test_sprite = gameobject.add_component<Sprite>( +				img, Sprite::Data{ +						 .color = {0, 0, 0, 0}, +						 .flip = {.flip_x = false, .flip_y = false}, +						 .sorting_in_layer = 1, +						 .order_in_layer = 1, +						 .size = {.y = 500}, +					 }); +		} + +		this->game_object_count++; + +		this->total_time = 0us; +		clear_timings(); +		for (int amount = 0; amount < this->average; amount++) { +			this->total_time += run_all_systems(); +		} + +		if (this->game_object_count >= this->max_gameobject_count) break; +	} +	log_timings(); +	EXPECT_GE(this->game_object_count, this->min_gameobject_count); +} + +TEST_F(DISABLED_ProfilingTest, Profiling_3) { +	while (this->total_time / this->average < this->duration) { + +		{ +			//define gameobject used for testing +			GameObject gameobject = mgr.new_object( +				"gameobject", "", {static_cast<float>(game_object_count * 2), 0}); +			gameobject.add_component<Rigidbody>(Rigidbody::Data{ +				.gravity_scale = 0, +				.body_type = Rigidbody::BodyType::STATIC, +			}); +			gameobject.add_component<BoxCollider>(vec2{0, 0}, vec2{1, 1}); +			gameobject.add_component<BehaviorScript>().set_script<TestScript>(); +			auto img = Texture("asset/texture/square.png"); +			Sprite & test_sprite = gameobject.add_component<Sprite>( +				img, Sprite::Data{ +						 .color = {0, 0, 0, 0}, +						 .flip = {.flip_x = false, .flip_y = false}, +						 .sorting_in_layer = 1, +						 .order_in_layer = 1, +						 .size = {.y = 500}, +					 }); +			auto & test = gameobject.add_component<ParticleEmitter>(ParticleEmitter::Data{ +				.max_particles = 10, +				.emission_rate = 100, +				.end_lifespan = 100000, +				.boundary{ +					.width = 1000, +					.height = 1000, +					.offset = vec2{0, 0}, +					.reset_on_exit = false, +				}, +				.sprite = test_sprite, +			}); +		} +		render_sys.update(); +		this->game_object_count++; + +		this->total_time = 0us; +		clear_timings(); +		for (int amount = 0; amount < this->average; amount++) { +			this->total_time += run_all_systems(); +		} + +		if (this->game_object_count >= this->max_gameobject_count) break; +	} +	log_timings(); +	EXPECT_GE(this->game_object_count, this->min_gameobject_count); +} diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp index c105dcb..205f534 100644 --- a/src/test/RenderSystemTest.cpp +++ b/src/test/RenderSystemTest.cpp @@ -1,3 +1,4 @@ +#include "types.h"  #include <functional>  #include <gtest/gtest.h>  #include <memory> @@ -35,28 +36,50 @@ public:  		auto s2 = Texture("asset/texture/img.png");  		auto s3 = Texture("asset/texture/img.png");  		auto s4 = Texture("asset/texture/img.png"); -		auto & sprite1 = entity1.add_component<Sprite>( -			s1, Color(0, 0, 0, 0), Sprite::FlipSettings{false, false}, 5, 5, 100); -		ASSERT_NE(sprite1.sprite_image.texture.get(), nullptr); -		EXPECT_EQ(sprite1.order_in_layer, 5); -		EXPECT_EQ(sprite1.sorting_in_layer, 5); -		auto & sprite2 = entity2.add_component<Sprite>( -			s2, Color(0, 0, 0, 0), Sprite::FlipSettings{false, false}, 2, 1, 100); -		ASSERT_NE(sprite2.sprite_image.texture.get(), nullptr); -		EXPECT_EQ(sprite2.sorting_in_layer, 2); -		EXPECT_EQ(sprite2.order_in_layer, 1); - -		auto & sprite3 = entity3.add_component<Sprite>( -			s3, Color(0, 0, 0, 0), Sprite::FlipSettings{false, false}, 1, 2, 100); -		ASSERT_NE(sprite3.sprite_image.texture.get(), nullptr); -		EXPECT_EQ(sprite3.sorting_in_layer, 1); -		EXPECT_EQ(sprite3.order_in_layer, 2); - -		auto & sprite4 = entity4.add_component<Sprite>( -			s4, Color(0, 0, 0, 0), Sprite::FlipSettings{false, false}, 1, 1, 100); -		ASSERT_NE(sprite4.sprite_image.texture.get(), nullptr); -		EXPECT_EQ(sprite4.sorting_in_layer, 1); -		EXPECT_EQ(sprite4.order_in_layer, 1); +		auto & sprite1 +			= entity1.add_component<Sprite>(s1, Sprite::Data{ +													.color = Color(0, 0, 0, 0), +													.flip = Sprite::FlipSettings{false, false}, +													.sorting_in_layer = 5, +													.order_in_layer = 5, +													.size = {10, 10}, +												}); + +		ASSERT_NE(sprite1.texture.texture.get(), nullptr); +		EXPECT_EQ(sprite1.data.order_in_layer, 5); +		EXPECT_EQ(sprite1.data.sorting_in_layer, 5); +		auto & sprite2 +			= entity2.add_component<Sprite>(s2, Sprite::Data{ +													.color = Color(0, 0, 0, 0), +													.flip = Sprite::FlipSettings{false, false}, +													.sorting_in_layer = 2, +													.order_in_layer = 1, +												}); +		ASSERT_NE(sprite2.texture.texture.get(), nullptr); +		EXPECT_EQ(sprite2.data.sorting_in_layer, 2); +		EXPECT_EQ(sprite2.data.order_in_layer, 1); + +		auto & sprite3 +			= entity3.add_component<Sprite>(s3, Sprite::Data{ +													.color = Color(0, 0, 0, 0), +													.flip = Sprite::FlipSettings{false, false}, +													.sorting_in_layer = 1, +													.order_in_layer = 2, +												}); +		ASSERT_NE(sprite3.texture.texture.get(), nullptr); +		EXPECT_EQ(sprite3.data.sorting_in_layer, 1); +		EXPECT_EQ(sprite3.data.order_in_layer, 2); + +		auto & sprite4 +			= entity4.add_component<Sprite>(s4, Sprite::Data{ +													.color = Color(0, 0, 0, 0), +													.flip = Sprite::FlipSettings{false, false}, +													.sorting_in_layer = 1, +													.order_in_layer = 1, +												}); +		ASSERT_NE(sprite4.texture.texture.get(), nullptr); +		EXPECT_EQ(sprite4.data.sorting_in_layer, 1); +		EXPECT_EQ(sprite4.data.order_in_layer, 1);  	}  }; @@ -66,8 +89,13 @@ TEST_F(RenderSystemTest, expected_throws) {  	// no texture img  	EXPECT_ANY_THROW({  		auto test = Texture(""); -		entity1.add_component<Sprite>(test, Color(0, 0, 0, 0), -									  Sprite::FlipSettings{false, false}, 1, 1, 100); +		auto & sprite1 = entity1.add_component<Sprite>( +			test, Sprite::Data{ +					  .color = Color(0, 0, 0, 0), +					  .flip = Sprite::FlipSettings{false, false}, +					  .sorting_in_layer = 1, +					  .order_in_layer = 1, +				  });  	});  	// No camera @@ -89,32 +117,33 @@ TEST_F(RenderSystemTest, sorting_sprites) {  	// 3. sorting_in_layer: 2, order_in_layer: 1 (entity2)  	// 4. sorting_in_layer: 5, order_in_layer: 5 (entity1) -	EXPECT_EQ(sorted_sprites[0].get().sorting_in_layer, 1); -	EXPECT_EQ(sorted_sprites[0].get().order_in_layer, 1); +	EXPECT_EQ(sorted_sprites[0].get().data.sorting_in_layer, 1); +	EXPECT_EQ(sorted_sprites[0].get().data.order_in_layer, 1); -	EXPECT_EQ(sorted_sprites[1].get().sorting_in_layer, 1); -	EXPECT_EQ(sorted_sprites[1].get().order_in_layer, 2); +	EXPECT_EQ(sorted_sprites[1].get().data.sorting_in_layer, 1); +	EXPECT_EQ(sorted_sprites[1].get().data.order_in_layer, 2); -	EXPECT_EQ(sorted_sprites[2].get().sorting_in_layer, 2); -	EXPECT_EQ(sorted_sprites[2].get().order_in_layer, 1); +	EXPECT_EQ(sorted_sprites[2].get().data.sorting_in_layer, 2); +	EXPECT_EQ(sorted_sprites[2].get().data.order_in_layer, 1); -	EXPECT_EQ(sorted_sprites[3].get().sorting_in_layer, 5); -	EXPECT_EQ(sorted_sprites[3].get().order_in_layer, 5); +	EXPECT_EQ(sorted_sprites[3].get().data.sorting_in_layer, 5); +	EXPECT_EQ(sorted_sprites[3].get().data.order_in_layer, 5);  	for (size_t i = 1; i < sorted_sprites.size(); ++i) {  		const Sprite & prev = sorted_sprites[i - 1].get();  		const Sprite & curr = sorted_sprites[i].get(); -		if (prev.sorting_in_layer == curr.sorting_in_layer) { -			EXPECT_LE(prev.order_in_layer, curr.order_in_layer); +		if (prev.data.sorting_in_layer == curr.data.sorting_in_layer) { +			EXPECT_LE(prev.data.order_in_layer, curr.data.order_in_layer);  		} else { -			EXPECT_LE(prev.sorting_in_layer, curr.sorting_in_layer); +			EXPECT_LE(prev.data.sorting_in_layer, curr.data.sorting_in_layer);  		}  	}  }  TEST_F(RenderSystemTest, Update) { -	entity1.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{2000, 2000}, 1.0f); +	entity1.add_component<Camera>(ivec2{100, 100}, vec2{100, 100}, +								  Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f});  	{  		vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>();  		ASSERT_EQ(sprites.size(), 4); @@ -142,7 +171,9 @@ TEST_F(RenderSystemTest, Camera) {  		EXPECT_NE(cameras.size(), 1);  	}  	{ -		entity1.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{2000, 2000}, 1.0f); +		entity1.add_component<Camera>(ivec2{100, 100}, vec2{100, 100}, +									  Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); +  		auto cameras = this->mgr.get_components_by_type<Camera>();  		EXPECT_EQ(cameras.size(), 1);  	} @@ -150,18 +181,20 @@ TEST_F(RenderSystemTest, Camera) {  	//TODO improve with newer version  }  TEST_F(RenderSystemTest, Color) { -	entity1.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{2000, 2000}, 1.0f); +	entity1.add_component<Camera>(ivec2{100, 100}, vec2{100, 100}, +								  Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); +  	auto & sprite = this->mgr.get_components_by_id<Sprite>(entity1.id).front().get(); -	ASSERT_NE(sprite.sprite_image.texture.get(), nullptr); +	ASSERT_NE(sprite.texture.texture.get(), nullptr); -	sprite.color = Color::GREEN; -	EXPECT_EQ(sprite.color.r, Color::GREEN.r); -	EXPECT_EQ(sprite.color.g, Color::GREEN.g); -	EXPECT_EQ(sprite.color.b, Color::GREEN.b); -	EXPECT_EQ(sprite.color.a, Color::GREEN.a); +	sprite.data.color = Color::GREEN; +	EXPECT_EQ(sprite.data.color.r, Color::GREEN.r); +	EXPECT_EQ(sprite.data.color.g, Color::GREEN.g); +	EXPECT_EQ(sprite.data.color.b, Color::GREEN.b); +	EXPECT_EQ(sprite.data.color.a, Color::GREEN.a);  	this->sys.update(); -	EXPECT_EQ(sprite.color.r, Color::GREEN.r); -	EXPECT_EQ(sprite.color.g, Color::GREEN.g); -	EXPECT_EQ(sprite.color.b, Color::GREEN.b); -	EXPECT_EQ(sprite.color.a, Color::GREEN.a); +	EXPECT_EQ(sprite.data.color.r, Color::GREEN.r); +	EXPECT_EQ(sprite.data.color.g, Color::GREEN.g); +	EXPECT_EQ(sprite.data.color.b, Color::GREEN.b); +	EXPECT_EQ(sprite.data.color.a, Color::GREEN.a);  }  |