Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 50ec04aa12 | |||
| 95deb65106 | |||
| 9f8686345c | |||
| 33226dfae1 | |||
| d0c28dc663 | |||
| a37910de25 | |||
| 4116678556 | |||
| 0cce80e956 | |||
| 05b0773189 | |||
| 283d057507 | |||
| 82886349ae | |||
| 670f65b37b | |||
| 766f7ecd7a | |||
| aa4c7cf58a |
@@ -0,0 +1,36 @@
|
||||
name: Sync Docs to Gitea Wiki
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync docs to Gitea wiki
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Start SSH agent
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.WIKI_SSH_KEY }}
|
||||
|
||||
- name: Sync docs to wiki
|
||||
env:
|
||||
WIKI_REPO: git@gitea.appstack.me:nick/simian.wiki.git
|
||||
run: |
|
||||
git config --global user.name "Simian CI"
|
||||
git config --global user.email "ci@simian.local"
|
||||
git clone "$WIKI_REPO" wiki
|
||||
rsync -av --delete docs/ wiki/
|
||||
cd wiki
|
||||
git add -A
|
||||
if git diff --staged --quiet; then
|
||||
echo "No wiki changes"
|
||||
else
|
||||
git commit -m "Update wiki from docs (CI)"
|
||||
git push origin HEAD
|
||||
fi
|
||||
+45
-20
@@ -42,11 +42,6 @@ else()
|
||||
set(HAVE_RLIMGUI FALSE)
|
||||
endif()
|
||||
# -------------------------
|
||||
# Tests
|
||||
# -------------------------
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
# -------------------------
|
||||
# 3️⃣ scriptstdstring add-on
|
||||
# -------------------------
|
||||
add_library(scriptstdstring STATIC
|
||||
@@ -69,26 +64,42 @@ target_include_directories(scriptbuilder
|
||||
)
|
||||
|
||||
# -------------------------
|
||||
# 4️⃣ Main executable
|
||||
# 3️⃣.2️⃣ scriptarray add-on
|
||||
# -------------------------
|
||||
add_executable(simian
|
||||
main.cpp
|
||||
add_library(scriptarray STATIC
|
||||
external/angelscript/sdk/add_on/scriptarray/scriptarray.cpp
|
||||
)
|
||||
|
||||
target_include_directories(scriptarray
|
||||
PUBLIC external/angelscript/sdk/angelscript/include
|
||||
)
|
||||
|
||||
# -------------------------
|
||||
# 4️⃣ Simian Core Library (SHARED CODE)
|
||||
# -------------------------
|
||||
add_library(simian_core STATIC
|
||||
src/Application.cpp
|
||||
src/scripting/ScriptEngine.cpp
|
||||
src/scripting/ScriptBindings.cpp
|
||||
src/scripting/ToastBindings.cpp
|
||||
src/scripting/LogBindings.cpp
|
||||
src/scripting/DrawBindings.cpp
|
||||
src/scripting/ImageBindings.cpp
|
||||
src/scripting/TextureBindings.cpp
|
||||
src/scripting/MathBindings.cpp
|
||||
src/scripting/ECSBindings.cpp
|
||||
src/scripting/ResourceBindings.cpp
|
||||
src/scripting/InputBindings.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
|
||||
src/InputManager.cpp
|
||||
)
|
||||
|
||||
target_include_directories(simian PUBLIC
|
||||
target_include_directories(simian_core PUBLIC
|
||||
include
|
||||
include/scripting
|
||||
include/gui
|
||||
@@ -97,29 +108,38 @@ target_include_directories(simian PUBLIC
|
||||
external/angelscript/sdk/angelscript/include
|
||||
external/angelscript/sdk/add_on/scriptstdstring
|
||||
external/angelscript/sdk/add_on/scriptbuilder
|
||||
external/angelscript/sdk/add_on/scriptarray
|
||||
external/imgui
|
||||
)
|
||||
|
||||
target_link_libraries(simian PRIVATE
|
||||
target_link_libraries(simian_core PUBLIC
|
||||
raylib
|
||||
angelscript
|
||||
scriptstdstring
|
||||
scriptbuilder
|
||||
scriptarray
|
||||
imgui
|
||||
)
|
||||
|
||||
if(HAVE_RLIMGUI)
|
||||
target_include_directories(simian PUBLIC ${RLIMGUI_ROOT}/include)
|
||||
target_link_libraries(simian PRIVATE rlImGui)
|
||||
target_include_directories(simian_core PUBLIC ${RLIMGUI_ROOT}/include)
|
||||
target_link_libraries(simian_core PUBLIC rlImGui)
|
||||
# Expose HAVE_RLIMGUI to the C++ compiler so source can guard ImGui calls
|
||||
target_compile_definitions(simian PRIVATE HAVE_RLIMGUI=1)
|
||||
message(STATUS "Linking rlImGui into simian")
|
||||
target_compile_definitions(simian_core PUBLIC HAVE_RLIMGUI=1)
|
||||
message(STATUS "Linking rlImGui into simian_core")
|
||||
else()
|
||||
message(STATUS "rlImGui not found - building without rlImGui integration")
|
||||
endif()
|
||||
|
||||
# -------------------------
|
||||
# 5️⃣ Platform-specific linking
|
||||
# 5️⃣ Main executable (just main.cpp)
|
||||
# -------------------------
|
||||
add_executable(simian main.cpp)
|
||||
|
||||
target_link_libraries(simian PRIVATE simian_core)
|
||||
|
||||
# -------------------------
|
||||
# 6️⃣ Platform-specific linking
|
||||
# -------------------------
|
||||
if(WIN32)
|
||||
target_link_libraries(simian PRIVATE winmm gdi32)
|
||||
@@ -128,11 +148,16 @@ elseif(UNIX)
|
||||
endif()
|
||||
|
||||
# -------------------------
|
||||
# 6️⃣ Optional: Compile flags
|
||||
# 7️⃣ Optional: Compile flags
|
||||
# -------------------------
|
||||
if(MSVC)
|
||||
target_compile_options(simian PRIVATE /W4 /EHsc)
|
||||
target_compile_options(simian_core PRIVATE /W4 /EHsc)
|
||||
else()
|
||||
target_compile_options(simian PRIVATE -Wall -Wextra -pedantic)
|
||||
target_compile_options(simian_core PRIVATE -Wall -Wextra -pedantic)
|
||||
endif()
|
||||
# -------------------------
|
||||
|
||||
# -------------------------
|
||||
# 8️⃣ Tests
|
||||
# -------------------------
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
@@ -0,0 +1,12 @@
|
||||
# Simian Documentation
|
||||
|
||||
This folder contains project documentation that is synced to the project's Gitea wiki.
|
||||
Edit markdown files here and push to the repository; CI will mirror them to the wiki.
|
||||
|
||||
## Pages
|
||||
- [Scripting](Scripting.md) — scripting API and examples
|
||||
- [Usage](Usage.md) — getting started and build instructions
|
||||
|
||||
## Notes
|
||||
The CI workflow (.github/workflows/sync-docs-to-wiki.yml) clones the wiki repository and copies these files into it.
|
||||
Make sure `WIKI_SSH_KEY` is configured in repository secrets (SSH private key with push access to the wiki).
|
||||
@@ -0,0 +1,13 @@
|
||||
# Scripting
|
||||
|
||||
This page is a starting point for scripting documentation.
|
||||
|
||||
## Overview
|
||||
- Scripts live in `scripts/` and are executed by AngelScript.
|
||||
- Predefined API and engine bindings are declared in `scripts/as.predefined`.
|
||||
|
||||
## Examples
|
||||
- `scripts/game.as` — demo showing ECS usage, rendering and input actions.
|
||||
|
||||
## How to document APIs
|
||||
Add Markdown pages in the `docs/` folder. Use relative links and include code fences for examples.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Usage
|
||||
|
||||
This page documents how to build, run and maintain the Simian project and how to publish documentation to the Gitea wiki.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Git
|
||||
- CMake (>= 3.16; 3.25 recommended)
|
||||
- A C++ toolchain (Visual Studio on Windows, GCC/Clang on Linux/macOS)
|
||||
- Optional: SSH access to your Gitea instance (for wiki sync)
|
||||
|
||||
If you just cloned the repository, initialise submodules:
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
## Build (Windows / MSVC)
|
||||
|
||||
```powershell
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -S .. -B . -G "Visual Studio 17 2022" -A x64
|
||||
cmake --build . --config Release
|
||||
# Run
|
||||
.\Release\simian.exe
|
||||
```
|
||||
|
||||
## Build (Linux / macOS)
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
./simian
|
||||
```
|
||||
|
||||
## Run tests
|
||||
|
||||
From the project root (after configuring the build):
|
||||
|
||||
```bash
|
||||
cmake --build build --config Release --target RUN_TESTS
|
||||
# or use CTest
|
||||
ctest --test-dir build --output-on-failure
|
||||
```
|
||||
|
||||
## Scripting / Hot-reload
|
||||
|
||||
- Scripts live in the `scripts/` directory (e.g. `scripts/game.as`).
|
||||
- The engine watches the scripts directory and will hot-reload scripts when changed.
|
||||
- Predefined API is declared in `scripts/as.predefined`.
|
||||
|
||||
## Documentation & Wiki
|
||||
|
||||
Docs source lives in the `docs/` folder. The repository contains a CI workflow and a local script to mirror `docs/` to the Gitea wiki.
|
||||
|
||||
Manual sync (local):
|
||||
|
||||
```bash
|
||||
bash scripts/sync_wiki.sh
|
||||
```
|
||||
|
||||
CI sync (automated):
|
||||
|
||||
1. The workflow `.github/workflows/sync-docs-to-wiki.yml` runs on pushes that affect `docs/**`.
|
||||
2. The workflow requires an SSH private key in repository secrets named `GITEA_SSH_KEY`.
|
||||
|
||||
To create the deploy key and CI secret:
|
||||
|
||||
```bash
|
||||
# Generate a keypair (do not commit the private key)
|
||||
ssh-keygen -t ed25519 -C "simian-wiki-ci" -f simian_wiki_ci -N ""
|
||||
|
||||
# Add simian_wiki_ci.pub as a deploy key in your Gitea repo: Settings → Deploy Keys
|
||||
# Enable write/push access for that key.
|
||||
|
||||
# Copy the private key (simian_wiki_ci) into your CI provider as secret WIKI_SSH_KEY.
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The workflow clones the wiki remote `git@gitea.appstack.me:nick/simian.wiki.git` and pushes updates.
|
||||
- If your Gitea host/paths differ, update `.github/workflows/sync-docs-to-wiki.yml` or the `scripts/sync_wiki.sh` script accordingly.
|
||||
|
||||
## Adding / Updating Documentation
|
||||
|
||||
1. Add or edit Markdown files under `docs/`.
|
||||
2. Put images in `docs/images/` and reference them with relative paths, e.g. ``.
|
||||
3. Commit and push to the repo; CI will sync changes automatically (once secrets/deploy-key are configured).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- If the CI job fails with authentication errors, confirm the deploy key was added to the Gitea repo and the private key is stored in the CI secrets as `GITEA_SSH_KEY`.
|
||||
- To debug locally, run the `scripts/sync_wiki.sh` script and watch for `git push` errors.
|
||||
@@ -21,13 +21,13 @@ Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][##TOAST0]
|
||||
Pos=1007,637
|
||||
Size=253,63
|
||||
Pos=1060,637
|
||||
Size=200,63
|
||||
Collapsed=0
|
||||
|
||||
[Window][##TOAST1]
|
||||
Pos=1064,564
|
||||
Size=196,63
|
||||
Pos=1060,564
|
||||
Size=200,63
|
||||
Collapsed=0
|
||||
|
||||
[Window][##TOAST2]
|
||||
@@ -36,13 +36,13 @@ Size=196,63
|
||||
Collapsed=0
|
||||
|
||||
[Window][##TOAST3]
|
||||
Pos=1048,442
|
||||
Size=212,57
|
||||
Pos=1064,418
|
||||
Size=196,63
|
||||
Collapsed=0
|
||||
|
||||
[Window][##TOAST4]
|
||||
Pos=1048,375
|
||||
Size=212,57
|
||||
Pos=1064,345
|
||||
Size=196,63
|
||||
Collapsed=0
|
||||
|
||||
[Window][Scene (0 entities)]
|
||||
@@ -70,6 +70,11 @@ Pos=60,60
|
||||
Size=71,108
|
||||
Collapsed=0
|
||||
|
||||
[Window][##TOAST5]
|
||||
Pos=1064,272
|
||||
Size=196,63
|
||||
Collapsed=0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,28 Size=1264,684 Split=Y Selected=0x27A02DAA
|
||||
DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,580 CentralNode=1 Selected=0x27A02DAA
|
||||
|
||||
+16
-2
@@ -3,6 +3,11 @@
|
||||
#include "HotReload.h"
|
||||
#include "gui/GuiManager.h"
|
||||
#include "raylib.h"
|
||||
#include "ShaderManager.h"
|
||||
#include "ModelManager.h"
|
||||
#include "MaterialManager.h"
|
||||
#include "InputManager.h"
|
||||
#include <entt.hpp>
|
||||
|
||||
class Application {
|
||||
public:
|
||||
@@ -27,6 +32,15 @@ private:
|
||||
bool queueShutdown = false;
|
||||
RenderTexture2D renderTexture; // renderTexture for Raylib rendering
|
||||
|
||||
// ECS
|
||||
entt::registry registry;
|
||||
|
||||
// Resource managers
|
||||
ShaderManager shaderManager;
|
||||
ModelManager modelManager;
|
||||
MaterialManager materialManager;
|
||||
InputManager inputManager;
|
||||
|
||||
static const int WINDOW_WIDTH = 1280;
|
||||
static const int WINDOW_HEIGHT = 720;
|
||||
static const int TARGET_FPS = 60;
|
||||
@@ -34,7 +48,7 @@ private:
|
||||
static const char* SCRIPT_FILE;
|
||||
|
||||
void Update(float deltaTime);
|
||||
void UpdateSystems(float deltaTime);
|
||||
void RenderScene();
|
||||
void Draw();
|
||||
|
||||
void SpawnCube();
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
#include "raylib.h"
|
||||
#include <string>
|
||||
|
||||
// ECSTransform component - position, rotation, and scale
|
||||
struct ECSTransform {
|
||||
Vector3 position;
|
||||
Vector3 scale;
|
||||
float rotation; // Rotation around Y axis in radians
|
||||
|
||||
ECSTransform()
|
||||
: position{0.0f, 0.0f, 0.0f}
|
||||
, scale{1.0f, 1.0f, 1.0f}
|
||||
, rotation(0.0f) {}
|
||||
|
||||
ECSTransform(Vector3 pos, Vector3 scl = {1.0f, 1.0f, 1.0f}, float rot = 0.0f)
|
||||
: position(pos), scale(scl), rotation(rot) {}
|
||||
};
|
||||
|
||||
// Velocity component - for movement
|
||||
struct Velocity {
|
||||
Vector3 linear; // Linear velocity (units per second)
|
||||
float angular; // Angular velocity (radians per second)
|
||||
|
||||
Velocity()
|
||||
: linear{0.0f, 0.0f, 0.0f}
|
||||
, angular(0.0f) {}
|
||||
|
||||
Velocity(Vector3 lin, float ang = 0.0f)
|
||||
: linear(lin), angular(ang) {}
|
||||
};
|
||||
|
||||
// Sprite component - visual representation
|
||||
struct Sprite {
|
||||
size_t modelId; // Index into models array
|
||||
Color color; // Tint color
|
||||
float outlineSize; // Outline thickness
|
||||
unsigned int shaderId; // Shader to use (0 = no shader)
|
||||
|
||||
Sprite()
|
||||
: modelId(0)
|
||||
, color(WHITE)
|
||||
, outlineSize(0.0f)
|
||||
, shaderId(0) {}
|
||||
|
||||
Sprite(int model, Color col = WHITE, float outline = 0.0f, unsigned int shader = 0)
|
||||
: modelId(model), color(col), outlineSize(outline), shaderId(shader) {}
|
||||
};
|
||||
|
||||
// Tag component - simple string identifier
|
||||
struct Tag {
|
||||
std::string name;
|
||||
|
||||
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,81 @@
|
||||
#pragma once
|
||||
#include "raylib.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
// Input types supported by the action system
|
||||
enum class InputType {
|
||||
Keyboard,
|
||||
MouseButton,
|
||||
GamepadButton,
|
||||
GamepadAxis
|
||||
};
|
||||
|
||||
// Represents a single input binding (e.g., KEY_SPACE, MOUSE_BUTTON_LEFT)
|
||||
struct InputBinding {
|
||||
InputType type;
|
||||
int code; // Key code, mouse button, or gamepad button
|
||||
int gamepadId; // For gamepad inputs (0-3)
|
||||
float axisThreshold; // For axis inputs (e.g., trigger > 0.5)
|
||||
bool negative; // For axis inputs (e.g., left stick left = negative)
|
||||
|
||||
InputBinding()
|
||||
: type(InputType::Keyboard)
|
||||
, code(0)
|
||||
, gamepadId(0)
|
||||
, axisThreshold(0.5f)
|
||||
, negative(false) {}
|
||||
|
||||
InputBinding(InputType t, int c, int gamepad = 0)
|
||||
: type(t)
|
||||
, code(c)
|
||||
, gamepadId(gamepad)
|
||||
, axisThreshold(0.5f)
|
||||
, negative(false) {}
|
||||
};
|
||||
|
||||
// Manages input actions with remappable bindings
|
||||
class InputManager {
|
||||
public:
|
||||
InputManager();
|
||||
~InputManager();
|
||||
|
||||
// Action management
|
||||
void RegisterAction(const std::string& actionName);
|
||||
void UnregisterAction(const std::string& actionName);
|
||||
bool HasAction(const std::string& actionName) const;
|
||||
|
||||
// Binding management
|
||||
void BindKeyboard(const std::string& actionName, int keyCode);
|
||||
void BindMouseButton(const std::string& actionName, int mouseButton);
|
||||
void BindGamepadButton(const std::string& actionName, int gamepadId, int button);
|
||||
void BindGamepadAxis(const std::string& actionName, int gamepadId, int axis, float threshold, bool negative = false);
|
||||
void ClearBindings(const std::string& actionName);
|
||||
|
||||
// Input queries (called per frame)
|
||||
bool IsActionPressed(const std::string& actionName) const;
|
||||
bool IsActionDown(const std::string& actionName) const;
|
||||
bool IsActionReleased(const std::string& actionName) const;
|
||||
float GetActionStrength(const std::string& actionName) const; // For analog inputs
|
||||
|
||||
// Configuration
|
||||
void LoadFromFile(const std::string& filepath);
|
||||
void SaveToFile(const std::string& filepath) const;
|
||||
void SetupDefaultBindings(); // Define default controls
|
||||
|
||||
// Get list of all actions
|
||||
std::vector<std::string> GetAllActions() const;
|
||||
|
||||
// Get bindings for an action
|
||||
const std::vector<InputBinding>& GetBindings(const std::string& actionName) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::vector<InputBinding>> actions;
|
||||
|
||||
// Helper to check if a single binding is active
|
||||
bool IsBindingPressed(const InputBinding& binding) const;
|
||||
bool IsBindingDown(const InputBinding& binding) const;
|
||||
bool IsBindingReleased(const InputBinding& binding) const;
|
||||
float GetBindingStrength(const InputBinding& binding) const;
|
||||
};
|
||||
@@ -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,8 @@
|
||||
#pragma once
|
||||
#include <angelscript.h>
|
||||
#include <entt.hpp>
|
||||
|
||||
// Set the global registry that scripts will interact with
|
||||
void SetGlobalRegistry(entt::registry* reg);
|
||||
|
||||
void RegisterECSBindings(asIScriptEngine *engine);
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class asIScriptEngine;
|
||||
class InputManager;
|
||||
|
||||
void SetGlobalInputManager(InputManager* inputManager);
|
||||
void RegisterInputBindings(asIScriptEngine* engine);
|
||||
@@ -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);
|
||||
@@ -25,14 +25,12 @@ public:
|
||||
|
||||
asIScriptEngine* GetEngine() const { return engine; }
|
||||
asIScriptFunction* GetUpdateFunction() const { return updateFunc; }
|
||||
asIScriptFunction* GetDrawFunction() const { return drawFunc; }
|
||||
asIScriptFunction* GetInitFunction() const { return initFunc; }
|
||||
asIScriptFunction* GetShutdownFunction() const { return shutdownFunc; }
|
||||
|
||||
private:
|
||||
asIScriptEngine* engine;
|
||||
asIScriptFunction* updateFunc;
|
||||
asIScriptFunction* drawFunc;
|
||||
asIScriptFunction* initFunc;
|
||||
asIScriptFunction* shutdownFunc;
|
||||
asIScriptModule* currentModule;
|
||||
|
||||
+144
-9
@@ -1,6 +1,18 @@
|
||||
typedef void string;
|
||||
string format(const string&in fmt, const ?&in ...);
|
||||
|
||||
// Array template (provided by scriptarray add-on)
|
||||
external shared class array<T> {
|
||||
uint length() const;
|
||||
void resize(uint);
|
||||
void insertAt(uint, const T&in);
|
||||
void insertLast(const T&in);
|
||||
void removeAt(uint);
|
||||
void removeLast();
|
||||
T& opIndex(uint);
|
||||
const T& opIndex(uint) const;
|
||||
}
|
||||
|
||||
// Logging functions
|
||||
void Print(const string&in);
|
||||
void Log(int level, const string&in);
|
||||
@@ -19,14 +31,98 @@ namespace Toast {
|
||||
void Success(const string&in);
|
||||
}
|
||||
|
||||
// Drawing functions
|
||||
namespace Draw {
|
||||
void Pixel(int x, int y, int color);
|
||||
void Line(int startX, int startY, int endX, int endY, int color);
|
||||
void Circle(int centerX, int centerY, float radius, int color);
|
||||
void Text(const string&in text, int x, int y, int fontSize, int color);
|
||||
void Rectangle(int x, int y, int width, int height, int color);
|
||||
void FPS(int x, int y);
|
||||
// ECS functions
|
||||
namespace ECS {
|
||||
// Entity management
|
||||
uint CreateEntity();
|
||||
void DestroyEntity(uint entity);
|
||||
bool IsValid(uint entity);
|
||||
|
||||
// Transform component
|
||||
void AddTransform(uint entity, float x, float y, float z);
|
||||
void SetPosition(uint entity, float x, float y, float z);
|
||||
void GetPosition(uint entity, float &out x, float &out y, float &out z);
|
||||
void SetScale(uint entity, float x, float y, float z);
|
||||
void SetRotation(uint entity, float rotation);
|
||||
float GetRotation(uint entity);
|
||||
bool HasTransform(uint entity);
|
||||
void RemoveTransform(uint entity);
|
||||
|
||||
// Velocity component
|
||||
void AddVelocity(uint entity, float vx, float vy, float vz, float angular = 0.0f);
|
||||
void SetVelocity(uint entity, float vx, float vy, float vz);
|
||||
void SetAngularVelocity(uint entity, float angular);
|
||||
bool HasVelocity(uint entity);
|
||||
void RemoveVelocity(uint entity);
|
||||
|
||||
// Sprite component
|
||||
void AddSprite(uint entity, int modelId, uint color, float outlineSize = 0.0f);
|
||||
void SetSpriteColor(uint entity, uint color);
|
||||
void SetSpriteModel(uint entity, int modelId);
|
||||
void SetSpriteOutline(uint entity, float outlineSize);
|
||||
void SetSpriteShader(uint entity, uint shaderId);
|
||||
bool HasSprite(uint entity);
|
||||
void RemoveSprite(uint entity);
|
||||
|
||||
// Tag component
|
||||
void AddTag(uint entity, const string &in name);
|
||||
string GetTag(uint entity);
|
||||
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 {
|
||||
@@ -68,4 +164,43 @@ 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);
|
||||
|
||||
// Input Action System
|
||||
bool IsActionPressed(const string &in actionName);
|
||||
bool IsActionDown(const string &in actionName);
|
||||
bool IsActionReleased(const string &in actionName);
|
||||
float GetActionStrength(const string &in actionName);
|
||||
void RegisterAction(const string &in actionName);
|
||||
void BindKeyboard(const string &in actionName, int keyCode);
|
||||
void BindMouseButton(const string &in actionName, int mouseButton);
|
||||
void ClearBindings(const string &in actionName);
|
||||
|
||||
namespace Key {
|
||||
const int SPACE = 32;
|
||||
const int ESCAPE = 256;
|
||||
const int ENTER = 257;
|
||||
const int TAB = 258;
|
||||
const int BACKSPACE = 259;
|
||||
const int RIGHT = 262;
|
||||
const int LEFT = 263;
|
||||
const int DOWN = 264;
|
||||
const int UP = 265;
|
||||
const int W = 87;
|
||||
const int A = 65;
|
||||
const int S = 83;
|
||||
const int D = 68;
|
||||
const int E = 69;
|
||||
const int LEFT_CONTROL = 341;
|
||||
}
|
||||
|
||||
namespace MouseButton {
|
||||
const int LEFT = 0;
|
||||
const int RIGHT = 1;
|
||||
const int MIDDLE = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Example script showing how to use the new script-driven rendering system
|
||||
|
||||
// Global variables to track our resources
|
||||
uint toonShader = 0;
|
||||
uint outlineShader = 0;
|
||||
uint cubeModel = 0;
|
||||
uint sphereModel = 0;
|
||||
uint redMaterial = 0;
|
||||
uint blueMaterial = 0;
|
||||
|
||||
// Entities
|
||||
uint camera = 0;
|
||||
uint light = 0;
|
||||
uint cube = 0;
|
||||
uint sphere = 0;
|
||||
|
||||
void Init() {
|
||||
Toast::Info("Initializing script-driven rendering...");
|
||||
|
||||
// === Load Shaders ===
|
||||
toonShader = Shader::Load("shaders/toon.vs", "shaders/toon.fs");
|
||||
outlineShader = Shader::Load("shaders/outline.vs", "shaders/outline.fs");
|
||||
|
||||
if (toonShader == 0 || outlineShader == 0) {
|
||||
Toast::Error("Failed to load shaders!");
|
||||
return;
|
||||
}
|
||||
|
||||
// === Load Models ===
|
||||
cubeModel = Model::LoadCube(1.0, 1.0, 1.0);
|
||||
sphereModel = Model::LoadSphere(0.5, 16, 16);
|
||||
|
||||
// === Create Materials ===
|
||||
// Red material with toon shader
|
||||
redMaterial = Material::Create(toonShader, 255, 100, 100, 255);
|
||||
|
||||
// Blue material with toon shader
|
||||
blueMaterial = Material::Create(toonShader, 100, 100, 255, 255);
|
||||
|
||||
// === Set up Camera ===
|
||||
camera = ECS::CreateEntity();
|
||||
ECS::AddCamera3D(camera,
|
||||
5.0, 5.0, 5.0, // position
|
||||
0.0, 0.0, 0.0, // target
|
||||
45.0); // fovy
|
||||
ECS::AddTag(camera, "MainCamera");
|
||||
|
||||
// === Set up Light ===
|
||||
light = ECS::CreateEntity();
|
||||
ECS::AddLight(light, 0.5, 0.7, 0.3, 1.0); // direction + intensity
|
||||
ECS::AddTag(light, "MainLight");
|
||||
|
||||
// === Create Cube with Outline ===
|
||||
cube = ECS::CreateEntity();
|
||||
ECS::AddTransform(cube, -1.5, 0.5, 0.0);
|
||||
ECS::AddSprite(cube, cubeModel, 0xFFFFFFFF, 0.0);
|
||||
ECS::AddMaterial(cube, redMaterial);
|
||||
ECS::AddTag(cube, "Cube");
|
||||
ECS::AddVelocity(cube, 0.0, 0.0, 0.0, 1.0); // rotating
|
||||
|
||||
// Add outline render pass (order 0 = first)
|
||||
ECS::AddRenderPass(cube, 0, false, true, outlineShader); // order=0, no depth test
|
||||
|
||||
// === Create Sphere ===
|
||||
sphere = ECS::CreateEntity();
|
||||
ECS::AddTransform(sphere, 1.5, 0.5, 0.0);
|
||||
ECS::AddSprite(sphere, sphereModel, 0xFFFFFFFF, 0.0);
|
||||
ECS::AddMaterial(sphere, blueMaterial);
|
||||
ECS::AddTag(sphere, "Sphere");
|
||||
ECS::AddVelocity(sphere, 0.5, 0.0, 0.2, 0.5); // moving and rotating
|
||||
|
||||
// Add main render pass (order 1 = second, after outlines)
|
||||
ECS::AddRenderPass(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(camera)) {
|
||||
float angle = GetTime() * 0.3;
|
||||
float radius = 7.0;
|
||||
float x = Math::Cos(angle) * radius;
|
||||
float z = Math::Sin(angle) * radius;
|
||||
ECS::SetCameraPosition(camera, x, 5.0, z);
|
||||
}
|
||||
|
||||
// Example: Toggle light direction with space
|
||||
if (IsKeyPressed(Key::SPACE)) {
|
||||
|
||||
lightToggle = !lightToggle;
|
||||
|
||||
if (lightToggle) {
|
||||
ECS::SetLightDirection(light, -0.5, 0.7, -0.3);
|
||||
Toast::Info("Light direction changed!");
|
||||
} else {
|
||||
ECS::SetLightDirection(light, 0.5, 0.7, 0.3);
|
||||
Toast::Info("Light direction reset!");
|
||||
}
|
||||
}
|
||||
}
|
||||
+196
-10
@@ -1,11 +1,197 @@
|
||||
#include "update.as"
|
||||
// ECS-based game script demo with script-driven rendering
|
||||
// This demonstrates creating entities with various components and managing rendering
|
||||
|
||||
void Draw() {
|
||||
Draw::FPS(10, 10);
|
||||
Draw::Text(format("Hello from AngelScript - at x {} and y {}", int(x), int(y)), int(x), int(y), 20, 0xFF0000FF);
|
||||
Draw::Rectangle(300, 200, 200, 100, 0x00FF00FF);
|
||||
Draw::Pixel(250, 200, 0x0000FFFF);
|
||||
Draw::Line(int(x), int(y), 400, 300, 0xFFFF00FF);
|
||||
Draw::Circle(600, 400, 50.0f, 0xFF00FFFF);
|
||||
Texture::Draw(tex, int(ix), int(iy), 0xFFFFFFFF);
|
||||
}
|
||||
// Resource IDs
|
||||
uint toonShader = 0;
|
||||
uint outlineShader = 0;
|
||||
uint cubeModel = 0;
|
||||
uint sphereModel = 0;
|
||||
uint defaultMaterial = 0;
|
||||
|
||||
// Scene entities
|
||||
uint camera = 0;
|
||||
uint light = 0;
|
||||
|
||||
// Store entity IDs for manipulation
|
||||
array<uint> entities;
|
||||
uint player;
|
||||
uint spinningCube;
|
||||
float time = 0.0f;
|
||||
|
||||
void Init() {
|
||||
Log(LOG_TRACE, "Initialization complete - ECS Demo with Script-Driven Rendering!");
|
||||
Toast::Success("ECS Script initialized successfully!");
|
||||
|
||||
// === Load shaders ===
|
||||
toonShader = Shader::Load("shaders/toon.vs", "shaders/toon.fs");
|
||||
outlineShader = Shader::Load("shaders/outline.vs", "shaders/outline.fs");
|
||||
|
||||
if (toonShader == 0 || outlineShader == 0) {
|
||||
Toast::Error("Failed to load shaders!");
|
||||
return;
|
||||
}
|
||||
|
||||
// === Load models ===
|
||||
cubeModel = Model::LoadCube(1.0, 1.0, 1.0);
|
||||
sphereModel = Model::LoadSphere(0.5, 16, 16);
|
||||
|
||||
// === Create default material ===
|
||||
defaultMaterial = Material::Create(toonShader, 255, 255, 255, 255);
|
||||
|
||||
// === Set up camera ===
|
||||
camera = ECS::CreateEntity();
|
||||
ECS::AddCamera3D(camera,
|
||||
5.0, 5.0, 5.0, // position
|
||||
0.0, 0.0, 0.0, // target
|
||||
45.0); // fovy
|
||||
ECS::AddTag(camera, "MainCamera");
|
||||
|
||||
// === Set up light ===
|
||||
light = ECS::CreateEntity();
|
||||
ECS::AddLight(light, 0.5, 0.7, 0.3, 1.0); // direction + intensity
|
||||
ECS::AddTag(light, "MainLight");
|
||||
|
||||
// === Create player entity at origin with outline ===
|
||||
player = ECS::CreateEntity();
|
||||
ECS::AddTransform(player, 0.0f, 0.0f, 0.0f);
|
||||
ECS::AddSprite(player, sphereModel, 0xFF0000FF, 0.0f); // Red sphere
|
||||
ECS::SetSpriteShader(player, toonShader); // Apply toon shader
|
||||
|
||||
ECS::AddTag(player, "Player");
|
||||
Log(LOG_INFO, "Created player entity: " + player);
|
||||
|
||||
// === Create a spinning cube ===
|
||||
spinningCube = ECS::CreateEntity();
|
||||
ECS::AddTransform(spinningCube, 2.0f, 0.0f, 0.0f);
|
||||
ECS::AddSprite(spinningCube, cubeModel, 0x00FF00FF, 0.0f); // Green cube
|
||||
ECS::SetSpriteShader(spinningCube, toonShader); // Apply toon shader
|
||||
ECS::AddVelocity(spinningCube, 0.0f, 0.0f, 0.0f, 2.0f); // Angular velocity
|
||||
|
||||
ECS::AddTag(spinningCube, "Spinner");
|
||||
Log(LOG_INFO, "Created spinning cube: " + spinningCube);
|
||||
|
||||
// === Create orbiting entities ===
|
||||
for (int i = 0; i < 5; i++) {
|
||||
uint entity = ECS::CreateEntity();
|
||||
|
||||
float angle = (float(i) / 5.0f) * 6.28318f; // 2*PI
|
||||
float radius = 3.0f;
|
||||
float x = Math::Cos(angle) * radius;
|
||||
float z = Math::Sin(angle) * radius;
|
||||
|
||||
ECS::AddTransform(entity, x, 0.0f, z);
|
||||
|
||||
// Different colors for each
|
||||
uint color = 0;
|
||||
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, sphereModel, color, 0.0f);
|
||||
ECS::SetSpriteShader(entity, toonShader); // Apply toon shader
|
||||
|
||||
ECS::AddTag(entity, "Orbiter" + i);
|
||||
entities.insertLast(entity);
|
||||
}
|
||||
|
||||
// === Create a moving entity ===
|
||||
uint mover = ECS::CreateEntity();
|
||||
ECS::AddTransform(mover, -3.0f, 0.5f, 0.0f);
|
||||
ECS::AddSprite(mover, cubeModel, 0xFFFFFFFF, 0.0f); // White cube
|
||||
ECS::SetSpriteShader(mover, toonShader); // Apply toon shader
|
||||
ECS::AddVelocity(mover, 1.0f, 0.0f, 0.5f, 0.0f); // Moving velocity
|
||||
|
||||
ECS::AddTag(mover, "Mover");
|
||||
entities.insertLast(mover);
|
||||
|
||||
Toast::Success("Created " + (entities.length() + 2) + " entities with script-driven rendering!");
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
Log(LOG_TRACE, "Shutdown complete!");
|
||||
}
|
||||
bool lightToggle = false;
|
||||
void Update(float dt) {
|
||||
time += dt;
|
||||
|
||||
// Update camera to orbit around the scene
|
||||
if (ECS::HasCamera3D(camera)) {
|
||||
float angle = time * 0.3;
|
||||
float radius = 7.0;
|
||||
float x = Math::Cos(angle) * radius;
|
||||
float z = Math::Sin(angle) * radius;
|
||||
ECS::SetCameraPosition(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);
|
||||
|
||||
// Update orbiting entities in a circle
|
||||
for (uint i = 0; i < entities.length() - 1; i++) {
|
||||
uint entity = entities[i];
|
||||
if (!ECS::IsValid(entity)) continue;
|
||||
|
||||
float angle = (float(i) / 5.0f) * 6.28318f + time * 0.5f;
|
||||
float radius = 3.0f;
|
||||
float x = Math::Cos(angle) * radius;
|
||||
float z = Math::Sin(angle) * radius;
|
||||
float y = Math::Sin(time * 2.0f + float(i)) * 0.5f; // Bobbing motion
|
||||
|
||||
ECS::SetPosition(entity, x, y, z);
|
||||
ECS::SetRotation(entity, time + float(i));
|
||||
}
|
||||
|
||||
// The mover entity wraps around using the Velocity component
|
||||
// which is handled by the C++ movement system
|
||||
if (entities.length() > 0) {
|
||||
uint mover = entities[entities.length() - 1];
|
||||
if (ECS::IsValid(mover) && ECS::HasTransform(mover)) {
|
||||
float x, y, z;
|
||||
ECS::GetPosition(mover, x, y, z);
|
||||
|
||||
// Wrap around
|
||||
if (x > 5.0f) {
|
||||
ECS::SetPosition(mover, -5.0f, y, z);
|
||||
}
|
||||
if (z > 5.0f) {
|
||||
ECS::SetPosition(mover, x, y, -5.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Input Action System Demo ===
|
||||
// Toggle light with jump action
|
||||
if (IsActionPressed("jump")) {
|
||||
lightToggle = !lightToggle;
|
||||
|
||||
if (lightToggle) {
|
||||
ECS::SetLightDirection(light, -0.5, 0.7, -0.3);
|
||||
Toast::Info("Light direction changed!");
|
||||
} else {
|
||||
ECS::SetLightDirection(light, 0.5, 0.7, 0.3);
|
||||
Toast::Info("Light direction reset!");
|
||||
}
|
||||
}
|
||||
|
||||
// Move player entity with input actions
|
||||
if (ECS::IsValid(player) && ECS::HasTransform(player)) {
|
||||
float x, y, z;
|
||||
ECS::GetPosition(player, x, y, z);
|
||||
float moveSpeed = 3.0f * dt;
|
||||
|
||||
if (IsActionDown("move_left")) {
|
||||
x -= moveSpeed;
|
||||
}
|
||||
if (IsActionDown("move_right")) {
|
||||
x += moveSpeed;
|
||||
}
|
||||
if (IsActionDown("move_up")) {
|
||||
z -= moveSpeed;
|
||||
}
|
||||
if (IsActionDown("move_down")) {
|
||||
z += moveSpeed;
|
||||
}
|
||||
|
||||
ECS::SetPosition(player, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Script to sync local docs/ to the Gitea wiki for this repo.
|
||||
# Requires SSH access to the wiki (deploy key or account key).
|
||||
|
||||
WIKI_REPO="git@gitea.appstack.me:nick/simian.wiki.git"
|
||||
|
||||
TMPDIR="$(mktemp -d)"
|
||||
echo "Cloning wiki to $TMPDIR"
|
||||
git clone "$WIKI_REPO" "$TMPDIR"
|
||||
|
||||
echo "Syncing docs/ -> wiki/"
|
||||
rsync -av --delete docs/ "$TMPDIR"/
|
||||
|
||||
pushd "$TMPDIR" > /dev/null
|
||||
git add -A
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to wiki"
|
||||
else
|
||||
git commit -m "Update wiki from docs"
|
||||
git push origin HEAD
|
||||
fi
|
||||
popd > /dev/null
|
||||
|
||||
rm -rf "$TMPDIR"
|
||||
echo "Done."
|
||||
+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);
|
||||
}
|
||||
+9
-12
@@ -3,23 +3,20 @@
|
||||
in vec3 fragNormal;
|
||||
in vec3 fragPosition;
|
||||
|
||||
uniform vec3 lightDir;
|
||||
uniform float bandCount;
|
||||
uniform vec4 albedo;
|
||||
uniform vec3 lightDir;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
// Normalize the normal and light direction
|
||||
vec3 normal = normalize(fragNormal);
|
||||
// Use uniform light direction, with fallback
|
||||
vec3 light = normalize(lightDir);
|
||||
|
||||
// Calculate the dot product for lighting
|
||||
float bandCount = 3.0;
|
||||
|
||||
vec3 normal = normalize(fragNormal);
|
||||
|
||||
float intensity = max(dot(normal, light), 0.0);
|
||||
|
||||
// Quantize the intensity for toon shading
|
||||
intensity = floor(intensity * bandCount) / bandCount;
|
||||
|
||||
// Set the fragment color
|
||||
//fragColor = vec4(vec3(intensity), 1.0);
|
||||
fragColor = vec4(albedo.rgb , albedo.a);
|
||||
intensity = max(intensity, 0.3); // Minimum visibility
|
||||
|
||||
fragColor = vec4(albedo.rgb * intensity, albedo.a);
|
||||
}
|
||||
+17
-7
@@ -1,17 +1,27 @@
|
||||
#version 330
|
||||
|
||||
// Raylib's default attribute locations
|
||||
layout(location = 0) in vec3 vertexPosition;
|
||||
layout(location = 1) in vec3 vertexNormal;
|
||||
layout(location = 1) in vec2 vertexTexCoord;
|
||||
layout(location = 2) in vec3 vertexNormal;
|
||||
layout(location = 3) in vec4 vertexColor;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
// Raylib's built-in uniforms (automatically provided)
|
||||
uniform mat4 mvp; // Model-View-Projection matrix
|
||||
uniform mat4 matModel; // Model matrix
|
||||
uniform mat4 matNormal; // Normal matrix for transforming normals
|
||||
|
||||
out vec3 fragNormal;
|
||||
out vec3 fragPosition;
|
||||
|
||||
void main() {
|
||||
fragPosition = vec3(model * vec4(vertexPosition, 1.0));
|
||||
fragNormal = mat3(transpose(inverse(model))) * vertexNormal; // Transform normals
|
||||
gl_Position = projection * view * vec4(fragPosition, 1.0);
|
||||
// Transform position
|
||||
vec4 worldPos = matModel * vec4(vertexPosition, 1.0);
|
||||
fragPosition = worldPos.xyz;
|
||||
|
||||
// Transform normal properly
|
||||
fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0)));
|
||||
|
||||
// Final position
|
||||
gl_Position = mvp * vec4(vertexPosition, 1.0);
|
||||
}
|
||||
+141
-157
@@ -1,5 +1,10 @@
|
||||
#include "Application.h"
|
||||
#include "ECSComponents.h"
|
||||
#include "scripting/ECSBindings.h"
|
||||
#include "scripting/ResourceBindings.h"
|
||||
#include "raylib.h"
|
||||
#include "rlgl.h"
|
||||
#include "raymath.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
@@ -18,10 +23,12 @@
|
||||
// On non-Windows platforms, define fopen_s as a macro for fopen.
|
||||
#define fopen_s(pFile, filename, mode) ((*(pFile) = fopen((filename), (mode))) == NULL)
|
||||
#endif
|
||||
|
||||
const char *Application::WINDOW_TITLE = "Simian";
|
||||
const char *Application::SCRIPT_FILE = "scripts/game.as";
|
||||
|
||||
Application::Application() : hotReload(nullptr), scriptCompilationError(false), logFile(nullptr), renderTexture{} // Initialize renderTexture
|
||||
Application::Application()
|
||||
: hotReload(nullptr), scriptCompilationError(false), logFile(nullptr), renderTexture{}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -32,36 +39,6 @@ Application::~Application()
|
||||
// Shutdown();
|
||||
}
|
||||
|
||||
/// Shader and rendering data for the demo scene
|
||||
Camera3D camera;
|
||||
RenderTexture2D sceneRT, maskRT;
|
||||
Shader toonShader, outlineShader;
|
||||
Model cube;
|
||||
|
||||
float globalBandCount = 3.0f;
|
||||
Vector3 lightDir = {0.5f, 0.7f, 0.3f};
|
||||
bool showEditor = true;
|
||||
bool dayMode = true;
|
||||
entt::registry ecs;
|
||||
std::vector<Model> models;
|
||||
struct ECS_Transform
|
||||
{
|
||||
Vector3 pos, scale;
|
||||
float rot;
|
||||
};
|
||||
struct Render
|
||||
{
|
||||
int modelId;
|
||||
float outlineSize;
|
||||
Color color;
|
||||
};
|
||||
struct ECS_Material
|
||||
{
|
||||
Shader shader;
|
||||
int lightLoc, bandLoc;
|
||||
};
|
||||
/// ======
|
||||
|
||||
bool Application::Initialize(int argc, char *argv[])
|
||||
{
|
||||
if (fopen_s(&logFile, "log.txt", "w") != 0)
|
||||
@@ -98,6 +75,20 @@ bool Application::Initialize(int argc, char *argv[])
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Set up input manager for script access
|
||||
extern void SetGlobalInputManager(InputManager*);
|
||||
SetGlobalInputManager(&inputManager);
|
||||
|
||||
// Setup default input bindings
|
||||
inputManager.SetupDefaultBindings();
|
||||
|
||||
// Initialize hot reload to watch the scripts directory so any script change
|
||||
// triggers a reload.
|
||||
{
|
||||
@@ -113,44 +104,13 @@ bool Application::Initialize(int argc, char *argv[])
|
||||
|
||||
if (enableEditor)
|
||||
{
|
||||
|
||||
guiManager.Initialize(this);
|
||||
|
||||
renderTexture = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
}
|
||||
|
||||
camera.position = {5, 5, 5};
|
||||
camera.target = {0, 0, 0};
|
||||
camera.up = {0, 1, 0};
|
||||
camera.fovy = 45;
|
||||
|
||||
// Assets
|
||||
cube = LoadModelFromMesh(GenMeshCube(1, 1, 1));
|
||||
models.push_back(cube);
|
||||
// Shaders (toon + outline)
|
||||
toonShader = LoadShader(0, "shaders/toon.fs");
|
||||
outlineShader = LoadShader(0, "shaders/outline.fs");
|
||||
|
||||
// Render textures
|
||||
sceneRT = LoadRenderTexture(1280, 720);
|
||||
maskRT = LoadRenderTexture(1280, 720);
|
||||
|
||||
// Cache uniform locations
|
||||
cube.materials[0].shader = toonShader;
|
||||
|
||||
return true;
|
||||
}
|
||||
void Application::SpawnCube()
|
||||
{
|
||||
auto e = ecs.create();
|
||||
ecs.emplace<ECS_Transform>(
|
||||
e,
|
||||
Vector3{static_cast<float>(rand() % 3 - 1), 0.0f, static_cast<float>(rand() % 3 - 1)}, // Position
|
||||
Vector3{1.0f, 1.0f, 1.0f}, // Scale
|
||||
0.0f // Rotation
|
||||
);
|
||||
ecs.emplace<Render>(e, 0, 2.0f, YELLOW);
|
||||
}
|
||||
|
||||
void Application::Run()
|
||||
{
|
||||
scriptEngine.CallScriptFunction(scriptEngine.GetInitFunction());
|
||||
@@ -189,24 +149,108 @@ void Application::Update(float deltaTime)
|
||||
// Call script Update function
|
||||
scriptEngine.CallScriptFunction(scriptEngine.GetUpdateFunction(), deltaTime);
|
||||
|
||||
UpdateCamera(&camera, CAMERA_ORBITAL);
|
||||
// Update ECS systems
|
||||
UpdateSystems(deltaTime);
|
||||
}
|
||||
|
||||
// Day/night toggle
|
||||
if (IsKeyPressed(KEY_SPACE))
|
||||
dayMode = !dayMode;
|
||||
globalBandCount = dayMode ? 3.0f : 2.0f;
|
||||
void Application::UpdateSystems(float deltaTime)
|
||||
{
|
||||
// Movement system - apply velocity to transform
|
||||
auto view = registry.view<ECSTransform, Velocity>();
|
||||
view.each([deltaTime](auto &transform, auto &velocity)
|
||||
{
|
||||
// Apply linear velocity
|
||||
transform.position.x += velocity.linear.x * deltaTime;
|
||||
transform.position.y += velocity.linear.y * deltaTime;
|
||||
transform.position.z += velocity.linear.z * deltaTime;
|
||||
|
||||
// Apply angular velocity
|
||||
transform.rotation += velocity.angular * deltaTime; });
|
||||
}
|
||||
|
||||
// F1 editor toggle
|
||||
if (IsKeyPressed(KEY_F1))
|
||||
showEditor = !showEditor;
|
||||
void Application::RenderScene()
|
||||
{
|
||||
// Query for active camera (first one found with Camera3DComponent)
|
||||
Camera3D camera = {};
|
||||
bool hasCamera = false;
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KEY_F2))
|
||||
SpawnCube();
|
||||
// Find the first light or use a default
|
||||
Vector3 lightDir = {0.5f, 0.7f, 0.3f};
|
||||
auto lightView = registry.view<LightComponent>();
|
||||
lightView.each([&](auto& lightComp) {
|
||||
lightDir = Vector3Normalize(lightComp.direction);
|
||||
});
|
||||
|
||||
BeginMode3D(camera);
|
||||
DrawGrid(10, 1);
|
||||
|
||||
// Render all entities with Transform and Sprite components
|
||||
auto view = registry.view<ECSTransform, Sprite>();
|
||||
view.each([&](auto& transform, auto& sprite) {
|
||||
if (sprite.modelId > 0) {
|
||||
Model* model = modelManager.Get(static_cast<unsigned int>(sprite.modelId));
|
||||
if (model) {
|
||||
// If sprite has a shader, apply it with uniforms
|
||||
if (sprite.shaderId > 0) {
|
||||
Shader* shader = shaderManager.Get(static_cast<unsigned int>(sprite.shaderId));
|
||||
if (shader) {
|
||||
// Set shader uniforms
|
||||
int albedoLoc = GetShaderLocation(*shader, "albedo");
|
||||
int lightDirLoc = GetShaderLocation(*shader, "lightDir");
|
||||
|
||||
if (albedoLoc != -1) {
|
||||
Vector4 albedo = {
|
||||
sprite.color.r / 255.0f,
|
||||
sprite.color.g / 255.0f,
|
||||
sprite.color.b / 255.0f,
|
||||
sprite.color.a / 255.0f
|
||||
};
|
||||
SetShaderValue(*shader, albedoLoc, &albedo, SHADER_UNIFORM_VEC4);
|
||||
}
|
||||
|
||||
if (lightDirLoc != -1) {
|
||||
SetShaderValue(*shader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
|
||||
}
|
||||
|
||||
// Assign shader to model's first material
|
||||
model->materials[0].shader = *shader;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw model using Sprite.color as the tint
|
||||
DrawModelEx(*model, transform.position, Vector3{0, 1, 0},
|
||||
transform.rotation * RAD2DEG, transform.scale,
|
||||
sprite.color);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
EndMode3D();
|
||||
}
|
||||
|
||||
void Application::Draw()
|
||||
{
|
||||
|
||||
int screenWidth = GetScreenWidth();
|
||||
int screenHeight = GetScreenHeight();
|
||||
|
||||
@@ -218,85 +262,26 @@ void Application::Draw()
|
||||
screenHeight = 600;
|
||||
}
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2((float)screenWidth, (float)screenHeight); // Set DisplaySize
|
||||
|
||||
BeginTextureMode(renderTexture);
|
||||
|
||||
ClearBackground(SKYBLUE);
|
||||
BeginMode3D(camera);
|
||||
DrawGrid(10, 1);
|
||||
DrawLine3D(Vector3{0, 0, 0}, Vector3{lightDir.x * 5.0f, lightDir.y * 5.0f, lightDir.z * 5.0f}, RED);
|
||||
BeginShaderMode(toonShader);
|
||||
SetShaderValue(toonShader, GetShaderLocation(toonShader, "lightDir"), &lightDir, SHADER_UNIFORM_VEC3);
|
||||
SetShaderValue(toonShader, GetShaderLocation(toonShader, "bandCount"), &globalBandCount, SHADER_UNIFORM_FLOAT);
|
||||
|
||||
auto view = ecs.view<ECS_Transform, Render>();
|
||||
view.each([&](const auto &t, const auto &r)
|
||||
{
|
||||
Vector4 color = {r.color.r / 255.0f, r.color.g / 255.0f, r.color.b / 255.0f, r.color.a / 255.0f};
|
||||
SetShaderValue(toonShader, GetShaderLocation(toonShader, "albedo"), &color, SHADER_UNIFORM_VEC4);
|
||||
DrawModel(models[r.modelId], t.pos, 1.0f, WHITE); // Use WHITE since color is handled by the shader
|
||||
});
|
||||
|
||||
EndShaderMode();
|
||||
|
||||
EndMode3D();
|
||||
// Call script Draw function
|
||||
scriptEngine.CallScriptFunction(scriptEngine.GetDrawFunction());
|
||||
|
||||
// 2. Silhouette mask
|
||||
// BeginTextureMode(maskRT);
|
||||
// ClearBackground(WHITE);
|
||||
// BeginMode3D(camera);
|
||||
|
||||
// for (auto e : ecs.view<ECS_Transform, Render>())
|
||||
// {
|
||||
// auto &t = ecs.get<ECS_Transform>(e);
|
||||
// DrawModel(models[ecs.get<Render>(e).modelId], t.pos, 1.0f, BLACK);
|
||||
// }
|
||||
|
||||
// EndMode3D();
|
||||
// EndTextureMode();
|
||||
|
||||
// // 3. Outline postprocess
|
||||
// BeginShaderMode(outlineShader);
|
||||
// Rectangle src = {0, 0, (float)maskRT.texture.width, (float)-maskRT.texture.height};
|
||||
// DrawTextureRec(maskRT.texture, src, {0, 0}, WHITE);
|
||||
EndShaderMode();
|
||||
EndTextureMode();
|
||||
if (enableEditor)
|
||||
{
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.DisplaySize = ImVec2((float)screenWidth, (float)screenHeight);
|
||||
|
||||
BeginTextureMode(renderTexture);
|
||||
ClearBackground(SKYBLUE);
|
||||
RenderScene();
|
||||
EndTextureMode();
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
guiManager.Render(renderTexture); // Render the GUI
|
||||
|
||||
// rlImGuiBegin();
|
||||
// ImGui::Begin("Comic Engine");
|
||||
// std::string fpsText = "FPS: " + std::to_string(GetFPS());
|
||||
// ImGui::Text(fpsText.c_str());
|
||||
// if (ImGui::Button("Spawn Cube"))
|
||||
// SpawnCube();
|
||||
// ImGui::Checkbox("Day Mode", &dayMode);
|
||||
// ImGui::SliderFloat("Band Count", &globalBandCount, 1.0f, 5.0f);
|
||||
// ImGui::ColorEdit3("Light Dir", &lightDir.x);
|
||||
// ImGui::Text("F1: Toggle editor");
|
||||
// ImGui::End();
|
||||
|
||||
// // Scene hierarchy
|
||||
// std::string sceneTitle = "Scene (" + std::to_string(ecs.view<ECS_Transform>().size()) + " entities)";
|
||||
// ImGui::Begin(sceneTitle.c_str());
|
||||
// for (auto e : ecs.view<ECS_Transform>())
|
||||
// {
|
||||
// if (ImGui::Button(std::string("Cube " + std::to_string((int)e)).c_str()))
|
||||
// {
|
||||
// ecs.get<ECS_Transform>(e).pos.x += 1.0f;
|
||||
// }
|
||||
// }
|
||||
// ImGui::End();
|
||||
// rlImGuiEnd();
|
||||
|
||||
guiManager.Render(renderTexture);
|
||||
EndDrawing();
|
||||
}
|
||||
else
|
||||
{
|
||||
BeginDrawing();
|
||||
ClearBackground(SKYBLUE);
|
||||
RenderScene();
|
||||
EndDrawing();
|
||||
}
|
||||
}
|
||||
@@ -309,6 +294,7 @@ void Application::Shutdown()
|
||||
hotReload = nullptr;
|
||||
}
|
||||
|
||||
|
||||
scriptEngine.Shutdown();
|
||||
if (enableEditor)
|
||||
{
|
||||
@@ -318,15 +304,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
|
||||
UnloadModel(cube);
|
||||
UnloadShader(toonShader);
|
||||
UnloadShader(outlineShader);
|
||||
UnloadRenderTexture(sceneRT);
|
||||
UnloadRenderTexture(maskRT);
|
||||
// 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,241 @@
|
||||
#include "InputManager.h"
|
||||
#include "log/log.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
InputManager::InputManager() {
|
||||
}
|
||||
|
||||
InputManager::~InputManager() {
|
||||
}
|
||||
|
||||
void InputManager::RegisterAction(const std::string& actionName) {
|
||||
if (actions.find(actionName) == actions.end()) {
|
||||
actions[actionName] = std::vector<InputBinding>();
|
||||
log_debug("Registered input action: %s", actionName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::UnregisterAction(const std::string& actionName) {
|
||||
actions.erase(actionName);
|
||||
}
|
||||
|
||||
bool InputManager::HasAction(const std::string& actionName) const {
|
||||
return actions.find(actionName) != actions.end();
|
||||
}
|
||||
|
||||
void InputManager::BindKeyboard(const std::string& actionName, int keyCode) {
|
||||
RegisterAction(actionName);
|
||||
actions[actionName].push_back(InputBinding(InputType::Keyboard, keyCode));
|
||||
log_debug("Bound key %d to action: %s", keyCode, actionName.c_str());
|
||||
}
|
||||
|
||||
void InputManager::BindMouseButton(const std::string& actionName, int mouseButton) {
|
||||
RegisterAction(actionName);
|
||||
actions[actionName].push_back(InputBinding(InputType::MouseButton, mouseButton));
|
||||
}
|
||||
|
||||
void InputManager::BindGamepadButton(const std::string& actionName, int gamepadId, int button) {
|
||||
RegisterAction(actionName);
|
||||
actions[actionName].push_back(InputBinding(InputType::GamepadButton, button, gamepadId));
|
||||
}
|
||||
|
||||
void InputManager::BindGamepadAxis(const std::string& actionName, int gamepadId, int axis, float threshold, bool negative) {
|
||||
RegisterAction(actionName);
|
||||
InputBinding binding(InputType::GamepadAxis, axis, gamepadId);
|
||||
binding.axisThreshold = threshold;
|
||||
binding.negative = negative;
|
||||
actions[actionName].push_back(binding);
|
||||
}
|
||||
|
||||
void InputManager::ClearBindings(const std::string& actionName) {
|
||||
if (HasAction(actionName)) {
|
||||
actions[actionName].clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool InputManager::IsActionPressed(const std::string& actionName) const {
|
||||
auto it = actions.find(actionName);
|
||||
if (it == actions.end()) return false;
|
||||
|
||||
for (const auto& binding : it->second) {
|
||||
if (IsBindingPressed(binding)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputManager::IsActionDown(const std::string& actionName) const {
|
||||
auto it = actions.find(actionName);
|
||||
if (it == actions.end()) return false;
|
||||
|
||||
for (const auto& binding : it->second) {
|
||||
if (IsBindingDown(binding)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputManager::IsActionReleased(const std::string& actionName) const {
|
||||
auto it = actions.find(actionName);
|
||||
if (it == actions.end()) return false;
|
||||
|
||||
for (const auto& binding : it->second) {
|
||||
if (IsBindingReleased(binding)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float InputManager::GetActionStrength(const std::string& actionName) const {
|
||||
auto it = actions.find(actionName);
|
||||
if (it == actions.end()) return 0.0f;
|
||||
|
||||
float maxStrength = 0.0f;
|
||||
for (const auto& binding : it->second) {
|
||||
float strength = GetBindingStrength(binding);
|
||||
if (std::abs(strength) > std::abs(maxStrength)) {
|
||||
maxStrength = strength;
|
||||
}
|
||||
}
|
||||
return maxStrength;
|
||||
}
|
||||
|
||||
bool InputManager::IsBindingPressed(const InputBinding& binding) const {
|
||||
switch (binding.type) {
|
||||
case InputType::Keyboard:
|
||||
return IsKeyPressed(binding.code);
|
||||
case InputType::MouseButton:
|
||||
return IsMouseButtonPressed(binding.code);
|
||||
case InputType::GamepadButton:
|
||||
if (IsGamepadAvailable(binding.gamepadId)) {
|
||||
return IsGamepadButtonPressed(binding.gamepadId, binding.code);
|
||||
}
|
||||
return false;
|
||||
case InputType::GamepadAxis: {
|
||||
// Axis "pressed" is harder to detect - simplified to just checking threshold
|
||||
if (IsGamepadAvailable(binding.gamepadId)) {
|
||||
float value = GetGamepadAxisMovement(binding.gamepadId, binding.code);
|
||||
return binding.negative ? value < -binding.axisThreshold : value > binding.axisThreshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputManager::IsBindingDown(const InputBinding& binding) const {
|
||||
switch (binding.type) {
|
||||
case InputType::Keyboard:
|
||||
return IsKeyDown(binding.code);
|
||||
case InputType::MouseButton:
|
||||
return IsMouseButtonDown(binding.code);
|
||||
case InputType::GamepadButton:
|
||||
if (IsGamepadAvailable(binding.gamepadId)) {
|
||||
return IsGamepadButtonDown(binding.gamepadId, binding.code);
|
||||
}
|
||||
return false;
|
||||
case InputType::GamepadAxis:
|
||||
if (IsGamepadAvailable(binding.gamepadId)) {
|
||||
float value = GetGamepadAxisMovement(binding.gamepadId, binding.code);
|
||||
return binding.negative ? value < -binding.axisThreshold : value > binding.axisThreshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputManager::IsBindingReleased(const InputBinding& binding) const {
|
||||
switch (binding.type) {
|
||||
case InputType::Keyboard:
|
||||
return IsKeyReleased(binding.code);
|
||||
case InputType::MouseButton:
|
||||
return IsMouseButtonReleased(binding.code);
|
||||
case InputType::GamepadButton:
|
||||
if (IsGamepadAvailable(binding.gamepadId)) {
|
||||
return IsGamepadButtonReleased(binding.gamepadId, binding.code);
|
||||
}
|
||||
return false;
|
||||
case InputType::GamepadAxis:
|
||||
return false; // Axes don't have a clear "released" state
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float InputManager::GetBindingStrength(const InputBinding& binding) const {
|
||||
switch (binding.type) {
|
||||
case InputType::Keyboard:
|
||||
return IsKeyDown(binding.code) ? 1.0f : 0.0f;
|
||||
case InputType::MouseButton:
|
||||
return IsMouseButtonDown(binding.code) ? 1.0f : 0.0f;
|
||||
case InputType::GamepadButton:
|
||||
if (IsGamepadAvailable(binding.gamepadId)) {
|
||||
return IsGamepadButtonDown(binding.gamepadId, binding.code) ? 1.0f : 0.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
case InputType::GamepadAxis:
|
||||
if (IsGamepadAvailable(binding.gamepadId)) {
|
||||
float value = GetGamepadAxisMovement(binding.gamepadId, binding.code);
|
||||
return binding.negative ? -value : value;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void InputManager::SetupDefaultBindings() {
|
||||
// Movement
|
||||
BindKeyboard("move_up", KEY_W);
|
||||
BindKeyboard("move_up", KEY_UP);
|
||||
BindKeyboard("move_down", KEY_S);
|
||||
BindKeyboard("move_down", KEY_DOWN);
|
||||
BindKeyboard("move_left", KEY_A);
|
||||
BindKeyboard("move_left", KEY_LEFT);
|
||||
BindKeyboard("move_right", KEY_D);
|
||||
BindKeyboard("move_right", KEY_RIGHT);
|
||||
|
||||
// Actions
|
||||
BindKeyboard("jump", KEY_SPACE);
|
||||
BindKeyboard("interact", KEY_E);
|
||||
BindKeyboard("attack", KEY_LEFT_CONTROL);
|
||||
BindMouseButton("attack", MOUSE_BUTTON_LEFT);
|
||||
|
||||
// Gamepad (if available)
|
||||
BindGamepadButton("jump", 0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN);
|
||||
BindGamepadButton("interact", 0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT);
|
||||
BindGamepadAxis("move_left", 0, GAMEPAD_AXIS_LEFT_X, 0.3f, true);
|
||||
BindGamepadAxis("move_right", 0, GAMEPAD_AXIS_LEFT_X, 0.3f, false);
|
||||
|
||||
log_info("Input manager: Default bindings configured");
|
||||
}
|
||||
|
||||
std::vector<std::string> InputManager::GetAllActions() const {
|
||||
std::vector<std::string> result;
|
||||
for (const auto& pair : actions) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::vector<InputBinding>& InputManager::GetBindings(const std::string& actionName) const {
|
||||
static std::vector<InputBinding> empty;
|
||||
auto it = actions.find(actionName);
|
||||
if (it != actions.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
void InputManager::LoadFromFile(const std::string& filepath) {
|
||||
// TODO: Implement config file loading (JSON or simple text format)
|
||||
log_info("Input config loading not yet implemented: %s", filepath.c_str());
|
||||
}
|
||||
|
||||
void InputManager::SaveToFile(const std::string& filepath) const {
|
||||
// TODO: Implement config file saving
|
||||
log_info("Input config saving not yet implemented: %s", filepath.c_str());
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
#include "scripting/InputBindings.h"
|
||||
#include "InputManager.h"
|
||||
#include <angelscript.h>
|
||||
#include <cassert>
|
||||
|
||||
static InputManager* g_inputManager = nullptr;
|
||||
|
||||
void SetGlobalInputManager(InputManager* inputManager) {
|
||||
g_inputManager = inputManager;
|
||||
}
|
||||
|
||||
// AngelScript wrapper functions
|
||||
static bool AS_IsActionPressed(const std::string& actionName) {
|
||||
return g_inputManager ? g_inputManager->IsActionPressed(actionName) : false;
|
||||
}
|
||||
|
||||
static bool AS_IsActionDown(const std::string& actionName) {
|
||||
return g_inputManager ? g_inputManager->IsActionDown(actionName) : false;
|
||||
}
|
||||
|
||||
static bool AS_IsActionReleased(const std::string& actionName) {
|
||||
return g_inputManager ? g_inputManager->IsActionReleased(actionName) : false;
|
||||
}
|
||||
|
||||
static float AS_GetActionStrength(const std::string& actionName) {
|
||||
return g_inputManager ? g_inputManager->GetActionStrength(actionName) : 0.0f;
|
||||
}
|
||||
|
||||
static void AS_RegisterAction(const std::string& actionName) {
|
||||
if (g_inputManager) g_inputManager->RegisterAction(actionName);
|
||||
}
|
||||
|
||||
static void AS_BindKeyboard(const std::string& actionName, int keyCode) {
|
||||
if (g_inputManager) g_inputManager->BindKeyboard(actionName, keyCode);
|
||||
}
|
||||
|
||||
static void AS_BindMouseButton(const std::string& actionName, int mouseButton) {
|
||||
if (g_inputManager) g_inputManager->BindMouseButton(actionName, mouseButton);
|
||||
}
|
||||
|
||||
static void AS_ClearBindings(const std::string& actionName) {
|
||||
if (g_inputManager) g_inputManager->ClearBindings(actionName);
|
||||
}
|
||||
|
||||
void RegisterInputBindings(asIScriptEngine* engine) {
|
||||
int r;
|
||||
|
||||
// Input action queries
|
||||
r = engine->RegisterGlobalFunction("bool IsActionPressed(const string &in)",
|
||||
asFUNCTION(AS_IsActionPressed), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("bool IsActionDown(const string &in)",
|
||||
asFUNCTION(AS_IsActionDown), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("bool IsActionReleased(const string &in)",
|
||||
asFUNCTION(AS_IsActionReleased), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("float GetActionStrength(const string &in)",
|
||||
asFUNCTION(AS_GetActionStrength), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// Allow runtime binding from scripts
|
||||
r = engine->RegisterGlobalFunction("void RegisterAction(const string &in)",
|
||||
asFUNCTION(AS_RegisterAction), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void BindKeyboard(const string &in, int)",
|
||||
asFUNCTION(AS_BindKeyboard), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void BindMouseButton(const string &in, int)",
|
||||
asFUNCTION(AS_BindMouseButton), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterGlobalFunction("void ClearBindings(const string &in)",
|
||||
asFUNCTION(AS_ClearBindings), asCALL_CDECL);
|
||||
assert(r >= 0);
|
||||
|
||||
// Key constants for binding
|
||||
r = engine->RegisterEnum("Key");
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "W", KEY_W);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "A", KEY_A);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "S", KEY_S);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "D", KEY_D);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "SPACE", KEY_SPACE);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "E", KEY_E);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "LEFT_CONTROL", KEY_LEFT_CONTROL);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "UP", KEY_UP);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "DOWN", KEY_DOWN);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "LEFT", KEY_LEFT);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("Key", "RIGHT", KEY_RIGHT);
|
||||
assert(r >= 0);
|
||||
|
||||
r = engine->RegisterEnum("MouseButton");
|
||||
assert(r >= 0);
|
||||
// Mouse button constants
|
||||
r = engine->RegisterEnumValue("MouseButton", "LEFT", MOUSE_BUTTON_LEFT);
|
||||
assert(r >= 0);
|
||||
r= engine->RegisterEnumValue("MouseButton", "RIGHT", MOUSE_BUTTON_RIGHT);
|
||||
assert(r >= 0);
|
||||
r = engine->RegisterEnumValue("MouseButton", "MIDDLE", MOUSE_BUTTON_MIDDLE);
|
||||
assert(r >= 0);
|
||||
}
|
||||
@@ -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("");
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
#include "scripting/ScriptBindings.h"
|
||||
#include "scripting/ToastBindings.h"
|
||||
#include "scripting/LogBindings.h"
|
||||
#include "scripting/DrawBindings.h"
|
||||
#include "scripting/ImageBindings.h"
|
||||
#include "scripting/TextureBindings.h"
|
||||
#include "scripting/MathBindings.h"
|
||||
#include "scripting/ECSBindings.h"
|
||||
#include "scripting/ResourceBindings.h"
|
||||
#include "scripting/InputBindings.h"
|
||||
|
||||
void ScriptBindings::RegisterAll(asIScriptEngine *engine)
|
||||
{
|
||||
// Delegate to separate binding files
|
||||
RegisterToastBindings(engine);
|
||||
RegisterLogBindings(engine);
|
||||
RegisterDrawBindings(engine);
|
||||
RegisterImageBindings(engine);
|
||||
RegisterTextureBindings(engine);
|
||||
RegisterMathBindings(engine);
|
||||
RegisterECSBindings(engine);
|
||||
RegisterResourceBindings(engine);
|
||||
RegisterInputBindings(engine);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "scripting/ScriptBindings.h"
|
||||
#include "scriptstdstring.h"
|
||||
#include "scriptbuilder.h"
|
||||
#include "scriptarray.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
@@ -11,7 +12,7 @@
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
ScriptEngine::ScriptEngine() : engine(nullptr), updateFunc(nullptr), drawFunc(nullptr), initFunc(nullptr), shutdownFunc(nullptr),currentModule(nullptr), hasValidScript(false) {
|
||||
ScriptEngine::ScriptEngine() : engine(nullptr), updateFunc(nullptr), initFunc(nullptr), shutdownFunc(nullptr),currentModule(nullptr), hasValidScript(false) {
|
||||
}
|
||||
|
||||
ScriptEngine::~ScriptEngine() {
|
||||
@@ -28,6 +29,9 @@ bool ScriptEngine::Initialize() {
|
||||
// Register std::string
|
||||
RegisterStdString(engine);
|
||||
|
||||
// Register array template
|
||||
RegisterScriptArray(engine, true);
|
||||
|
||||
// Register script bindings
|
||||
ScriptBindings::RegisterAll(engine);
|
||||
|
||||
@@ -164,7 +168,6 @@ bool ScriptEngine::CompileScript(const std::string& filename) {
|
||||
// Cache new functions
|
||||
initFunc = currentModule->GetFunctionByName("Init");
|
||||
updateFunc = currentModule->GetFunctionByName("Update");
|
||||
drawFunc = currentModule->GetFunctionByName("Draw");
|
||||
shutdownFunc = currentModule->GetFunctionByName("Shutdown");
|
||||
hasValidScript = true;
|
||||
log_info("Script compiled and cached: %s", filename.c_str());
|
||||
@@ -199,7 +202,6 @@ void ScriptEngine::CallScriptFunction(asIScriptFunction* func, float dt) {
|
||||
|
||||
void ScriptEngine::ClearCachedFunctions() {
|
||||
updateFunc = nullptr;
|
||||
drawFunc = nullptr;
|
||||
hasValidScript = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,16 @@
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
Color ColorFromUInt(unsigned int c);
|
||||
// Utility function to convert uint color to Raylib Color
|
||||
Color ColorFromUInt(unsigned int c)
|
||||
{
|
||||
Color col;
|
||||
col.r = (c >> 24) & 0xFF;
|
||||
col.g = (c >> 16) & 0xFF;
|
||||
col.b = (c >> 8) & 0xFF;
|
||||
col.a = c & 0xFF;
|
||||
return col;
|
||||
}
|
||||
|
||||
Texture2D AS_LoadTexture(const std::string &filename)
|
||||
{
|
||||
|
||||
@@ -2,25 +2,30 @@
|
||||
#include "gui/Toast.h"
|
||||
#include <angelscript.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <string>
|
||||
#include "../log/log.h"
|
||||
// Toast binding functions
|
||||
static void AS_ToastInfo(const std::string& message)
|
||||
{
|
||||
log_info("%s", message.c_str());
|
||||
Toast::Info(message);
|
||||
}
|
||||
|
||||
static void AS_ToastWarning(const std::string& message)
|
||||
{
|
||||
log_warn("%s", message.c_str());
|
||||
Toast::Warning(message);
|
||||
}
|
||||
|
||||
static void AS_ToastError(const std::string& message)
|
||||
{
|
||||
log_error("%s", message.c_str());
|
||||
Toast::Error(message);
|
||||
}
|
||||
|
||||
static void AS_ToastSuccess(const std::string& message)
|
||||
{
|
||||
log_info("%s", message.c_str());
|
||||
Toast::Success(message);
|
||||
}
|
||||
|
||||
|
||||
+14
-28
@@ -1,43 +1,29 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Simple unit test executable that compiles acutest.c directly
|
||||
# Unit tests just need test sources + link to simian_core
|
||||
add_executable(unit_tests
|
||||
test.cpp
|
||||
hot_reload.cpp
|
||||
script_engine_tests.cpp
|
||||
math_bindings_tests.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/HotReload.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/ScriptEngine.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/ScriptBindings.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/ToastBindings.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/LogBindings.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/scripting/DrawBindings.cpp
|
||||
${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/log/log.c
|
||||
)
|
||||
|
||||
target_include_directories(unit_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/tests
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/src/log
|
||||
${CMAKE_SOURCE_DIR}/external/angelscript/sdk/angelscript/include
|
||||
${CMAKE_SOURCE_DIR}/external/angelscript/sdk/add_on/scriptstdstring
|
||||
${CMAKE_SOURCE_DIR}/external/angelscript/sdk/add_on/scriptbuilder
|
||||
${CMAKE_SOURCE_DIR}/external/raylib/src
|
||||
${CMAKE_SOURCE_DIR}/external/imgui
|
||||
${CMAKE_SOURCE_DIR}/external/rlImGui
|
||||
|
||||
${CMAKE_SOURCE_DIR}/external/acutest
|
||||
)
|
||||
|
||||
# Link AngelScript and its add-ons needed by ScriptEngine
|
||||
target_link_libraries(unit_tests PRIVATE
|
||||
angelscript
|
||||
scriptstdstring
|
||||
scriptbuilder
|
||||
raylib
|
||||
imgui
|
||||
)
|
||||
# Link the shared simian_core library - that's it!
|
||||
target_link_libraries(unit_tests PRIVATE simian_core)
|
||||
|
||||
add_test(NAME unit_tests COMMAND unit_tests)
|
||||
# Platform-specific linking for tests too
|
||||
if(WIN32)
|
||||
target_link_libraries(unit_tests PRIVATE winmm gdi32)
|
||||
endif()
|
||||
|
||||
add_test(NAME unit_tests COMMAND unit_tests)
|
||||
add_test(NAME hotreload_dir COMMAND unit_tests hotreload_dir)
|
||||
add_test(NAME script_compile COMMAND unit_tests script_compile)
|
||||
add_test(NAME script_include COMMAND unit_tests script_include)
|
||||
add_test(NAME math_bindings COMMAND unit_tests math_bindings)
|
||||
Reference in New Issue
Block a user