feat: refactor to move more elements to the scripting layer
CI / build-and-test (push) Successful in 2m13s
CI / build-and-test (push) Successful in 2m13s
This commit is contained in:
@@ -97,10 +97,14 @@ add_executable(simian
|
||||
src/scripting/TextureBindings.cpp
|
||||
src/scripting/MathBindings.cpp
|
||||
src/scripting/ECSBindings.cpp
|
||||
src/scripting/ResourceBindings.cpp
|
||||
src/HotReload.cpp
|
||||
src/gui/GuiManager.cpp
|
||||
src/gui/LogWindow.cpp
|
||||
src/log/log.c
|
||||
src/ShaderManager.cpp
|
||||
src/ModelManager.cpp
|
||||
src/MaterialManager.cpp
|
||||
)
|
||||
|
||||
target_include_directories(simian PUBLIC
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
#include "HotReload.h"
|
||||
#include "gui/GuiManager.h"
|
||||
#include "raylib.h"
|
||||
#include "ShaderManager.h"
|
||||
#include "ModelManager.h"
|
||||
#include "MaterialManager.h"
|
||||
#include <entt.hpp>
|
||||
#include <vector>
|
||||
|
||||
class Application {
|
||||
public:
|
||||
@@ -31,14 +33,11 @@ private:
|
||||
|
||||
// ECS
|
||||
entt::registry registry;
|
||||
std::vector<Model> models;
|
||||
|
||||
// Scene/rendering data
|
||||
Camera3D camera;
|
||||
Shader toonShader;
|
||||
Vector3 lightDir;
|
||||
float globalBandCount;
|
||||
bool dayMode;
|
||||
// Resource managers
|
||||
ShaderManager shaderManager;
|
||||
ModelManager modelManager;
|
||||
MaterialManager materialManager;
|
||||
|
||||
static const int WINDOW_WIDTH = 1280;
|
||||
static const int WINDOW_HEIGHT = 720;
|
||||
|
||||
+51
-1
@@ -32,7 +32,7 @@ struct Velocity {
|
||||
|
||||
// Sprite component - visual representation
|
||||
struct Sprite {
|
||||
int modelId; // Index into models array
|
||||
size_t modelId; // Index into models array
|
||||
Color color; // Tint color
|
||||
float outlineSize; // Outline thickness
|
||||
|
||||
@@ -52,3 +52,53 @@ struct Tag {
|
||||
Tag() : name("") {}
|
||||
Tag(const std::string& n) : name(n) {}
|
||||
};
|
||||
|
||||
// Camera3D component - camera settings
|
||||
struct Camera3DComponent {
|
||||
Vector3 position;
|
||||
Vector3 target;
|
||||
Vector3 up;
|
||||
float fovy;
|
||||
int projection; // 0 = CAMERA_PERSPECTIVE, 1 = CAMERA_ORTHOGRAPHIC
|
||||
|
||||
Camera3DComponent()
|
||||
: position{0.0f, 10.0f, 10.0f}
|
||||
, target{0.0f, 0.0f, 0.0f}
|
||||
, up{0.0f, 1.0f, 0.0f}
|
||||
, fovy(45.0f)
|
||||
, projection(0) {}
|
||||
};
|
||||
|
||||
// Light component - directional light
|
||||
struct LightComponent {
|
||||
Vector3 direction;
|
||||
Color color;
|
||||
float intensity;
|
||||
|
||||
LightComponent()
|
||||
: direction{0.5f, 0.7f, 0.3f}
|
||||
, color(WHITE)
|
||||
, intensity(1.0f) {}
|
||||
};
|
||||
|
||||
// Material component - references shader and material managers
|
||||
struct MaterialComponent {
|
||||
unsigned int materialId; // ID in MaterialManager
|
||||
|
||||
MaterialComponent() : materialId(0) {}
|
||||
MaterialComponent(unsigned int id) : materialId(id) {}
|
||||
};
|
||||
|
||||
// RenderPass component - controls rendering order and properties
|
||||
struct RenderPassComponent {
|
||||
int order; // Render order (lower = earlier)
|
||||
bool depthTest; // Enable depth testing
|
||||
bool depthWrite; // Enable depth writing
|
||||
unsigned int shaderId; // Override shader for this pass (0 = use material shader)
|
||||
|
||||
RenderPassComponent()
|
||||
: order(0)
|
||||
, depthTest(true)
|
||||
, depthWrite(true)
|
||||
, shaderId(0) {}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "raylib.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
struct MaterialData
|
||||
{
|
||||
unsigned int shaderId;
|
||||
Color albedo;
|
||||
|
||||
MaterialData() : shaderId(0), albedo(WHITE) {}
|
||||
};
|
||||
|
||||
class MaterialManager
|
||||
{
|
||||
public:
|
||||
MaterialManager();
|
||||
~MaterialManager();
|
||||
|
||||
// Create a material with a shader ID
|
||||
// Returns a unique ID for the material
|
||||
unsigned int Create(unsigned int shaderId, Color albedo = WHITE);
|
||||
|
||||
// Remove a material by ID
|
||||
void Remove(unsigned int id);
|
||||
|
||||
// Get material data by ID (returns nullptr if not found)
|
||||
MaterialData* Get(unsigned int id);
|
||||
|
||||
// Update material shader
|
||||
void SetShader(unsigned int id, unsigned int shaderId);
|
||||
|
||||
// Update material albedo
|
||||
void SetAlbedo(unsigned int id, Color albedo);
|
||||
|
||||
// Clear all materials
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
std::unordered_map<unsigned int, MaterialData> materials;
|
||||
unsigned int nextId;
|
||||
bool cleared;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "raylib.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
class ModelManager
|
||||
{
|
||||
public:
|
||||
ModelManager();
|
||||
~ModelManager();
|
||||
|
||||
// Load a model from a file
|
||||
// Returns a unique ID for the model, or 0 on failure
|
||||
unsigned int Load(const std::string& path);
|
||||
|
||||
// Load a model from a mesh
|
||||
// Returns a unique ID for the model, or 0 on failure
|
||||
unsigned int LoadFromMesh(Mesh mesh);
|
||||
|
||||
// Unload a model by ID
|
||||
void Unload(unsigned int id);
|
||||
|
||||
// Get a model by ID (returns nullptr if not found)
|
||||
Model* Get(unsigned int id);
|
||||
|
||||
// Unload all models
|
||||
void UnloadAll();
|
||||
|
||||
private:
|
||||
std::unordered_map<unsigned int, Model> models;
|
||||
unsigned int nextId;
|
||||
bool unloaded;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "raylib.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
class ShaderManager
|
||||
{
|
||||
public:
|
||||
ShaderManager();
|
||||
~ShaderManager();
|
||||
|
||||
// Load a shader from vertex and fragment shader files
|
||||
// Returns a unique ID for the shader, or 0 on failure
|
||||
unsigned int Load(const std::string& vsPath, const std::string& fsPath);
|
||||
|
||||
// Unload a shader by ID
|
||||
void Unload(unsigned int id);
|
||||
|
||||
// Get a shader by ID (returns nullptr if not found)
|
||||
Shader* Get(unsigned int id);
|
||||
|
||||
// Unload all shaders
|
||||
void UnloadAll();
|
||||
|
||||
private:
|
||||
std::unordered_map<unsigned int, Shader> shaders;
|
||||
unsigned int nextId;
|
||||
bool unloaded;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class asIScriptEngine;
|
||||
class ShaderManager;
|
||||
class ModelManager;
|
||||
class MaterialManager;
|
||||
|
||||
void SetResourceManagers(ShaderManager* shaderMgr, ModelManager* modelMgr, MaterialManager* materialMgr);
|
||||
void RegisterResourceBindings(asIScriptEngine *engine);
|
||||
+69
-1
@@ -69,6 +69,59 @@ namespace ECS {
|
||||
void SetTag(uint entity, const string &in name);
|
||||
bool HasTag(uint entity);
|
||||
void RemoveTag(uint entity);
|
||||
|
||||
// Camera3D component
|
||||
void AddCamera3D(uint entity, float px, float py, float pz, float tx, float ty, float tz, float fovy);
|
||||
void SetCameraPosition(uint entity, float x, float y, float z);
|
||||
void SetCameraTarget(uint entity, float x, float y, float z);
|
||||
void SetCameraFovy(uint entity, float fovy);
|
||||
bool HasCamera3D(uint entity);
|
||||
void RemoveCamera3D(uint entity);
|
||||
|
||||
// Light component
|
||||
void AddLight(uint entity, float dx, float dy, float dz, float intensity = 1.0f);
|
||||
void SetLightDirection(uint entity, float x, float y, float z);
|
||||
void SetLightIntensity(uint entity, float intensity);
|
||||
void SetLightColor(uint entity, uint color);
|
||||
bool HasLight(uint entity);
|
||||
void RemoveLight(uint entity);
|
||||
|
||||
// Material component
|
||||
void AddMaterial(uint entity, uint materialId);
|
||||
void SetMaterialId(uint entity, uint materialId);
|
||||
uint GetMaterialId(uint entity);
|
||||
bool HasMaterial(uint entity);
|
||||
void RemoveMaterial(uint entity);
|
||||
|
||||
// RenderPass component
|
||||
void AddRenderPass(uint entity, int order, bool depthTest, bool depthWrite, uint shaderId);
|
||||
void SetRenderPassOrder(uint entity, int order);
|
||||
void SetRenderPassShader(uint entity, uint shaderId);
|
||||
bool HasRenderPass(uint entity);
|
||||
void RemoveRenderPass(uint entity);
|
||||
}
|
||||
|
||||
// Shader resource management
|
||||
namespace Shader {
|
||||
uint Load(const string &in vsPath, const string &in fsPath);
|
||||
void Unload(uint id);
|
||||
}
|
||||
|
||||
// Model resource management
|
||||
namespace Model {
|
||||
uint Load(const string &in path);
|
||||
uint LoadCube(float width, float height, float length);
|
||||
uint LoadSphere(float radius, int rings, int slices);
|
||||
uint LoadPlane(float width, float length, int resX, int resZ);
|
||||
void Unload(uint id);
|
||||
}
|
||||
|
||||
// Material resource management
|
||||
namespace Material {
|
||||
uint Create(uint shaderId, uint8 r, uint8 g, uint8 b, uint8 a);
|
||||
void Remove(uint id);
|
||||
void SetShader(uint id, uint shaderId);
|
||||
void SetAlbedo(uint id, uint8 r, uint8 g, uint8 b, uint8 a);
|
||||
}
|
||||
|
||||
namespace Texture {
|
||||
@@ -110,4 +163,19 @@ namespace Math {
|
||||
float Ceil(float value);
|
||||
float Modf(float x, float y);
|
||||
float Abs(float value);
|
||||
}
|
||||
}
|
||||
|
||||
// Raylib input and timing functions
|
||||
float GetTime();
|
||||
bool IsKeyPressed(int key);
|
||||
|
||||
// Raylib key constants
|
||||
const int KEY_SPACE = 32;
|
||||
const int KEY_ESCAPE = 256;
|
||||
const int KEY_ENTER = 257;
|
||||
const int KEY_TAB = 258;
|
||||
const int KEY_BACKSPACE = 259;
|
||||
const int KEY_RIGHT = 262;
|
||||
const int KEY_LEFT = 263;
|
||||
const int KEY_DOWN = 264;
|
||||
const int KEY_UP = 265;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Example script showing how to use the new script-driven rendering system
|
||||
|
||||
// Global variables to track our resources
|
||||
uint g_toonShader = 0;
|
||||
uint g_outlineShader = 0;
|
||||
uint g_cubeModel = 0;
|
||||
uint g_sphereModel = 0;
|
||||
uint g_redMaterial = 0;
|
||||
uint g_blueMaterial = 0;
|
||||
|
||||
// Entities
|
||||
uint g_camera = 0;
|
||||
uint g_light = 0;
|
||||
uint g_cube = 0;
|
||||
uint g_sphere = 0;
|
||||
|
||||
void Init() {
|
||||
Toast::Info("Initializing script-driven rendering...");
|
||||
|
||||
// === Load Shaders ===
|
||||
g_toonShader = Shader::Load("shaders/toon.vs", "shaders/toon.fs");
|
||||
g_outlineShader = Shader::Load("shaders/outline.vs", "shaders/outline.fs");
|
||||
|
||||
if (g_toonShader == 0 || g_outlineShader == 0) {
|
||||
Toast::Error("Failed to load shaders!");
|
||||
return;
|
||||
}
|
||||
|
||||
// === Load Models ===
|
||||
g_cubeModel = Model::LoadCube(1.0, 1.0, 1.0);
|
||||
g_sphereModel = Model::LoadSphere(0.5, 16, 16);
|
||||
|
||||
// === Create Materials ===
|
||||
// Red material with toon shader
|
||||
g_redMaterial = Material::Create(g_toonShader, 255, 100, 100, 255);
|
||||
|
||||
// Blue material with toon shader
|
||||
g_blueMaterial = Material::Create(g_toonShader, 100, 100, 255, 255);
|
||||
|
||||
// === Set up Camera ===
|
||||
g_camera = ECS::CreateEntity();
|
||||
ECS::AddCamera3D(g_camera,
|
||||
5.0, 5.0, 5.0, // position
|
||||
0.0, 0.0, 0.0, // target
|
||||
45.0); // fovy
|
||||
ECS::AddTag(g_camera, "MainCamera");
|
||||
|
||||
// === Set up Light ===
|
||||
g_light = ECS::CreateEntity();
|
||||
ECS::AddLight(g_light, 0.5, 0.7, 0.3, 1.0); // direction + intensity
|
||||
ECS::AddTag(g_light, "MainLight");
|
||||
|
||||
// === Create Cube with Outline ===
|
||||
g_cube = ECS::CreateEntity();
|
||||
ECS::AddTransform(g_cube, -1.5, 0.5, 0.0);
|
||||
ECS::AddSprite(g_cube, g_cubeModel, 0xFFFFFFFF, 0.0);
|
||||
ECS::AddMaterial(g_cube, g_redMaterial);
|
||||
ECS::AddTag(g_cube, "Cube");
|
||||
ECS::AddVelocity(g_cube, 0.0, 0.0, 0.0, 1.0); // rotating
|
||||
|
||||
// Add outline render pass (order 0 = first)
|
||||
ECS::AddRenderPass(g_cube, 0, false, true, g_outlineShader); // order=0, no depth test
|
||||
|
||||
// === Create Sphere ===
|
||||
g_sphere = ECS::CreateEntity();
|
||||
ECS::AddTransform(g_sphere, 1.5, 0.5, 0.0);
|
||||
ECS::AddSprite(g_sphere, g_sphereModel, 0xFFFFFFFF, 0.0);
|
||||
ECS::AddMaterial(g_sphere, g_blueMaterial);
|
||||
ECS::AddTag(g_sphere, "Sphere");
|
||||
ECS::AddVelocity(g_sphere, 0.5, 0.0, 0.2, 0.5); // moving and rotating
|
||||
|
||||
// Add main render pass (order 1 = second, after outlines)
|
||||
ECS::AddRenderPass(g_sphere, 1, true, true, 0); // order=1, with depth test
|
||||
|
||||
Toast::Success("Script-driven rendering initialized!");
|
||||
}
|
||||
bool lightToggle = false;
|
||||
void Update(float dt) {
|
||||
// Camera orbital movement
|
||||
if (ECS::HasCamera3D(g_camera)) {
|
||||
float angle = GetTime() * 0.3;
|
||||
float radius = 7.0;
|
||||
float x = Math::Cos(angle) * radius;
|
||||
float z = Math::Sin(angle) * radius;
|
||||
ECS::SetCameraPosition(g_camera, x, 5.0, z);
|
||||
}
|
||||
|
||||
// Example: Toggle light direction with space
|
||||
if (IsKeyPressed(KEY_SPACE)) {
|
||||
|
||||
lightToggle = !lightToggle;
|
||||
|
||||
if (lightToggle) {
|
||||
ECS::SetLightDirection(g_light, -0.5, 0.7, -0.3);
|
||||
Toast::Info("Light direction changed!");
|
||||
} else {
|
||||
ECS::SetLightDirection(g_light, 0.5, 0.7, 0.3);
|
||||
Toast::Info("Light direction reset!");
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
-18
@@ -1,5 +1,16 @@
|
||||
// ECS-based game script demo
|
||||
// This demonstrates creating entities with various components
|
||||
// ECS-based game script demo with script-driven rendering
|
||||
// This demonstrates creating entities with various components and managing rendering
|
||||
|
||||
// Resource IDs
|
||||
uint g_toonShader = 0;
|
||||
uint g_outlineShader = 0;
|
||||
uint g_cubeModel = 0;
|
||||
uint g_sphereModel = 0;
|
||||
uint g_defaultMaterial = 0;
|
||||
|
||||
// Scene entities
|
||||
uint g_camera = 0;
|
||||
uint g_light = 0;
|
||||
|
||||
// Store entity IDs for manipulation
|
||||
array<uint> entities;
|
||||
@@ -8,25 +19,67 @@ uint spinningCube;
|
||||
float time = 0.0f;
|
||||
|
||||
void Init() {
|
||||
Log(LOG_TRACE, "Initialization complete - ECS Demo!");
|
||||
Log(LOG_TRACE, "Initialization complete - ECS Demo with Script-Driven Rendering!");
|
||||
Toast::Success("ECS Script initialized successfully!");
|
||||
|
||||
// Create a player entity at origin
|
||||
// === Load shaders ===
|
||||
g_toonShader = Shader::Load("shaders/toon.vs", "shaders/toon.fs");
|
||||
g_outlineShader = Shader::Load("shaders/outline.vs", "shaders/outline.fs");
|
||||
|
||||
if (g_toonShader == 0 || g_outlineShader == 0) {
|
||||
Toast::Error("Failed to load shaders!");
|
||||
return;
|
||||
}
|
||||
|
||||
// === Load models ===
|
||||
g_cubeModel = Model::LoadCube(1.0, 1.0, 1.0);
|
||||
g_sphereModel = Model::LoadSphere(0.5, 16, 16);
|
||||
|
||||
// === Create default material ===
|
||||
g_defaultMaterial = Material::Create(g_toonShader, 255, 255, 255, 255);
|
||||
|
||||
// === Set up camera ===
|
||||
g_camera = ECS::CreateEntity();
|
||||
ECS::AddCamera3D(g_camera,
|
||||
5.0, 5.0, 5.0, // position
|
||||
0.0, 0.0, 0.0, // target
|
||||
45.0); // fovy
|
||||
ECS::AddTag(g_camera, "MainCamera");
|
||||
|
||||
// === Set up light ===
|
||||
g_light = ECS::CreateEntity();
|
||||
ECS::AddLight(g_light, 0.5, 0.7, 0.3, 1.0); // direction + intensity
|
||||
ECS::AddTag(g_light, "MainLight");
|
||||
|
||||
// === Create player entity at origin with outline ===
|
||||
player = ECS::CreateEntity();
|
||||
ECS::AddTransform(player, 0.0f, 0.0f, 0.0f);
|
||||
ECS::AddSprite(player, 1, 0xFF0000FF, 0.0f); // Red cube, model 0
|
||||
ECS::AddSprite(player, g_sphereModel, 0xFF0000FF, 0.0f); // Red sphere
|
||||
|
||||
// Create material for player
|
||||
uint playerMat = Material::Create(g_toonShader, 255, 0, 0, 255);
|
||||
ECS::AddMaterial(player, playerMat);
|
||||
|
||||
// Add outline pass (order=0) and main pass (order=1)
|
||||
ECS::AddRenderPass(player, 0, false, true, g_outlineShader);
|
||||
|
||||
ECS::AddTag(player, "Player");
|
||||
Log(LOG_INFO, "Created player entity: " + player);
|
||||
|
||||
// Create a spinning cube
|
||||
// === Create a spinning cube ===
|
||||
spinningCube = ECS::CreateEntity();
|
||||
ECS::AddTransform(spinningCube, 2.0f, 0.0f, 0.0f);
|
||||
ECS::AddSprite(spinningCube, 0, 0x00FF00FF, 0.0f); // Green cube
|
||||
ECS::AddSprite(spinningCube, g_cubeModel, 0x00FF00FF, 0.0f); // Green cube
|
||||
ECS::AddVelocity(spinningCube, 0.0f, 0.0f, 0.0f, 2.0f); // Angular velocity
|
||||
|
||||
uint greenMat = Material::Create(g_toonShader, 0, 255, 0, 255);
|
||||
ECS::AddMaterial(spinningCube, greenMat);
|
||||
ECS::AddRenderPass(spinningCube, 1, true, true, 0); // Main pass
|
||||
|
||||
ECS::AddTag(spinningCube, "Spinner");
|
||||
Log(LOG_INFO, "Created spinning cube: " + spinningCube);
|
||||
|
||||
// Create orbiting entities
|
||||
// === Create orbiting entities ===
|
||||
for (int i = 0; i < 5; i++) {
|
||||
uint entity = ECS::CreateEntity();
|
||||
|
||||
@@ -39,27 +92,36 @@ void Init() {
|
||||
|
||||
// Different colors for each
|
||||
uint color = 0;
|
||||
if (i == 0) color = 0xFFFF00FF; // Yellow
|
||||
else if (i == 1) color = 0xFF00FFFF; // Magenta
|
||||
else if (i == 2) color = 0x00FFFFFF; // Cyan
|
||||
else if (i == 3) color = 0xFFA500FF; // Orange
|
||||
else color = 0xFF69B4FF; // Pink
|
||||
uint8 r = 255, g = 255, b = 255;
|
||||
if (i == 0) { color = 0xFFFF00FF; r = 255; g = 255; b = 0; } // Yellow
|
||||
else if (i == 1) { color = 0xFF00FFFF; r = 255; g = 0; b = 255; } // Magenta
|
||||
else if (i == 2) { color = 0x00FFFFFF; r = 0; g = 255; b = 255; } // Cyan
|
||||
else if (i == 3) { color = 0xFFA500FF; r = 255; g = 165; b = 0; } // Orange
|
||||
else { color = 0xFF69B4FF; r = 255; g = 105; b = 180; } // Pink
|
||||
|
||||
ECS::AddSprite(entity, g_sphereModel, color, 0.0f);
|
||||
|
||||
uint mat = Material::Create(g_toonShader, r, g, b, 255);
|
||||
ECS::AddMaterial(entity, mat);
|
||||
ECS::AddRenderPass(entity, 1, true, true, 0);
|
||||
|
||||
ECS::AddSprite(entity, 1, color, 0.0f);
|
||||
ECS::AddTag(entity, "Orbiter" + i);
|
||||
|
||||
entities.insertLast(entity);
|
||||
}
|
||||
|
||||
// Create a moving entity
|
||||
// === Create a moving entity ===
|
||||
uint mover = ECS::CreateEntity();
|
||||
ECS::AddTransform(mover, -3.0f, 0.5f, 0.0f);
|
||||
ECS::AddSprite(mover, 0, 0xFFFFFFFF, 0.0f); // White cube
|
||||
ECS::AddSprite(mover, g_cubeModel, 0xFFFFFFFF, 0.0f); // White cube
|
||||
ECS::AddVelocity(mover, 1.0f, 0.0f, 0.5f, 0.0f); // Moving velocity
|
||||
|
||||
ECS::AddMaterial(mover, g_defaultMaterial);
|
||||
ECS::AddRenderPass(mover, 1, true, true, 0);
|
||||
|
||||
ECS::AddTag(mover, "Mover");
|
||||
entities.insertLast(mover);
|
||||
|
||||
Toast::Success("Created " + (entities.length() + 3) + " entities!");
|
||||
Toast::Success("Created " + (entities.length() + 2) + " entities with script-driven rendering!");
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
@@ -69,6 +131,15 @@ void Shutdown() {
|
||||
void Update(float dt) {
|
||||
time += dt;
|
||||
|
||||
// Update camera to orbit around the scene
|
||||
if (ECS::HasCamera3D(g_camera)) {
|
||||
float angle = time * 0.3;
|
||||
float radius = 7.0;
|
||||
float x = Math::Cos(angle) * radius;
|
||||
float z = Math::Sin(angle) * radius;
|
||||
ECS::SetCameraPosition(g_camera, x, 5.0, z);
|
||||
}
|
||||
|
||||
// Update player position - simple back and forth motion
|
||||
float playerX = Math::Sin(time) * 2.0f;
|
||||
ECS::SetPosition(player, playerX, 0.0f, 0.0f);
|
||||
|
||||
+4
-32
@@ -1,37 +1,9 @@
|
||||
#version 330
|
||||
|
||||
uniform sampler2D texture0;
|
||||
uniform vec2 textureSize;
|
||||
uniform int outlineSize = 2;
|
||||
out vec4 fragColor;
|
||||
|
||||
in vec2 fragTexCoord;
|
||||
out vec4 finalColor;
|
||||
uniform vec4 outlineColor; // Color of the outline
|
||||
|
||||
void main() {
|
||||
vec2 texel = 1.0 / textureSize;
|
||||
float center = texture(texture0, fragTexCoord).r;
|
||||
|
||||
if (center < 0.5) {
|
||||
discard; // Transparent, show scene underneath
|
||||
return;
|
||||
}
|
||||
|
||||
// Edge detection
|
||||
bool edge = false;
|
||||
for (int y = -outlineSize; y <= outlineSize; y++) {
|
||||
for (int x = -outlineSize; x <= outlineSize; x++) {
|
||||
vec2 offset = vec2(float(x), float(y)) * texel;
|
||||
if (texture(texture0, fragTexCoord + offset).r < 0.5) {
|
||||
edge = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (edge) break;
|
||||
}
|
||||
|
||||
if (edge) {
|
||||
finalColor = vec4(0, 0, 0, 1); // Black outline
|
||||
} else {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
fragColor = outlineColor;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#version 330
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition;
|
||||
layout(location = 2) in vec3 vertexNormal;
|
||||
|
||||
uniform mat4 mvp;
|
||||
uniform mat4 matModel;
|
||||
uniform mat4 matNormal;
|
||||
uniform float outlineThickness;
|
||||
|
||||
void main() {
|
||||
// Transform normal to world space
|
||||
vec3 worldNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0)));
|
||||
|
||||
// Expand vertices along normals in world space
|
||||
vec3 worldPos = vec3(matModel * vec4(vertexPosition, 1.0));
|
||||
vec3 expandedPos = worldPos + worldNormal * outlineThickness;
|
||||
|
||||
// Transform to clip space
|
||||
gl_Position = mvp * vec4(vertexPosition + vertexNormal * outlineThickness, 1.0);
|
||||
}
|
||||
+176
-96
@@ -1,7 +1,9 @@
|
||||
#include "Application.h"
|
||||
#include "ECSComponents.h"
|
||||
#include "scripting/ECSBindings.h"
|
||||
#include "scripting/ResourceBindings.h"
|
||||
#include "raylib.h"
|
||||
#include "rlgl.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
@@ -25,7 +27,7 @@ const char *Application::WINDOW_TITLE = "Simian";
|
||||
const char *Application::SCRIPT_FILE = "scripts/game.as";
|
||||
|
||||
Application::Application()
|
||||
: hotReload(nullptr), scriptCompilationError(false), logFile(nullptr), renderTexture{}, lightDir{0.5f, 0.7f, 0.3f}, globalBandCount(3.0f), dayMode(true)
|
||||
: hotReload(nullptr), scriptCompilationError(false), logFile(nullptr), renderTexture{}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -75,6 +77,10 @@ bool Application::Initialize(int argc, char *argv[])
|
||||
// Set up ECS bindings with our registry
|
||||
SetGlobalRegistry(®istry);
|
||||
|
||||
// Set up resource managers for script access
|
||||
extern void SetResourceManagers(ShaderManager*, ModelManager*, MaterialManager*);
|
||||
SetResourceManagers(&shaderManager, &modelManager, &materialManager);
|
||||
|
||||
// Initialize hot reload to watch the scripts directory so any script change
|
||||
// triggers a reload.
|
||||
{
|
||||
@@ -94,43 +100,6 @@ bool Application::Initialize(int argc, char *argv[])
|
||||
renderTexture = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
}
|
||||
|
||||
// Set up camera
|
||||
camera.position = {5, 5, 5};
|
||||
camera.target = {0, 0, 0};
|
||||
camera.up = {0, 1, 0};
|
||||
camera.fovy = 45;
|
||||
camera.projection = CAMERA_PERSPECTIVE; // ADD THIS LINE!
|
||||
|
||||
// Load models
|
||||
Model cube = LoadModelFromMesh(GenMeshCube(1, 1, 1));
|
||||
Model sphere = LoadModelFromMesh(GenMeshSphere(0.5f, 16, 16));
|
||||
|
||||
// Load shaders (use both vertex and fragment shader files explicitly)
|
||||
toonShader = LoadShader("shaders/toon.vs", "shaders/toon.fs");
|
||||
if (toonShader.id == 0) {
|
||||
log_warn("Failed to load toon shader (id=0). Check shader paths and GL support.");
|
||||
} else {
|
||||
log_info("Loaded toon shader id=%d", toonShader.id);
|
||||
|
||||
// Verify shader locations exist
|
||||
int lightLoc = GetShaderLocation(toonShader, "lightDir");
|
||||
int bandLoc = GetShaderLocation(toonShader, "bandCount");
|
||||
int albedoLoc = GetShaderLocation(toonShader, "albedo");
|
||||
|
||||
log_info("Shader locations: lightDir=%d, bandCount=%d, albedo=%d", lightLoc, bandLoc, albedoLoc);
|
||||
|
||||
if (lightLoc == -1 || bandLoc == -1 || albedoLoc == -1) {
|
||||
log_warn("Some shader uniforms not found - shader may not work correctly");
|
||||
}
|
||||
}
|
||||
|
||||
// Assign shader to cube material
|
||||
cube.materials[0].shader = toonShader;
|
||||
sphere.materials[0].shader = toonShader;
|
||||
|
||||
models.push_back(cube);
|
||||
models.push_back(sphere);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -174,13 +143,6 @@ void Application::Update(float deltaTime)
|
||||
|
||||
// Update ECS systems
|
||||
UpdateSystems(deltaTime);
|
||||
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
|
||||
// Day/night toggle
|
||||
if (IsKeyPressed(KEY_SPACE))
|
||||
dayMode = !dayMode;
|
||||
globalBandCount = dayMode ? 3.0f : 2.0f;
|
||||
}
|
||||
|
||||
void Application::UpdateSystems(float deltaTime)
|
||||
@@ -200,53 +162,172 @@ void Application::UpdateSystems(float deltaTime)
|
||||
|
||||
void Application::RenderScene()
|
||||
{
|
||||
BeginMode3D(camera);
|
||||
DrawGrid(10, 1);
|
||||
|
||||
BeginShaderMode(toonShader);
|
||||
// Query for active camera (first one found with Camera3DComponent)
|
||||
Camera3D camera = {};
|
||||
bool hasCamera = false;
|
||||
|
||||
// Get shader locations once
|
||||
int lightLoc = GetShaderLocation(toonShader, "lightDir");
|
||||
int bandLoc = GetShaderLocation(toonShader, "bandCount");
|
||||
int albedoLoc = GetShaderLocation(toonShader, "albedo");
|
||||
|
||||
auto view = registry.view<ECSTransform, Sprite>();
|
||||
|
||||
view.each([&](auto &transform, auto &sprite)
|
||||
{
|
||||
// Validate model index
|
||||
// if (sprite.modelId < 0 || sprite.modelId >= models.size()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Set ALL uniforms before each draw call
|
||||
if (lightLoc != -1) {
|
||||
SetShaderValue(toonShader, lightLoc, &lightDir, SHADER_UNIFORM_VEC3);
|
||||
auto cameraView = registry.view<Camera3DComponent>();
|
||||
cameraView.each([&](auto& camComp) {
|
||||
if (!hasCamera) {
|
||||
camera.position = camComp.position;
|
||||
camera.target = camComp.target;
|
||||
camera.up = camComp.up;
|
||||
camera.fovy = camComp.fovy;
|
||||
camera.projection = camComp.projection;
|
||||
hasCamera = true;
|
||||
}
|
||||
|
||||
if (bandLoc != -1) {
|
||||
SetShaderValue(toonShader, bandLoc, &globalBandCount, SHADER_UNIFORM_FLOAT);
|
||||
}
|
||||
|
||||
// Set color for this entity
|
||||
Vector4 color = {
|
||||
sprite.color.r / 255.0f,
|
||||
sprite.color.g / 255.0f,
|
||||
sprite.color.b / 255.0f,
|
||||
sprite.color.a / 255.0f
|
||||
};
|
||||
|
||||
if (albedoLoc != -1) {
|
||||
SetShaderValue(toonShader, albedoLoc, &color, SHADER_UNIFORM_VEC4);
|
||||
}
|
||||
|
||||
// Draw the model
|
||||
Model& model = models[sprite.modelId];
|
||||
DrawModelEx(model, transform.position, Vector3{0, 1, 0}, transform.rotation * RAD2DEG,
|
||||
transform.scale, WHITE);
|
||||
});
|
||||
|
||||
EndShaderMode();
|
||||
// If no camera found, use a default one
|
||||
if (!hasCamera) {
|
||||
camera.position = {5, 5, 5};
|
||||
camera.target = {0, 0, 0};
|
||||
camera.up = {0, 1, 0};
|
||||
camera.fovy = 45;
|
||||
camera.projection = CAMERA_PERSPECTIVE;
|
||||
}
|
||||
|
||||
// Get the first light (if any) for shader uniforms
|
||||
Vector3 lightDir = {0.5f, 0.7f, 0.3f};
|
||||
float lightIntensity = 1.0f;
|
||||
|
||||
auto lightView = registry.view<LightComponent>();
|
||||
lightView.each([&](auto& light) {
|
||||
lightDir = light.direction;
|
||||
lightIntensity = light.intensity;
|
||||
return; // Use first light only for now
|
||||
});
|
||||
|
||||
BeginMode3D(camera);
|
||||
DrawGrid(10, 1);
|
||||
|
||||
// Collect entities with renderables and organize by render pass
|
||||
struct RenderableEntity {
|
||||
entt::entity entity;
|
||||
ECSTransform* transform;
|
||||
Sprite* sprite;
|
||||
RenderPassComponent* renderPass;
|
||||
};
|
||||
|
||||
std::vector<RenderableEntity> renderables;
|
||||
|
||||
auto renderView = registry.view<ECSTransform, Sprite>();
|
||||
renderView.each([&](auto entity, auto& transform, auto& sprite) {
|
||||
RenderableEntity re;
|
||||
re.entity = entity;
|
||||
re.transform = &transform;
|
||||
re.sprite = &sprite;
|
||||
re.renderPass = registry.try_get<RenderPassComponent>(entity);
|
||||
renderables.push_back(re);
|
||||
});
|
||||
|
||||
// Sort by render pass order (lower = earlier)
|
||||
std::sort(renderables.begin(), renderables.end(), [](const RenderableEntity& a, const RenderableEntity& b) {
|
||||
int orderA = a.renderPass ? a.renderPass->order : 0;
|
||||
int orderB = b.renderPass ? b.renderPass->order : 0;
|
||||
return orderA < orderB;
|
||||
});
|
||||
|
||||
// Group by render pass and render
|
||||
int currentPassOrder = INT_MIN;
|
||||
Shader* currentShader = nullptr;
|
||||
|
||||
for (const auto& re : renderables) {
|
||||
int passOrder = re.renderPass ? re.renderPass->order : 0;
|
||||
|
||||
// If we've moved to a new render pass, update settings
|
||||
if (passOrder != currentPassOrder) {
|
||||
if (currentShader) {
|
||||
EndShaderMode();
|
||||
currentShader = nullptr;
|
||||
}
|
||||
|
||||
// Apply render pass settings
|
||||
if (re.renderPass) {
|
||||
if (!re.renderPass->depthTest) {
|
||||
rlDisableDepthTest();
|
||||
} else {
|
||||
rlEnableDepthTest();
|
||||
}
|
||||
|
||||
// If pass has a shader override, use it
|
||||
if (re.renderPass->shaderId > 0) {
|
||||
currentShader = shaderManager.Get(re.renderPass->shaderId);
|
||||
if (currentShader) {
|
||||
BeginShaderMode(*currentShader);
|
||||
|
||||
// Set uniforms for this shader
|
||||
int lightLoc = GetShaderLocation(*currentShader, "lightDir");
|
||||
if (lightLoc != -1) {
|
||||
SetShaderValue(*currentShader, lightLoc, &lightDir, SHADER_UNIFORM_VEC3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentPassOrder = passOrder;
|
||||
}
|
||||
|
||||
// Get material if entity has one
|
||||
MaterialComponent* matComp = registry.try_get<MaterialComponent>(re.entity);
|
||||
Shader* matShader = nullptr;
|
||||
Color albedo = WHITE;
|
||||
|
||||
if (matComp && matComp->materialId > 0) {
|
||||
MaterialData* matData = materialManager.Get(matComp->materialId);
|
||||
if (matData) {
|
||||
albedo = matData->albedo;
|
||||
if (matData->shaderId > 0 && !currentShader) {
|
||||
matShader = shaderManager.Get(matData->shaderId);
|
||||
if (matShader) {
|
||||
BeginShaderMode(*matShader);
|
||||
currentShader = matShader;
|
||||
|
||||
// Set uniforms
|
||||
int lightLoc = GetShaderLocation(*matShader, "lightDir");
|
||||
if (lightLoc != -1) {
|
||||
SetShaderValue(*matShader, lightLoc, &lightDir, SHADER_UNIFORM_VEC3);
|
||||
}
|
||||
|
||||
Vector4 color = {
|
||||
albedo.r / 255.0f,
|
||||
albedo.g / 255.0f,
|
||||
albedo.b / 255.0f,
|
||||
albedo.a / 255.0f
|
||||
};
|
||||
int albedoLoc = GetShaderLocation(*matShader, "albedo");
|
||||
if (albedoLoc != -1) {
|
||||
SetShaderValue(*matShader, albedoLoc, &color, SHADER_UNIFORM_VEC4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the model
|
||||
if (re.sprite->modelId > 0) {
|
||||
Model* model = modelManager.Get(re.sprite->modelId);
|
||||
if (model) {
|
||||
DrawModelEx(*model, re.transform->position, Vector3{0, 1, 0},
|
||||
re.transform->rotation * RAD2DEG, re.transform->scale,
|
||||
re.sprite->color);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up material shader if we started it
|
||||
if (matShader && currentShader == matShader) {
|
||||
EndShaderMode();
|
||||
currentShader = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up any active shader
|
||||
if (currentShader) {
|
||||
EndShaderMode();
|
||||
}
|
||||
|
||||
rlEnableDepthTest(); // Ensure depth test is re-enabled
|
||||
|
||||
EndMode3D();
|
||||
}
|
||||
void Application::Draw()
|
||||
@@ -294,6 +375,7 @@ void Application::Shutdown()
|
||||
hotReload = nullptr;
|
||||
}
|
||||
|
||||
|
||||
scriptEngine.Shutdown();
|
||||
if (enableEditor)
|
||||
{
|
||||
@@ -303,15 +385,13 @@ void Application::Shutdown()
|
||||
// Shutdown rlImGui first while the GL context and window are still valid
|
||||
rlImGuiShutdown();
|
||||
|
||||
// Shutdown script engine
|
||||
scriptEngine.Shutdown();
|
||||
// Clear ECS registry first to release all component data
|
||||
registry.clear();
|
||||
|
||||
// Unload assets
|
||||
for (auto &model : models)
|
||||
{
|
||||
UnloadModel(model);
|
||||
}
|
||||
UnloadShader(toonShader);
|
||||
// Unload all resources before closing window (while GL context is still valid)
|
||||
materialManager.Clear();
|
||||
shaderManager.UnloadAll();
|
||||
modelManager.UnloadAll();
|
||||
|
||||
// Clear the trace log callback before closing window to prevent logging after cleanup
|
||||
SetTraceLogCallback(nullptr);
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "MaterialManager.h"
|
||||
#include "log/log.h"
|
||||
|
||||
MaterialManager::MaterialManager() : nextId(1), cleared(false)
|
||||
{
|
||||
}
|
||||
|
||||
MaterialManager::~MaterialManager()
|
||||
{
|
||||
// Only clear if not already done
|
||||
if (!cleared) {
|
||||
materials.clear();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int MaterialManager::Create(unsigned int shaderId, Color albedo)
|
||||
{
|
||||
unsigned int id = nextId++;
|
||||
|
||||
MaterialData data;
|
||||
data.shaderId = shaderId;
|
||||
data.albedo = albedo;
|
||||
|
||||
materials[id] = data;
|
||||
log_info("Created material id=%u with shader=%u", id, shaderId);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void MaterialManager::Remove(unsigned int id)
|
||||
{
|
||||
auto it = materials.find(id);
|
||||
if (it != materials.end())
|
||||
{
|
||||
materials.erase(it);
|
||||
log_info("Removed material id=%u", id);
|
||||
}
|
||||
}
|
||||
|
||||
MaterialData* MaterialManager::Get(unsigned int id)
|
||||
{
|
||||
auto it = materials.find(id);
|
||||
if (it != materials.end())
|
||||
{
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MaterialManager::SetShader(unsigned int id, unsigned int shaderId)
|
||||
{
|
||||
auto it = materials.find(id);
|
||||
if (it != materials.end())
|
||||
{
|
||||
it->second.shaderId = shaderId;
|
||||
log_info("Updated material id=%u shader to %u", id, shaderId);
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialManager::SetAlbedo(unsigned int id, Color albedo)
|
||||
{
|
||||
auto it = materials.find(id);
|
||||
if (it != materials.end())
|
||||
{
|
||||
it->second.albedo = albedo;
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialManager::Clear()
|
||||
{
|
||||
if (!cleared) {
|
||||
materials.clear();
|
||||
cleared = true;
|
||||
log_info("Cleared all materials");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "ModelManager.h"
|
||||
#include "log/log.h"
|
||||
|
||||
ModelManager::ModelManager() : nextId(1), unloaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
ModelManager::~ModelManager()
|
||||
{
|
||||
// Only unload if not already done
|
||||
if (!unloaded) {
|
||||
for (auto& pair : models) {
|
||||
UnloadModel(pair.second);
|
||||
}
|
||||
models.clear();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ModelManager::Load(const std::string& path)
|
||||
{
|
||||
Model model = LoadModel(path.c_str());
|
||||
|
||||
if (model.meshCount == 0)
|
||||
{
|
||||
log_warn("Failed to load model: %s", path.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int id = nextId++;
|
||||
models[id] = model;
|
||||
log_info("Loaded model id=%u from %s", id, path.c_str());
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
unsigned int ModelManager::LoadFromMesh(Mesh mesh)
|
||||
{
|
||||
Model model = LoadModelFromMesh(mesh);
|
||||
|
||||
unsigned int id = nextId++;
|
||||
models[id] = model;
|
||||
log_info("Loaded model id=%u from mesh", id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ModelManager::Unload(unsigned int id)
|
||||
{
|
||||
auto it = models.find(id);
|
||||
if (it != models.end())
|
||||
{
|
||||
UnloadModel(it->second);
|
||||
models.erase(it);
|
||||
log_info("Unloaded model id=%u", id);
|
||||
}
|
||||
}
|
||||
|
||||
Model* ModelManager::Get(unsigned int id)
|
||||
{
|
||||
auto it = models.find(id);
|
||||
if (it != models.end())
|
||||
{
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ModelManager::UnloadAll()
|
||||
{
|
||||
if (!unloaded) {
|
||||
for (auto& pair : models)
|
||||
{
|
||||
UnloadModel(pair.second);
|
||||
}
|
||||
models.clear();
|
||||
unloaded = true;
|
||||
log_info("Unloaded all models");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "ShaderManager.h"
|
||||
#include "log/log.h"
|
||||
|
||||
ShaderManager::ShaderManager() : nextId(1), unloaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
ShaderManager::~ShaderManager()
|
||||
{
|
||||
// Only unload if not already done
|
||||
if (!unloaded) {
|
||||
for (auto& pair : shaders) {
|
||||
UnloadShader(pair.second);
|
||||
}
|
||||
shaders.clear();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ShaderManager::Load(const std::string& vsPath, const std::string& fsPath)
|
||||
{
|
||||
Shader shader = LoadShader(vsPath.c_str(), fsPath.c_str());
|
||||
|
||||
if (shader.id == 0)
|
||||
{
|
||||
log_warn("Failed to load shader: %s / %s", vsPath.c_str(), fsPath.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int id = nextId++;
|
||||
shaders[id] = shader;
|
||||
log_info("Loaded shader id=%u from %s / %s (raylib id=%u)", id, vsPath.c_str(), fsPath.c_str(), shader.id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ShaderManager::Unload(unsigned int id)
|
||||
{
|
||||
auto it = shaders.find(id);
|
||||
if (it != shaders.end())
|
||||
{
|
||||
UnloadShader(it->second);
|
||||
shaders.erase(it);
|
||||
log_info("Unloaded shader id=%u", id);
|
||||
}
|
||||
}
|
||||
|
||||
Shader* ShaderManager::Get(unsigned int id)
|
||||
{
|
||||
auto it = shaders.find(id);
|
||||
if (it != shaders.end())
|
||||
{
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ShaderManager::UnloadAll()
|
||||
{
|
||||
if (!unloaded) {
|
||||
for (auto& pair : shaders)
|
||||
{
|
||||
UnloadShader(pair.second);
|
||||
}
|
||||
shaders.clear();
|
||||
unloaded = true;
|
||||
log_info("Unloaded all shaders");
|
||||
}
|
||||
}
|
||||
@@ -193,6 +193,163 @@ void AS_RemoveTag(uint32_t entity) {
|
||||
g_registry->remove<Tag>((entt::entity)entity);
|
||||
}
|
||||
|
||||
// === Camera3D Component ===
|
||||
|
||||
void AS_AddCamera3D(uint32_t entity, float px, float py, float pz, float tx, float ty, float tz, float fovy) {
|
||||
if (!g_registry) return;
|
||||
Camera3DComponent cam;
|
||||
cam.position = {px, py, pz};
|
||||
cam.target = {tx, ty, tz};
|
||||
cam.up = {0.0f, 1.0f, 0.0f};
|
||||
cam.fovy = fovy;
|
||||
cam.projection = 0; // CAMERA_PERSPECTIVE
|
||||
g_registry->emplace<Camera3DComponent>((entt::entity)entity, cam);
|
||||
}
|
||||
|
||||
void AS_SetCameraPosition(uint32_t entity, float x, float y, float z) {
|
||||
if (!g_registry) return;
|
||||
if (auto* c = g_registry->try_get<Camera3DComponent>((entt::entity)entity)) {
|
||||
c->position = {x, y, z};
|
||||
}
|
||||
}
|
||||
|
||||
void AS_SetCameraTarget(uint32_t entity, float x, float y, float z) {
|
||||
if (!g_registry) return;
|
||||
if (auto* c = g_registry->try_get<Camera3DComponent>((entt::entity)entity)) {
|
||||
c->target = {x, y, z};
|
||||
}
|
||||
}
|
||||
|
||||
void AS_SetCameraFovy(uint32_t entity, float fovy) {
|
||||
if (!g_registry) return;
|
||||
if (auto* c = g_registry->try_get<Camera3DComponent>((entt::entity)entity)) {
|
||||
c->fovy = fovy;
|
||||
}
|
||||
}
|
||||
|
||||
bool AS_HasCamera3D(uint32_t entity) {
|
||||
if (!g_registry) return false;
|
||||
return g_registry->all_of<Camera3DComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
void AS_RemoveCamera3D(uint32_t entity) {
|
||||
if (!g_registry) return;
|
||||
g_registry->remove<Camera3DComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
// === Light Component ===
|
||||
|
||||
void AS_AddLight(uint32_t entity, float dx, float dy, float dz, float intensity) {
|
||||
if (!g_registry) return;
|
||||
LightComponent light;
|
||||
light.direction = {dx, dy, dz};
|
||||
light.color = WHITE;
|
||||
light.intensity = intensity;
|
||||
g_registry->emplace<LightComponent>((entt::entity)entity, light);
|
||||
}
|
||||
|
||||
void AS_SetLightDirection(uint32_t entity, float x, float y, float z) {
|
||||
if (!g_registry) return;
|
||||
if (auto* l = g_registry->try_get<LightComponent>((entt::entity)entity)) {
|
||||
l->direction = {x, y, z};
|
||||
}
|
||||
}
|
||||
|
||||
void AS_SetLightIntensity(uint32_t entity, float intensity) {
|
||||
if (!g_registry) return;
|
||||
if (auto* l = g_registry->try_get<LightComponent>((entt::entity)entity)) {
|
||||
l->intensity = intensity;
|
||||
}
|
||||
}
|
||||
|
||||
void AS_SetLightColor(uint32_t entity, uint32_t color) {
|
||||
if (!g_registry) return;
|
||||
if (auto* l = g_registry->try_get<LightComponent>((entt::entity)entity)) {
|
||||
l->color.r = (color >> 24) & 0xFF;
|
||||
l->color.g = (color >> 16) & 0xFF;
|
||||
l->color.b = (color >> 8) & 0xFF;
|
||||
l->color.a = color & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
bool AS_HasLight(uint32_t entity) {
|
||||
if (!g_registry) return false;
|
||||
return g_registry->all_of<LightComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
void AS_RemoveLight(uint32_t entity) {
|
||||
if (!g_registry) return;
|
||||
g_registry->remove<LightComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
// === Material Component ===
|
||||
|
||||
void AS_AddMaterial(uint32_t entity, unsigned int materialId) {
|
||||
if (!g_registry) return;
|
||||
g_registry->emplace<MaterialComponent>((entt::entity)entity, materialId);
|
||||
}
|
||||
|
||||
void AS_SetMaterialId(uint32_t entity, unsigned int materialId) {
|
||||
if (!g_registry) return;
|
||||
if (auto* m = g_registry->try_get<MaterialComponent>((entt::entity)entity)) {
|
||||
m->materialId = materialId;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int AS_GetMaterialId(uint32_t entity) {
|
||||
if (!g_registry) return 0;
|
||||
if (auto* m = g_registry->try_get<MaterialComponent>((entt::entity)entity)) {
|
||||
return m->materialId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AS_HasMaterial(uint32_t entity) {
|
||||
if (!g_registry) return false;
|
||||
return g_registry->all_of<MaterialComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
void AS_RemoveMaterialComponent(uint32_t entity) {
|
||||
if (!g_registry) return;
|
||||
g_registry->remove<MaterialComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
// === RenderPass Component ===
|
||||
|
||||
void AS_AddRenderPass(uint32_t entity, int order, bool depthTest, bool depthWrite, unsigned int shaderId) {
|
||||
if (!g_registry) return;
|
||||
RenderPassComponent pass;
|
||||
pass.order = order;
|
||||
pass.depthTest = depthTest;
|
||||
pass.depthWrite = depthWrite;
|
||||
pass.shaderId = shaderId;
|
||||
g_registry->emplace<RenderPassComponent>((entt::entity)entity, pass);
|
||||
}
|
||||
|
||||
void AS_SetRenderPassOrder(uint32_t entity, int order) {
|
||||
if (!g_registry) return;
|
||||
if (auto* p = g_registry->try_get<RenderPassComponent>((entt::entity)entity)) {
|
||||
p->order = order;
|
||||
}
|
||||
}
|
||||
|
||||
void AS_SetRenderPassShader(uint32_t entity, unsigned int shaderId) {
|
||||
if (!g_registry) return;
|
||||
if (auto* p = g_registry->try_get<RenderPassComponent>((entt::entity)entity)) {
|
||||
p->shaderId = shaderId;
|
||||
}
|
||||
}
|
||||
|
||||
bool AS_HasRenderPass(uint32_t entity) {
|
||||
if (!g_registry) return false;
|
||||
return g_registry->all_of<RenderPassComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
void AS_RemoveRenderPass(uint32_t entity) {
|
||||
if (!g_registry) return;
|
||||
g_registry->remove<RenderPassComponent>((entt::entity)entity);
|
||||
}
|
||||
|
||||
// === Registration ===
|
||||
|
||||
void RegisterECSBindings(asIScriptEngine *engine) {
|
||||
@@ -313,5 +470,97 @@ void RegisterECSBindings(asIScriptEngine *engine) {
|
||||
asFUNCTION(AS_RemoveTag), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// Camera3D component
|
||||
r = engine->RegisterGlobalFunction("void AddCamera3D(uint, float, float, float, float, float, float, float)",
|
||||
asFUNCTION(AS_AddCamera3D), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetCameraPosition(uint, float, float, float)",
|
||||
asFUNCTION(AS_SetCameraPosition), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetCameraTarget(uint, float, float, float)",
|
||||
asFUNCTION(AS_SetCameraTarget), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetCameraFovy(uint, float)",
|
||||
asFUNCTION(AS_SetCameraFovy), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("bool HasCamera3D(uint)",
|
||||
asFUNCTION(AS_HasCamera3D), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void RemoveCamera3D(uint)",
|
||||
asFUNCTION(AS_RemoveCamera3D), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// Light component
|
||||
r = engine->RegisterGlobalFunction("void AddLight(uint, float, float, float, float = 1.0f)",
|
||||
asFUNCTION(AS_AddLight), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetLightDirection(uint, float, float, float)",
|
||||
asFUNCTION(AS_SetLightDirection), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetLightIntensity(uint, float)",
|
||||
asFUNCTION(AS_SetLightIntensity), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetLightColor(uint, uint)",
|
||||
asFUNCTION(AS_SetLightColor), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("bool HasLight(uint)",
|
||||
asFUNCTION(AS_HasLight), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void RemoveLight(uint)",
|
||||
asFUNCTION(AS_RemoveLight), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// Material component
|
||||
r = engine->RegisterGlobalFunction("void AddMaterial(uint, uint)",
|
||||
asFUNCTION(AS_AddMaterial), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetMaterialId(uint, uint)",
|
||||
asFUNCTION(AS_SetMaterialId), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("uint GetMaterialId(uint)",
|
||||
asFUNCTION(AS_GetMaterialId), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("bool HasMaterial(uint)",
|
||||
asFUNCTION(AS_HasMaterial), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void RemoveMaterial(uint)",
|
||||
asFUNCTION(AS_RemoveMaterialComponent), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// RenderPass component
|
||||
r = engine->RegisterGlobalFunction("void AddRenderPass(uint, int, bool, bool, uint)",
|
||||
asFUNCTION(AS_AddRenderPass), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetRenderPassOrder(uint, int)",
|
||||
asFUNCTION(AS_SetRenderPassOrder), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetRenderPassShader(uint, uint)",
|
||||
asFUNCTION(AS_SetRenderPassShader), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("bool HasRenderPass(uint)",
|
||||
asFUNCTION(AS_HasRenderPass), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void RemoveRenderPass(uint)",
|
||||
asFUNCTION(AS_RemoveRenderPass), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
engine->SetDefaultNamespace("");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#include "scripting/ResourceBindings.h"
|
||||
#include "ShaderManager.h"
|
||||
#include "ModelManager.h"
|
||||
#include "MaterialManager.h"
|
||||
#include <angelscript.h>
|
||||
#include <assert.h>
|
||||
#include "raylib.h"
|
||||
|
||||
// Global resource manager pointers for script access
|
||||
static ShaderManager* g_shaderManager = nullptr;
|
||||
static ModelManager* g_modelManager = nullptr;
|
||||
static MaterialManager* g_materialManager = nullptr;
|
||||
|
||||
void SetResourceManagers(ShaderManager* shaderMgr, ModelManager* modelMgr, MaterialManager* materialMgr) {
|
||||
g_shaderManager = shaderMgr;
|
||||
g_modelManager = modelMgr;
|
||||
g_materialManager = materialMgr;
|
||||
}
|
||||
|
||||
// === Shader Management ===
|
||||
|
||||
unsigned int AS_LoadShader(const std::string& vsPath, const std::string& fsPath) {
|
||||
if (!g_shaderManager) return 0;
|
||||
return g_shaderManager->Load(vsPath, fsPath);
|
||||
}
|
||||
|
||||
void AS_UnloadShader(unsigned int id) {
|
||||
if (!g_shaderManager) return;
|
||||
g_shaderManager->Unload(id);
|
||||
}
|
||||
|
||||
// === Model Management ===
|
||||
|
||||
unsigned int AS_LoadModel(const std::string& path) {
|
||||
if (!g_modelManager) return 0;
|
||||
return g_modelManager->Load(path);
|
||||
}
|
||||
|
||||
unsigned int AS_LoadModelCube(float width, float height, float length) {
|
||||
if (!g_modelManager) return 0;
|
||||
Mesh mesh = GenMeshCube(width, height, length);
|
||||
return g_modelManager->LoadFromMesh(mesh);
|
||||
}
|
||||
|
||||
unsigned int AS_LoadModelSphere(float radius, int rings, int slices) {
|
||||
if (!g_modelManager) return 0;
|
||||
Mesh mesh = GenMeshSphere(radius, rings, slices);
|
||||
return g_modelManager->LoadFromMesh(mesh);
|
||||
}
|
||||
|
||||
unsigned int AS_LoadModelPlane(float width, float length, int resX, int resZ) {
|
||||
if (!g_modelManager) return 0;
|
||||
Mesh mesh = GenMeshPlane(width, length, resX, resZ);
|
||||
return g_modelManager->LoadFromMesh(mesh);
|
||||
}
|
||||
|
||||
void AS_UnloadModel(unsigned int id) {
|
||||
if (!g_modelManager) return;
|
||||
g_modelManager->Unload(id);
|
||||
}
|
||||
|
||||
// === Material Management ===
|
||||
|
||||
unsigned int AS_CreateMaterial(unsigned int shaderId, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
||||
if (!g_materialManager) return 0;
|
||||
Color albedo = {r, g, b, a};
|
||||
return g_materialManager->Create(shaderId, albedo);
|
||||
}
|
||||
|
||||
void AS_RemoveMaterial(unsigned int id) {
|
||||
if (!g_materialManager) return;
|
||||
g_materialManager->Remove(id);
|
||||
}
|
||||
|
||||
void AS_SetMaterialShader(unsigned int id, unsigned int shaderId) {
|
||||
if (!g_materialManager) return;
|
||||
g_materialManager->SetShader(id, shaderId);
|
||||
}
|
||||
|
||||
void AS_SetMaterialAlbedo(unsigned int id, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
||||
if (!g_materialManager) return;
|
||||
Color albedo = {r, g, b, a};
|
||||
g_materialManager->SetAlbedo(id, albedo);
|
||||
}
|
||||
|
||||
void RegisterResourceBindings(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// === Shader namespace ===
|
||||
engine->SetDefaultNamespace("Shader");
|
||||
|
||||
r = engine->RegisterGlobalFunction("uint Load(const string &in, const string &in)",
|
||||
asFUNCTION(AS_LoadShader), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void Unload(uint)",
|
||||
asFUNCTION(AS_UnloadShader), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// === Model namespace ===
|
||||
engine->SetDefaultNamespace("Model");
|
||||
|
||||
r = engine->RegisterGlobalFunction("uint Load(const string &in)",
|
||||
asFUNCTION(AS_LoadModel), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("uint LoadCube(float, float, float)",
|
||||
asFUNCTION(AS_LoadModelCube), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("uint LoadSphere(float, int, int)",
|
||||
asFUNCTION(AS_LoadModelSphere), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("uint LoadPlane(float, float, int, int)",
|
||||
asFUNCTION(AS_LoadModelPlane), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void Unload(uint)",
|
||||
asFUNCTION(AS_UnloadModel), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// === Material namespace ===
|
||||
engine->SetDefaultNamespace("Material");
|
||||
|
||||
r = engine->RegisterGlobalFunction("uint Create(uint, uint8, uint8, uint8, uint8)",
|
||||
asFUNCTION(AS_CreateMaterial), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void Remove(uint)",
|
||||
asFUNCTION(AS_RemoveMaterial), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetShader(uint, uint)",
|
||||
asFUNCTION(AS_SetMaterialShader), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void SetAlbedo(uint, uint8, uint8, uint8, uint8)",
|
||||
asFUNCTION(AS_SetMaterialAlbedo), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
engine->SetDefaultNamespace("");
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "scripting/TextureBindings.h"
|
||||
#include "scripting/MathBindings.h"
|
||||
#include "scripting/ECSBindings.h"
|
||||
#include "scripting/ResourceBindings.h"
|
||||
|
||||
void ScriptBindings::RegisterAll(asIScriptEngine *engine)
|
||||
{
|
||||
@@ -15,4 +16,5 @@ void ScriptBindings::RegisterAll(asIScriptEngine *engine)
|
||||
RegisterTextureBindings(engine);
|
||||
RegisterMathBindings(engine);
|
||||
RegisterECSBindings(engine);
|
||||
RegisterResourceBindings(engine);
|
||||
}
|
||||
@@ -15,6 +15,10 @@ add_executable(unit_tests
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/ImageBindings.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/TextureBindings.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/MathBindings.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/ResourceBindings.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ShaderManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ModelManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/MaterialManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/log/log.c
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user