Compare commits
No commits in common. "dev/variant-engine" and "dev/upgrade-015" have entirely different histories.
dev/varian
...
dev/upgrad
169
CLAUDE.md
169
CLAUDE.md
|
|
@ -1,169 +0,0 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Backlog is a game engine written in Zig (version 0.14) with a modular architecture. The engine supports cross-platform development with Windows, macOS, and Linux targets, and includes support for both static and dynamic module loading.
|
||||
|
||||
## Build System
|
||||
|
||||
The project uses Zig's build system with a custom `BuildSystem` wrapper:
|
||||
|
||||
### Key Build Commands
|
||||
|
||||
- `zig build` - Build the engine and default projects
|
||||
- `zig build -Dcookshaders` - Build with shader compilation enabled
|
||||
- `zig build -Dstatic_build` - Force static linking (required for Linux)
|
||||
- `zig build tools` - Install development tools (gltf2ozz, spirv-reflect)
|
||||
- `zig build gltf2ozz -- [args]` - Run GLTF animation converter
|
||||
- `zig build spv-reflect -- [args]` - Run SPIRV reflection tool
|
||||
- `python tools/scripts/cookShaders.py` - Compile shaders from HLSL to SPIR-V, MSL, and DXIL formats, must be run after editing shader files
|
||||
|
||||
### Project-Specific Commands
|
||||
|
||||
From the `projects/` directory:
|
||||
- `zig build` - Build sample game and tools
|
||||
- `zig build run-sampleGame` - Run the sample game
|
||||
- `zig build run-newProject` - Run the project creation tool
|
||||
|
||||
### Testing
|
||||
|
||||
- `zig build test` - Run all tests (individual modules have their own test files in `tests/` subdirectories)
|
||||
|
||||
### Setup Commands
|
||||
|
||||
- `python tools/scripts/first-time-setup.py` - Initial setup script
|
||||
|
||||
## Architecture
|
||||
|
||||
### Engine Modules
|
||||
|
||||
The engine is organized into these core modules:
|
||||
|
||||
- **core** - Foundation systems (ECS, logging, memory tracking, jobs, scripting)
|
||||
- **platform** - Cross-platform windowing and input handling
|
||||
- **assets** - Asset loading and management system
|
||||
- **rend** - 3D rendering system with mesh, camera, and material support
|
||||
- **audio** - Sound engine integration
|
||||
- **ui** - User interface rendering system
|
||||
- **papyrus** - Text rendering and UI primitives
|
||||
- **imgui** - Debug UI integration
|
||||
- **physics** - Physics engine integration (Jolt)
|
||||
- **sys** - System utilities and subprocess management
|
||||
|
||||
### Key Directories
|
||||
|
||||
- `engine/` - Core engine modules, each with their own `build.zig`
|
||||
- `projects/` - Sample projects and games
|
||||
- `lib/` - Third-party dependencies and wrappers
|
||||
- `tools/` - Development utilities and scripts
|
||||
- `content/` - Game assets and resources (located at `projects/content/`)
|
||||
- `extras/` - Optional engine extensions
|
||||
|
||||
### Engine Initialization
|
||||
|
||||
The engine uses a spec-based initialization system where modules are conditionally enabled:
|
||||
|
||||
```zig
|
||||
// Programs define which modules to enable
|
||||
sampleGame.setModuleEnabled("imgui", true);
|
||||
sampleGame.setModuleEnabled("physics", true);
|
||||
```
|
||||
|
||||
### Dynamic Modules
|
||||
|
||||
The engine supports dynamic module loading for game-specific code:
|
||||
|
||||
```zig
|
||||
const externGame = sampleGame.addDynamicModule("externGame", b.path("sampleGame/externGame/externGame.zig"));
|
||||
```
|
||||
|
||||
### Shader Pipeline
|
||||
|
||||
Shaders are written in HLSL and compiled to multiple targets:
|
||||
- SPIR-V for Vulkan
|
||||
- MSL for Metal (macOS)
|
||||
- DXIL for DirectX
|
||||
|
||||
The shader compilation system automatically discovers `.hlsl` files in `engine/*/shaders/` directories.
|
||||
|
||||
## Development Notes
|
||||
|
||||
- All memory allocations go through a centralized `MemoryTracker` for leak detection
|
||||
- The engine uses Tracy for profiling when enabled
|
||||
- Lua scripting is integrated for game logic
|
||||
- The build system generates API wrappers automatically for enabled modules
|
||||
- Content directory location is determined by `content.txt` file pointing to `projects/content/`
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
- Adding new engine modules: Create in `engine/` with `build.zig` and add to `engineDepList`
|
||||
- Creating new projects: Use the project template in `projects/minimal/`
|
||||
- Shader development: Add HLSL files to module `shaders/` directories, run `cookShaders.py`
|
||||
- Asset pipeline: Assets go in `projects/content/` and use `.cook` extensions for processed assets
|
||||
|
||||
## Issue Tracking with git-bug
|
||||
|
||||
This project uses git-bug for distributed issue tracking. Bug data is stored directly in the git repository. While named "git-bug", it can track all types of development work including bugs, features, tasks, and documentation.
|
||||
|
||||
### Common Commands
|
||||
|
||||
- `git bug bug` - List all issues
|
||||
- `git bug bug new -t "title" -m "message"` - Create a new issue with title and message
|
||||
- `git bug bug show <id>` - Display issue details
|
||||
- `git bug bug comment <id>` - Add a comment to an issue
|
||||
- `git bug bug status <id>` - Display issue status
|
||||
- `git bug bug label new <id> <label>` - Add a label to an issue
|
||||
- `git bug bug label rm <id> <label>` - Remove a label from an issue
|
||||
- `git bug bug label <id>` - Display labels for an issue
|
||||
- `git bug bug status:open` - List only open issues
|
||||
- `git bug pull` - Pull issue updates from remote
|
||||
- `git bug push` - Push issue updates to remote
|
||||
|
||||
### Workflow
|
||||
|
||||
- Issues are stored in the repository and sync with `git bug pull`/`git bug push`
|
||||
- Issue IDs can be abbreviated to the first few characters
|
||||
- Use labels to categorize issues by both type and component
|
||||
- Use `--non-interactive` flag for scripting
|
||||
- Keep issue descriptions factual and clear
|
||||
|
||||
### Standard Labels
|
||||
|
||||
Use these standard labels to categorize issues:
|
||||
|
||||
- **Component labels**: `core`, `rendering`, `physics`, `build-system`, `platform`, `assets`, `audio`, `ui`, `documentation`
|
||||
- **Issue type labels**:
|
||||
- `bug` - Defects, errors, or incorrect behavior
|
||||
- `feature` - New functionality to implement
|
||||
- `enhancement` - Improvements to existing features
|
||||
- `task` - General development work items
|
||||
- `documentation` - Documentation improvements or additions
|
||||
- `question` - Design decisions or technical discussions
|
||||
- `refactoring` - Code cleanup and restructuring
|
||||
- **Problem type labels** (for bugs):
|
||||
- `memory` - Memory allocation, leaks, or performance issues
|
||||
- `threading` - Job system, parallelization, race conditions, or deadlocks
|
||||
- `crash` - Application crashes or critical failures
|
||||
- `build` - Build system or compilation issues
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Create a feature request
|
||||
git bug bug new -t "Add procedural terrain generation" -m "Implement heightmap-based terrain system"
|
||||
git bug bug label new <id> feature rendering
|
||||
|
||||
# Create a task
|
||||
git bug bug new -t "Update to Zig 0.15" -m "Migrate codebase to latest Zig version"
|
||||
git bug bug label new <id> task build-system
|
||||
|
||||
# Create a documentation issue
|
||||
git bug bug new -t "Document shader pipeline" -m "Add comprehensive guide for shader development workflow"
|
||||
git bug bug label new <id> documentation rendering
|
||||
|
||||
# Create a bug report
|
||||
git bug bug new -t "Memory leak in asset loader" -m "Assets not freed when unloading scenes"
|
||||
git bug bug label new <id> bug assets memory
|
||||
```
|
||||
92
README.md
92
README.md
|
|
@ -1,100 +1,18 @@
|
|||
# Backlog
|
||||
|
||||
|
||||
zig version: 0.15.1
|
||||
|
||||
Backlog Labs Game Engine.
|
||||
|
||||
## Getting Started
|
||||
|
||||
run tools/scripts/first-time-setup.py
|
||||
|
||||
ffmpeg -i INPUT.mp4 -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 OUTPUT.ogv
|
||||
|
||||
## git-bug Cheat Sheet
|
||||
|
||||
This project uses git-bug for distributed issue tracking. Issues are stored directly in the repository.
|
||||
|
||||
### Common Commands
|
||||
|
||||
**Listing Issues**
|
||||
```bash
|
||||
git bug bug # List all issues
|
||||
git bug bug status:open # List only open issues
|
||||
git bug bug status:closed # List only closed issues
|
||||
```
|
||||
|
||||
**Creating Issues**
|
||||
```bash
|
||||
git bug bug new -t "title" -m "message" # Create new issue
|
||||
git bug bug new -t "Add feature X" -m "Description" # Example
|
||||
```
|
||||
|
||||
**Viewing & Managing Issues**
|
||||
```bash
|
||||
git bug bug show <id> # Show issue details
|
||||
git bug bug comment <id> # Add a comment to an issue
|
||||
git bug bug close <id> # Close an issue
|
||||
git bug bug open <id> # Reopen an issue
|
||||
git bug bug status <id> # Show issue status
|
||||
```
|
||||
|
||||
**Labels**
|
||||
```bash
|
||||
git bug bug label <id> # Show labels for an issue
|
||||
git bug bug label new <id> <label> # Add a label
|
||||
git bug bug label rm <id> <label> # Remove a label
|
||||
```
|
||||
|
||||
**Syncing**
|
||||
```bash
|
||||
git bug pull # Pull issue updates from remote
|
||||
git bug push # Push issue updates to remote
|
||||
```
|
||||
|
||||
### Standard Labels
|
||||
|
||||
**Component Labels**: `core`, `rendering`, `physics`, `build-system`, `platform`, `assets`, `audio`, `ui`, `documentation`
|
||||
|
||||
**Type Labels**:
|
||||
- `bug` - Defects or incorrect behavior
|
||||
- `feature` - New functionality
|
||||
- `enhancement` - Improvements to existing features
|
||||
- `task` - General development work
|
||||
- `documentation` - Documentation improvements
|
||||
- `question` - Design decisions or discussions
|
||||
- `refactoring` - Code cleanup
|
||||
|
||||
**Problem Type Labels** (for bugs):
|
||||
- `memory` - Memory issues
|
||||
- `threading` - Concurrency issues
|
||||
- `crash` - Application crashes
|
||||
- `build` - Build system issues
|
||||
|
||||
### Quick Examples
|
||||
|
||||
```bash
|
||||
# Create a bug report
|
||||
git bug bug new -t "Memory leak in asset loader" -m "Assets not freed when unloading scenes"
|
||||
git bug bug label new <id> bug assets memory
|
||||
|
||||
# Create a feature request
|
||||
git bug bug new -t "Add terrain generation" -m "Implement heightmap-based terrain"
|
||||
git bug bug label new <id> feature rendering
|
||||
|
||||
# Create a task
|
||||
git bug bug new -t "Update to Zig 0.15" -m "Migrate to latest Zig version"
|
||||
git bug bug label new <id> task build-system
|
||||
|
||||
# View and comment on an issue
|
||||
git bug bug show abc123
|
||||
git bug bug comment abc123
|
||||
```
|
||||
|
||||
### Tips
|
||||
|
||||
- Issue IDs can be abbreviated (first few characters)
|
||||
- Use `--non-interactive` flag for scripting
|
||||
- Issues sync with `git bug pull/push`
|
||||
- Keep descriptions factual and clear
|
||||
/// --------------------------------------------------------
|
||||
void* malloc(size_t size); // gives you a pointer to a memory buffer of size
|
||||
void free(void* ptr); // releases a pointer to memory
|
||||
/// --------------------------------------------------------
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,662 +0,0 @@
|
|||
// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
|
||||
b: *std.Build,
|
||||
nw_builder: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
nw_mod: *std.Build.Module,
|
||||
gltf2ozz: ozz.GltfToOzz,
|
||||
options: *std.Build.Step.Options,
|
||||
cookShaders: bool,
|
||||
|
||||
backlogRoot: []const u8,
|
||||
// list of all shaders discovered under
|
||||
// content/_shaders/def
|
||||
reflectShaderPathList: [][]u8 = undefined,
|
||||
|
||||
staticBuild: bool = false,
|
||||
|
||||
nwdep: *std.Build.Dependency,
|
||||
apigen: *std.Build.Step.Compile,
|
||||
shaderEmbedGen: *std.Build.Step.Compile,
|
||||
loadDynamicsGen: *std.Build.Step.Compile,
|
||||
rcGen: *std.Build.Step.Compile,
|
||||
generatedApis: std.StringHashMapUnmanaged(*std.Build.Module) = .{},
|
||||
|
||||
const engineDepList = [_][]const u8{
|
||||
"assets",
|
||||
"audio",
|
||||
"core",
|
||||
"net",
|
||||
"papyrus",
|
||||
"platform",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"physics",
|
||||
"sys",
|
||||
};
|
||||
|
||||
const BuildSystem = @This();
|
||||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const LazyPath = Build.LazyPath;
|
||||
|
||||
const ozz = @import("ozz");
|
||||
|
||||
pub const InitOptions = struct {
|
||||
import_name: []const u8 = "Backlog",
|
||||
backlogRoot: []const u8 = "./BacklogEngine",
|
||||
staticBuild: bool = false,
|
||||
target: Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
};
|
||||
|
||||
pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
|
||||
var buildOpts = declareBuildOptions(b);
|
||||
|
||||
if (opts.target.result.os.tag == .linux) {
|
||||
// using a static build for linux... object loading hell is not fun
|
||||
std.debug.print("Important! only supporting static builds for linux", .{});
|
||||
buildOpts.static_build = true;
|
||||
}
|
||||
|
||||
const nwdep = b.dependency(opts.import_name, .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.static_build = buildOpts.static_build,
|
||||
});
|
||||
|
||||
var self = BuildSystem{
|
||||
.b = b,
|
||||
.nw_builder = nwdep.builder,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.nw_mod = nwdep.module("Backlog"),
|
||||
.backlogRoot = opts.backlogRoot,
|
||||
.options = createGameOptions(b, buildOpts),
|
||||
.gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}),
|
||||
.cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false,
|
||||
|
||||
.staticBuild = buildOpts.static_build,
|
||||
.nwdep = nwdep,
|
||||
.apigen = nwdep.artifact("backlog-apigen"),
|
||||
.loadDynamicsGen = nwdep.artifact("backlog-generate-loadDynamics"),
|
||||
.rcGen = nwdep.artifact("backlog-generate-rc"),
|
||||
.shaderEmbedGen = nwdep.artifact("backlog-shaderEmbedGen"),
|
||||
};
|
||||
|
||||
const exeList = [1]*std.Build.Step.Compile{self.gltf2ozz.exe};
|
||||
const install_tools = b.step("tools", "installs tools needed to generate outputs for the engine");
|
||||
for (exeList) |exe| {
|
||||
const toolsInstall = b.addInstallArtifact(exe, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "tools" } },
|
||||
});
|
||||
|
||||
install_tools.dependOn(&toolsInstall.step);
|
||||
}
|
||||
|
||||
{
|
||||
const runArtifact = b.addRunArtifact(self.gltf2ozz.exe);
|
||||
if (b.args) |args| {
|
||||
runArtifact.addArgs(args);
|
||||
}
|
||||
|
||||
const run_exe = b.step("gltf2ozz", "runs the gltf animation converter.");
|
||||
run_exe.dependOn(&runArtifact.step);
|
||||
}
|
||||
|
||||
self.addDependencyInstalls(self.b, .ReleaseFast);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub const AddProgramOptions = struct {
|
||||
name: []const u8,
|
||||
desc: []const u8,
|
||||
root_source_file: LazyPath,
|
||||
imports: []const Build.Module.Import = &.{},
|
||||
};
|
||||
|
||||
pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Module {
|
||||
const b = self.b;
|
||||
|
||||
const exe = self.nw_builder.addExecutable(.{
|
||||
.name = opts.name,
|
||||
.root_module = b.createModule(.{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = self.nw_builder.path("engine/main.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
const runArtifact = b.addRunArtifact(exe);
|
||||
if (b.args) |args| {
|
||||
runArtifact.addArgs(args);
|
||||
}
|
||||
const run_exe = b.step(self.b.fmt("run-{s}", .{opts.name}), opts.desc);
|
||||
run_exe.dependOn(&runArtifact.step);
|
||||
|
||||
// main path = name/main.zig
|
||||
const mod = b.addModule(opts.name, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = opts.root_source_file,
|
||||
.imports = opts.imports,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("main", mod);
|
||||
// todo.. remove this one and see what happens
|
||||
exe.root_module.addImport("core", self.nwdep.module("core"));
|
||||
// mod.addImport("Backlog", self.nw_mod);
|
||||
exe.root_module.addOptions("BacklogOptions", self.options);
|
||||
|
||||
if (self.cookShaders) {
|
||||
const cookShadersScript = b.fmt("{s}/tools/scripts/cookShaders.py", .{self.backlogRoot});
|
||||
const cookShadersCommand = b.addSystemCommand(&[_][]const u8{"python"});
|
||||
cookShadersCommand.addArg(cookShadersScript);
|
||||
|
||||
run_exe.dependOn(&cookShadersCommand.step);
|
||||
}
|
||||
|
||||
b.getInstallStep().dependOn(self.nw_builder.getInstallStep());
|
||||
|
||||
// run_exe.dependOn(b.getInstallStep());
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub const BuildOptions = struct {
|
||||
mutex_job_queue: bool,
|
||||
static_build: bool,
|
||||
zero_logging: bool,
|
||||
slow_logging: bool,
|
||||
force_mailbox: bool,
|
||||
};
|
||||
|
||||
pub fn declareBuildOptions(b: *std.Build) BuildOptions {
|
||||
return .{
|
||||
.mutex_job_queue = b.option(bool, "mutex_job_queue", "temporary test, reverts to old mutex based queue behaviour in jobs.zig:JobManager") orelse false,
|
||||
.static_build = b.option(bool, "static_build", "builds the entire game as a single executable") orelse false,
|
||||
.zero_logging = b.option(bool, "zero_logging", "disables all logging, only intended for use on job dispatch testing") orelse false,
|
||||
.slow_logging = b.option(bool, "slow_logging", "Disables buffered logging, takes a hit to performance but gain timing information on logging") orelse false,
|
||||
.force_mailbox = b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn createGameOptions(b: *std.Build, options: BuildOptions) *std.Build.Step.Options {
|
||||
const opts = b.addOptions();
|
||||
|
||||
inline for (@typeInfo(BuildOptions).@"struct".fields) |field| {
|
||||
opts.addOption(bool, field.name, @field(options, field.name));
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
pub fn addExtraModule(self: *BuildSystem, mod: *std.Build.Module, moduleName: []const u8) void {
|
||||
const dep = self.b.dependency(moduleName, .{ .target = self.target, .optimize = self.optimize, .static_build = self.staticBuild });
|
||||
mod.addImport(moduleName, dep.module(moduleName));
|
||||
}
|
||||
|
||||
pub fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
|
||||
//if (self.staticBuild) {
|
||||
//return;
|
||||
// }
|
||||
|
||||
inline for (DynamicDepList) |d| {
|
||||
b.installArtifact(b.dependency(
|
||||
d.dep,
|
||||
.{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
|
||||
).artifact(d.artifact));
|
||||
}
|
||||
|
||||
// b.installArtifact(b.dependency(
|
||||
// "sdl3",
|
||||
// .{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
|
||||
// ).artifact("SDL3"));
|
||||
}
|
||||
|
||||
const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } = &.{
|
||||
.{ .dep = "sdl3", .artifact = "SDL3" },
|
||||
.{ .dep = "spng", .artifact = "spng_c" },
|
||||
.{ .dep = "lua", .artifact = "luac" },
|
||||
.{ .dep = "miniaudio", .artifact = "miniaudio_c" },
|
||||
.{ .dep = "zphysics", .artifact = "joltc" },
|
||||
// .{ .dep = "enet", .artifact = "enet_c" },
|
||||
.{ .dep = "ozz", .artifact = "ozz_cpp" },
|
||||
};
|
||||
|
||||
// all other modules are disabled by default
|
||||
pub const defaultEnabledModules: []const []const u8 = &.{
|
||||
"core",
|
||||
"assets",
|
||||
"platform",
|
||||
"rend",
|
||||
};
|
||||
|
||||
pub const moduleOrder: []const []const u8 = &.{
|
||||
"core",
|
||||
"sys",
|
||||
"assets",
|
||||
"platform",
|
||||
"net",
|
||||
"physics",
|
||||
"audio",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"papyrus",
|
||||
};
|
||||
|
||||
pub const DynamicModule = struct {
|
||||
name: []const u8,
|
||||
allocator: std.mem.Allocator,
|
||||
buildSystem: *BuildSystem,
|
||||
programName: []const u8,
|
||||
opts: AddProgramOptions,
|
||||
|
||||
extras: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
|
||||
library: ?*std.Build.Step.Compile = null,
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn init(name: []const u8, p: *Program) *@This() {
|
||||
var self: *@This() = p.allocator.create(@This()) catch @panic("out of memory");
|
||||
|
||||
self.* = .{
|
||||
.opts = p.opts,
|
||||
.programName = p.opts.name,
|
||||
.buildSystem = p.buildSystem,
|
||||
.allocator = p.allocator,
|
||||
.name = name,
|
||||
};
|
||||
|
||||
for (p.gameModules.items) |module| {
|
||||
self.gameModules.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
for (p.extras.items) |module| {
|
||||
self.extras.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Step.Compile {
|
||||
if (self.library) |lib| {
|
||||
return lib;
|
||||
}
|
||||
|
||||
const b = self.buildSystem.b;
|
||||
const static = self.buildSystem.staticBuild;
|
||||
const lib = b.addLibrary(.{
|
||||
.name = self.name,
|
||||
.linkage = if (static) .static else .dynamic,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = self.opts.root_source_file,
|
||||
.link_libc = true,
|
||||
.optimize = self.buildSystem.optimize,
|
||||
.target = self.buildSystem.target,
|
||||
}),
|
||||
});
|
||||
|
||||
const allocator = self.buildSystem.b.allocator;
|
||||
|
||||
var modlist = std.ArrayList([]const u8){};
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(allocator, mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(lib.root_module, extra.name);
|
||||
}
|
||||
|
||||
lib.root_module.addImport("backlog", self.buildSystem.generateApi(self.name, modlist.items, null));
|
||||
lib.root_module.addOptions("BacklogOptions", self.buildSystem.options);
|
||||
|
||||
if (static) {
|
||||
// generateApi should create static callers for the main program
|
||||
} else {
|
||||
const installExtern = b.addInstallArtifact(lib, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "modules" } },
|
||||
});
|
||||
b.getInstallStep().dependOn(&installExtern.step);
|
||||
}
|
||||
|
||||
return lib;
|
||||
}
|
||||
};
|
||||
|
||||
pub const GameModule = struct {
|
||||
name: []const u8,
|
||||
enabled: bool = true,
|
||||
};
|
||||
|
||||
pub const Program = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
opts: AddProgramOptions,
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
extras: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
dynamicModules: std.ArrayListUnmanaged(*DynamicModule) = .{},
|
||||
buildSystem: *BuildSystem,
|
||||
|
||||
iconPath: ?std.Build.LazyPath = null,
|
||||
|
||||
pub fn setIconPath(self: *@This(), path: []const u8) void {
|
||||
if (self.iconPath != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.iconPath = self.buildSystem.generateRc(self.opts.name, path) catch return;
|
||||
}
|
||||
|
||||
pub fn setModuleEnabled(self: *@This(), module: []const u8, enable: bool) void {
|
||||
for (self.gameModules.items) |*m| {
|
||||
if (std.mem.eql(u8, m.name, module)) {
|
||||
m.enabled = enable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// module not found, add it here.
|
||||
self.gameModules.append(self.allocator, .{ .name = module, .enabled = enable }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addDynamicModule(self: *@This(), module: []const u8, root_source_file: std.Build.LazyPath) *DynamicModule {
|
||||
const dynamicModule = DynamicModule.init(module, self);
|
||||
dynamicModule.opts.root_source_file = root_source_file;
|
||||
self.dynamicModules.append(self.allocator, dynamicModule) catch @panic("out of memory");
|
||||
|
||||
return dynamicModule;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Module {
|
||||
const exe = self.buildSystem.addProgram(self.opts);
|
||||
const allocator = self.buildSystem.b.allocator;
|
||||
|
||||
var modlist = std.ArrayList([]const u8){};
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(allocator, mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(exe, extra.name);
|
||||
}
|
||||
|
||||
exe.addImport("backlog", self.buildSystem.generateApi(self.opts.name, modlist.items, self.dynamicModules.items));
|
||||
|
||||
if (self.buildSystem.target.result.os.tag == .windows) {
|
||||
if (self.iconPath) |ip| {
|
||||
exe.addWin32ResourceFile(.{
|
||||
.file = ip,
|
||||
.flags = &.{},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn program(self: *BuildSystem, opts: AddProgramOptions) *Program {
|
||||
const prog = self.b.allocator.create(Program) catch unreachable;
|
||||
|
||||
prog.* = .{
|
||||
.allocator = self.b.allocator,
|
||||
.opts = opts,
|
||||
.buildSystem = self,
|
||||
};
|
||||
|
||||
for (moduleOrder) |module| {
|
||||
prog.setModuleEnabled(module, false);
|
||||
}
|
||||
|
||||
for (defaultEnabledModules) |module| {
|
||||
prog.setModuleEnabled(module, true);
|
||||
}
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
// ========= standalone build instance =======
|
||||
// maybe the default should be like an engine launcher/project launcher or something
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false;
|
||||
|
||||
const fwdGeneratorExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-fwd",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateFwd.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateExe = b.addExecutable(.{
|
||||
.name = "backlog-apigen",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateApi.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateShaders = b.addExecutable(.{
|
||||
.name = "backlog-shaderEmbedGen",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateEmbeddedShaders.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateRcExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-rc",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateRc.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateLoadDynamicsExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-loadDynamics",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateLoadDynamics.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
b.installArtifact(generateLoadDynamicsExe);
|
||||
b.installArtifact(generateExe);
|
||||
b.installArtifact(generateShaders);
|
||||
b.installArtifact(generateRcExe);
|
||||
|
||||
{
|
||||
const loadDynamicsStub = b.addModule("loadDynamicsStub", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/loadDynamicsStub.zig"),
|
||||
});
|
||||
|
||||
_ = loadDynamicsStub;
|
||||
|
||||
const mod = b.addModule("Backlog", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/backlog.zig"),
|
||||
});
|
||||
|
||||
for (engineDepList) |depName| {
|
||||
const dep = b.dependency(
|
||||
depName,
|
||||
.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
},
|
||||
);
|
||||
|
||||
const run = b.addRunArtifact(fwdGeneratorExe);
|
||||
const output = run.addOutputFileArg(b.fmt("{s}_fwd.zig", .{depName}));
|
||||
|
||||
const modFwd = b.addModule(depName, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport(b.fmt("{s}", .{depName}), modFwd);
|
||||
modFwd.addImport("module", dep.module(depName));
|
||||
|
||||
// mod.addImport(depName, dep.module(depName));
|
||||
}
|
||||
|
||||
// link in large platform support functions... special case.
|
||||
{
|
||||
const dep = b.dependency("sdl3", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
});
|
||||
const lib = dep.module("SDL3");
|
||||
|
||||
mod.addImport("sdl3_fwd", lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generateApi(self: *@This(), programName: []const u8, moduleList: []const []const u8, dynamicModules: ?[]const *DynamicModule) *std.Build.Module {
|
||||
// std.debug.print("GENERATE API:: {s}\n", .{programName});
|
||||
|
||||
for (moduleList) |mod| {
|
||||
_ = mod;
|
||||
// std.debug.print("{s}\n", .{mod});
|
||||
}
|
||||
//
|
||||
if (self.generatedApis.get(programName)) |m| {
|
||||
return m;
|
||||
}
|
||||
|
||||
const run = self.b.addRunArtifact(self.apigen);
|
||||
const pfile = self.b.fmt("{s}Api.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
for (moduleList) |modName| {
|
||||
run.addArg(modName);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
for (moduleList) |modName| {
|
||||
mod.addImport(modName, self.nwdep.module(modName));
|
||||
}
|
||||
|
||||
const loadStatics = self.generateInstallStaticResources(programName, "content/_shaders") catch unreachable;
|
||||
mod.addImport("staticShaderLoader", loadStatics);
|
||||
|
||||
// deal with dynamics
|
||||
if (dynamicModules) |dynamics| {
|
||||
const loadDynamics = self.generateLoadDynamics(programName, dynamics) catch @panic("unknown");
|
||||
mod.addImport("loadDynamics", loadDynamics);
|
||||
} else {
|
||||
mod.addImport("loadDynamics", self.nwdep.module("loadDynamicsStub"));
|
||||
}
|
||||
|
||||
self.generatedApis.put(self.b.allocator, programName, mod) catch @panic("out of memory");
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8) !std.Build.LazyPath {
|
||||
const run = self.b.addRunArtifact(self.rcGen);
|
||||
const pfile = self.b.fmt("{s}.rc", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
run.addArg(iconPath);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
pub fn generateLoadDynamics(self: *@This(), programName: []const u8, dynamics: []const *DynamicModule) !*std.Build.Module {
|
||||
const run = self.b.addRunArtifact(self.loadDynamicsGen);
|
||||
const pfile = self.b.fmt("{s}LoadDynamics.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
if (self.staticBuild) {
|
||||
run.addArg("static");
|
||||
} else {
|
||||
run.addArg("dynamic");
|
||||
}
|
||||
|
||||
for (dynamics) |dyn| {
|
||||
run.addArg(dyn.name);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
for (dynamics) |dyn| {
|
||||
const dynmod = dyn.compileInstall();
|
||||
mod.addImport(dyn.name, dynmod.root_module);
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, shadersPath: []const u8) !*std.Build.Module {
|
||||
const run = self.b.addRunArtifact(self.shaderEmbedGen);
|
||||
const pfile = self.b.fmt("{s}ShaderGen.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
run.addArg(shadersPath);
|
||||
|
||||
const b = self.b;
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
|
||||
var dir = try std.fs.cwd().openDir("content/_shaders", .{ .iterate = true });
|
||||
defer dir.close();
|
||||
|
||||
var walker = dir.iterate();
|
||||
while (try walker.next()) |shaderType| {
|
||||
if (shaderType.kind == .directory) {
|
||||
var d2 = try dir.openDir(shaderType.name, .{ .iterate = true });
|
||||
defer d2.close();
|
||||
var w2 = d2.iterate();
|
||||
while (try w2.next()) |shaderName| {
|
||||
const shaderPath = b.fmt("content/_shaders/{s}/{s}", .{ shaderType.name, shaderName.name });
|
||||
mod.addAnonymousImport(shaderName.name, .{
|
||||
.root_source_file = b.path(shaderPath),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
|
@ -1,696 +0,0 @@
|
|||
// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
|
||||
b: *std.Build,
|
||||
nw_builder: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.Mode,
|
||||
nw_mod: *std.Build.Module,
|
||||
spirvReflect: SpirvReflect.SpirvGenerator2,
|
||||
gltf2ozz: ozz.GltfToOzz,
|
||||
options: *std.Build.Step.Options,
|
||||
cookShaders: bool,
|
||||
|
||||
backlogRoot: []const u8,
|
||||
// list of all shaders discovered under
|
||||
// content/_shaders/def
|
||||
reflectShaderPathList: [][]u8 = undefined,
|
||||
|
||||
staticBuild: bool = false,
|
||||
|
||||
nwdep: *std.Build.Dependency,
|
||||
apigen: *std.Build.Step.Compile,
|
||||
shaderEmbedGen: *std.Build.Step.Compile,
|
||||
loadDynamicsGen: *std.Build.Step.Compile,
|
||||
rcGen: *std.Build.Step.Compile,
|
||||
generatedApis: std.StringHashMapUnmanaged(*std.Build.Module) = .{},
|
||||
|
||||
const engineDepList = [_][]const u8{
|
||||
"assets",
|
||||
"audio",
|
||||
"core",
|
||||
"net",
|
||||
"papyrus",
|
||||
"platform",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"physics",
|
||||
"sys",
|
||||
};
|
||||
|
||||
const BuildSystem = @This();
|
||||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const LazyPath = Build.LazyPath;
|
||||
|
||||
const SpirvReflect = @import("SpirvReflect");
|
||||
const ozz = @import("ozz");
|
||||
|
||||
pub const InitOptions = struct {
|
||||
import_name: []const u8 = "Backlog",
|
||||
backlogRoot: []const u8 = "./BacklogEngine",
|
||||
staticBuild: bool = false,
|
||||
target: Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
};
|
||||
|
||||
pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
|
||||
var buildOpts = declareBuildOptions(b);
|
||||
|
||||
if (opts.target.result.os.tag == .linux) {
|
||||
// using a static build for linux... object loading hell is not fun
|
||||
std.debug.print("Important! only supporting static builds for linux", .{});
|
||||
buildOpts.static_build = true;
|
||||
}
|
||||
|
||||
const nwdep = b.dependency(opts.import_name, .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.static_build = buildOpts.static_build,
|
||||
});
|
||||
|
||||
var self = BuildSystem{
|
||||
.b = b,
|
||||
.nw_builder = nwdep.builder,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.nw_mod = nwdep.module("Backlog"),
|
||||
.backlogRoot = opts.backlogRoot,
|
||||
.spirvReflect = SpirvReflect.SpirvGenerator2.init(nwdep.builder, .{}),
|
||||
.options = createGameOptions(b, buildOpts),
|
||||
.gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}),
|
||||
.cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false,
|
||||
|
||||
.staticBuild = buildOpts.static_build,
|
||||
.nwdep = nwdep,
|
||||
.apigen = nwdep.artifact("backlog-apigen"),
|
||||
.loadDynamicsGen = nwdep.artifact("backlog-generate-loadDynamics"),
|
||||
.rcGen = nwdep.artifact("backlog-generate-rc"),
|
||||
.shaderEmbedGen = nwdep.artifact("backlog-shaderEmbedGen"),
|
||||
};
|
||||
|
||||
const exeList = [2]*std.Build.Step.Compile{ self.gltf2ozz.exe, self.spirvReflect.reflect };
|
||||
const install_tools = b.step("tools", "installs tools needed to generate outputs for the engine");
|
||||
for (exeList) |exe| {
|
||||
const toolsInstall = b.addInstallArtifact(exe, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "tools" } },
|
||||
});
|
||||
|
||||
install_tools.dependOn(&toolsInstall.step);
|
||||
}
|
||||
|
||||
{
|
||||
const runArtifact = b.addRunArtifact(self.gltf2ozz.exe);
|
||||
if (b.args) |args| {
|
||||
runArtifact.addArgs(args);
|
||||
}
|
||||
|
||||
const run_exe = b.step("gltf2ozz", "runs the gltf animation converter.");
|
||||
run_exe.dependOn(&runArtifact.step);
|
||||
}
|
||||
|
||||
{
|
||||
const runArtifact = b.addRunArtifact(self.spirvReflect.reflect);
|
||||
if (b.args) |args| {
|
||||
runArtifact.addArgs(args);
|
||||
}
|
||||
|
||||
const run_exe = b.step("spv-reflect", "runs the gltf animation converter.");
|
||||
run_exe.dependOn(&runArtifact.step);
|
||||
}
|
||||
|
||||
self.addDependencyInstalls(self.b, .ReleaseFast);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub const AddProgramOptions = struct {
|
||||
name: []const u8,
|
||||
desc: []const u8,
|
||||
root_source_file: LazyPath,
|
||||
imports: []const Build.Module.Import = &.{},
|
||||
};
|
||||
|
||||
pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Module {
|
||||
const b = self.b;
|
||||
|
||||
const exe = self.nw_builder.addExecutable(.{
|
||||
.name = opts.name,
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = self.nw_builder.path("engine/main.zig"),
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
const runArtifact = b.addRunArtifact(exe);
|
||||
if (b.args) |args| {
|
||||
runArtifact.addArgs(args);
|
||||
}
|
||||
const run_exe = b.step(self.b.fmt("run-{s}", .{opts.name}), opts.desc);
|
||||
run_exe.dependOn(&runArtifact.step);
|
||||
|
||||
// main path = name/main.zig
|
||||
const mod = b.addModule(opts.name, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = opts.root_source_file,
|
||||
.imports = opts.imports,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("main", mod);
|
||||
// todo.. remove this one and see what happens
|
||||
exe.root_module.addImport("core", self.nwdep.module("core"));
|
||||
// mod.addImport("Backlog", self.nw_mod);
|
||||
exe.root_module.addOptions("BacklogOptions", self.options);
|
||||
|
||||
if (self.cookShaders) {
|
||||
const cookShadersScript = b.fmt("{s}/tools/scripts/cookShaders.py", .{self.backlogRoot});
|
||||
const cookShadersCommand = b.addSystemCommand(&[_][]const u8{"python"});
|
||||
cookShadersCommand.addArg(cookShadersScript);
|
||||
|
||||
run_exe.dependOn(&cookShadersCommand.step);
|
||||
}
|
||||
|
||||
b.getInstallStep().dependOn(self.nw_builder.getInstallStep());
|
||||
|
||||
// run_exe.dependOn(b.getInstallStep());
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub const BuildOptions = struct {
|
||||
mutex_job_queue: bool,
|
||||
static_build: bool,
|
||||
zero_logging: bool,
|
||||
slow_logging: bool,
|
||||
force_mailbox: bool,
|
||||
};
|
||||
|
||||
pub fn declareBuildOptions(b: *std.Build) BuildOptions {
|
||||
return .{
|
||||
.mutex_job_queue = b.option(bool, "mutex_job_queue", "temporary test, reverts to old mutex based queue behaviour in jobs.zig:JobManager") orelse false,
|
||||
.static_build = b.option(bool, "static_build", "builds the entire game as a single executable") orelse false,
|
||||
.zero_logging = b.option(bool, "zero_logging", "disables all logging, only intended for use on job dispatch testing") orelse false,
|
||||
.slow_logging = b.option(bool, "slow_logging", "Disables buffered logging, takes a hit to performance but gain timing information on logging") orelse false,
|
||||
.force_mailbox = b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn createGameOptions(b: *std.Build, options: BuildOptions) *std.Build.Step.Options {
|
||||
const opts = b.addOptions();
|
||||
|
||||
inline for (@typeInfo(BuildOptions).@"struct".fields) |field| {
|
||||
opts.addOption(bool, field.name, @field(options, field.name));
|
||||
}
|
||||
|
||||
// build options for core.zig
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "mutex_job_queue",
|
||||
// );
|
||||
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "static_build",
|
||||
// );
|
||||
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "zero_logging",
|
||||
// );
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "slow_logging",
|
||||
// );
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "force_mailbox",
|
||||
// );
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
pub fn addExtraModule(self: *BuildSystem, mod: *std.Build.Module, moduleName: []const u8) void {
|
||||
const dep = self.b.dependency(moduleName, .{ .target = self.target, .optimize = self.optimize, .static_build = self.staticBuild });
|
||||
mod.addImport(moduleName, dep.module(moduleName));
|
||||
}
|
||||
|
||||
pub fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
|
||||
//if (self.staticBuild) {
|
||||
//return;
|
||||
// }
|
||||
|
||||
inline for (DynamicDepList) |d| {
|
||||
b.installArtifact(b.dependency(
|
||||
d.dep,
|
||||
.{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
|
||||
).artifact(d.artifact));
|
||||
}
|
||||
|
||||
// b.installArtifact(b.dependency(
|
||||
// "sdl3",
|
||||
// .{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
|
||||
// ).artifact("SDL3"));
|
||||
}
|
||||
|
||||
const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } = &.{
|
||||
.{ .dep = "sdl3", .artifact = "SDL3" },
|
||||
.{ .dep = "spng", .artifact = "spng_c" },
|
||||
.{ .dep = "lua", .artifact = "luac" },
|
||||
// .{ .dep = "miniaudio", .artifact = "miniaudio_c" },
|
||||
.{ .dep = "zphysics", .artifact = "joltc" },
|
||||
// .{ .dep = "enet", .artifact = "enet_c" },
|
||||
.{ .dep = "ozz", .artifact = "ozz_cpp" },
|
||||
};
|
||||
|
||||
// all other modules are disabled by default
|
||||
pub const defaultEnabledModules: []const []const u8 = &.{
|
||||
"core",
|
||||
"assets",
|
||||
"platform",
|
||||
"rend",
|
||||
};
|
||||
|
||||
pub const moduleOrder: []const []const u8 = &.{
|
||||
"core",
|
||||
"sys",
|
||||
"assets",
|
||||
"platform",
|
||||
"net",
|
||||
"physics",
|
||||
"audio",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"papyrus",
|
||||
};
|
||||
|
||||
pub const DynamicModule = struct {
|
||||
name: []const u8,
|
||||
allocator: std.mem.Allocator,
|
||||
buildSystem: *BuildSystem,
|
||||
programName: []const u8,
|
||||
opts: AddProgramOptions,
|
||||
|
||||
extras: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
|
||||
library: ?*std.Build.Step.Compile = null,
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn init(name: []const u8, p: *Program) *@This() {
|
||||
var self: *@This() = p.allocator.create(@This()) catch @panic("out of memory");
|
||||
|
||||
self.* = .{
|
||||
.opts = p.opts,
|
||||
.programName = p.opts.name,
|
||||
.buildSystem = p.buildSystem,
|
||||
.allocator = p.allocator,
|
||||
.name = name,
|
||||
};
|
||||
|
||||
for (p.gameModules.items) |module| {
|
||||
self.gameModules.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
for (p.extras.items) |module| {
|
||||
self.extras.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Step.Compile {
|
||||
if (self.library) |lib| {
|
||||
return lib;
|
||||
}
|
||||
|
||||
const b = self.buildSystem.b;
|
||||
const static = self.buildSystem.staticBuild;
|
||||
const lib = if (static)
|
||||
b.addStaticLibrary(.{
|
||||
.root_source_file = self.opts.root_source_file,
|
||||
.link_libc = true,
|
||||
.optimize = self.buildSystem.optimize,
|
||||
.target = self.buildSystem.target,
|
||||
.name = self.name,
|
||||
})
|
||||
else
|
||||
b.addSharedLibrary(.{
|
||||
.root_source_file = self.opts.root_source_file,
|
||||
.link_libc = true,
|
||||
.optimize = self.buildSystem.optimize,
|
||||
.target = self.buildSystem.target,
|
||||
.name = self.name,
|
||||
});
|
||||
|
||||
var modlist = std.ArrayList([]const u8).init(self.buildSystem.b.allocator);
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(lib.root_module, extra.name);
|
||||
}
|
||||
|
||||
lib.root_module.addImport("backlog", self.buildSystem.generateApi(self.name, modlist.items, null));
|
||||
lib.root_module.addOptions("BacklogOptions", self.buildSystem.options);
|
||||
|
||||
if (static) {
|
||||
// generateApi should create static callers for the main program
|
||||
} else {
|
||||
const installExtern = b.addInstallArtifact(lib, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "modules" } },
|
||||
});
|
||||
b.getInstallStep().dependOn(&installExtern.step);
|
||||
}
|
||||
|
||||
return lib;
|
||||
}
|
||||
};
|
||||
|
||||
pub const GameModule = struct {
|
||||
name: []const u8,
|
||||
enabled: bool = true,
|
||||
};
|
||||
|
||||
pub const Program = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
opts: AddProgramOptions,
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
extras: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
dynamicModules: std.ArrayListUnmanaged(*DynamicModule) = .{},
|
||||
buildSystem: *BuildSystem,
|
||||
|
||||
iconPath: ?std.Build.LazyPath = null,
|
||||
|
||||
pub fn setIconPath(self: *@This(), path: []const u8) void {
|
||||
if (self.iconPath != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.iconPath = self.buildSystem.generateRc(self.opts.name, path) catch return;
|
||||
}
|
||||
|
||||
pub fn setModuleEnabled(self: *@This(), module: []const u8, enable: bool) void {
|
||||
for (self.gameModules.items) |*m| {
|
||||
if (std.mem.eql(u8, m.name, module)) {
|
||||
m.enabled = enable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// module not found, add it here.
|
||||
self.gameModules.append(self.allocator, .{ .name = module, .enabled = enable }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addDynamicModule(self: *@This(), module: []const u8, root_source_file: std.Build.LazyPath) *DynamicModule {
|
||||
const dynamicModule = DynamicModule.init(module, self);
|
||||
dynamicModule.opts.root_source_file = root_source_file;
|
||||
self.dynamicModules.append(self.allocator, dynamicModule) catch @panic("out of memory");
|
||||
|
||||
return dynamicModule;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Module {
|
||||
const exe = self.buildSystem.addProgram(self.opts);
|
||||
var modlist = std.ArrayList([]const u8).init(self.buildSystem.b.allocator);
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(exe, extra.name);
|
||||
}
|
||||
|
||||
exe.addImport("backlog", self.buildSystem.generateApi(self.opts.name, modlist.items, self.dynamicModules.items));
|
||||
|
||||
if (self.buildSystem.target.result.os.tag == .windows) {
|
||||
if (self.iconPath) |ip| {
|
||||
exe.addWin32ResourceFile(.{
|
||||
.file = ip,
|
||||
.flags = &.{},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn program(self: *BuildSystem, opts: AddProgramOptions) *Program {
|
||||
const prog = self.b.allocator.create(Program) catch unreachable;
|
||||
|
||||
prog.* = .{
|
||||
.allocator = self.b.allocator,
|
||||
.opts = opts,
|
||||
.buildSystem = self,
|
||||
};
|
||||
|
||||
for (moduleOrder) |module| {
|
||||
prog.setModuleEnabled(module, false);
|
||||
}
|
||||
|
||||
for (defaultEnabledModules) |module| {
|
||||
prog.setModuleEnabled(module, true);
|
||||
}
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
// ========= standalone build instance =======
|
||||
// maybe the default should be like an engine launcher/project launcher or something
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false;
|
||||
|
||||
const spirvDep = b.dependency("SpirvReflect", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
});
|
||||
_ = spirvDep;
|
||||
|
||||
const fwdGeneratorExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-fwd",
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateFwd.zig"),
|
||||
});
|
||||
|
||||
const generateExe = b.addExecutable(.{
|
||||
.name = "backlog-apigen",
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateApi.zig"),
|
||||
});
|
||||
|
||||
const generateShaders = b.addExecutable(.{
|
||||
.name = "backlog-shaderEmbedGen",
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateEmbeddedShaders.zig"),
|
||||
});
|
||||
|
||||
const generateRcExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-rc",
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateRc.zig"),
|
||||
});
|
||||
|
||||
const generateLoadDynamicsExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-loadDynamics",
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateLoadDynamics.zig"),
|
||||
});
|
||||
|
||||
b.installArtifact(generateLoadDynamicsExe);
|
||||
b.installArtifact(generateExe);
|
||||
b.installArtifact(generateShaders);
|
||||
b.installArtifact(generateRcExe);
|
||||
|
||||
{
|
||||
const loadDynamicsStub = b.addModule("loadDynamicsStub", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/loadDynamicsStub.zig"),
|
||||
});
|
||||
|
||||
_ = loadDynamicsStub;
|
||||
|
||||
const mod = b.addModule("Backlog", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/backlog.zig"),
|
||||
});
|
||||
|
||||
for (engineDepList) |depName| {
|
||||
const dep = b.dependency(
|
||||
depName,
|
||||
.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
},
|
||||
);
|
||||
|
||||
const run = b.addRunArtifact(fwdGeneratorExe);
|
||||
const output = run.addOutputFileArg(b.fmt("{s}_fwd.zig", .{depName}));
|
||||
|
||||
const modFwd = b.addModule(depName, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport(b.fmt("{s}", .{depName}), modFwd);
|
||||
modFwd.addImport("module", dep.module(depName));
|
||||
|
||||
// mod.addImport(depName, dep.module(depName));
|
||||
}
|
||||
|
||||
// link in large platform support functions... special case.
|
||||
{
|
||||
const dep = b.dependency("sdl3", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
});
|
||||
const lib = dep.module("SDL3");
|
||||
|
||||
mod.addImport("sdl3_fwd", lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generateApi(self: *@This(), programName: []const u8, moduleList: []const []const u8, dynamicModules: ?[]const *DynamicModule) *std.Build.Module {
|
||||
// std.debug.print("GENERATE API:: {s}\n", .{programName});
|
||||
|
||||
for (moduleList) |mod| {
|
||||
_ = mod;
|
||||
// std.debug.print("{s}\n", .{mod});
|
||||
}
|
||||
//
|
||||
if (self.generatedApis.get(programName)) |m| {
|
||||
return m;
|
||||
}
|
||||
|
||||
const run = self.b.addRunArtifact(self.apigen);
|
||||
const pfile = self.b.fmt("{s}Api.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
for (moduleList) |modName| {
|
||||
run.addArg(modName);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
for (moduleList) |modName| {
|
||||
mod.addImport(modName, self.nwdep.module(modName));
|
||||
}
|
||||
|
||||
const loadStatics = self.generateInstallStaticResources(programName, "content/_shaders") catch unreachable;
|
||||
mod.addImport("staticShaderLoader", loadStatics);
|
||||
|
||||
// deal with dynamics
|
||||
if (dynamicModules) |dynamics| {
|
||||
const loadDynamics = self.generateLoadDynamics(programName, dynamics) catch @panic("unknown");
|
||||
mod.addImport("loadDynamics", loadDynamics);
|
||||
} else {
|
||||
mod.addImport("loadDynamics", self.nwdep.module("loadDynamicsStub"));
|
||||
}
|
||||
|
||||
self.generatedApis.put(self.b.allocator, programName, mod) catch @panic("out of memory");
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8) !std.Build.LazyPath {
|
||||
const run = self.b.addRunArtifact(self.rcGen);
|
||||
const pfile = self.b.fmt("{s}.rc", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
run.addArg(iconPath);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
pub fn generateLoadDynamics(self: *@This(), programName: []const u8, dynamics: []const *DynamicModule) !*std.Build.Module {
|
||||
const run = self.b.addRunArtifact(self.loadDynamicsGen);
|
||||
const pfile = self.b.fmt("{s}LoadDynamics.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
if (self.staticBuild) {
|
||||
run.addArg("static");
|
||||
} else {
|
||||
run.addArg("dynamic");
|
||||
}
|
||||
|
||||
for (dynamics) |dyn| {
|
||||
run.addArg(dyn.name);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
for (dynamics) |dyn| {
|
||||
const dynmod = dyn.compileInstall();
|
||||
mod.addImport(dyn.name, dynmod.root_module);
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, shadersPath: []const u8) !*std.Build.Module {
|
||||
const run = self.b.addRunArtifact(self.shaderEmbedGen);
|
||||
const pfile = self.b.fmt("{s}ShaderGen.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
run.addArg(shadersPath);
|
||||
|
||||
const b = self.b;
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
|
||||
var dir = try std.fs.cwd().openDir("content/_shaders", .{ .iterate = true });
|
||||
defer dir.close();
|
||||
|
||||
var walker = dir.iterate();
|
||||
while (try walker.next()) |shaderType| {
|
||||
if (shaderType.kind == .directory) {
|
||||
var d2 = try dir.openDir(shaderType.name, .{ .iterate = true });
|
||||
defer d2.close();
|
||||
var w2 = d2.iterate();
|
||||
while (try w2.next()) |shaderName| {
|
||||
const shaderPath = b.fmt("content/_shaders/{s}/{s}", .{ shaderType.name, shaderName.name });
|
||||
mod.addAnonymousImport(shaderName.name, .{
|
||||
.root_source_file = b.path(shaderPath),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
584
build.zig
584
build.zig
|
|
@ -1,27 +1,42 @@
|
|||
// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
|
||||
b: *std.Build,
|
||||
bEngine: *std.Build, // used to resolve executable paths from within the engine's root directory
|
||||
opts: bh.Options,
|
||||
nw_builder: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
nw_mod: *std.Build.Module,
|
||||
gltf2ozz: ozz.GltfToOzz,
|
||||
options: *std.Build.Step.Options,
|
||||
cookShaders: bool,
|
||||
|
||||
backlogRoot: []const u8,
|
||||
|
||||
// list of all shaders discovered under
|
||||
// content/_shaders/def
|
||||
reflectShaderPathList: [][]u8 = undefined,
|
||||
|
||||
staticBuild: bool = false,
|
||||
|
||||
nwdep: *std.Build.Dependency,
|
||||
// apigen: *std.Build.Step.Compile,
|
||||
apigen: *std.Build.Step.Compile,
|
||||
shaderEmbedGen: *std.Build.Step.Compile,
|
||||
loadDynamicsGen: *std.Build.Step.Compile,
|
||||
rcGen: *std.Build.Step.Compile,
|
||||
specGen: *std.Build.Step.Compile,
|
||||
generatedApis: std.StringHashMapUnmanaged(*std.Build.Module) = .{},
|
||||
|
||||
pub const BuildSystem = @This();
|
||||
const engineDepList = [_][]const u8{
|
||||
"assets",
|
||||
"audio",
|
||||
"core",
|
||||
"net",
|
||||
"papyrus",
|
||||
"platform",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"physics",
|
||||
"sys",
|
||||
};
|
||||
|
||||
const BuildSystem = @This();
|
||||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const LazyPath = Build.LazyPath;
|
||||
|
|
@ -31,47 +46,41 @@ const ozz = @import("ozz");
|
|||
pub const InitOptions = struct {
|
||||
import_name: []const u8 = "Backlog",
|
||||
backlogRoot: []const u8 = "./BacklogEngine",
|
||||
target: std.Build.ResolvedTarget,
|
||||
staticBuild: bool = false,
|
||||
target: Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
};
|
||||
|
||||
pub fn init(b: *std.Build, initOptions: InitOptions) BuildSystem {
|
||||
const buildOpts = declareBuildOptions(b);
|
||||
|
||||
const opts = bh.Options{
|
||||
.target = initOptions.target,
|
||||
.optimize = initOptions.optimize,
|
||||
.static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false,
|
||||
.tracy = b.option(bool, "tracy", "builds with tracy support") orelse false,
|
||||
};
|
||||
pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
|
||||
var buildOpts = declareBuildOptions(b);
|
||||
|
||||
if (opts.target.result.os.tag == .linux) {
|
||||
// using a static build for linux... object loading hell is not fun...
|
||||
// i have skill issues in that realm right now
|
||||
// std.debug.print("Important! only supporting static builds for linux", .{});
|
||||
// opts.static_build = true;
|
||||
// using a static build for linux... object loading hell is not fun
|
||||
std.debug.print("Important! only supporting static builds for linux", .{});
|
||||
buildOpts.static_build = true;
|
||||
}
|
||||
|
||||
const nwdep = b.dependency(initOptions.import_name, .{
|
||||
const nwdep = b.dependency(opts.import_name, .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.static_build = opts.static_build,
|
||||
.static_build = buildOpts.static_build,
|
||||
});
|
||||
|
||||
var self = BuildSystem{
|
||||
.b = b,
|
||||
.bEngine = nwdep.builder,
|
||||
.opts = opts,
|
||||
.nw_builder = nwdep.builder,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.nw_mod = nwdep.module("Backlog"),
|
||||
.backlogRoot = initOptions.backlogRoot,
|
||||
.options = createGameOptions(b, buildOpts, opts),
|
||||
.backlogRoot = opts.backlogRoot,
|
||||
.options = createGameOptions(b, buildOpts),
|
||||
.gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}),
|
||||
.cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false,
|
||||
|
||||
.staticBuild = buildOpts.static_build,
|
||||
.nwdep = nwdep,
|
||||
// .apigen = nwdep.artifact("backlog-apigen"),
|
||||
.apigen = nwdep.artifact("backlog-apigen"),
|
||||
.loadDynamicsGen = nwdep.artifact("backlog-generate-loadDynamics"),
|
||||
.specGen = nwdep.artifact("backlog-generate-spec"),
|
||||
.rcGen = nwdep.artifact("backlog-generate-rc"),
|
||||
.shaderEmbedGen = nwdep.artifact("backlog-shaderEmbedGen"),
|
||||
};
|
||||
|
|
@ -101,55 +110,135 @@ pub fn init(b: *std.Build, initOptions: InitOptions) BuildSystem {
|
|||
return self;
|
||||
}
|
||||
|
||||
// Build Options for the engine,
|
||||
// should generally be written as false = default
|
||||
// true = enabling something
|
||||
const BuildOptions = struct {
|
||||
pub const AddProgramOptions = struct {
|
||||
name: []const u8,
|
||||
desc: []const u8,
|
||||
root_source_file: LazyPath,
|
||||
imports: []const Build.Module.Import = &.{},
|
||||
};
|
||||
|
||||
pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Module {
|
||||
const b = self.b;
|
||||
|
||||
const exe = self.nw_builder.addExecutable(.{
|
||||
.name = opts.name,
|
||||
.root_module = b.createModule(.{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = self.nw_builder.path("engine/main.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
const runArtifact = b.addRunArtifact(exe);
|
||||
if (b.args) |args| {
|
||||
runArtifact.addArgs(args);
|
||||
}
|
||||
const run_exe = b.step(self.b.fmt("run-{s}", .{opts.name}), opts.desc);
|
||||
run_exe.dependOn(&runArtifact.step);
|
||||
|
||||
// main path = name/main.zig
|
||||
const mod = b.addModule(opts.name, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = opts.root_source_file,
|
||||
.imports = opts.imports,
|
||||
});
|
||||
|
||||
exe.root_module.addImport("main", mod);
|
||||
// todo.. remove this one and see what happens
|
||||
exe.root_module.addImport("core", self.nwdep.module("core"));
|
||||
// mod.addImport("Backlog", self.nw_mod);
|
||||
exe.root_module.addOptions("BacklogOptions", self.options);
|
||||
|
||||
if (self.cookShaders) {
|
||||
const cookShadersScript = b.fmt("{s}/tools/scripts/cookShaders.py", .{self.backlogRoot});
|
||||
const cookShadersCommand = b.addSystemCommand(&[_][]const u8{"python"});
|
||||
cookShadersCommand.addArg(cookShadersScript);
|
||||
|
||||
run_exe.dependOn(&cookShadersCommand.step);
|
||||
}
|
||||
|
||||
b.getInstallStep().dependOn(self.nw_builder.getInstallStep());
|
||||
|
||||
// run_exe.dependOn(b.getInstallStep());
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub const BuildOptions = struct {
|
||||
mutex_job_queue: bool,
|
||||
static_build: bool,
|
||||
zero_logging: bool,
|
||||
slow_logging: bool,
|
||||
force_mailbox: bool,
|
||||
};
|
||||
|
||||
fn declareBuildOptions(b: *std.Build) BuildOptions {
|
||||
pub fn declareBuildOptions(b: *std.Build) BuildOptions {
|
||||
return .{
|
||||
.mutex_job_queue = b.option(bool, "mutex_job_queue", "temporary test, reverts to old mutex based queue behaviour in jobs.zig:JobManager") orelse false,
|
||||
.static_build = b.option(bool, "static_build", "builds the entire game as a single executable") orelse false,
|
||||
.zero_logging = b.option(bool, "zero_logging", "disables all logging, only intended for use on job dispatch testing") orelse false,
|
||||
.slow_logging = b.option(bool, "slow_logging", "Disables buffered logging, takes a hit to performance but gain timing information on logging") orelse false,
|
||||
.force_mailbox = b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false,
|
||||
};
|
||||
}
|
||||
|
||||
fn createGameOptions(b: *std.Build, options: BuildOptions, bhopts: bh.Options) *std.Build.Step.Options {
|
||||
pub fn createGameOptions(b: *std.Build, options: BuildOptions) *std.Build.Step.Options {
|
||||
const opts = b.addOptions();
|
||||
|
||||
inline for (@typeInfo(BuildOptions).@"struct".fields) |field| {
|
||||
opts.addOption(bool, field.name, @field(options, field.name));
|
||||
}
|
||||
|
||||
// forward the options to the build options
|
||||
opts.addOption(bool, "tracy", bhopts.tracy);
|
||||
opts.addOption(bool, "static_build", bhopts.static_build);
|
||||
// build options for core.zig
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "mutex_job_queue",
|
||||
// );
|
||||
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "static_build",
|
||||
// );
|
||||
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "zero_logging",
|
||||
// );
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "slow_logging",
|
||||
// );
|
||||
// opts.addOption(
|
||||
// bool,
|
||||
// "force_mailbox",
|
||||
// );
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
pub fn addExtraModule(self: *BuildSystem, mod: *std.Build.Module, moduleName: []const u8) void {
|
||||
const dep = self.b.dependency(moduleName, .{ .target = self.opts.target, .optimize = self.opts.optimize, .static_build = self.opts.static_build });
|
||||
const dep = self.b.dependency(moduleName, .{ .target = self.target, .optimize = self.optimize, .static_build = self.staticBuild });
|
||||
mod.addImport(moduleName, dep.module(moduleName));
|
||||
}
|
||||
|
||||
fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
|
||||
if (self.opts.static_build) {
|
||||
return;
|
||||
}
|
||||
pub fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
|
||||
//if (self.staticBuild) {
|
||||
//return;
|
||||
// }
|
||||
|
||||
inline for (DynamicDepList) |d| {
|
||||
b.installArtifact(b.dependency(
|
||||
d.dep,
|
||||
.{ .target = self.opts.target, .optimize = optimize, .static_build = self.opts.static_build },
|
||||
.{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
|
||||
).artifact(d.artifact));
|
||||
}
|
||||
|
||||
// b.installArtifact(b.dependency(
|
||||
// "sdl3",
|
||||
// .{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
|
||||
// ).artifact("SDL3"));
|
||||
}
|
||||
|
||||
const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } = &.{
|
||||
|
|
@ -162,19 +251,365 @@ const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } =
|
|||
.{ .dep = "ozz", .artifact = "ozz_cpp" },
|
||||
};
|
||||
|
||||
pub fn addProgram(
|
||||
self: *@This(),
|
||||
// all other modules are disabled by default
|
||||
pub const defaultEnabledModules: []const []const u8 = &.{
|
||||
"core",
|
||||
"assets",
|
||||
"platform",
|
||||
"rend",
|
||||
};
|
||||
|
||||
pub const moduleOrder: []const []const u8 = &.{
|
||||
"core",
|
||||
"sys",
|
||||
"assets",
|
||||
"platform",
|
||||
"net",
|
||||
"physics",
|
||||
"audio",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"papyrus",
|
||||
};
|
||||
|
||||
pub const DynamicModule = struct {
|
||||
name: []const u8,
|
||||
) *Program {
|
||||
const p = self.b.allocator.create(Program) catch unreachable;
|
||||
p.* = .{
|
||||
.name = name,
|
||||
.opts = self.opts,
|
||||
.buildSystem = self,
|
||||
allocator: std.mem.Allocator,
|
||||
buildSystem: *BuildSystem,
|
||||
programName: []const u8,
|
||||
opts: AddProgramOptions,
|
||||
|
||||
extras: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
|
||||
library: ?*std.Build.Step.Compile = null,
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn init(name: []const u8, p: *Program) *@This() {
|
||||
var self: *@This() = p.allocator.create(@This()) catch @panic("out of memory");
|
||||
|
||||
self.* = .{
|
||||
.opts = p.opts,
|
||||
.programName = p.opts.name,
|
||||
.buildSystem = p.buildSystem,
|
||||
.allocator = p.allocator,
|
||||
.name = name,
|
||||
};
|
||||
|
||||
for (p.gameModules.items) |module| {
|
||||
self.gameModules.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
for (p.extras.items) |module| {
|
||||
self.extras.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Step.Compile {
|
||||
if (self.library) |lib| {
|
||||
return lib;
|
||||
}
|
||||
|
||||
const b = self.buildSystem.b;
|
||||
const static = self.buildSystem.staticBuild;
|
||||
const lib = b.addLibrary(.{
|
||||
.name = self.name,
|
||||
.linkage = if (static) .static else .dynamic,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = self.opts.root_source_file,
|
||||
.link_libc = true,
|
||||
.optimize = self.buildSystem.optimize,
|
||||
.target = self.buildSystem.target,
|
||||
}),
|
||||
});
|
||||
|
||||
const allocator = self.buildSystem.b.allocator;
|
||||
|
||||
var modlist = std.ArrayList([]const u8){};
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(allocator, mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(lib.root_module, extra.name);
|
||||
}
|
||||
|
||||
lib.root_module.addImport("backlog", self.buildSystem.generateApi(self.name, modlist.items, null));
|
||||
lib.root_module.addOptions("BacklogOptions", self.buildSystem.options);
|
||||
|
||||
if (static) {
|
||||
// generateApi should create static callers for the main program
|
||||
} else {
|
||||
const installExtern = b.addInstallArtifact(lib, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "modules" } },
|
||||
});
|
||||
b.getInstallStep().dependOn(&installExtern.step);
|
||||
}
|
||||
|
||||
return lib;
|
||||
}
|
||||
};
|
||||
|
||||
pub const GameModule = struct {
|
||||
name: []const u8,
|
||||
enabled: bool = true,
|
||||
};
|
||||
|
||||
pub const Program = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
opts: AddProgramOptions,
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
extras: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
dynamicModules: std.ArrayListUnmanaged(*DynamicModule) = .{},
|
||||
buildSystem: *BuildSystem,
|
||||
|
||||
iconPath: ?std.Build.LazyPath = null,
|
||||
|
||||
pub fn setIconPath(self: *@This(), path: []const u8) void {
|
||||
if (self.iconPath != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.iconPath = self.buildSystem.generateRc(self.opts.name, path) catch return;
|
||||
}
|
||||
|
||||
pub fn setModuleEnabled(self: *@This(), module: []const u8, enable: bool) void {
|
||||
for (self.gameModules.items) |*m| {
|
||||
if (std.mem.eql(u8, m.name, module)) {
|
||||
m.enabled = enable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// module not found, add it here.
|
||||
self.gameModules.append(self.allocator, .{ .name = module, .enabled = enable }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addDynamicModule(self: *@This(), module: []const u8, root_source_file: std.Build.LazyPath) *DynamicModule {
|
||||
const dynamicModule = DynamicModule.init(module, self);
|
||||
dynamicModule.opts.root_source_file = root_source_file;
|
||||
self.dynamicModules.append(self.allocator, dynamicModule) catch @panic("out of memory");
|
||||
|
||||
return dynamicModule;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Module {
|
||||
const exe = self.buildSystem.addProgram(self.opts);
|
||||
const allocator = self.buildSystem.b.allocator;
|
||||
|
||||
var modlist = std.ArrayList([]const u8){};
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(allocator, mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(exe, extra.name);
|
||||
}
|
||||
|
||||
exe.addImport("backlog", self.buildSystem.generateApi(self.opts.name, modlist.items, self.dynamicModules.items));
|
||||
|
||||
if (self.buildSystem.target.result.os.tag == .windows) {
|
||||
if (self.iconPath) |ip| {
|
||||
exe.addWin32ResourceFile(.{
|
||||
.file = ip,
|
||||
.flags = &.{},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn program(self: *BuildSystem, opts: AddProgramOptions) *Program {
|
||||
const prog = self.b.allocator.create(Program) catch unreachable;
|
||||
|
||||
prog.* = .{
|
||||
.allocator = self.b.allocator,
|
||||
.opts = opts,
|
||||
.buildSystem = self,
|
||||
};
|
||||
|
||||
return p;
|
||||
for (moduleOrder) |module| {
|
||||
prog.setModuleEnabled(module, false);
|
||||
}
|
||||
|
||||
for (defaultEnabledModules) |module| {
|
||||
prog.setModuleEnabled(module, true);
|
||||
}
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
// ========= standalone build instance =======
|
||||
// maybe the default should be like an engine launcher/project launcher or something
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false;
|
||||
|
||||
const fwdGeneratorExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-fwd",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateFwd.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateExe = b.addExecutable(.{
|
||||
.name = "backlog-apigen",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateApi.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateShaders = b.addExecutable(.{
|
||||
.name = "backlog-shaderEmbedGen",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateEmbeddedShaders.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateRcExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-rc",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateRc.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateLoadDynamicsExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-loadDynamics",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("build/generateLoadDynamics.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
b.installArtifact(generateLoadDynamicsExe);
|
||||
b.installArtifact(generateExe);
|
||||
b.installArtifact(generateShaders);
|
||||
b.installArtifact(generateRcExe);
|
||||
|
||||
{
|
||||
const loadDynamicsStub = b.addModule("loadDynamicsStub", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/loadDynamicsStub.zig"),
|
||||
});
|
||||
|
||||
_ = loadDynamicsStub;
|
||||
|
||||
const mod = b.addModule("Backlog", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/backlog.zig"),
|
||||
});
|
||||
|
||||
for (engineDepList) |depName| {
|
||||
const dep = b.dependency(
|
||||
depName,
|
||||
.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
},
|
||||
);
|
||||
|
||||
const run = b.addRunArtifact(fwdGeneratorExe);
|
||||
const output = run.addOutputFileArg(b.fmt("{s}_fwd.zig", .{depName}));
|
||||
|
||||
const modFwd = b.addModule(depName, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport(b.fmt("{s}", .{depName}), modFwd);
|
||||
modFwd.addImport("module", dep.module(depName));
|
||||
|
||||
// mod.addImport(depName, dep.module(depName));
|
||||
}
|
||||
|
||||
// link in large platform support functions... special case.
|
||||
{
|
||||
const dep = b.dependency("sdl3", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
});
|
||||
const lib = dep.module("SDL3");
|
||||
|
||||
mod.addImport("sdl3_fwd", lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generateApi(self: *@This(), programName: []const u8, moduleList: []const []const u8, dynamicModules: ?[]const *DynamicModule) *std.Build.Module {
|
||||
// std.debug.print("GENERATE API:: {s}\n", .{programName});
|
||||
|
||||
for (moduleList) |mod| {
|
||||
_ = mod;
|
||||
// std.debug.print("{s}\n", .{mod});
|
||||
}
|
||||
//
|
||||
if (self.generatedApis.get(programName)) |m| {
|
||||
return m;
|
||||
}
|
||||
|
||||
const run = self.b.addRunArtifact(self.apigen);
|
||||
const pfile = self.b.fmt("{s}Api.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
for (moduleList) |modName| {
|
||||
run.addArg(modName);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
for (moduleList) |modName| {
|
||||
mod.addImport(modName, self.nwdep.module(modName));
|
||||
}
|
||||
|
||||
const loadStatics = self.generateInstallStaticResources(programName, "content/_shaders") catch unreachable;
|
||||
mod.addImport("staticShaderLoader", loadStatics);
|
||||
|
||||
// deal with dynamics
|
||||
if (dynamicModules) |dynamics| {
|
||||
const loadDynamics = self.generateLoadDynamics(programName, dynamics) catch @panic("unknown");
|
||||
mod.addImport("loadDynamics", loadDynamics);
|
||||
} else {
|
||||
mod.addImport("loadDynamics", self.nwdep.module("loadDynamicsStub"));
|
||||
}
|
||||
|
||||
self.generatedApis.put(self.b.allocator, programName, mod) catch @panic("out of memory");
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8) !std.Build.LazyPath {
|
||||
|
|
@ -186,6 +621,35 @@ pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8)
|
|||
return output;
|
||||
}
|
||||
|
||||
pub fn generateLoadDynamics(self: *@This(), programName: []const u8, dynamics: []const *DynamicModule) !*std.Build.Module {
|
||||
const run = self.b.addRunArtifact(self.loadDynamicsGen);
|
||||
const pfile = self.b.fmt("{s}LoadDynamics.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
if (self.staticBuild) {
|
||||
run.addArg("static");
|
||||
} else {
|
||||
run.addArg("dynamic");
|
||||
}
|
||||
|
||||
for (dynamics) |dyn| {
|
||||
run.addArg(dyn.name);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
for (dynamics) |dyn| {
|
||||
const dynmod = dyn.compileInstall();
|
||||
mod.addImport(dyn.name, dynmod.root_module);
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, shadersPath: []const u8) !*std.Build.Module {
|
||||
const run = self.b.addRunArtifact(self.shaderEmbedGen);
|
||||
const pfile = self.b.fmt("{s}ShaderGen.zig", .{programName});
|
||||
|
|
@ -194,8 +658,8 @@ pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, s
|
|||
|
||||
const b = self.b;
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.opts.target,
|
||||
.optimize = self.opts.optimize,
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
|
|
@ -220,15 +684,3 @@ pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, s
|
|||
|
||||
return mod;
|
||||
}
|
||||
|
||||
// ========= standalone build instance =======
|
||||
// maybe the default should be like an engine launcher/project launcher or something
|
||||
pub fn build(b: *std.Build) void {
|
||||
const opts = bh.declareOptions(b);
|
||||
engineDeps.buildGenerators(b, opts);
|
||||
}
|
||||
|
||||
const bh = @import("bh");
|
||||
const engineDeps = @import("build/engineDeps.zig");
|
||||
const program = @import("build/program.zig");
|
||||
const Program = program.Program;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,28 @@
|
|||
.{ .name = .Backlog, .version = "0.0.0", .dependencies = .{
|
||||
.assets = .{ .path = "engine/assets" },
|
||||
.audio = .{ .path = "engine/audio" },
|
||||
.core = .{ .path = "engine/core" },
|
||||
.net = .{ .path = "engine/net" },
|
||||
.papyrus = .{ .path = "engine/papyrus" },
|
||||
.physics = .{ .path = "engine/physics" },
|
||||
.platform = .{ .path = "engine/platform" },
|
||||
.imgui = .{ .path = "engine/imgui" },
|
||||
.ui = .{ .path = "engine/ui" },
|
||||
.sys = .{ .path = "engine/sys" },
|
||||
.rend = .{ .path = "engine/rend" },
|
||||
.{
|
||||
.name = .Backlog,
|
||||
.version = "0.0.0",
|
||||
.dependencies = .{
|
||||
.assets = .{ .path = "engine/assets" },
|
||||
.audio = .{ .path = "engine/audio" },
|
||||
.core = .{ .path = "engine/core" },
|
||||
.net = .{ .path = "engine/net" },
|
||||
.papyrus = .{ .path = "engine/papyrus" },
|
||||
.physics = .{ .path = "engine/physics" },
|
||||
.platform = .{ .path = "engine/platform" },
|
||||
.imgui = .{ .path = "engine/imgui" },
|
||||
.ui = .{ .path = "engine/ui" },
|
||||
.sys = .{ .path = "engine/sys" },
|
||||
.rend = .{.path = "engine/rend" },
|
||||
|
||||
.ozz = .{ .path = "lib/ozz" },
|
||||
.spng = .{ .path = "lib/spng" },
|
||||
.zphysics = .{ .path = "lib/zphysics" },
|
||||
.ozz = .{ .path = "lib/ozz" },
|
||||
.spng = .{ .path = "lib/spng" },
|
||||
.zphysics = .{ .path = "lib/zphysics" },
|
||||
|
||||
.sdl3 = .{ .path = "lib/sdl3" },
|
||||
.enet = .{ .path = "lib/enet" },
|
||||
.bh = .{ .path = "lib/bh" },
|
||||
}, .paths = .{
|
||||
"",
|
||||
}, .fingerprint = 0xcf9bab998abe37e3 }
|
||||
.sdl3 = .{ .path = "lib/sdl3" },
|
||||
.enet = .{ .path = "lib/enet" },
|
||||
},
|
||||
.paths = .{
|
||||
"",
|
||||
},
|
||||
.fingerprint = 0xcf9bab998abe37e3
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
pub const AddProgramOptions = struct {
|
||||
name: []const u8,
|
||||
desc: []const u8,
|
||||
root_source_file: LazyPath,
|
||||
imports: []const Build.Module.Import = &.{},
|
||||
};
|
||||
|
||||
pub const Program = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
opts: AddProgramOptions,
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
extras: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
dynamicModules: std.ArrayListUnmanaged(*DynamicModule) = .{},
|
||||
buildSystem: *BuildSystem,
|
||||
|
||||
iconPath: ?std.Build.LazyPath = null,
|
||||
|
||||
pub fn setIconPath(self: *@This(), path: []const u8) void {
|
||||
if (self.iconPath != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.iconPath = self.buildSystem.generateRc(self.opts.name, path) catch return;
|
||||
}
|
||||
|
||||
pub fn setModuleEnabled(self: *@This(), module: []const u8, enable: bool) void {
|
||||
for (self.gameModules.items) |*m| {
|
||||
if (std.mem.eql(u8, m.name, module)) {
|
||||
m.enabled = enable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// module not found, add it here.
|
||||
self.gameModules.append(self.allocator, .{ .name = module, .enabled = enable }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn addDynamicModule(self: *@This(), module: []const u8, root_source_file: std.Build.LazyPath) *DynamicModule {
|
||||
const dynamicModule = DynamicModule.init(module, self);
|
||||
dynamicModule.opts.root_source_file = root_source_file;
|
||||
self.dynamicModules.append(self.allocator, dynamicModule) catch @panic("out of memory");
|
||||
|
||||
return dynamicModule;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Module {
|
||||
const exe = self.buildSystem.addProgram(self.opts);
|
||||
const allocator = self.buildSystem.b.allocator;
|
||||
|
||||
var modlist = std.ArrayList([]const u8){};
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(allocator, mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(exe, extra.name);
|
||||
}
|
||||
|
||||
exe.addImport("backlog", self.buildSystem.generateApi(self.opts.name, modlist.items, self.dynamicModules.items));
|
||||
|
||||
if (self.buildSystem.target.result.os.tag == .windows) {
|
||||
if (self.iconPath) |ip| {
|
||||
exe.addWin32ResourceFile(.{
|
||||
.file = ip,
|
||||
.flags = &.{},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const LazyPath = Build.LazyPath;
|
||||
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
const engineDepList = [_][]const u8{
|
||||
"assets",
|
||||
"audio",
|
||||
"core",
|
||||
"net",
|
||||
"papyrus",
|
||||
"platform",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"physics",
|
||||
"sys",
|
||||
};
|
||||
|
||||
pub fn depList() []const []const u8 {
|
||||
return &engineDepList;
|
||||
}
|
||||
|
||||
pub fn buildGenerators(b: *std.Build, opts: bh.Options) void {
|
||||
const target = opts.target;
|
||||
const optimize = opts.optimize;
|
||||
const static_build = opts.static_build;
|
||||
|
||||
const fwdGeneratorExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-fwd",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateFwd.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateModApiExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-modapi",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateModApi.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateProgramSpecExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-spec",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateProgramSpec.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateShaders = b.addExecutable(.{
|
||||
.name = "backlog-shaderEmbedGen",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateEmbeddedShaders.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateRcExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-rc",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateRc.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateLoadDynamicsExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-loadDynamics",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateLoadDynamics.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
b.installArtifact(fwdGeneratorExe);
|
||||
b.installArtifact(generateLoadDynamicsExe);
|
||||
b.installArtifact(generateProgramSpecExe);
|
||||
b.installArtifact(generateShaders);
|
||||
b.installArtifact(generateRcExe);
|
||||
b.installArtifact(generateModApiExe);
|
||||
|
||||
{
|
||||
const loadDynamicsStub = b.addModule("loadDynamicsStub", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/loadDynamicsStub.zig"),
|
||||
});
|
||||
|
||||
_ = loadDynamicsStub;
|
||||
|
||||
const mod = b.addModule("Backlog", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/backlog.zig"),
|
||||
});
|
||||
|
||||
for (depList()) |depName| {
|
||||
const dep = b.dependency(
|
||||
depName,
|
||||
.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
},
|
||||
);
|
||||
|
||||
const run = b.addRunArtifact(fwdGeneratorExe);
|
||||
const output = run.addOutputFileArg(b.fmt("{s}_fwd.zig", .{depName}));
|
||||
|
||||
const modFwd = b.addModule(depName, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport(b.fmt("{s}", .{depName}), modFwd);
|
||||
modFwd.addImport("module", dep.module(depName));
|
||||
}
|
||||
|
||||
// link in large platform support functions... special case.
|
||||
{
|
||||
const dep = b.dependency("sdl3", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
});
|
||||
const lib = dep.module("SDL3");
|
||||
|
||||
mod.addImport("sdl3_fwd", lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const bh = @import("bh");
|
||||
|
|
@ -90,7 +90,7 @@ pub fn main() !void {
|
|||
\\ pub fn startEngine(vtable: *core.EngineObjectVTable, spec: *core.SpecVariantMap) bool {
|
||||
\\ const args = getArgs() catch return false;
|
||||
\\
|
||||
\\ var backingAllocator: std.mem.Allocator = std.heap.smp_allocator;
|
||||
\\ var backingAllocator: std.mem.Allocator = std.heap.c_allocator;
|
||||
\\ var gpa: std.heap.GeneralPurposeAllocator(.{
|
||||
\\ .stack_trace_frames = 20,
|
||||
\\ }) = .{};
|
||||
|
|
@ -162,7 +162,7 @@ pub fn main() !void {
|
|||
try writer.print(allocator, ".{s} = true,\n", .{mod});
|
||||
}
|
||||
|
||||
try writer.print(allocator, "}}, std.heap.smp_allocator); }}\n", .{});
|
||||
try writer.print(allocator, "}}, std.heap.c_allocator); }}\n", .{});
|
||||
|
||||
try writer.print(allocator, "const std = @import(\"std\");\n\n", .{});
|
||||
|
||||
|
|
@ -73,10 +73,10 @@ pub fn main() !void {
|
|||
|
||||
var ast = try std.zig.Ast.parse(allocator, @as([:0]const u8, @ptrCast(writer.items[0 .. writer.items.len - 1])), .zig);
|
||||
|
||||
// std.debug.print("output=\n{s}", .{content.items[0 .. content.items.len - 1]});
|
||||
defer ast.deinit(allocator);
|
||||
const out = try ast.renderAlloc(allocator);
|
||||
|
||||
std.debug.print("output=\n{s}", .{out});
|
||||
// Write the content to the file
|
||||
try file.writeAll(out);
|
||||
}
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
// legacy code, kept here as reference
|
||||
pub const GameModule = struct {
|
||||
name: []const u8,
|
||||
enabled: bool = true,
|
||||
};
|
||||
|
||||
pub fn program(self: *BuildSystem, opts: AddProgramOptions) *Program {
|
||||
const prog = self.b.allocator.create(Program) catch unreachable;
|
||||
|
||||
prog.* = .{
|
||||
.allocator = self.b.allocator,
|
||||
.opts = opts,
|
||||
.buildSystem = self,
|
||||
};
|
||||
|
||||
for (moduleOrder) |module| {
|
||||
prog.setModuleEnabled(module, false);
|
||||
}
|
||||
|
||||
for (defaultEnabledModules) |module| {
|
||||
prog.setModuleEnabled(module, true);
|
||||
}
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
pub const DynamicModule = struct {
|
||||
name: []const u8,
|
||||
allocator: std.mem.Allocator,
|
||||
buildSystem: *BuildSystem,
|
||||
programName: []const u8,
|
||||
opts: Program.AddProgramOptions,
|
||||
|
||||
extras: std.ArrayListUnmanaged(Program.GameModule) = .{},
|
||||
gameModules: std.ArrayListUnmanaged(Program.GameModule) = .{},
|
||||
|
||||
library: ?*std.Build.Step.Compile = null,
|
||||
|
||||
pub fn addExtraModule(self: *@This(), module: []const u8) void {
|
||||
self.extras.append(self.allocator, .{ .name = module, .enabled = true }) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
pub fn init(name: []const u8, p: *Program) *@This() {
|
||||
var self: *@This() = p.allocator.create(@This()) catch @panic("out of memory");
|
||||
|
||||
self.* = .{
|
||||
.opts = p.opts,
|
||||
.programName = p.opts.name,
|
||||
.buildSystem = p.buildSystem,
|
||||
.allocator = p.allocator,
|
||||
.name = name,
|
||||
};
|
||||
|
||||
for (p.gameModules.items) |module| {
|
||||
self.gameModules.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
for (p.extras.items) |module| {
|
||||
self.extras.append(self.allocator, module) catch @panic("out of memory");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Step.Compile {
|
||||
if (self.library) |lib| {
|
||||
return lib;
|
||||
}
|
||||
|
||||
const b = self.buildSystem.b;
|
||||
const static = self.buildSystem.staticBuild;
|
||||
const lib = b.addLibrary(.{
|
||||
.name = self.name,
|
||||
.linkage = if (static) .static else .dynamic,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = self.opts.root_source_file,
|
||||
.link_libc = true,
|
||||
.optimize = self.buildSystem.optimize,
|
||||
.target = self.buildSystem.target,
|
||||
}),
|
||||
});
|
||||
|
||||
const allocator = self.buildSystem.b.allocator;
|
||||
|
||||
var modlist = std.ArrayList([]const u8){};
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
modlist.append(allocator, mod.name) catch @panic("out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
for (self.extras.items) |extra| {
|
||||
self.buildSystem.addExtraModule(lib.root_module, extra.name);
|
||||
}
|
||||
|
||||
lib.root_module.addImport("backlog", self.buildSystem.generateApi(self.name, modlist.items, null));
|
||||
lib.root_module.addOptions("BacklogOptions", self.buildSystem.options);
|
||||
|
||||
if (!static) {
|
||||
const installExtern = b.addInstallArtifact(lib, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "modules" } },
|
||||
});
|
||||
b.getInstallStep().dependOn(&installExtern.step);
|
||||
}
|
||||
|
||||
return lib;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn generateApi(self: *@This(), programName: []const u8, moduleList: []const []const u8, dynamicModules: ?[]const *DynamicModule,) *std.Build.Module {
|
||||
// std.debug.print("GENERATE API:: {s}\n", .{programName});
|
||||
|
||||
for (moduleList) |mod| {
|
||||
_ = mod;
|
||||
// std.debug.print("{s}\n", .{mod});
|
||||
}
|
||||
//
|
||||
if (self.generatedApis.get(programName)) |m| {
|
||||
return m;
|
||||
}
|
||||
|
||||
const run = self.b.addRunArtifact(self.apigen);
|
||||
const pfile = self.b.fmt("{s}Api.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
for (moduleList) |modName| {
|
||||
run.addArg(modName);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
for (moduleList) |modName| {
|
||||
mod.addImport(modName, self.nwdep.module(modName));
|
||||
}
|
||||
|
||||
const loadStatics = self.generateInstallStaticResources(programName, "content/_shaders") catch unreachable;
|
||||
mod.addImport("staticShaderLoader", loadStatics);
|
||||
|
||||
// deal with dynamics
|
||||
if (dynamicModules) |dynamics| {
|
||||
const loadDynamics = self.generateLoadDynamics(programName, dynamics) catch @panic("unknown");
|
||||
mod.addImport("loadDynamics", loadDynamics);
|
||||
} else {
|
||||
mod.addImport("loadDynamics", self.nwdep.module("loadDynamicsStub"));
|
||||
}
|
||||
|
||||
self.generatedApis.put(self.b.allocator, programName, mod) catch @panic("out of memory");
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn generateLoadDynamics(self: *@This(), programName: []const u8, dynamics: []const *DynamicModule) !*std.Build.Module {
|
||||
const run = self.b.addRunArtifact(self.loadDynamicsGen);
|
||||
const pfile = self.b.fmt("{s}LoadDynamics.zig", .{programName});
|
||||
const output = run.addOutputFileArg(pfile);
|
||||
if (self.staticBuild) {
|
||||
run.addArg("static");
|
||||
} else {
|
||||
run.addArg("dynamic");
|
||||
}
|
||||
|
||||
for (dynamics) |dyn| {
|
||||
run.addArg(dyn.name);
|
||||
}
|
||||
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
for (dynamics) |dyn| {
|
||||
const dynmod = dyn.compileInstall();
|
||||
mod.addImport(dyn.name, dynmod.root_module);
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
pub const AddProgramOptions = struct {
|
||||
name: []const u8,
|
||||
opt: bh.Options,
|
||||
root_source_file: std.Build.LazyPath,
|
||||
};
|
||||
|
||||
// all other modules are disabled by default
|
||||
pub const defaultEnabledModules: []const []const u8 = &.{
|
||||
"core",
|
||||
"assets",
|
||||
"platform",
|
||||
"rend",
|
||||
};
|
||||
|
||||
pub const moduleOrder: []const []const u8 = &.{
|
||||
"core",
|
||||
"sys",
|
||||
"assets",
|
||||
"platform",
|
||||
"net",
|
||||
"physics",
|
||||
"audio",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"papyrus",
|
||||
};
|
||||
|
||||
pub const GameModule = struct {
|
||||
name: []const u8,
|
||||
enabled: bool = true,
|
||||
};
|
||||
|
||||
pub const Program = struct {
|
||||
name: []const u8,
|
||||
opts: bh.Options,
|
||||
|
||||
// root_path: not used yet, we will create a second module that dynamically loads in to that root_path
|
||||
|
||||
gameModules: std.ArrayListUnmanaged(GameModule) = .{},
|
||||
gameModulesMap: std.StringHashMapUnmanaged(bool) = .{},
|
||||
buildSystem: *backlog.BuildSystem,
|
||||
iconPath: ?std.Build.LazyPath = null,
|
||||
allocator: std.mem.Allocator, // just set this to the build allocator
|
||||
|
||||
pub fn setModuleEnabled(self: *@This(), name: []const u8, enabled: bool) void {
|
||||
const r = self.gameModulesMap.getOrPut(self.allocator, name) catch unreachable;
|
||||
r.value_ptr.* = enabled;
|
||||
}
|
||||
|
||||
fn finalizeModules(self: *@This()) void {
|
||||
self.gameModules.clearRetainingCapacity();
|
||||
|
||||
for (moduleOrder) |mod| {
|
||||
var enabled: bool = false;
|
||||
for (defaultEnabledModules) |default| {
|
||||
if (std.mem.eql(u8, default, mod)) {
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.gameModulesMap.get(mod)) |value| {
|
||||
enabled = value;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
self.gameModules.append(self.allocator, .{ .name = mod, .enabled = enabled }) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setIconPath(self: *@This(), path: []const u8) void {
|
||||
if (self.iconPath != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.iconPath = self.buildSystem.generateRc(self.name, path) catch return;
|
||||
}
|
||||
|
||||
pub fn makeSpec(self: *@This()) *std.Build.Module {
|
||||
const b = self.buildSystem.b;
|
||||
const run = b.addRunArtifact(self.buildSystem.specGen);
|
||||
const pfile = run.addOutputFileArg(b.fmt("{s}-spec.zig", .{self.name}));
|
||||
run.addArg(self.name);
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled)
|
||||
run.addArg(mod.name);
|
||||
}
|
||||
|
||||
const mod = b.createModule(.{
|
||||
.target = self.opts.target,
|
||||
.optimize = self.opts.optimize,
|
||||
.root_source_file = pfile,
|
||||
});
|
||||
|
||||
const core = self.buildSystem.nwdep.module("core");
|
||||
mod.addImport("core", core);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
pub fn makeGameApi(self: *@This()) *std.Build.Module {
|
||||
const b = self.buildSystem.b;
|
||||
const apigen = self.buildSystem.nwdep.artifact("backlog-generate-modapi");
|
||||
const run = b.addRunArtifact(apigen);
|
||||
const pfile = run.addOutputFileArg(b.fmt("{s}-api.zig", .{self.name}));
|
||||
|
||||
run.addArg(self.name);
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
run.addArg(mod.name);
|
||||
}
|
||||
}
|
||||
|
||||
const api = b.createModule(.{
|
||||
.target = self.opts.target,
|
||||
.optimize = self.opts.optimize,
|
||||
.root_source_file = pfile,
|
||||
});
|
||||
|
||||
for (self.gameModules.items) |mod| {
|
||||
if (mod.enabled) {
|
||||
api.addImport(mod.name, self.buildSystem.nwdep.module(mod.name));
|
||||
}
|
||||
}
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
pub fn makeGameModule(self: *@This()) *std.Build.Step.Compile {
|
||||
const b = self.buildSystem.b;
|
||||
const bEngine = self.buildSystem.bEngine;
|
||||
|
||||
const opts = self.opts;
|
||||
|
||||
// 1. generate the main launch codeset.
|
||||
const lib = b.addLibrary(.{
|
||||
.name = b.fmt("{s}-mod", .{self.name}),
|
||||
.linkage = if (opts.static_build) .static else .dynamic,
|
||||
.root_module = b.createModule(.{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.root_source_file = bEngine.path("engine/modulelaunch.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
if (opts.static_build) {
|
||||
// b.installArtifact(lib);
|
||||
} else {
|
||||
const installExtern = b.addInstallArtifact(lib, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "modules" } },
|
||||
});
|
||||
b.getInstallStep().dependOn(&installExtern.step);
|
||||
}
|
||||
|
||||
return lib;
|
||||
}
|
||||
|
||||
pub fn generateStaticShaders(self: *@This()) *std.Build.Module {
|
||||
const loadStatics = self.buildSystem.generateInstallStaticResources(self.name, "content/_shaders") catch unreachable;
|
||||
return loadStatics;
|
||||
}
|
||||
|
||||
// spec is linked in both the base trampoline
|
||||
pub fn compileInstall(self: *@This()) *std.Build.Step.Compile {
|
||||
const bEngine = self.buildSystem.bEngine;
|
||||
const b = self.buildSystem.b;
|
||||
const core = self.buildSystem.nwdep.module("core");
|
||||
self.finalizeModules();
|
||||
|
||||
// 1. create the loader
|
||||
const exe = bEngine.addExecutable(.{
|
||||
.name = self.name,
|
||||
.root_module = bEngine.createModule(.{
|
||||
.target = self.opts.target,
|
||||
.optimize = self.opts.optimize,
|
||||
.root_source_file = bEngine.path("engine/main2.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
// link core
|
||||
const spec = self.makeSpec();
|
||||
exe.root_module.addImport("gamespec", spec);
|
||||
exe.root_module.addImport("core", core);
|
||||
exe.root_module.addOptions("BacklogOptions", self.buildSystem.options);
|
||||
|
||||
if (!self.opts.static_build) {
|
||||
const platform = self.buildSystem.nwdep.module("platform");
|
||||
exe.root_module.addImport("platform", platform);
|
||||
}
|
||||
|
||||
if (self.opts.static_build) {
|
||||
exe.root_module.addImport("staticShaderLoader", self.generateStaticShaders());
|
||||
}
|
||||
|
||||
const gameModule = self.makeGameModule();
|
||||
const gameApi = self.makeGameApi();
|
||||
gameModule.root_module.addImport("backlog", gameApi);
|
||||
gameModule.root_module.addImport("gamespec", spec);
|
||||
|
||||
if (self.opts.static_build) {
|
||||
exe.root_module.addImport("backlog", gameApi);
|
||||
}
|
||||
|
||||
// self.buildSystem.b.installArtifact(exe);
|
||||
const installed = self.buildSystem.b.addInstallArtifact(exe, .{});
|
||||
|
||||
{
|
||||
const run = b.addSystemCommand(&.{b.fmt("zig-out/bin/{s}", .{self.name})});
|
||||
run.step.dependOn(&installed.step);
|
||||
|
||||
if (b.args) |args| {
|
||||
run.addArgs(args);
|
||||
}
|
||||
|
||||
const run_exe = b.step(b.fmt("run-{s}", .{self.name}), "runs the program");
|
||||
run_exe.dependOn(&run.step);
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const bh = @import("bh");
|
||||
const backlog = @import("../build.zig");
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const LazyPath = Build.LazyPath;
|
||||
125
build/utils.zig
125
build/utils.zig
|
|
@ -1,114 +1,19 @@
|
|||
|
||||
pub fn buildGenerators(b: *std.Build, opts: bh.Options) void {
|
||||
const target = opts.target;
|
||||
const optimize = opts.optimize;
|
||||
const static_build = opts.static_build;
|
||||
|
||||
const fwdGeneratorExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-fwd",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateFwd.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateExe = b.addExecutable(.{
|
||||
.name = "backlog-apigen",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateApi.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateShaders = b.addExecutable(.{
|
||||
.name = "backlog-shaderEmbedGen",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateEmbeddedShaders.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateRcExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-rc",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateRc.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
const generateLoadDynamicsExe = b.addExecutable(.{
|
||||
.name = "backlog-generate-loadDynamics",
|
||||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.root_source_file = b.path("buildgen/generateLoadDynamics.zig"),
|
||||
}),
|
||||
});
|
||||
|
||||
b.installArtifact(fwdGeneratorExe);
|
||||
b.installArtifact(generateLoadDynamicsExe);
|
||||
b.installArtifact(generateExe);
|
||||
b.installArtifact(generateShaders);
|
||||
b.installArtifact(generateRcExe);
|
||||
|
||||
{
|
||||
const loadDynamicsStub = b.addModule("loadDynamicsStub", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/loadDynamicsStub.zig"),
|
||||
});
|
||||
|
||||
_ = loadDynamicsStub;
|
||||
|
||||
const mod = b.addModule("Backlog", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = b.path("engine/backlog.zig"),
|
||||
});
|
||||
|
||||
for (depList()) |depName| {
|
||||
const dep = b.dependency(
|
||||
depName,
|
||||
.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
},
|
||||
);
|
||||
|
||||
const run = b.addRunArtifact(fwdGeneratorExe);
|
||||
const output = run.addOutputFileArg(b.fmt("{s}_fwd.zig", .{depName}));
|
||||
|
||||
const modFwd = b.addModule(depName, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
|
||||
mod.addImport(b.fmt("{s}", .{depName}), modFwd);
|
||||
modFwd.addImport("module", dep.module(depName));
|
||||
|
||||
// mod.addImport(depName, dep.module(depName));
|
||||
}
|
||||
|
||||
// link in large platform support functions... special case.
|
||||
{
|
||||
const dep = b.dependency("sdl3", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.static_build = static_build,
|
||||
});
|
||||
const lib = dep.module("SDL3");
|
||||
|
||||
mod.addImport("sdl3_fwd", lib);
|
||||
}
|
||||
}
|
||||
pub fn loadFileAlloc(filename: []const u8, comptime alignment: usize, allocator: std.mem.Allocator) ![]u8 {
|
||||
var file = try std.fs.cwd().openFile(filename, .{});
|
||||
defer file.close();
|
||||
const filesize = (try file.stat()).size + 1; // add null byte
|
||||
const buffer: []align(alignment) u8 = try allocator.alignedAlloc(u8, alignment, filesize);
|
||||
errdefer allocator.free(buffer);
|
||||
try file.reader().readNoEof(buffer[0 .. buffer.len - 1]);
|
||||
buffer[buffer.len - 1] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
pub fn checkAndFormatAst(allocator: std.mem.Allocator, input: []const u8) ![]u8 {
|
||||
var ast = try std.zig.Ast.parse(allocator, @as([:0]const u8, @ptrCast(input)), .zig);
|
||||
const out = try ast.render(allocator);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const bh = @import("bh");
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn usage() void {
|
||||
std.debug.print("generates a loader module which calls the start_modules for all the modules that the game module depends on.\ngenerate-mod-api <output file name> <module_name> <module list>\n", .{});
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
// Get output filename from build system
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
if (args.len < 3) {
|
||||
usage();
|
||||
@panic("Missing output filename\n");
|
||||
}
|
||||
const out_path = args[1];
|
||||
const module_name = args[2];
|
||||
const module_list = args[3..];
|
||||
|
||||
var writer = std.ArrayList(u8){};
|
||||
|
||||
try writer.print(allocator, "pub const moduleName:[]const u8 = \"{s}\";\n", .{module_name});
|
||||
|
||||
try writer.print(allocator, "pub const moduleList:[]const []const u8 = &.{{\n", .{});
|
||||
for (module_list) |mod| {
|
||||
try writer.print(allocator, "\"{s}\",\n", .{mod});
|
||||
}
|
||||
try writer.print(allocator, "}};\n", .{});
|
||||
|
||||
for (module_list) |mod| {
|
||||
try writer.print(allocator, "pub const {s} = @import(\"{s}\").module;\n", .{ mod, mod });
|
||||
}
|
||||
|
||||
try writer.append(allocator, 0);
|
||||
|
||||
const file = try std.fs.cwd().createFile(out_path, .{});
|
||||
defer file.close();
|
||||
|
||||
var ast = try std.zig.Ast.parse(allocator, @as([:0]const u8, @ptrCast(writer.items[0 .. writer.items.len - 1])), .zig);
|
||||
|
||||
// std.debug.print("output=\n{s}", .{writer.items[0 .. writer.items.len - 1]});
|
||||
defer ast.deinit(allocator);
|
||||
const out = try ast.renderAlloc(allocator);
|
||||
|
||||
// Write the content to the file
|
||||
try file.writeAll(out);
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn usage() void {
|
||||
std.debug.print("generate-program-spec <output file name> <spec_name> <module list>\n", .{});
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
// Get output filename from build system
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
if (args.len < 3) {
|
||||
usage();
|
||||
@panic("Missing output filename\n");
|
||||
}
|
||||
const out_path = args[1];
|
||||
|
||||
const programName = args[2];
|
||||
const modules = args[3..];
|
||||
|
||||
// Generate Zig code content
|
||||
var writer = std.ArrayList(u8){};
|
||||
_ = try writer.appendSlice(allocator,
|
||||
\\const core = @import("core").module;
|
||||
\\
|
||||
\\ const std = @import("std");
|
||||
\\ pub fn getSpec() !*core.SpecVariantMap {
|
||||
\\ const spec = try std.heap.smp_allocator.create(core.SpecVariantMap);
|
||||
\\ spec.* = try core.createSpecVariant(.{
|
||||
\\ .useGPA = true,
|
||||
);
|
||||
|
||||
try writer.print(allocator, ".name = \"{s}\",\n", .{programName});
|
||||
try writer.print(allocator, ".moduleName = \"{s}-mod\",\n", .{programName});
|
||||
|
||||
for (modules) |mod| {
|
||||
try writer.print(allocator, ".{s} = true,\n", .{mod});
|
||||
}
|
||||
|
||||
_ = try writer.appendSlice(allocator,
|
||||
\\ }, std.heap.smp_allocator);
|
||||
\\
|
||||
\\ return spec;
|
||||
\\ }
|
||||
\\
|
||||
);
|
||||
|
||||
_ = try writer.print(allocator, "pub const programName = \"{s}\";", .{programName});
|
||||
|
||||
try writer.append(allocator, 0);
|
||||
|
||||
// Write to specified output file
|
||||
// Open or create the file for writing (overwrites if it exists)
|
||||
const file = try std.fs.cwd().createFile(out_path, .{});
|
||||
defer file.close();
|
||||
|
||||
var ast = try std.zig.Ast.parse(allocator, @as([:0]const u8, @ptrCast(writer.items[0 .. writer.items.len - 1])), .zig);
|
||||
|
||||
// std.debug.print("output=\n{s}", .{content.items[0 .. content.items.len - 1]});
|
||||
defer ast.deinit(allocator);
|
||||
const out = try ast.renderAlloc(allocator);
|
||||
|
||||
// Write the content to the file
|
||||
try file.writeAll(out);
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// templated file
|
||||
//
|
||||
// should still be a formattable zig file
|
||||
// even if it is not compile-able
|
||||
//
|
||||
// definitions marked with // TEMPLATE_DEFINITION
|
||||
// are removed during the templating step
|
||||
|
||||
const programName = "__program_name"; // TEMPLATE_DEFINITION
|
||||
|
||||
export fn loadModule() void {}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// templated file
|
||||
//
|
||||
// should still be a formattable zig file
|
||||
// even if it is not compile-able
|
||||
//
|
||||
// definitions marked with // TEMPLATE_DEFINITION
|
||||
// are removed during the templating step
|
||||
|
||||
const core = @import("core");
|
||||
|
||||
const programName = "__program_name"; // TEMPLATE_DEFINITION
|
||||
|
||||
pub export fn startup_module(p_allocator: *anyopaque, p_a: ?*anyopaque) bool {
|
||||
const allocator = core.modulePreamble(p_allocator, p_a) catch return false;
|
||||
_ = allocator;
|
||||
// imgui.setupFromModule();
|
||||
// platform.setupFromModule();
|
||||
// sys.setupFromModule();
|
||||
// rend.setupFromModule();
|
||||
// backlog.physics.setupFromModule();
|
||||
|
||||
core.engine_logs("creating externgame");
|
||||
// start_module(core.startup_getArgs(p_a.?)) catch return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
pub export fn startup_module(p_allocator: *anyopaque, p_a: ?*anyopaque) bool {
|
||||
const allocator = core.modulePreamble(p_allocator, p_a) catch return false;
|
||||
_ = allocator;
|
||||
core.engine_logs("module started");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub export fn shutdown_module() void {}
|
||||
|
||||
// const backlog = @import("backlog");
|
||||
// const core = backlog.core;
|
||||
|
||||
const core = @import("core").module;
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
pub fn loadFileAlloc(filename: []const u8, comptime alignment: usize, allocator: std.mem.Allocator) ![]u8 {
|
||||
var file = try std.fs.cwd().openFile(filename, .{});
|
||||
defer file.close();
|
||||
const filesize = (try file.stat()).size + 1; // add null byte
|
||||
const buffer: []align(alignment) u8 = try allocator.alignedAlloc(u8, alignment, filesize);
|
||||
errdefer allocator.free(buffer);
|
||||
try file.reader().readNoEof(buffer[0 .. buffer.len - 1]);
|
||||
buffer[buffer.len - 1] = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
pub fn checkAndFormatAst(allocator: std.mem.Allocator, input: []const u8) ![]u8 {
|
||||
var ast = try std.zig.Ast.parse(allocator, @as([:0]const u8, @ptrCast(input)), .zig);
|
||||
const out = try ast.render(allocator);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
.fontcache/
|
||||
Saved/
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
const std = @import("std");
|
||||
const Backlog = @import("Backlog");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
var blbuild = Backlog.init(b, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.backlogRoot = "../",
|
||||
});
|
||||
|
||||
// blbuild.addDependencyInstalls(b, .ReleaseFast); //.ReleaseFast);
|
||||
const zuck = blbuild.program(.{
|
||||
.name = "zuck",
|
||||
.desc = "A tiny shooter made with this engine.",
|
||||
.root_source_file = b.path("zuck/main.zig"),
|
||||
});
|
||||
|
||||
zuck.setModuleEnabled("imgui", true);
|
||||
zuck.setModuleEnabled("audio", true);
|
||||
zuck.setModuleEnabled("physics", true);
|
||||
zuck.setModuleEnabled("ui", true);
|
||||
zuck.setModuleEnabled("sys", true);
|
||||
zuck.setModuleEnabled("net", true);
|
||||
zuck.addExtraModule("gameExtras");
|
||||
|
||||
{
|
||||
const zuckGame = zuck.addDynamicModule("zuckGame", b.path("zuck/game.zig"));
|
||||
_ = zuckGame.compileInstall();
|
||||
}
|
||||
|
||||
_ = zuck.compileInstall();
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
.{
|
||||
.name = .MyProjects,
|
||||
.version = "0.0.0",
|
||||
.dependencies = .{
|
||||
// Neonwood's /lib folder contains
|
||||
// third party libraries.
|
||||
//
|
||||
// They can be directly accessed
|
||||
//
|
||||
// as well as any of the engine/ libraries
|
||||
//
|
||||
// in the general case though,
|
||||
|
||||
// engine libraries
|
||||
.Backlog = .{ .path = "../" },
|
||||
|
||||
|
||||
.SpirvReflect = .{ .path = "../lib/spirv-reflect-zig" },
|
||||
|
||||
.gameExtras = .{.path = "../extras/gameExtras"},
|
||||
|
||||
.zphysics = .{.path = "../lib/zphysics"},
|
||||
.spng = .{ .path = "../lib/spng" },
|
||||
.ozz = .{ .path = "../lib/ozz" },
|
||||
.sdl3 = .{ .path = "../lib/sdl3" },
|
||||
.miniaudio = .{ .path = "../lib/miniaudio" },
|
||||
.lua = .{ .path = "../lib/lua" },
|
||||
|
||||
.enet = .{ .path = "../lib/enet" },
|
||||
},
|
||||
.paths = .{
|
||||
"",
|
||||
},
|
||||
.fingerprint = 0x14099f73bcfb78d8,
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,198 +0,0 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct type_Uniforms
|
||||
{
|
||||
float4 viewPos;
|
||||
float4 lightPosition;
|
||||
float4 directionalLight;
|
||||
float4 directionalLightColor;
|
||||
float2 screenSize;
|
||||
float lightPower;
|
||||
float time;
|
||||
};
|
||||
|
||||
struct Scene
|
||||
{
|
||||
float4x4 Model;
|
||||
uint textureMode;
|
||||
int animation;
|
||||
uint flags;
|
||||
float float0;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_Scene
|
||||
{
|
||||
Scene _m0[1];
|
||||
};
|
||||
|
||||
constant float4 _92 = {};
|
||||
constant float3 _94 = {};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 out_var_SV_Target0 [[color(0)]];
|
||||
float4 out_var_SV_Target1 [[color(1)]];
|
||||
float4 out_var_SV_Target2 [[color(2)]];
|
||||
float4 out_var_SV_Target3 [[color(3)]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float2 in_var_TEXCOORD0 [[user(locn0)]];
|
||||
float3 in_var_TEXCOORD1 [[user(locn1)]];
|
||||
float3 in_var_TEXCOORD2 [[user(locn2)]];
|
||||
float4 in_var_TEXCOORD3 [[user(locn3)]];
|
||||
uint in_var_TEXCOORD4 [[user(locn4)]];
|
||||
float3 in_var_TEXCOORD5 [[user(locn5)]];
|
||||
};
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], const device type_StructuredBuffer_Scene& scene [[buffer(1)]], texture2d<float> Texture0 [[texture(0)]], texture2d<float> Texture1 [[texture(1)]], texture2d<float> Texture2 [[texture(2)]], texture2d<float> DirectionalShadowDepthMap [[texture(3)]], sampler Sampler0 [[sampler(0)]], sampler Sampler1 [[sampler(1)]], sampler Sampler2 [[sampler(2)]], sampler DirectionalShadowSampler [[sampler(3)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
int _104 = int(scene._m0[in.in_var_TEXCOORD4].textureMode);
|
||||
int _105 = _104 & 240;
|
||||
float4 _139;
|
||||
if (_105 == 16)
|
||||
{
|
||||
float3 _126 = float3(Texture0.sample(Sampler0, in.in_var_TEXCOORD0).x, Texture1.sample(Sampler1, in.in_var_TEXCOORD0).x, Texture2.sample(Sampler2, in.in_var_TEXCOORD0).x) + float3(-0.0625, -0.5, -0.5);
|
||||
_139 = float4(dot(_126, float3(1.164000034332275390625, 0.0, 1.7929999828338623046875)), dot(_126, float3(1.164000034332275390625, -0.212999999523162841796875, -0.53299999237060546875)), dot(_126, float3(1.164000034332275390625, 2.111999988555908203125, 0.0)), 1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
float4 _138;
|
||||
if (_105 == 0)
|
||||
{
|
||||
_138 = Texture0.sample(Sampler0, in.in_var_TEXCOORD0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_138 = _92;
|
||||
}
|
||||
_139 = _138;
|
||||
}
|
||||
float4 _145;
|
||||
if ((_104 & 1) == 1)
|
||||
{
|
||||
_145 = powr(_139, float4(2.2000000476837158203125));
|
||||
}
|
||||
else
|
||||
{
|
||||
_145 = _139;
|
||||
}
|
||||
if (_145.w < 0.00999999977648258209228515625)
|
||||
{
|
||||
discard_fragment();
|
||||
}
|
||||
float3 _155 = float3(0.800000011920928955078125, 0.800000011920928955078125, 0.5) * Uniforms.lightPower;
|
||||
float3 _202;
|
||||
do
|
||||
{
|
||||
float3 _159 = Uniforms.lightPosition.xyz - in.in_var_TEXCOORD1;
|
||||
float _160 = length(_159);
|
||||
float _161 = _160 * _160;
|
||||
float _167;
|
||||
if (_160 > 2.0)
|
||||
{
|
||||
_167 = 0.5 / _161;
|
||||
}
|
||||
else
|
||||
{
|
||||
_167 = 1.0 / _161;
|
||||
}
|
||||
float _172;
|
||||
if (_160 > 4.0)
|
||||
{
|
||||
_172 = _167 * 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
_172 = _167;
|
||||
}
|
||||
float _174 = (_160 > 15.0) ? 0.0 : _172;
|
||||
float _176 = (_174 > 1.0) ? 1.0 : _174;
|
||||
if (length(in.in_var_TEXCOORD2) < 0.100000001490116119384765625)
|
||||
{
|
||||
_202 = (in.in_var_TEXCOORD1 * _155) * _176;
|
||||
break;
|
||||
}
|
||||
float3 _183 = fast::normalize(_159);
|
||||
_202 = ((_155 * precise::max(dot(_183, in.in_var_TEXCOORD2), 0.0)) * _176) + (((_155 * powr(precise::max(dot(in.in_var_TEXCOORD2, fast::normalize(_183 + fast::normalize(in.in_var_TEXCOORD1 - Uniforms.viewPos.xyz))), 0.0), 30.0)) * 2.0) * _176);
|
||||
break;
|
||||
} while(false);
|
||||
float3 _219 = ((in.in_var_TEXCOORD3.xyz / float3(in.in_var_TEXCOORD3.w)) * 0.5) + float3(0.5);
|
||||
float3 _221;
|
||||
_221.x = _219.x;
|
||||
float2 _222 = _221.xy;
|
||||
_222.y = -_219.y;
|
||||
float _229;
|
||||
int _232;
|
||||
int _234;
|
||||
_229 = 0.0;
|
||||
_232 = 0;
|
||||
_234 = -2;
|
||||
float _230;
|
||||
int _233;
|
||||
for (; _234 <= 2; _229 = _230, _232 = _233, _234++)
|
||||
{
|
||||
_233 = _232;
|
||||
_230 = _229;
|
||||
for (int _243 = -2; _243 <= 2; )
|
||||
{
|
||||
_233++;
|
||||
_230 += float((in.in_var_TEXCOORD3.z - 0.0030000000260770320892333984375) > DirectionalShadowDepthMap.sample(DirectionalShadowSampler, (_222 + float2(float(_243) * 0.000244140625, float(_234) * 0.000244140625))).x);
|
||||
_243++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
float3 _272;
|
||||
if ((_104 & 2) > 0)
|
||||
{
|
||||
_272 = powr(_145.xyz, float3(0.4545454680919647216796875));
|
||||
}
|
||||
else
|
||||
{
|
||||
_272 = _145.xyz * ((float3(0.100000001490116119384765625) + _202) + ((Uniforms.directionalLightColor.xyz * precise::max(dot(Uniforms.directionalLight.xyz, in.in_var_TEXCOORD2), 0.0)) * (1.0 - (_229 / float(_232)))));
|
||||
}
|
||||
float4 _289;
|
||||
if (_272.x > 1.0)
|
||||
{
|
||||
float4 _288 = float4(0.0);
|
||||
_288.x = _272.x;
|
||||
_289 = _288;
|
||||
}
|
||||
else
|
||||
{
|
||||
_289 = float4(0.0);
|
||||
}
|
||||
float4 _294;
|
||||
if (_272.y > 1.0)
|
||||
{
|
||||
float4 _293 = _289;
|
||||
_293.y = _272.y;
|
||||
_294 = _293;
|
||||
}
|
||||
else
|
||||
{
|
||||
_294 = _289;
|
||||
}
|
||||
float4 _299;
|
||||
if (_272.z > 1.0)
|
||||
{
|
||||
float4 _298 = _294;
|
||||
_298.z = _272.z;
|
||||
_299 = _298;
|
||||
}
|
||||
else
|
||||
{
|
||||
_299 = _294;
|
||||
}
|
||||
out.out_var_SV_Target0 = float4(_272, _145.w);
|
||||
out.out_var_SV_Target1 = _299;
|
||||
out.out_var_SV_Target2 = float4(in.in_var_TEXCOORD1, 1.0);
|
||||
out.out_var_SV_Target3 = float4(in.in_var_TEXCOORD5, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct Scene
|
||||
{
|
||||
float4x4 Model;
|
||||
uint textureMode;
|
||||
int animation;
|
||||
uint flags;
|
||||
float float0;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_Scene
|
||||
{
|
||||
Scene _m0[1];
|
||||
};
|
||||
|
||||
struct BoneTransform
|
||||
{
|
||||
float4x4 final;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_BoneTransform
|
||||
{
|
||||
BoneTransform _m0[1];
|
||||
};
|
||||
|
||||
struct type_Uniforms
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
float4x4 NoTranslateView;
|
||||
float4x4 ViewTransform;
|
||||
float4x4 ShadowMapProjection;
|
||||
float4 cameraPosition;
|
||||
float time;
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float2 out_var_TEXCOORD0 [[user(locn0)]];
|
||||
float3 out_var_TEXCOORD1 [[user(locn1)]];
|
||||
float3 out_var_TEXCOORD2 [[user(locn2)]];
|
||||
float4 out_var_TEXCOORD3 [[user(locn3)]];
|
||||
uint out_var_TEXCOORD4 [[user(locn4)]];
|
||||
float3 out_var_TEXCOORD5 [[user(locn5)]];
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float3 in_var_TEXCOORD0 [[attribute(0)]];
|
||||
float3 in_var_TEXCOORD1 [[attribute(1)]];
|
||||
float2 in_var_TEXCOORD3 [[attribute(3)]];
|
||||
uint in_var_TEXCOORD4 [[attribute(4)]];
|
||||
uint in_var_TEXCOORD5 [[attribute(5)]];
|
||||
};
|
||||
|
||||
vertex main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], const device type_StructuredBuffer_Scene& scene [[buffer(1)]], const device type_StructuredBuffer_BoneTransform& animationBuffer [[buffer(2)]], uint gl_InstanceIndex [[instance_id]])
|
||||
{
|
||||
main0_out out = {};
|
||||
float3 _111;
|
||||
if (scene._m0[gl_InstanceIndex].animation != (-1))
|
||||
{
|
||||
float3 _83;
|
||||
_83 = float3(0.0);
|
||||
for (int _86 = 0; _86 < 4; )
|
||||
{
|
||||
uint _92 = (8u * uint(_86)) & 31u;
|
||||
_83 += ((animationBuffer._m0[uint(scene._m0[gl_InstanceIndex].animation) + ((in.in_var_TEXCOORD4 >> _92) & 255u)].final * float4(in.in_var_TEXCOORD0, 1.0)).xyz * (float((in.in_var_TEXCOORD5 >> _92) & 255u) * 0.0039215688593685626983642578125));
|
||||
_86++;
|
||||
continue;
|
||||
}
|
||||
_111 = _83;
|
||||
}
|
||||
else
|
||||
{
|
||||
_111 = in.in_var_TEXCOORD0;
|
||||
}
|
||||
float4 _115 = float4(_111, 1.0);
|
||||
float3 _207;
|
||||
float3 _208;
|
||||
float3 _209;
|
||||
float4 _210;
|
||||
float4 _211;
|
||||
if (int(scene._m0[gl_InstanceIndex].flags & 4u) > 0)
|
||||
{
|
||||
float3 _127 = (scene._m0[gl_InstanceIndex].Model * float4(0.0, 0.0, 0.0, 1.0)).xyz;
|
||||
float3 _132 = fast::normalize(Uniforms.cameraPosition.xyz - _127);
|
||||
float3 _134 = fast::normalize(cross(float3(0.0, 1.0, 0.0), _132));
|
||||
float4 _152 = float4((_127 + ((_134 * (-in.in_var_TEXCOORD0.x)) * scene._m0[gl_InstanceIndex].float0)) + ((cross(_132, _134) * in.in_var_TEXCOORD0.y) * scene._m0[gl_InstanceIndex].float0), 1.0);
|
||||
_207 = _152.xyz;
|
||||
_208 = _132;
|
||||
_209 = float3(0.0, 0.0, 1.0);
|
||||
_210 = Uniforms.ViewProjection * _152;
|
||||
_211 = _152;
|
||||
}
|
||||
else
|
||||
{
|
||||
float4 _159 = scene._m0[gl_InstanceIndex].Model * _115;
|
||||
float4x4 _194 = float4x4(float4(fast::normalize(float3(scene._m0[gl_InstanceIndex].Model[0][0], scene._m0[gl_InstanceIndex].Model[1][0], scene._m0[gl_InstanceIndex].Model[2][0])), 0.0), float4(fast::normalize(float3(scene._m0[gl_InstanceIndex].Model[0][1], scene._m0[gl_InstanceIndex].Model[1][1], scene._m0[gl_InstanceIndex].Model[2][1])), 0.0), float4(fast::normalize(float3(scene._m0[gl_InstanceIndex].Model[0][2], scene._m0[gl_InstanceIndex].Model[1][2], scene._m0[gl_InstanceIndex].Model[2][2])), 0.0), float4(0.0, 0.0, 0.0, 1.0));
|
||||
float4 _198 = float4(in.in_var_TEXCOORD1, 1.0);
|
||||
_207 = _159.xyz;
|
||||
_208 = fast::normalize(_198 * _194).xyz;
|
||||
_209 = (_198 * (_194 * transpose(Uniforms.NoTranslateView))).xyz;
|
||||
_210 = Uniforms.ViewProjection * (scene._m0[gl_InstanceIndex].Model * _115);
|
||||
_211 = _159;
|
||||
}
|
||||
out.out_var_TEXCOORD0 = in.in_var_TEXCOORD3;
|
||||
out.out_var_TEXCOORD1 = _207;
|
||||
out.out_var_TEXCOORD2 = _208;
|
||||
out.out_var_TEXCOORD3 = Uniforms.ShadowMapProjection * _211;
|
||||
out.out_var_TEXCOORD4 = gl_InstanceIndex;
|
||||
out.out_var_TEXCOORD5 = _209;
|
||||
out.gl_Position = _210;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct Scene
|
||||
{
|
||||
float2 imagePosition;
|
||||
float2 _imageSize;
|
||||
float2 anchorPoint;
|
||||
float2 scale;
|
||||
float alpha;
|
||||
float borderWidth;
|
||||
uint flags;
|
||||
float4 baseColor;
|
||||
float4 rounding;
|
||||
float4 edgeColor;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_Scene
|
||||
{
|
||||
Scene _m0[1];
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 out_var_SV_Target0 [[color(0)]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 in_var_TEXCOORD0 [[user(locn0)]];
|
||||
float2 in_var_TEXCOORD1 [[user(locn1)]];
|
||||
float2 in_var_TEXCOORD2 [[user(locn2)]];
|
||||
uint in_var_TEXCOORD3 [[user(locn3)]];
|
||||
};
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_Scene& scene [[buffer(0)]], texture2d<float> Texture0 [[texture(0)]], sampler Sampler0 [[sampler(0)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
bool _71 = in.in_var_TEXCOORD2.x < scene._m0[in.in_var_TEXCOORD3].rounding.x;
|
||||
bool _78;
|
||||
if (_71)
|
||||
{
|
||||
_78 = in.in_var_TEXCOORD2.y < scene._m0[in.in_var_TEXCOORD3].rounding.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
_78 = false;
|
||||
}
|
||||
float _95;
|
||||
float3 _96;
|
||||
if (_78)
|
||||
{
|
||||
float _82 = distance(in.in_var_TEXCOORD2, float2(scene._m0[in.in_var_TEXCOORD3].rounding.x));
|
||||
float _93;
|
||||
float3 _94;
|
||||
if (_82 > scene._m0[in.in_var_TEXCOORD3].rounding.x)
|
||||
{
|
||||
discard_fragment();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abs(_82 - scene._m0[in.in_var_TEXCOORD3].rounding.x) < 1.0)
|
||||
{
|
||||
_93 = scene._m0[in.in_var_TEXCOORD3].edgeColor.w;
|
||||
_94 = scene._m0[in.in_var_TEXCOORD3].edgeColor.xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
_93 = scene._m0[in.in_var_TEXCOORD3].alpha;
|
||||
_94 = in.in_var_TEXCOORD0.xyz;
|
||||
}
|
||||
}
|
||||
_95 = _93;
|
||||
_96 = _94;
|
||||
}
|
||||
else
|
||||
{
|
||||
_95 = scene._m0[in.in_var_TEXCOORD3].alpha;
|
||||
_96 = in.in_var_TEXCOORD0.xyz;
|
||||
}
|
||||
float _99 = scene._m0[in.in_var_TEXCOORD3]._imageSize.x - scene._m0[in.in_var_TEXCOORD3].rounding.y;
|
||||
bool _106;
|
||||
if (in.in_var_TEXCOORD2.x > _99)
|
||||
{
|
||||
_106 = in.in_var_TEXCOORD2.y < scene._m0[in.in_var_TEXCOORD3].rounding.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
_106 = false;
|
||||
}
|
||||
float _123;
|
||||
float3 _124;
|
||||
if (_106)
|
||||
{
|
||||
float _110 = distance(in.in_var_TEXCOORD2, float2(_99, scene._m0[in.in_var_TEXCOORD3].rounding.y));
|
||||
float _121;
|
||||
float3 _122;
|
||||
if (_110 > scene._m0[in.in_var_TEXCOORD3].rounding.y)
|
||||
{
|
||||
discard_fragment();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abs(_110 - scene._m0[in.in_var_TEXCOORD3].rounding.y) < 1.0)
|
||||
{
|
||||
_121 = scene._m0[in.in_var_TEXCOORD3].edgeColor.w;
|
||||
_122 = scene._m0[in.in_var_TEXCOORD3].edgeColor.xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
_121 = _95;
|
||||
_122 = _96;
|
||||
}
|
||||
}
|
||||
_123 = _121;
|
||||
_124 = _122;
|
||||
}
|
||||
else
|
||||
{
|
||||
_123 = _95;
|
||||
_124 = _96;
|
||||
}
|
||||
bool _132;
|
||||
if (_71)
|
||||
{
|
||||
_132 = in.in_var_TEXCOORD2.y > (scene._m0[in.in_var_TEXCOORD3]._imageSize.y - scene._m0[in.in_var_TEXCOORD3].rounding.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
_132 = false;
|
||||
}
|
||||
float _151;
|
||||
float3 _152;
|
||||
if (_132)
|
||||
{
|
||||
float _138 = distance(in.in_var_TEXCOORD2, float2(scene._m0[in.in_var_TEXCOORD3].rounding.x, scene._m0[in.in_var_TEXCOORD3]._imageSize.y - scene._m0[in.in_var_TEXCOORD3].rounding.y));
|
||||
float _149;
|
||||
float3 _150;
|
||||
if (_138 > scene._m0[in.in_var_TEXCOORD3].rounding.y)
|
||||
{
|
||||
discard_fragment();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abs(_138 - scene._m0[in.in_var_TEXCOORD3].rounding.y) < 1.0)
|
||||
{
|
||||
_149 = scene._m0[in.in_var_TEXCOORD3].edgeColor.w;
|
||||
_150 = scene._m0[in.in_var_TEXCOORD3].edgeColor.xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
_149 = _123;
|
||||
_150 = _124;
|
||||
}
|
||||
}
|
||||
_151 = _149;
|
||||
_152 = _150;
|
||||
}
|
||||
else
|
||||
{
|
||||
_151 = _123;
|
||||
_152 = _124;
|
||||
}
|
||||
bool _163;
|
||||
if (in.in_var_TEXCOORD2.x > (scene._m0[in.in_var_TEXCOORD3]._imageSize.x - scene._m0[in.in_var_TEXCOORD3].rounding.w))
|
||||
{
|
||||
_163 = (scene._m0[in.in_var_TEXCOORD3]._imageSize.y - in.in_var_TEXCOORD2.y) < scene._m0[in.in_var_TEXCOORD3].rounding.w;
|
||||
}
|
||||
else
|
||||
{
|
||||
_163 = false;
|
||||
}
|
||||
float _183;
|
||||
float3 _184;
|
||||
if (_163)
|
||||
{
|
||||
float _170 = distance(in.in_var_TEXCOORD2, float2(scene._m0[in.in_var_TEXCOORD3]._imageSize.x - scene._m0[in.in_var_TEXCOORD3].rounding.x, scene._m0[in.in_var_TEXCOORD3]._imageSize.y - scene._m0[in.in_var_TEXCOORD3].rounding.y));
|
||||
float _181;
|
||||
float3 _182;
|
||||
if (_170 > scene._m0[in.in_var_TEXCOORD3].rounding.y)
|
||||
{
|
||||
discard_fragment();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abs(_170 - scene._m0[in.in_var_TEXCOORD3].rounding.y) < 1.0)
|
||||
{
|
||||
_181 = scene._m0[in.in_var_TEXCOORD3].edgeColor.w;
|
||||
_182 = scene._m0[in.in_var_TEXCOORD3].edgeColor.xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
_181 = _151;
|
||||
_182 = _152;
|
||||
}
|
||||
}
|
||||
_183 = _181;
|
||||
_184 = _182;
|
||||
}
|
||||
else
|
||||
{
|
||||
_183 = _151;
|
||||
_184 = _152;
|
||||
}
|
||||
bool _191;
|
||||
if (!(in.in_var_TEXCOORD2.x < scene._m0[in.in_var_TEXCOORD3].borderWidth))
|
||||
{
|
||||
_191 = in.in_var_TEXCOORD2.x > (scene._m0[in.in_var_TEXCOORD3]._imageSize.x - scene._m0[in.in_var_TEXCOORD3].borderWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
_191 = true;
|
||||
}
|
||||
bool _198;
|
||||
if (!_191)
|
||||
{
|
||||
_198 = in.in_var_TEXCOORD2.y < scene._m0[in.in_var_TEXCOORD3].borderWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
_198 = true;
|
||||
}
|
||||
bool _207;
|
||||
if (!_198)
|
||||
{
|
||||
_207 = in.in_var_TEXCOORD2.y > (scene._m0[in.in_var_TEXCOORD3]._imageSize.y - scene._m0[in.in_var_TEXCOORD3].borderWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
_207 = true;
|
||||
}
|
||||
float _212;
|
||||
float3 _213;
|
||||
if (_207)
|
||||
{
|
||||
_212 = scene._m0[in.in_var_TEXCOORD3].edgeColor.w;
|
||||
_213 = scene._m0[in.in_var_TEXCOORD3].edgeColor.xyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
_212 = _183;
|
||||
_213 = _184;
|
||||
}
|
||||
float4 _238;
|
||||
if ((scene._m0[in.in_var_TEXCOORD3].flags & 1u) > 0u)
|
||||
{
|
||||
float4 _227 = Texture0.sample(Sampler0, float2(in.in_var_TEXCOORD1.x, 1.0 - in.in_var_TEXCOORD1.y));
|
||||
_238 = float4(_227.xyz, _227.w * _212);
|
||||
}
|
||||
else
|
||||
{
|
||||
_238 = float4(_213, _212);
|
||||
}
|
||||
out.out_var_SV_Target0 = _238;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct Scene
|
||||
{
|
||||
float2 imagePosition;
|
||||
float2 _imageSize;
|
||||
float2 anchorPoint;
|
||||
float2 scale;
|
||||
float alpha;
|
||||
float borderWidth;
|
||||
uint flags;
|
||||
float4 baseColor;
|
||||
float4 rounding;
|
||||
float4 edgeColor;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_Scene
|
||||
{
|
||||
Scene _m0[1];
|
||||
};
|
||||
|
||||
struct type_Uniforms
|
||||
{
|
||||
float2 Extents;
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 out_var_TEXCOORD0 [[user(locn0)]];
|
||||
float2 out_var_TEXCOORD1 [[user(locn1)]];
|
||||
float2 out_var_TEXCOORD2 [[user(locn2)]];
|
||||
uint out_var_TEXCOORD3 [[user(locn3)]];
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float3 in_var_TEXCOORD0 [[attribute(0)]];
|
||||
float2 in_var_TEXCOORD3 [[attribute(3)]];
|
||||
};
|
||||
|
||||
vertex main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], const device type_StructuredBuffer_Scene& scene [[buffer(1)]], uint gl_InstanceIndex [[instance_id]])
|
||||
{
|
||||
main0_out out = {};
|
||||
float2 _63 = scene._m0[gl_InstanceIndex]._imageSize / Uniforms.Extents;
|
||||
float2 _69 = (((scene._m0[gl_InstanceIndex].imagePosition / Uniforms.Extents) * 2.0) - float2(1.0)) - ((scene._m0[gl_InstanceIndex].anchorPoint * _63) * scene._m0[gl_InstanceIndex].scale);
|
||||
float _85 = _69.y + ((in.in_var_TEXCOORD0.y * _63.y) * scene._m0[gl_InstanceIndex].scale.y);
|
||||
float4 _88 = float4(_69.x + ((in.in_var_TEXCOORD0.x * _63.x) * scene._m0[gl_InstanceIndex].scale.x), _85, in.in_var_TEXCOORD0.z, 1.0);
|
||||
_88.y = -_85;
|
||||
float2 _100 = ((in.in_var_TEXCOORD0.xy - scene._m0[gl_InstanceIndex].anchorPoint) * float2(0.5)) * scene._m0[gl_InstanceIndex]._imageSize;
|
||||
_100.y = scene._m0[gl_InstanceIndex]._imageSize.y - _100.y;
|
||||
out.out_var_TEXCOORD0 = scene._m0[gl_InstanceIndex].baseColor;
|
||||
out.out_var_TEXCOORD1 = float2(1.0 - in.in_var_TEXCOORD3.x, in.in_var_TEXCOORD3.y);
|
||||
out.out_var_TEXCOORD2 = _100;
|
||||
out.out_var_TEXCOORD3 = gl_InstanceIndex;
|
||||
out.gl_Position = _88;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct FontInfo
|
||||
{
|
||||
float2 position;
|
||||
float2 size;
|
||||
uint isSdf;
|
||||
uint pad0;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_FontInfo
|
||||
{
|
||||
FontInfo _m0[1];
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 out_var_SV_Target0 [[color(0)]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
uint in_var_TEXCOORD0 [[user(locn0)]];
|
||||
float2 in_var_TEXCOORD1 [[user(locn1)]];
|
||||
float2 in_var_TEXCOORD2 [[user(locn2)]];
|
||||
uint in_var_TEXCOORD3 [[user(locn3)]];
|
||||
};
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_FontInfo& fontBuffer [[buffer(0)]], texture2d<float> Texture0 [[texture(0)]], sampler Sampler0 [[sampler(0)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
float4 _67 = Texture0.sample(Sampler0, in.in_var_TEXCOORD1);
|
||||
bool _104;
|
||||
do
|
||||
{
|
||||
bool _85;
|
||||
if (in.in_var_TEXCOORD2.x >= fontBuffer._m0[in.in_var_TEXCOORD3].position.x)
|
||||
{
|
||||
_85 = in.in_var_TEXCOORD2.x <= (fontBuffer._m0[in.in_var_TEXCOORD3].position.x + fontBuffer._m0[in.in_var_TEXCOORD3].size.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
_85 = false;
|
||||
}
|
||||
bool _92;
|
||||
if (_85)
|
||||
{
|
||||
_92 = in.in_var_TEXCOORD2.y >= fontBuffer._m0[in.in_var_TEXCOORD3].position.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
_92 = false;
|
||||
}
|
||||
bool _101;
|
||||
if (_92)
|
||||
{
|
||||
_101 = in.in_var_TEXCOORD2.y <= (fontBuffer._m0[in.in_var_TEXCOORD3].position.y + fontBuffer._m0[in.in_var_TEXCOORD3].size.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
_101 = false;
|
||||
}
|
||||
if (_101)
|
||||
{
|
||||
_104 = true;
|
||||
break;
|
||||
}
|
||||
_104 = false;
|
||||
break;
|
||||
} while(false);
|
||||
if (!_104)
|
||||
{
|
||||
discard_fragment();
|
||||
}
|
||||
float _110 = float(in.in_var_TEXCOORD0 & 255u) * 0.0039215688593685626983642578125;
|
||||
float _114 = float((in.in_var_TEXCOORD0 >> 8u) & 255u) * 0.0039215688593685626983642578125;
|
||||
float _118 = float((in.in_var_TEXCOORD0 >> 16u) & 255u) * 0.0039215688593685626983642578125;
|
||||
float _122 = float((in.in_var_TEXCOORD0 >> 24u) & 255u) * 0.0039215688593685626983642578125;
|
||||
float4 _187;
|
||||
if (fontBuffer._m0[in.in_var_TEXCOORD3].isSdf == 1u)
|
||||
{
|
||||
float _128 = _67.x;
|
||||
float _129 = fwidth(_128);
|
||||
float _130 = 0.529411792755126953125 - _129;
|
||||
float _131 = 0.529411792755126953125 + _129;
|
||||
float2 _137 = (dfdx(in.in_var_TEXCOORD1) + dfdy(in.in_var_TEXCOORD1)) * 0.3540000021457672119140625;
|
||||
float4 _144 = float4(in.in_var_TEXCOORD1 - _137, in.in_var_TEXCOORD1 + _137);
|
||||
float4 _176 = float4(_110, _114, _118, ((fast::clamp(smoothstep(_130, _131, _128), 0.0, 1.0) + (0.5 * (((fast::clamp(smoothstep(_130, _131, Texture0.sample(Sampler0, _144.xy).x), 0.0, 1.0) + fast::clamp(smoothstep(_130, _131, Texture0.sample(Sampler0, _144.zw).x), 0.0, 1.0)) + fast::clamp(smoothstep(_130, _131, Texture0.sample(Sampler0, _144.xw).x), 0.0, 1.0)) + fast::clamp(smoothstep(_130, _131, Texture0.sample(Sampler0, _144.zy).x), 0.0, 1.0)))) * 0.3333333432674407958984375) * _122);
|
||||
float3 _178 = powr(_176.xyz, float3(2.2000000476837158203125));
|
||||
_187 = float4(_178.x, _178.y, _178.z, _176.w);
|
||||
}
|
||||
else
|
||||
{
|
||||
_187 = float4(_110, _114, _118, powr(_67.x / dot(float4(_110, _114, _118, _122).xyz, float3(0.2125999927520751953125, 0.715200006961822509765625, 0.072200000286102294921875)), 0.4545454680919647216796875) * _122);
|
||||
}
|
||||
out.out_var_SV_Target0 = _187;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct FontInfo
|
||||
{
|
||||
float2 position;
|
||||
float2 size;
|
||||
uint isSdf;
|
||||
uint pad0;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_FontInfo
|
||||
{
|
||||
FontInfo _m0[1];
|
||||
};
|
||||
|
||||
struct type_Uniforms
|
||||
{
|
||||
float2 Extents;
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
uint out_var_TEXCOORD0 [[user(locn0)]];
|
||||
float2 out_var_TEXCOORD1 [[user(locn1)]];
|
||||
float2 out_var_TEXCOORD2 [[user(locn2)]];
|
||||
uint out_var_TEXCOORD3 [[user(locn3)]];
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float2 in_var_TEXCOORD0 [[attribute(0)]];
|
||||
float2 in_var_TEXCOORD1 [[attribute(1)]];
|
||||
uint in_var_TEXCOORD2 [[attribute(2)]];
|
||||
};
|
||||
|
||||
vertex main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Uniforms [[buffer(0)]], const device type_StructuredBuffer_FontInfo& fontBuffer [[buffer(1)]], uint gl_InstanceIndex [[instance_id]])
|
||||
{
|
||||
main0_out out = {};
|
||||
float2 _46 = in.in_var_TEXCOORD0 + fontBuffer._m0[gl_InstanceIndex].position;
|
||||
float2 _51 = ((_46 / Uniforms.Extents) * 2.0) + float2(-1.0);
|
||||
float4 _54 = float4(_51, 0.0, 1.0);
|
||||
_54.y = -_51.y;
|
||||
out.out_var_TEXCOORD0 = in.in_var_TEXCOORD2;
|
||||
out.out_var_TEXCOORD1 = in.in_var_TEXCOORD1;
|
||||
out.out_var_TEXCOORD2 = _46;
|
||||
out.out_var_TEXCOORD3 = gl_InstanceIndex;
|
||||
out.gl_Position = _54;
|
||||
return out;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,3 +0,0 @@
|
|||
# studio games depot
|
||||
|
||||
!! not for open source.
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
pub const BoxObject = struct {
|
||||
playing: bool = false,
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator, entity: core.Entity, params: core.SpawnParameters) !*@This() {
|
||||
const position = params.posRot.position;
|
||||
|
||||
const box2 = entity;
|
||||
const scene = box2.addComponent(core.Scene).?;
|
||||
|
||||
const mesh = box2.addComponent(rend.MeshComponent).?;
|
||||
mesh.setMesh("m_crate");
|
||||
mesh.setTexture("t_crate");
|
||||
|
||||
scene.setPosition(position);
|
||||
scene.setScaleV(params.posRot.scale);
|
||||
scene.setMobility(.moveable);
|
||||
|
||||
const collider = box2.addComponent(physics.PhysicsCollider).?;
|
||||
try collider.setupByShapeName(core.MakeName("ph_small_box"), .{
|
||||
.motion_type = .dynamic,
|
||||
.object_layer = physics.ObjectLayers.moving,
|
||||
.friction = 0.8,
|
||||
.mass_properties_override = .{ .mass = 12 },
|
||||
.override_mass_properties = .calc_inertia,
|
||||
});
|
||||
|
||||
return try allocator.create(@This());
|
||||
}
|
||||
|
||||
pub fn createSmoky(allocator: std.mem.Allocator, entity: core.Entity, params: core.SpawnParameters) !*@This() {
|
||||
const rv = try create(allocator, entity, params);
|
||||
|
||||
const particle = entity.addComponent(rend.ParticleEmitter).?;
|
||||
particle.start();
|
||||
particle.gravity = .{ .y = 1.0 };
|
||||
particle.particleVelocity = .{ .min = 0.6, .max = 1.2 };
|
||||
particle.particleLife = .{ .min = 5, .max = 6 };
|
||||
var name = core.MakeName("t_pixelSmoke");
|
||||
particle.texture = rend.getTexture(&name).?;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This(), alloc: std.mem.Allocator) void {
|
||||
alloc.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const Backlog = @import("Backlog");
|
||||
const core = Backlog.core;
|
||||
const rend = Backlog.rend;
|
||||
const physics = Backlog.physics;
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
const backlog = @import("backlog");
|
||||
const core = backlog.core;
|
||||
const imgui = backlog.imgui;
|
||||
const physics = backlog.physics;
|
||||
const rend = backlog.rend;
|
||||
const ig = imgui.api;
|
||||
const sys = backlog.sys;
|
||||
const ui = backlog.ui;
|
||||
const net = backlog.net;
|
||||
const audio = backlog.audio;
|
||||
const platform = backlog.platform;
|
||||
|
||||
pub export fn shutdown_module() void {}
|
||||
pub export fn startup_module(p_allocator: *anyopaque, p_a: ?*anyopaque) bool {
|
||||
const allocator = core.modulePreamble(p_allocator, p_a) catch return false;
|
||||
_ = allocator;
|
||||
imgui.setupFromModule();
|
||||
platform.setupFromModule();
|
||||
sys.setupFromModule();
|
||||
rend.setupFromModule();
|
||||
backlog.physics.setupFromModule();
|
||||
|
||||
start_module(core.startup_getArgs(p_a.?)) catch return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn start_module(args: core.ModuleLoaderArgs) !void {
|
||||
_ = args;
|
||||
core.PatchOrCreateObject(ZuckGame, .{});
|
||||
// core.PatchOrCreateObject(ZuckEditor, .{});
|
||||
}
|
||||
|
||||
const ZuckGame = @import("games/ZuckGame.zig");
|
||||
const ZuckEditor = @import("games/ZuckEditor.zig");
|
||||
|
||||
const ZuckPlayer = @import("actors/ZuckPlayer.zig");
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "zuck.game.Editor");
|
||||
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
|
||||
pub const Slack = core.SlackStruct(@This(), 8192);
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try Slack.create(allocator);
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
_ = self;
|
||||
_ = dt;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.allocator.destroy(Slack.fromPtr(self));
|
||||
}
|
||||
|
||||
const backlog = @import("backlog");
|
||||
const std = @import("std");
|
||||
const extras = @import("gameExtras");
|
||||
|
||||
const core = backlog.core;
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "zuck.game.Root");
|
||||
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
|
||||
pub const Slack = core.SlackStruct(@This(), 1024);
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try Slack.create(allocator);
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
_ = self;
|
||||
_ = dt;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.allocator.destroy(Slack.fromPtr(self));
|
||||
}
|
||||
|
||||
const backlog = @import("backlog");
|
||||
const std = @import("std");
|
||||
const extras = @import("gameExtras");
|
||||
|
||||
const core = backlog.core;
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "main");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn prepare(self: *@This()) !void {
|
||||
_ = self;
|
||||
try api.loadDynamicModules();
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
_ = self;
|
||||
_ = dt;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
// Engine handles memory deallocation
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
var spec = try api.getSpec("minimal");
|
||||
_ = api.startEngine(&NeonObjectTable, &spec);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const api = @import("backlog");
|
||||
const core = api.core;
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
# Backlog Engine Build Options
|
||||
|
||||
This document provides comprehensive documentation for all `core.BuildOption` flags available in the Backlog game engine.
|
||||
|
||||
## Overview
|
||||
|
||||
Build options in Backlog are boolean flags that control various compilation behaviors and engine features. They are accessed at compile-time using `core.BuildOption("option_name")` and can be set via Zig's build system command line arguments.
|
||||
|
||||
## Usage
|
||||
|
||||
Build options are set when building the project:
|
||||
|
||||
```bash
|
||||
zig build -Doption_name
|
||||
# or
|
||||
zig build -Doption_name=true
|
||||
```
|
||||
|
||||
To disable an option explicitly:
|
||||
```bash
|
||||
zig build -Doption_name=false
|
||||
```
|
||||
|
||||
## Core Engine Build Options
|
||||
|
||||
### Performance & Development Options
|
||||
|
||||
#### `mutex_job_queue`
|
||||
- **Type**: `bool`
|
||||
- **Default**: `false`
|
||||
- **Description**: Temporary test option that reverts to old mutex-based queue behavior in `jobs.zig:JobManager`
|
||||
- **Usage**: `zig build -Dmutex_job_queue`
|
||||
- **Impact**: Changes job system threading behavior for debugging/testing purposes
|
||||
- **Files**: `engine/core/src/jobs.zig:8`
|
||||
|
||||
#### `force_mailbox`
|
||||
- **Type**: `bool`
|
||||
- **Default**: `false`
|
||||
- **Description**: Forces mailbox mode for present mode, unlocking framerate to very high levels
|
||||
- **Usage**: `zig build -Dforce_mailbox`
|
||||
- **Impact**: Disables VSync and allows unlimited framerate rendering
|
||||
- **Warning**: Can cause excessive GPU usage and heat generation
|
||||
|
||||
### Logging Options
|
||||
|
||||
#### `zero_logging`
|
||||
- **Type**: `bool`
|
||||
- **Default**: `false`
|
||||
- **Description**: Disables all logging completely, intended primarily for job dispatch performance testing
|
||||
- **Usage**: `zig build -Dzero_logging`
|
||||
- **Impact**: Removes all log output, improving performance but eliminating debug information
|
||||
- **Files**: `engine/core/src/logging.zig:7`
|
||||
|
||||
#### `slow_logging`
|
||||
- **Type**: `bool`
|
||||
- **Default**: `false`
|
||||
- **Description**: Disables buffered logging, taking a performance hit but providing accurate timing information
|
||||
- **Usage**: `zig build -Dslow_logging`
|
||||
- **Impact**: Each log call is immediately flushed, providing precise timing but reducing performance
|
||||
- **Files**: `engine/core/src/logging.zig:6`
|
||||
|
||||
### Build & Deployment Options
|
||||
|
||||
#### `static_build`
|
||||
- **Type**: `bool`
|
||||
- **Default**: `false`
|
||||
- **Description**: Builds the entire game as a single executable with static linking
|
||||
- **Usage**: `zig build -Dstatic_build`
|
||||
- **Impact**:
|
||||
- Required for Linux builds
|
||||
- Disables dynamic module loading
|
||||
- Creates self-contained executable
|
||||
- **Files**:
|
||||
- `build.zig:190`
|
||||
- `engine/core/src/extern/externModule.zig:139`
|
||||
- `build/generateApi.zig:118`
|
||||
- `engine/rend/src/sgpu/renderer.zig:1126`
|
||||
|
||||
#### `RootDeploymentOnly`
|
||||
- **Type**: `bool`
|
||||
- **Default**: Not explicitly set (appears to default to `false`)
|
||||
- **Description**: Controls whether only root deployment operations are performed
|
||||
- **Usage**: `zig build -DRootDeploymentOnly`
|
||||
- **Impact**: Appears to limit engine initialization to root-level deployment only
|
||||
- **Files**: `engine/main.zig:28`
|
||||
|
||||
### Development & Shader Options
|
||||
|
||||
#### `cookShaders`
|
||||
- **Type**: `bool`
|
||||
- **Default**: `false`
|
||||
- **Description**: Generates shaders and updates `.json` files before running the build
|
||||
- **Usage**: `zig build -DcookShaders`
|
||||
- **Impact**:
|
||||
- Automatically runs `tools/scripts/cookShaders.py`
|
||||
- Compiles HLSL shaders to SPIR-V, MSL, and DXIL formats
|
||||
- Must be run whenever shader files are modified
|
||||
- **Files**: `build.zig:80`
|
||||
|
||||
### Third-Party Integration Options
|
||||
|
||||
#### `tracy` (Tracy Profiler)
|
||||
- **Type**: `bool`
|
||||
- **Default**: `false`
|
||||
- **Description**: Enables Tracy profiler integration for performance analysis
|
||||
- **Usage**: `zig build -Dtracy`
|
||||
- **Impact**:
|
||||
- Adds Tracy profiling instrumentation
|
||||
- Enables real-time performance monitoring
|
||||
- Increases binary size and may affect performance
|
||||
- **Files**: `lib/tracy/build_tracy.zig:37`
|
||||
|
||||
## Build Option Categories
|
||||
|
||||
### By Purpose
|
||||
|
||||
**Performance Testing**:
|
||||
- `mutex_job_queue` - Job system behavior testing
|
||||
- `zero_logging` - Maximum performance logging
|
||||
- `force_mailbox` - Unlimited framerate testing
|
||||
|
||||
**Development & Debugging**:
|
||||
- `slow_logging` - Precise timing information
|
||||
- `tracy` - Performance profiling
|
||||
- `cookShaders` - Shader development workflow
|
||||
|
||||
**Deployment & Distribution**:
|
||||
- `static_build` - Self-contained executable creation
|
||||
- `RootDeploymentOnly` - Deployment scope control
|
||||
|
||||
### By Impact Level
|
||||
|
||||
**High Impact** (affects core engine behavior):
|
||||
- `static_build`
|
||||
- `zero_logging`
|
||||
- `mutex_job_queue`
|
||||
|
||||
**Medium Impact** (affects specific subsystems):
|
||||
- `slow_logging`
|
||||
- `force_mailbox`
|
||||
- `cookShaders`
|
||||
|
||||
**Low Impact** (development/profiling tools):
|
||||
- `tracy`
|
||||
- `RootDeploymentOnly`
|
||||
|
||||
## Common Build Configurations
|
||||
|
||||
### Development Build
|
||||
```bash
|
||||
zig build -Dslow_logging -Dtracy -DcookShaders
|
||||
```
|
||||
|
||||
### Performance Testing Build
|
||||
```bash
|
||||
zig build -Dzero_logging -Dforce_mailbox -Dmutex_job_queue
|
||||
```
|
||||
|
||||
### Production/Release Build
|
||||
```bash
|
||||
zig build -Dstatic_build
|
||||
```
|
||||
|
||||
### Linux Build (Required)
|
||||
```bash
|
||||
zig build -Dstatic_build
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
Build options are implemented through Zig's compile-time options system:
|
||||
|
||||
1. **Definition**: Options are defined in `build.zig` in the `BuildOptions` struct
|
||||
2. **Access**: Runtime code accesses options via `core.BuildOption("option_name")`
|
||||
3. **Compilation**: The `core.BuildOption()` function checks for the option at compile-time
|
||||
4. **Fallback**: If an option is not defined, it defaults to `false`
|
||||
|
||||
The `core.BuildOption()` function implementation:
|
||||
```zig
|
||||
pub fn BuildOption(comptime option: []const u8) bool {
|
||||
if (@hasDecl(@import("root"), "options")) {
|
||||
const r = @import("root").options;
|
||||
if (@hasDecl(r, option)) {
|
||||
return @field(r, option);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- All build options are boolean flags
|
||||
- Options not explicitly set default to `false`
|
||||
- Some combinations may be incompatible (e.g., `zero_logging` + `slow_logging`)
|
||||
- Linux builds require `static_build=true`
|
||||
- Shader compilation via `cookShaders` should be run after modifying any `.hlsl` files
|
||||
152
docs/overview.md
152
docs/overview.md
|
|
@ -1,152 +0,0 @@
|
|||
# Backlog Game Engine Overview
|
||||
|
||||
The Backlog Game Engine is a modern, modular game engine built with Zig, designed for cross-platform game development. It provides a comprehensive set of tools and systems for creating interactive 3D applications and games.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Zig 0.14** or later
|
||||
- **Python 3** for build scripts
|
||||
|
||||
## Running Examples
|
||||
|
||||
The engine comes with several examples to demonstrate its capabilities:
|
||||
|
||||
### Sample Game
|
||||
|
||||
The main example showcasing most engine features including:
|
||||
- 3D rendering with meshes and textures
|
||||
- Physics simulation
|
||||
- ImGui debug interface
|
||||
- Input handling
|
||||
- Dynamic module loading
|
||||
|
||||
**To run the sample game:**
|
||||
|
||||
```bash
|
||||
cd projects
|
||||
zig build run-sampleGame
|
||||
```
|
||||
|
||||
To compile and target linux with cross compiling,
|
||||
|
||||
```
|
||||
zig build -Dtarget=x86_64-linux-gnu
|
||||
```
|
||||
|
||||
The linux build is always compiled as a static executable.
|
||||
|
||||
**Sample Game Controls:**
|
||||
- `WASD` - Move camera
|
||||
- `Mouse` - Look around (toggle menu with `T`)
|
||||
- `E/Q` - Move up/down
|
||||
- `R` - Reload shaders
|
||||
- `Escape` - Exit
|
||||
|
||||
### Minimal Example
|
||||
|
||||
A basic template demonstrating the minimal setup required for a Backlog application:
|
||||
|
||||
```bash
|
||||
cd projects/minimal
|
||||
zig build
|
||||
zig build run-minimal
|
||||
```
|
||||
|
||||
This example shows:
|
||||
- Basic engine initialization
|
||||
- ImGui integration
|
||||
- Simple game loop structure
|
||||
|
||||
## Creating New Projects
|
||||
|
||||
The engine includes a project creation tool to help you get started with new projects quickly.
|
||||
|
||||
### Using the New Project Tool
|
||||
|
||||
1. Navigate to the projects directory:
|
||||
```bash
|
||||
cd projects
|
||||
```
|
||||
|
||||
2. Run the project creator:
|
||||
```bash
|
||||
zig build run-newProject
|
||||
```
|
||||
|
||||
3. Click "Create Project" to generate your new project structure
|
||||
|
||||
### Manual Project Creation
|
||||
|
||||
Alternatively, you can create projects manually by copying the minimal template:
|
||||
|
||||
1. Copy the `projects/minimal` directory:
|
||||
```bash
|
||||
cp -r projects/minimal projects/my-new-project
|
||||
```
|
||||
|
||||
2. Edit `projects/my-new-project/build.zig` and update:
|
||||
- Project name
|
||||
- Root source file path
|
||||
- Enabled modules as needed
|
||||
|
||||
3. Modify `src/main.zig` to implement your game logic
|
||||
|
||||
### Project Structure
|
||||
|
||||
A typical Backlog project includes:
|
||||
|
||||
```
|
||||
my-project/
|
||||
├── build.zig # Build configuration
|
||||
├── build.zig.zon # Dependencies
|
||||
├── src/
|
||||
│ └── main.zig # Main game code
|
||||
├── content/ # Game assets (optional)
|
||||
└── Saved/ # Runtime generated files
|
||||
```
|
||||
|
||||
## Engine Modules
|
||||
|
||||
When creating projects, you can enable these modules:
|
||||
|
||||
- **core** - Essential engine systems (always enabled)
|
||||
- **platform** - Windowing and input (always enabled)
|
||||
- **assets** - Asset loading and management (always enabled)
|
||||
- **rend** - 3D rendering system (always enabled)
|
||||
- **imgui** - Debug UI and developer tools
|
||||
- **audio** - Sound and music playback
|
||||
- **physics** - Physics simulation (Jolt Physics)
|
||||
- **ui** - User interface rendering
|
||||
- **papyrus** - Text rendering and UI primitives
|
||||
- **sys** - System utilities
|
||||
|
||||
## Asset Pipeline
|
||||
|
||||
Assets are placed in the `content/` directory within your project:
|
||||
|
||||
all shaders are cooked to the target system's native format. DXIL for dx12, msl for metal and spv for vulkan.
|
||||
|
||||
These cooked format shaders are placed under content/_shaders
|
||||
|
||||
## Building and Development
|
||||
|
||||
### Building Projects
|
||||
|
||||
From your project directory:
|
||||
```bash
|
||||
zig build # Build project
|
||||
zig build run-[projectname] # Build and run
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
Run tests for the entire engine:
|
||||
```bash
|
||||
zig build test
|
||||
```
|
||||
|
||||
Or test individual modules:
|
||||
```bash
|
||||
cd engine/core
|
||||
zig build test
|
||||
```
|
||||
|
|
@ -120,6 +120,11 @@ pub const AssetLoaderInterface = struct {
|
|||
unreachable;
|
||||
}
|
||||
|
||||
if (!@hasDecl(TargetType, "destroy")) {
|
||||
@compileLog("Tried to generate AssetLoaderInterface for type ", TargetType, "but it's missing func destroy.");
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const self = @This(){
|
||||
.typeName = @typeName(TargetType),
|
||||
.typeSize = @sizeOf(TargetType),
|
||||
|
|
@ -154,15 +159,15 @@ pub const AssetReferenceSys = struct {
|
|||
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "AssetReference");
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = @This(){
|
||||
.loaders = .{},
|
||||
.allocator = allocator,
|
||||
.outstandingAssetJobs = std.atomic.Value(i32).init(0),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn registerLoader(self: *@This(), loader: anytype) !void {
|
||||
|
|
@ -205,5 +210,6 @@ pub const AssetReferenceSys = struct {
|
|||
// i.destroy(self.allocator);
|
||||
// }
|
||||
self.loaders.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -73,10 +73,8 @@ pub const SoundEngine = struct {
|
|||
allocator: std.mem.Allocator,
|
||||
volume: f32 = 1.0,
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = @This(){
|
||||
.engine = allocator.create(ma.ma_engine) catch unreachable,
|
||||
.sounds = .{},
|
||||
|
|
@ -85,6 +83,8 @@ pub const SoundEngine = struct {
|
|||
};
|
||||
|
||||
_ = ma.ma_engine_init(null, self.engine);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn shutdown(self: *@This()) void {
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ pub const createSpecVariant = core.createSpecVariant;
|
|||
pub export fn initAndRun(vtable: *core.EngineObjectVTable, spec: *core.SpecVariantMap) bool {
|
||||
const args = getArgs() catch return false;
|
||||
|
||||
var backingAllocator: std.mem.Allocator = std.heap.smp_allocator;
|
||||
var backingAllocator: std.mem.Allocator = std.heap.c_allocator;
|
||||
var gpa: std.heap.GeneralPurposeAllocator(.{
|
||||
.stack_trace_frames = 20,
|
||||
}) = .{};
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
pub const std = @import("std");
|
||||
const core = @import("core");
|
||||
const impl = core;
|
||||
|
||||
pub const inputs = struct {
|
||||
pub const getInputStack = inputs.getInputStack;
|
||||
};
|
||||
|
|
@ -2,9 +2,14 @@ const std = @import("std");
|
|||
|
||||
const dependencyList = [_][]const u8{
|
||||
"p2",
|
||||
"tracy", // usually doesnt have C deps... unless we're compiled with it on, in which case we only support static builds
|
||||
"tracy",
|
||||
"zmath",
|
||||
"packer", // packer no longer has C deps.
|
||||
"packer", // packer might need to be split into another setup that doesn't have C deps.
|
||||
|
||||
// these ones use c deps, but if i move them out to platform... then core will have zero c dependencies.
|
||||
"lua",
|
||||
"spng",
|
||||
// "nfd",
|
||||
};
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
|
|
@ -28,10 +33,8 @@ pub fn build(b: *std.Build) void {
|
|||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
.root_source_file = b.path("tests/tests.zig"),
|
||||
}),
|
||||
.use_llvm = true,
|
||||
});
|
||||
|
||||
const sampleGameExtern = b.addLibrary(.{
|
||||
|
|
@ -42,10 +45,8 @@ pub fn build(b: *std.Build) void {
|
|||
.target = target,
|
||||
}),
|
||||
.linkage = .dynamic,
|
||||
.use_llvm = true,
|
||||
.name = "external",
|
||||
});
|
||||
sampleGameExtern.root_module.addImport("core", mod);
|
||||
|
||||
const installExtern = b.addInstallArtifact(sampleGameExtern, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "modules" } },
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
.name = .core,
|
||||
.version = "0.0.0",
|
||||
.dependencies = .{
|
||||
// .spng = .{ .path = "../../lib/spng" },
|
||||
.spng = .{ .path = "../../lib/spng" },
|
||||
// .nfd = .{ .path = "../../lib/nfd" },
|
||||
|
||||
// with -Dtracy = false, this one pulls in no C dependencies
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
.zmath = .{ .path = "../../lib/zmath" },
|
||||
.packer = .{ .path = "../../lib/packer" },
|
||||
.p2 = .{ .path = "../../lib/p2" },
|
||||
// .lua = .{ .path = "../../lib/lua" },
|
||||
.lua = .{ .path = "../../lib/lua" },
|
||||
// .zgltf = .{ .path = "../../lib/zgltf" },
|
||||
},
|
||||
.paths = .{
|
||||
|
|
|
|||
|
|
@ -169,12 +169,12 @@ pub fn init(backingAllocator: std.mem.Allocator, settings: SetupSettings) @This(
|
|||
core.engine_logs("[dmt] Enabling Detailed Memory Tracking");
|
||||
}
|
||||
|
||||
const stackCompactor = if (EnableMemoryTimeline) core.StackCompactor.create(std.heap.smp_allocator) catch null else null;
|
||||
const stackCompactor = if (EnableMemoryTimeline) core.StackCompactor.create(std.heap.c_allocator) catch null else null;
|
||||
|
||||
return .{
|
||||
.backingAllocator = backingAllocator,
|
||||
.stackCompactor = stackCompactor,
|
||||
.timeline = if (EnableMemoryTimeline) EventTimeline.init(std.heap.smp_allocator) catch null else null,
|
||||
.timeline = if (EnableMemoryTimeline) EventTimeline.init(std.heap.c_allocator) catch null else null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const std = @import("std");
|
|||
|
||||
pub fn ParseArgs(comptime T: type) !T {
|
||||
// I think i am actually totally cool with leaking this one and leaving it around... just dont spam call this function
|
||||
var iter = try std.process.argsWithAllocator(std.heap.smp_allocator);
|
||||
var iter = try std.process.argsWithAllocator(std.heap.c_allocator);
|
||||
var args: T = .{};
|
||||
|
||||
const shortBuf: [32]u8 = undefined;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
pub const ConfigRegistry = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.ConfigRegistry");
|
||||
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
allocator: std.mem.Allocator,
|
||||
configMap: ?ConfigMap = null,
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// by convention this should be in the root
|
||||
|
|
@ -37,10 +38,11 @@ pub const ConfigRegistry = struct {
|
|||
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
pub fn destroy(self: *@This()) void {
|
||||
if (self.configMap) |*map| {
|
||||
map.deinit();
|
||||
}
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,22 +8,21 @@ pub const ConsoleCommand = struct {
|
|||
pub const Console = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.Console");
|
||||
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
arena: std.heap.ArenaAllocator = undefined,
|
||||
allocator: std.mem.Allocator,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
||||
commandMap: std.StringHashMapUnmanaged(ConsoleCommand) = .{},
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = .{
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
core.engine_logs("console system created");
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn addConsoleCommand(self: *@This(), funcName: []const u8, func: ConsoleFunc) !void {
|
||||
|
|
@ -57,9 +56,10 @@ pub const Console = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.arena.deinit();
|
||||
self.commandMap.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// main public facing root file for core
|
||||
const std = @import("std");
|
||||
|
||||
pub const machinery = @import("machinery/machinery.zig");
|
||||
|
||||
pub const gameObjectList = @import("utils/gameObjectList.zig");
|
||||
pub const GameObjectList = gameObjectList.GameObjectList;
|
||||
pub const GameObjectInterface = gameObjectList.GameObjectInterface;
|
||||
|
|
@ -27,10 +25,9 @@ pub const debugBox = debug_draw.debugBox;
|
|||
pub const debugLine = debug_draw.debugLine;
|
||||
// pub const script_bindings = script.script_bindings;
|
||||
|
||||
pub const misc = @import("misc.zig");
|
||||
pub const engineTime = @import("engineTime.zig");
|
||||
pub const engineObject = @import("engineObject.zig");
|
||||
pub const EngineObjectOpts = engineObject.EngineObjectOpts;
|
||||
pub const ObjectOpts = engineObject.EngineObjectOpts;
|
||||
pub const EngineObjectVTable = engineObject.EngineObjectVTable;
|
||||
pub const MakeTypeName = engineObject.MakeTypeName;
|
||||
pub const PatchStruct = engineObject.PatchStruct;
|
||||
|
|
@ -41,13 +38,16 @@ pub const EngineObjectDelegate = engineObject.EngineObjectDelegate;
|
|||
pub const FieldInfo = engineObject.FieldInfo;
|
||||
|
||||
pub const jobs = @import("jobs.zig");
|
||||
pub const JobContext = jobs.ThreadContext;
|
||||
pub const ThreadContext = jobs.ThreadContext;
|
||||
pub const JobContext = jobs.JobContext;
|
||||
pub const JobManager = jobs.JobManager;
|
||||
pub const JobWorker = jobs.JobWorker;
|
||||
|
||||
pub const file_utils = @import("file_utils.zig");
|
||||
pub const string_utils = @import("string_utils.zig");
|
||||
pub const type_utils = @import("type_utils.zig");
|
||||
pub const engine = @import("engine.zig");
|
||||
pub const tracy = @import("tracy").t;
|
||||
pub const png = @import("png.zig");
|
||||
|
||||
pub const colors = @import("colors.zig");
|
||||
|
||||
|
|
@ -140,6 +140,8 @@ pub const SceneSystem = scene.SceneSystem;
|
|||
|
||||
pub const Engine = engine.Engine;
|
||||
|
||||
pub const spng = @import("spng");
|
||||
|
||||
pub const MemoryTracker = @import("MemoryTracker.zig");
|
||||
|
||||
pub const DefaultSavePath = "Saved";
|
||||
|
|
@ -170,16 +172,14 @@ pub const defaultHighlight = logging.defaultHighlight;
|
|||
|
||||
pub const packer = @import("packer");
|
||||
|
||||
pub const FileSystem = packer.PackerFS;
|
||||
pub const PackerFS = packer.PackerFS;
|
||||
|
||||
pub const StackCompactor = stacks.StackCompactor;
|
||||
|
||||
pub var staticsInitialized = false;
|
||||
var staticsInitialized = false;
|
||||
var gEngine: *Engine = undefined;
|
||||
pub const PackerFS = packer.PackerFS;
|
||||
var gPackerFS: *PackerFS = undefined;
|
||||
|
||||
pub fn fs() *PackerFS {
|
||||
return gPackerFS;
|
||||
}
|
||||
var fsInitialized: bool = false;
|
||||
|
||||
pub fn EngineObject(comptime T: type) type {
|
||||
|
|
@ -216,6 +216,8 @@ pub const undefineComponentList = ecs.undefineComponentList;
|
|||
pub const Entity = ecs.Entity;
|
||||
pub const createEntity = ecs.createEntity;
|
||||
|
||||
pub const script = @import("script.zig");
|
||||
|
||||
pub const stacks = @import("stacks.zig");
|
||||
pub const walkAndPrintStack = stacks.walkAndPrintStack;
|
||||
|
||||
|
|
@ -234,6 +236,10 @@ pub fn registerObjectAdvanced(comptime T: type, name: []const u8, comptime funcN
|
|||
try get(GameObjectSystem).registerObjectAdvanced(name, T, funcName);
|
||||
}
|
||||
|
||||
pub fn fs() *PackerFS {
|
||||
return gPackerFS;
|
||||
}
|
||||
|
||||
pub const Module = ModuleDescription{
|
||||
.name = "core",
|
||||
.enabledByDefault = true,
|
||||
|
|
@ -274,6 +280,8 @@ pub fn maybeInitPackerFs(allocator: std.mem.Allocator) !void {
|
|||
}
|
||||
|
||||
pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.Allocator) !void {
|
||||
staticsInitialized = true;
|
||||
|
||||
if (map.get("utility")) |x| {
|
||||
if (x.boolean == true) {
|
||||
engine_logs("utility mode - no gui");
|
||||
|
|
@ -286,9 +294,15 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All
|
|||
fatDump = true;
|
||||
}
|
||||
|
||||
script.lua.setupMiniDump(fatDump);
|
||||
_ = try algorithm.createNameRegistry(allocator);
|
||||
// LUA BEGIN -- what if i want to make the scripting integration optional?
|
||||
// try script.start_lua(allocator);
|
||||
// LUA END
|
||||
try maybeInitPackerFs(allocator);
|
||||
|
||||
// load configs.
|
||||
//const name = if (@hasField(@TypeOf(programSpec), "configName")) programSpec.configName else programSpec.name;
|
||||
var name = map.get("name").?.string;
|
||||
|
||||
if (map.get("configName")) |x| {
|
||||
|
|
@ -297,7 +311,8 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All
|
|||
|
||||
gEngine = try allocator.create(Engine);
|
||||
gEngine.* = try Engine.init(allocator);
|
||||
staticsInitialized = true;
|
||||
_ = try createObject(ModuleLoader, .{});
|
||||
try console.start();
|
||||
|
||||
const engineName = try std.fmt.allocPrint(allocator, "{s}Engine.ini", .{name});
|
||||
defer allocator.free(engineName);
|
||||
|
|
@ -308,15 +323,16 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All
|
|||
try logging.setupLogging(gEngine);
|
||||
}
|
||||
|
||||
try algorithm.string_pool.setup(allocator);
|
||||
gEngine.stringContext = algorithm.string_pool.gStringContext;
|
||||
try ecs.setup(allocator);
|
||||
|
||||
_ = try gEngine.createObject(scene.SceneSystem, .{ .can_tick = true });
|
||||
|
||||
try algorithm.string_pool.setup(allocator);
|
||||
|
||||
// _ = try gEngine.createObject(script_bindings.ScriptTicks, .{ .can_tick = true });
|
||||
|
||||
_ = try createObject(ModuleLoader, .{});
|
||||
_ = try createObject(ecs.EcsRegistry, .{ .can_tick = true, .isCore = true });
|
||||
_ = try createObject(scene.SceneSystem, .{ .can_tick = true });
|
||||
_ = try createObject(GameObjectSystem, .{ .can_tick = true });
|
||||
_ = try createObject(inputs.InputStack, .{});
|
||||
_ = try createObject(console.Console, .{});
|
||||
_ = try inputs.initInputStack();
|
||||
|
||||
// components define
|
||||
try ecs.defineComponentList(ComponentList, allocator);
|
||||
|
|
@ -327,8 +343,8 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All
|
|||
pub fn setupFromModule(__args: ModuleLoaderArgs) !void {
|
||||
gEngine = __args.engine;
|
||||
gPackerFS = __args.packerFS;
|
||||
// script.setupLuaFromModule(__args.luaState, __args.luaAllocator);
|
||||
algorithm.names.gRegistry = __args.nameRegistry;
|
||||
algorithm.string_pool.gStringContext = gEngine.stringContext;
|
||||
logging.setupLoggingFromModule();
|
||||
staticsInitialized = true;
|
||||
|
||||
|
|
@ -350,36 +366,18 @@ pub fn shutdown_module(_: std.mem.Allocator) void {
|
|||
|
||||
ecs.shutdown();
|
||||
algorithm.destroyNameRegistry();
|
||||
gEngine.deinit();
|
||||
gPackerFS.destroy();
|
||||
// LUA BEGIN
|
||||
// script.shutdown_lua();
|
||||
// LUA END
|
||||
console.shutdown();
|
||||
algorithm.string_pool.shutdown();
|
||||
|
||||
gEngine.deinit();
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn parallelJob(capture: anytype, async: bool, maxWorkerCount: ?u32) !void {
|
||||
const z = tracy.ZoneNC(@src(), "parallel Job", 0xCF8774);
|
||||
defer z.End();
|
||||
|
||||
const jobManager = getEngine().jobManager;
|
||||
const requestedWorkers = if (maxWorkerCount) |m| m else jobManager.numWorkers;
|
||||
const baseWorkerCount = @min(requestedWorkers, @max(getEngine().jobManager.numWorkers - 2, 1));
|
||||
|
||||
const workerStart: u32 = if (async) 0 else 1;
|
||||
|
||||
for (workerStart..baseWorkerCount) |workerId| {
|
||||
try jobManager.newJob(capture, .{ .threadId = @intCast(workerId), .threadCount = baseWorkerCount });
|
||||
}
|
||||
|
||||
// start working on the job locally
|
||||
if (!async) {
|
||||
try jobManager.runLocally(capture, .{ .threadId = 0, .threadCount = baseWorkerCount });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatchJob(capture: anytype) !void {
|
||||
try gEngine.jobManager.newJob(capture, .{ .threadId = 0, .threadCount = 1 }); //0, 1, null);
|
||||
try gEngine.jobManager.newJob(capture);
|
||||
}
|
||||
|
||||
pub fn createObject(comptime T: type, params: engine.NeonObjectParams) !*T {
|
||||
|
|
@ -526,11 +524,7 @@ pub fn startup_getArgs(p_allocator: *anyopaque) ModuleLoaderArgs {
|
|||
pub fn modulePreamble(p_allocator: *anyopaque, p_a: ?*anyopaque) !std.mem.Allocator {
|
||||
const args = startup_getArgs(p_a.?);
|
||||
const allocator = startup_getAllocator(p_allocator);
|
||||
|
||||
if (comptime !BuildOption("static_build")) {
|
||||
try setupFromModule(args);
|
||||
}
|
||||
|
||||
try setupFromModule(args);
|
||||
return allocator;
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +608,3 @@ pub fn itof32(i: anytype) f32 {
|
|||
pub fn itof64(i: anytype) f32 {
|
||||
return @as(f32, @floatFromInt(i));
|
||||
}
|
||||
|
||||
pub fn setShutdownModule(shutdownFunction: *const fn () callconv(.c) void) void {
|
||||
getEngine().shutdownModuleFunction = shutdownFunction;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
|
||||
pub fn createEntity() !Entity {
|
||||
const rv = Entity{ .handle = try getRegistry().baseSet.createObject(.{}) };
|
||||
// core.engine_log("entity created: {d}", .{rv.handle.index});
|
||||
core.engine_log("entity created: {d}", .{rv.handle.index});
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ pub fn destroyAllEntities() void {
|
|||
}
|
||||
|
||||
pub fn destroyEntity(e: Entity) void {
|
||||
// core.engine_log("entity destroyed: {d}", .{e.handle.index});
|
||||
core.engine_log("entity destroyed: {d}", .{e.handle.index});
|
||||
const registry = getRegistry();
|
||||
if (registry.baseSet.get(e.handle)) |entityEntry| {
|
||||
for (entityEntry.containers.items) |ref| {
|
||||
|
|
@ -210,15 +210,15 @@ pub const EcsRegistry = struct {
|
|||
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "core.EcsRegistry");
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.baseSet = BaseSet.init(allocator),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn registerContainer(self: *@This(), ref: EcsContainerRef, _containerName: core.Name) !void {
|
||||
|
|
@ -279,11 +279,13 @@ pub const EcsRegistry = struct {
|
|||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
// this should never work... wtf?
|
||||
// for (self.containers.items) |ref| {
|
||||
// ref.vtable.evictFromRegistry(ref.ptr);
|
||||
// }
|
||||
self.destroy();
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
for (self.containers.items) |ref| {
|
||||
ref.vtable.evictFromRegistry(ref.ptr);
|
||||
}
|
||||
for (self.systems.items) |ref| {
|
||||
ref.vtable.destroy(ref.ptr);
|
||||
}
|
||||
|
|
@ -298,6 +300,7 @@ pub const EcsRegistry = struct {
|
|||
self.containers.deinit(self.allocator);
|
||||
self.containerNames.deinit(self.allocator);
|
||||
self.containersByName.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -374,7 +377,7 @@ pub const Entity = struct {
|
|||
}
|
||||
|
||||
pub fn addComponent(self: @This(), comptime Component: type) ?*Component {
|
||||
// core.engine_log("adding component: {d} {s}", .{ self.handle.index, @typeName(Component) });
|
||||
core.engine_log("adding component: {d} {s}", .{ self.handle.index, @typeName(Component) });
|
||||
const rv = Component.BaseContainer.createWithHandleECS(self.handle);
|
||||
|
||||
const list = &getRegistry().baseSet.get(self.handle).?.containers;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ const time = @import("engineTime.zig");
|
|||
const core = @import("core.zig");
|
||||
const jobs = @import("jobs.zig");
|
||||
const math = @import("math.zig");
|
||||
const builtin = @import("builtin");
|
||||
const pscopes = core.algorithm.pscopes;
|
||||
|
||||
const tracy = @import("tracy").t;
|
||||
const p2 = @import("p2");
|
||||
|
|
@ -86,15 +84,8 @@ pub const Engine = struct {
|
|||
|
||||
delegates: EngineDelegates,
|
||||
|
||||
rootTimer: std.time.Timer,
|
||||
scopesContext: *pscopes.ScopesContext,
|
||||
timeTilCalibration: f64 = 1.0, // in seconds
|
||||
calibrationPeriod: f64 = 1.0, // in seconds
|
||||
first: bool = true,
|
||||
|
||||
shutdownModuleFunction: ?*const fn () callconv(.c) void = null,
|
||||
stringContext: *core.algorithm.string_pool.StringContext = undefined,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !@This() {
|
||||
const rv = Engine{
|
||||
.allocator = allocator,
|
||||
|
|
@ -105,10 +96,8 @@ pub const Engine = struct {
|
|||
.lastEngineTime = 0.0,
|
||||
.jobManager = try JobManager.create(allocator),
|
||||
.eventors = .{},
|
||||
.rootTimer = try std.time.Timer.start(),
|
||||
.frameNumber = 1,
|
||||
.exitListeners = .{},
|
||||
.scopesContext = try pscopes.ScopesContext.create(allocator),
|
||||
// .nfdRuntime = try nfd.NFDRuntime.create(allocator, .{}),
|
||||
.delegates = EngineDelegates.init(allocator),
|
||||
};
|
||||
|
|
@ -124,13 +113,14 @@ pub const Engine = struct {
|
|||
self.jobManager.destroy();
|
||||
|
||||
self.engineObjectsByName.deinit(self.allocator);
|
||||
self.scopesContext.destroy();
|
||||
|
||||
if (self.destroyListCore.items.len > 0) {
|
||||
var i: i32 = @intCast(self.destroyListCore.items.len - 1);
|
||||
while (i >= 0) : (i -= 1) {
|
||||
const item = self.destroyListCore.items[@as(usize, @intCast(i))];
|
||||
self.destroyObject(item);
|
||||
if (item.vtable.deinit_func) |deinitFn| {
|
||||
deinitFn(item.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.destroyListCore.deinit(self.allocator);
|
||||
|
|
@ -161,10 +151,6 @@ pub const Engine = struct {
|
|||
return @ptrCast(@alignCast(rv));
|
||||
}
|
||||
|
||||
const DebugSlack = struct {
|
||||
slack: [1024 * 64]u8 align(16) = undefined,
|
||||
};
|
||||
|
||||
// creates an engine object using the engine's allocator.
|
||||
pub fn createObjectVTable(self: *@This(), vtable: *core.EngineObjectVTable, params: NeonObjectParams) !*anyopaque {
|
||||
if (self.createObjectLock) {
|
||||
|
|
@ -176,17 +162,7 @@ pub const Engine = struct {
|
|||
self.createObjectLock = true;
|
||||
defer self.createObjectLock = false;
|
||||
const newIndex = self.engineObjects.items.len;
|
||||
var newObjectPtr: *anyopaque = undefined; //self.allocator.create(DebugSlack);
|
||||
|
||||
if (comptime core.BuildOption("static_build")) {
|
||||
newObjectPtr = @ptrCast(@alignCast((try self.allocator.alignedAlloc(u8, .@"16", vtable.typeSize))));
|
||||
} else {
|
||||
newObjectPtr = @ptrCast(@alignCast((try self.allocator.create(DebugSlack))));
|
||||
}
|
||||
|
||||
try vtable.init_func(newObjectPtr, self.allocator, true);
|
||||
|
||||
// try vtable.init_func(self.allocator); // call this thing with the special allocator that adds vtable. slackSize to it.
|
||||
const newObjectPtr = try vtable.init_func(self.allocator); // call this thing with the special allocator that adds vtable. slackSize to it.
|
||||
|
||||
const newObjectRef = EngineObjectRef{
|
||||
.ptr = @as(*anyopaque, @ptrCast(newObjectPtr)),
|
||||
|
|
@ -254,7 +230,6 @@ pub const Engine = struct {
|
|||
}
|
||||
|
||||
pub fn tick(self: *@This()) !void {
|
||||
// ------------ frame updates ---------
|
||||
tracy.FrameMark();
|
||||
tracy.FrameMarkStart("frame");
|
||||
defer tracy.FrameMarkEnd("frame");
|
||||
|
|
@ -263,18 +238,14 @@ pub const Engine = struct {
|
|||
|
||||
var z1 = tracy.ZoneN(@src(), "time updates");
|
||||
|
||||
var shouldCalibrate: bool = false;
|
||||
|
||||
if (self.first) {
|
||||
self.first = false;
|
||||
self.engineStartTime = newTime;
|
||||
self.lastEngineTime = newTime;
|
||||
self.sessionStamp = std.time.microTimestamp();
|
||||
shouldCalibrate = true;
|
||||
}
|
||||
|
||||
if (newTime < self.lastEngineTime) {
|
||||
try core.assertf(false, "negative deltaTime this should not be possible", .{});
|
||||
std.debug.print("Warning! negative deltaTime? clamping to 0.0 newTime: {d} lastEngineTime:{d}", .{
|
||||
newTime,
|
||||
self.lastEngineTime,
|
||||
|
|
@ -282,16 +253,6 @@ pub const Engine = struct {
|
|||
}
|
||||
|
||||
self.deltaTime = @max(newTime - self.lastEngineTime, 0.0);
|
||||
|
||||
self.timeTilCalibration -= self.deltaTime;
|
||||
|
||||
if (self.timeTilCalibration < 0 or shouldCalibrate) {
|
||||
const zcalibrate = core.tracy.ZoneN(@src(), "calibrate timing scopeContext");
|
||||
self.scopesContext.calibrate();
|
||||
zcalibrate.End();
|
||||
self.timeTilCalibration = self.calibrationPeriod;
|
||||
}
|
||||
|
||||
math.rollingAverage(&self.averageFrameTime, self.deltaTime, @floatFromInt(self.averageFrameSampleWindow));
|
||||
z1.End();
|
||||
|
||||
|
|
@ -302,9 +263,7 @@ pub const Engine = struct {
|
|||
z2.End();
|
||||
|
||||
self.frameNumber += 1;
|
||||
// -------------------------------------------
|
||||
|
||||
// sync
|
||||
var z3 = tracy.ZoneN(@src(), "platform event updates");
|
||||
if (self.platformProcEventsFunc) |procEventsFn| {
|
||||
try procEventsFn(self.platformCtx, self.frameNumber);
|
||||
|
|
@ -315,14 +274,12 @@ pub const Engine = struct {
|
|||
objectRef.vtable.processEvents.?(objectRef.ptr, self.frameNumber) catch @panic("process event error");
|
||||
}
|
||||
|
||||
// pretick
|
||||
var z4 = tracy.ZoneN(@src(), "pretick event updates");
|
||||
for (self.preTickables.items) |*objectRef| {
|
||||
objectRef.vtable.preTick_func.?(objectRef.ptr, self.deltaTime) catch @panic("pretick event error");
|
||||
}
|
||||
z4.End();
|
||||
|
||||
// tick
|
||||
var z5 = tracy.ZoneN(@src(), "system object ticks");
|
||||
var index: isize = @as(isize, @intCast(self.tickables.items.len)) - 1;
|
||||
while (index >= 0) : (index -= 1) {
|
||||
|
|
@ -336,7 +293,6 @@ pub const Engine = struct {
|
|||
|
||||
const systemsThreadTime: f64 = time.getEngineTime() - newTime;
|
||||
|
||||
// renderer
|
||||
for (self.renderers.items) |*renderer| {
|
||||
var z = tracy.Zone(@src());
|
||||
z.Name(renderer.vtable.typeName);
|
||||
|
|
@ -372,29 +328,14 @@ pub const Engine = struct {
|
|||
self.destroyDependents();
|
||||
}
|
||||
|
||||
fn destroyObject(self: *@This(), item: EngineObjectRef) void {
|
||||
core.engine_log("destroying object {s}", .{item.vtable.typeName});
|
||||
if (item.vtable.deinit_func) |deinitFn| {
|
||||
deinitFn(item.ptr);
|
||||
}
|
||||
|
||||
if (comptime core.BuildOption("static_build")) {
|
||||
var slice: []u8 = undefined;
|
||||
slice.ptr = @ptrCast(@alignCast(item.ptr));
|
||||
slice.len = item.vtable.typeSize;
|
||||
self.allocator.rawFree(slice, .@"16", @returnAddress());
|
||||
} else {
|
||||
const asStruct: *DebugSlack = @ptrCast(@alignCast(item.ptr));
|
||||
self.allocator.destroy(asStruct);
|
||||
}
|
||||
}
|
||||
|
||||
fn destroyDependents(self: *@This()) void {
|
||||
if (self.destroyListSimple.items.len > 0) {
|
||||
var i: i32 = @intCast(self.destroyListSimple.items.len - 1);
|
||||
while (i >= 0) : (i -= 1) {
|
||||
const item = self.destroyListSimple.items[@as(usize, @intCast(i))];
|
||||
self.destroyObject(item);
|
||||
if (item.vtable.deinit_func) |deinitFn| {
|
||||
deinitFn(item.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.dependentsDestroyed.store(true, .seq_cst);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ pub fn updateEngineVTable(comptime T: type) void {
|
|||
|
||||
vtable.* = T.NeonObjectTable;
|
||||
vtable.version = version + 1;
|
||||
|
||||
if (@hasDecl(T, "objectReload")) {
|
||||
core.get(T).objectReload();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn PatchStruct(comptime T: type, p: *T, old: []const FieldInfo) void {
|
||||
|
|
@ -163,8 +167,7 @@ pub const EngineObjectVTable = struct {
|
|||
|
||||
singletonName: ?[]const u8 = null,
|
||||
|
||||
// new init_function passes in an already created object
|
||||
init_func: *const fn (*anyopaque, std.mem.Allocator, bool) EngineDataEventError!void,
|
||||
init_func: *const fn (std.mem.Allocator) EngineDataEventError!*anyopaque,
|
||||
tick_func: ?*const fn (*anyopaque, f64) void = null,
|
||||
engineDraw_func: ?*const fn (*anyopaque, f64) void = null,
|
||||
preTick_func: ?*const fn (*anyopaque, f64) EngineDataEventError!void = null,
|
||||
|
|
@ -268,10 +271,11 @@ pub const EngineObjectVTable = struct {
|
|||
|
||||
if (@hasDecl(TargetType, "init")) {
|
||||
const wrappedInit = struct {
|
||||
pub fn func(p: *anyopaque, allocator: std.mem.Allocator, first: bool) EngineDataEventError!void {
|
||||
const newObject: *TargetType = @ptrCast(@alignCast(p)); // funcFind(allocator) catch return error.BadInit;
|
||||
newObject.init(allocator, first) catch return error.BadInit;
|
||||
// return @as(*anyopaque, @ptrCast(newObject));
|
||||
const funcFind: @TypeOf(@field(TargetType, "init")) = @field(TargetType, "init");
|
||||
|
||||
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
|
||||
const newObject = funcFind(allocator) catch return error.BadInit;
|
||||
return @as(*anyopaque, @ptrCast(newObject));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -280,10 +284,11 @@ pub const EngineObjectVTable = struct {
|
|||
|
||||
if (@hasDecl(TargetType, "create")) {
|
||||
const wrappedInit = struct {
|
||||
pub fn func(p: *anyopaque, allocator: std.mem.Allocator, first: bool) EngineDataEventError!void {
|
||||
const newObject: *TargetType = @ptrCast(@alignCast(p)); // funcFind(allocator) catch return error.BadInit;
|
||||
newObject.create(allocator, first) catch return error.BadInit;
|
||||
// return @as(*anyopaque, @ptrCast(newObject));
|
||||
const funcFind: @TypeOf(@field(TargetType, "create")) = @field(TargetType, "create");
|
||||
|
||||
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
|
||||
const newObject = funcFind(allocator) catch return error.BadInit;
|
||||
return @as(*anyopaque, @ptrCast(newObject));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,13 @@
|
|||
const std = @import("std");
|
||||
|
||||
const core = @import("core.zig");
|
||||
const pscopes = core.algorithm.pscopes;
|
||||
|
||||
// returns time since engine started in nanoseconds
|
||||
// todo, replace with monotonic timer
|
||||
pub fn getEngineTime() f64 {
|
||||
if (core.staticsInitialized) {
|
||||
const read = core.getEngine().rootTimer.read();
|
||||
return @as(f64, @floatFromInt(read)) / std.time.ns_per_s;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return @as(f64, @floatFromInt(std.time.milliTimestamp())) / 1000;
|
||||
}
|
||||
|
||||
// returns a pscopes.TimingScope for the current time.
|
||||
pub fn takeProfilingStamp() pscopes.TimingScope {
|
||||
return core.getEngine().scopesContext.stampScope();
|
||||
// return the current system timestamp in nanoseconds
|
||||
pub fn getEngineTimeStamp() i128 {
|
||||
return std.time.nanoTimestamp();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,23 +121,21 @@ fn dllChangedCallback(pathChanged: []const u8, ctx: ?*anyopaque) void {
|
|||
}
|
||||
|
||||
pub const ModuleLoader = struct {
|
||||
backingAllocator: std.mem.Allocator = undefined,
|
||||
arena: std.heap.ArenaAllocator = undefined,
|
||||
backingAllocator: std.mem.Allocator,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
loadedModules: std.ArrayListUnmanaged(*LoadedModule) = .{},
|
||||
watchInitialized: bool = false,
|
||||
watchInitializeFn: ?*const fn () void = null,
|
||||
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "core.ModuleLoader");
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.backingAllocator = allocator,
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn addModule(self: *@This(), moduleName: []const u8) !void {
|
||||
|
|
@ -152,11 +150,8 @@ pub const ModuleLoader = struct {
|
|||
const loaded = try self.arena.allocator().create(LoadedModule);
|
||||
|
||||
if (self.watchInitialized == false) {
|
||||
// core.fs().watchPath("zig-out/modules/");
|
||||
if (self.watchInitializeFn) |watchInitializeFn| {
|
||||
watchInitializeFn();
|
||||
self.watchInitialized = true;
|
||||
}
|
||||
core.fs().watchPath("zig-out/modules/");
|
||||
self.watchInitialized = true;
|
||||
}
|
||||
|
||||
loaded.* = LoadedModule{
|
||||
|
|
@ -170,7 +165,6 @@ pub const ModuleLoader = struct {
|
|||
}
|
||||
|
||||
try core.fs().addFileChangedCallback(libFileName, dllChangedCallback, loaded);
|
||||
core.engine_log("[ModuleLoader]: addFileChangedCallback '{s}'", .{libFileName});
|
||||
|
||||
try self.loadedModules.append(self.arena.allocator(), loaded);
|
||||
}
|
||||
|
|
@ -216,9 +210,10 @@ pub const ModuleLoader = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
pub fn destroy(self: *@This()) void {
|
||||
// std.fs.cwd().deleteTree(".modulecache") catch {};
|
||||
self.arena.deinit();
|
||||
self.backingAllocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
const std = @import("std");
|
||||
const logging = @import("logging.zig");
|
||||
|
||||
pub fn writeToFile(data: []const u8, path: []const u8) !void {
|
||||
const file = try std.fs.cwd().createFile(
|
||||
path,
|
||||
.{
|
||||
.read = true,
|
||||
},
|
||||
);
|
||||
|
||||
const bytes_written = try file.writeAll(data);
|
||||
_ = bytes_written;
|
||||
logging.engine_log("written: bytes to {s}", .{path});
|
||||
}
|
||||
|
||||
pub fn splitIntoLines(file_contents: []const u8) std.mem.SplitIterator(u8) {
|
||||
// find a \n and see if it has \r\n
|
||||
var index: u32 = 0;
|
||||
while (index < file_contents.len) : (index += 1) {
|
||||
if (file_contents[index] == '\n') {
|
||||
if (index > 0) {
|
||||
if (file_contents[index - 1] == '\r') {
|
||||
return std.mem.split(u8, file_contents, "\r\n");
|
||||
} else {
|
||||
return std.mem.split(u8, file_contents, "\n");
|
||||
}
|
||||
} else {
|
||||
return std.mem.split(u8, file_contents, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return std.mem.split(u8, file_contents, "\n");
|
||||
}
|
||||
|
|
@ -71,20 +71,21 @@ pub const GameObjectSystem = struct {
|
|||
// this is the new one, GameObjectList should be deleted after this passes initial usability
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "core.GameObjectSystem");
|
||||
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
allocator: std.mem.Allocator,
|
||||
objectDefinitions: std.AutoHashMapUnmanaged(u32, GameObjectInterfaceVTable) = .{},
|
||||
typesArena: std.heap.ArenaAllocator = undefined,
|
||||
typesArena: std.heap.ArenaAllocator,
|
||||
|
||||
objectSpawnEvents: std.ArrayListUnmanaged(SpawnEvent) = .{},
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.typesArena = std.heap.ArenaAllocator.init(self.allocator),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn spawnObject(self: *@This(), comptime T: type, objectName: []const u8, parameters: SpawnParameters) !*T {
|
||||
|
|
@ -176,8 +177,9 @@ pub const GameObjectSystem = struct {
|
|||
_ = dt;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.typesArena.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ fn BindingData(Func: type) type {
|
|||
pub const ActionBinding = struct {
|
||||
data: BindingData(ActionFunc),
|
||||
keys: std.ArrayListUnmanaged(ActionBindingKey) = .{},
|
||||
layer: ?*BindingLayer = null,
|
||||
|
||||
// pub const Listener = struct {
|
||||
// id: u32,
|
||||
|
|
@ -574,11 +573,11 @@ pub const BindingLayer = struct {
|
|||
|
||||
// not gonna actually deal with layers right now
|
||||
pub const InputStack = struct {
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
active: ?*BindingLayer = undefined,
|
||||
active: ?*BindingLayer,
|
||||
bindingStack: std.ArrayListUnmanaged(*BindingLayer) = .{},
|
||||
arena: std.heap.ArenaAllocator = undefined,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
||||
keysDown: std.AutoHashMapUnmanaged(Key, bool) = .{},
|
||||
|
||||
|
|
@ -591,10 +590,8 @@ pub const InputStack = struct {
|
|||
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.InputStack");
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
|
|
@ -603,6 +600,8 @@ pub const InputStack = struct {
|
|||
};
|
||||
|
||||
core.EngineObject(@This()).gInstance = self;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn updatePreviousInputs(self: *@This()) void {
|
||||
|
|
@ -716,6 +715,7 @@ pub const InputStack = struct {
|
|||
if (self.active) |active| {
|
||||
active.destroy();
|
||||
}
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
moveAxis = CreateInput2DAxis()
|
||||
moveAxis:addKey(Keys.W, 1.0, Axis.Y)
|
||||
moveAxis:addKey(Keys.S, -1.0, Axis.Y)
|
||||
|
||||
moveAxis:addKey(Keys.A, -1.0, Axis.X)
|
||||
moveAxis:addKey(Keys.D, 1.0, Axis.X)
|
||||
moveAxis:addListener(balls, )
|
||||
|
||||
Input.addBinding("move", moveAxis, true)
|
||||
|
||||
Input.removeBinding("move")
|
||||
|
||||
|
||||
|
|
@ -7,18 +7,13 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged;
|
|||
|
||||
const mutex_job_queue = core.BuildOption("mutex_job_queue");
|
||||
|
||||
pub const TaskInstanceOptions = struct {
|
||||
threadId: u32,
|
||||
threadCount: u32,
|
||||
};
|
||||
|
||||
pub const JobManager = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
// need a mutex for the jobQueue... todo later
|
||||
jobQueue: RingQueueU(ThreadContext),
|
||||
jobQueue: RingQueueU(JobContext),
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
jobQueueConcurrent: core.ConcurrentQueueU(ThreadContext),
|
||||
jobQueueConcurrent: core.ConcurrentQueueU(JobContext),
|
||||
workers: []*JobWorker,
|
||||
numCpus: usize,
|
||||
numWorkers: u32 = 0,
|
||||
|
|
@ -29,8 +24,8 @@ pub const JobManager = struct {
|
|||
self.* = JobManager{
|
||||
.allocator = allocator,
|
||||
.numCpus = std.Thread.getCpuCount() catch 4,
|
||||
.jobQueue = RingQueueU(ThreadContext).init(allocator, 4096) catch unreachable,
|
||||
.jobQueueConcurrent = core.ConcurrentQueueU(ThreadContext).initCapacity(allocator, 4096) catch unreachable,
|
||||
.jobQueue = RingQueueU(JobContext).init(allocator, 4096) catch unreachable,
|
||||
.jobQueueConcurrent = core.ConcurrentQueueU(JobContext).initCapacity(allocator, 4096) catch unreachable,
|
||||
.workers = undefined,
|
||||
};
|
||||
|
||||
|
|
@ -40,28 +35,17 @@ pub const JobManager = struct {
|
|||
|
||||
var i: usize = 0;
|
||||
while (i < self.workers.len) : (i += 1) {
|
||||
self.workers[i] = JobWorker.init(self.allocator, i, true) catch unreachable;
|
||||
self.workers[i].workerThreadNumber = i;
|
||||
self.workers[i] = JobWorker.init(self.allocator, i) catch unreachable;
|
||||
self.workers[i].workerId = i;
|
||||
self.workers[i].manager = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn runLocally(self: *@This(), capture: anytype, task: TaskInstanceOptions) !void {
|
||||
pub fn newJob(self: *@This(), capture: anytype) !void {
|
||||
const Lambda = @TypeOf(capture);
|
||||
const ctx = try ThreadContext.new(self.allocator, Lambda, capture, task);
|
||||
|
||||
//pub fn init(allocator: std.mem.Allocator, workerNumber: usize, detached:bool) !*@This() {
|
||||
const worker = try JobWorker.init(self.allocator, 0xff, false); // todo move these things into a pool of non-detached workers that we can just grab whenever
|
||||
defer worker.deinit();
|
||||
worker.currentJobContext = ctx;
|
||||
worker.run();
|
||||
}
|
||||
|
||||
pub fn newJob(self: *@This(), capture: anytype, task: TaskInstanceOptions) !void {
|
||||
const Lambda = @TypeOf(capture);
|
||||
const ctx = try ThreadContext.new(self.allocator, Lambda, capture, task);
|
||||
const ctx = try JobContext.new(self.allocator, Lambda, capture);
|
||||
|
||||
if (mutex_job_queue) {
|
||||
self.mutex.lock();
|
||||
|
|
@ -107,7 +91,7 @@ pub const JobManager = struct {
|
|||
}
|
||||
|
||||
pub fn clearJobs(self: *@This()) void {
|
||||
var jobCtx: ?ThreadContext = null;
|
||||
var jobCtx: ?JobContext = null;
|
||||
|
||||
if (mutex_job_queue) {
|
||||
self.mutex.lock();
|
||||
|
|
@ -133,16 +117,15 @@ pub const JobManager = struct {
|
|||
|
||||
pub const JobWorker = struct {
|
||||
detached: bool = true, // most threads are detached, completions are handled via callbacks.
|
||||
currentJobContext: ?ThreadContext = null,
|
||||
workerThread: ?std.Thread = null,
|
||||
currentJobContext: ?JobContext = null,
|
||||
workerThread: std.Thread,
|
||||
futex: Atomic(u32) = Atomic(u32).init(0),
|
||||
current: u32 = 0,
|
||||
shouldDie: Atomic(bool) = Atomic(bool).init(false),
|
||||
busy: Atomic(bool) = Atomic(bool).init(false),
|
||||
allocator: std.mem.Allocator,
|
||||
workerThreadNumber: usize = 0, // identifier for debugging purposes, never changes.
|
||||
workerId: usize = 0,
|
||||
manager: ?*JobManager = null,
|
||||
scratchArena: std.heap.ArenaAllocator,
|
||||
|
||||
pub fn wake(self: *JobWorker) void {
|
||||
// this needs to be re-evaluated, it's nice for system performance but
|
||||
|
|
@ -150,20 +133,15 @@ pub const JobWorker = struct {
|
|||
std.Thread.Futex.wake(&self.futex, 1);
|
||||
}
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, workerNumber: usize, detached: bool) !*@This() {
|
||||
pub fn init(allocator: std.mem.Allocator, workerNumber: usize) !*@This() {
|
||||
const self = try allocator.create(JobWorker);
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.detached = detached,
|
||||
.workerThreadNumber = workerNumber,
|
||||
.scratchArena = std.heap.ArenaAllocator.init(allocator),
|
||||
.workerThread = try std.Thread.spawn(.{}, @This().workerThreadFunc, .{self}),
|
||||
.workerId = workerNumber,
|
||||
};
|
||||
|
||||
if (detached) {
|
||||
self.workerThread = try std.Thread.spawn(.{}, @This().workerThreadFunc, .{self});
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
|
@ -171,26 +149,8 @@ pub const JobWorker = struct {
|
|||
return self.busy.load(.acquire);
|
||||
}
|
||||
|
||||
fn run(self: *@This()) void {
|
||||
self.busy.store(true, .seq_cst);
|
||||
|
||||
if(self.manager)|manager|
|
||||
{
|
||||
manager.bump();
|
||||
}
|
||||
|
||||
var ctx = self.currentJobContext.?;
|
||||
ctx.workerInfo = self;
|
||||
ctx.func(ctx.capture, &ctx);
|
||||
self.busy.store(false, .seq_cst);
|
||||
|
||||
ctx.deinit();
|
||||
if (self.scratchArena.reset(.retain_capacity)) {}
|
||||
self.currentJobContext = null;
|
||||
}
|
||||
|
||||
pub fn workerThreadFunc(self: *@This()) void {
|
||||
const printed = std.fmt.allocPrintSentinel(self.allocator, "WorkerThread_{d}", .{self.workerThreadNumber}, 0) catch unreachable;
|
||||
const printed = std.fmt.allocPrintSentinel(self.allocator, "WorkerThread_{d}", .{self.workerId}, 0) catch unreachable;
|
||||
tracy.InitThread();
|
||||
tracy.SetThreadName(@as([*:0]u8, @ptrCast(printed.ptr)));
|
||||
|
||||
|
|
@ -201,7 +161,12 @@ pub const JobWorker = struct {
|
|||
while (!self.shouldDie.load(.acquire)) {
|
||||
if (self.currentJobContext != null) {
|
||||
wakeGrabCount = 0;
|
||||
self.run();
|
||||
self.busy.store(true, .seq_cst);
|
||||
var ctx = self.currentJobContext.?;
|
||||
ctx.func(ctx.capture, &ctx);
|
||||
self.busy.store(false, .seq_cst);
|
||||
ctx.deinit();
|
||||
self.currentJobContext = null;
|
||||
} else {
|
||||
std.Thread.Futex.wait(&self.futex, self.current);
|
||||
}
|
||||
|
|
@ -215,6 +180,13 @@ pub const JobWorker = struct {
|
|||
self.manager.?.mutex.unlock();
|
||||
} else {
|
||||
self.currentJobContext = manager.jobQueueConcurrent.pop();
|
||||
//while (wakeGrabCount > 0) : (wakeGrabCount -= 1) {
|
||||
// self.currentJobContext = manager.jobQueueConcurrent.pop();
|
||||
// if (self.currentJobContext != null) {
|
||||
// break;
|
||||
// }
|
||||
// std.Thread.sleep(1000 * 1000);
|
||||
//}
|
||||
wakeGrabCount = 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -228,99 +200,28 @@ pub const JobWorker = struct {
|
|||
pub fn deinit(self: *@This()) void {
|
||||
self.shouldDie.store(true, .seq_cst);
|
||||
self.wake();
|
||||
if(self.workerThread)|workerThread|
|
||||
{
|
||||
workerThread.join();
|
||||
}
|
||||
|
||||
self.scratchArena.deinit();
|
||||
self.workerThread.join();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const Src = std.builtin.SourceLocation;
|
||||
pub fn BarrierCV(src: Src) type {
|
||||
return struct {
|
||||
pub const Src = src;
|
||||
|
||||
pub var count: u32 = 0;
|
||||
pub var generation: u32 = 0;
|
||||
pub var mutex: std.Thread.Mutex = .{};
|
||||
pub var cv: std.Thread.Condition = .{};
|
||||
|
||||
pub fn sync(threadId: u32, barrierCount: u32) !void {
|
||||
_ = threadId;
|
||||
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
|
||||
count += 1;
|
||||
|
||||
if (count == barrierCount) {
|
||||
// 2 second timeout
|
||||
count = 0;
|
||||
generation += 1;
|
||||
cv.broadcast();
|
||||
} else {
|
||||
try cv.timedWait(&mutex, 1000 * 1000 * 1000 * 2);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn BarrierSpin(src: Src) type {
|
||||
return struct {
|
||||
pub const Src = src;
|
||||
|
||||
pub var count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0);
|
||||
pub var park: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
|
||||
|
||||
pub fn sync(threadId: u32, barrierCount: u32) !void {
|
||||
_ = threadId;
|
||||
|
||||
const c = count.fetchAdd(1, .release);
|
||||
if(c + 1 == barrierCount)
|
||||
{
|
||||
park.store(true, .release);
|
||||
while(count.load(.acquire) > 1) {}
|
||||
_ = count.fetchSub(1, .release);
|
||||
park.store(false, .release);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(park.load(.acquire) == false) {}
|
||||
_ = count.fetchSub(1, .seq_cst);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Barrier = BarrierSpin;
|
||||
|
||||
pub const ThreadContext = struct {
|
||||
pub const JobContext = struct {
|
||||
const Self = @This();
|
||||
|
||||
funcName: []const u8 = "unnamed",
|
||||
allocator: std.mem.Allocator, //todo, backed arena allocator would be sick for this.
|
||||
func: *const fn (*anyopaque, *ThreadContext) void, // todo, add an error for job funcs
|
||||
func: *const fn (*anyopaque, *JobContext) void, // todo, add an error for job funcs
|
||||
capture: *anyopaque = undefined,
|
||||
threadId: u32,
|
||||
threadIdCount: u32,
|
||||
destroyFunc: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||
workerInfo: ?*JobWorker = null,
|
||||
|
||||
pub fn new(
|
||||
allocator: std.mem.Allocator,
|
||||
comptime CaptureType: type,
|
||||
capture: CaptureType,
|
||||
task: TaskInstanceOptions,
|
||||
) !ThreadContext {
|
||||
const align8_struct = struct { size: u64 };
|
||||
|
||||
pub fn new(allocator: std.mem.Allocator, comptime CaptureType: type, capture: CaptureType) !JobContext {
|
||||
if (!@hasDecl(CaptureType, "func")) {
|
||||
return error.NoValidLambda;
|
||||
}
|
||||
|
||||
const Wrap = struct {
|
||||
pub fn wrappedFunc(pointer: *anyopaque, context: *ThreadContext) void {
|
||||
pub fn wrappedFunc(pointer: *anyopaque, context: *JobContext) void {
|
||||
var ptr = @as(*CaptureType, @ptrCast(@alignCast(pointer)));
|
||||
ptr.func(context);
|
||||
}
|
||||
|
|
@ -333,9 +234,6 @@ pub const ThreadContext = struct {
|
|||
|
||||
const self = Self{
|
||||
.allocator = allocator,
|
||||
.threadId = task.threadId,
|
||||
.threadIdCount = task.threadCount,
|
||||
.funcName = @typeName(CaptureType),
|
||||
.func = Wrap.wrappedFunc,
|
||||
.destroyFunc = Wrap.wrappedDestroy,
|
||||
.capture = try allocator.create(CaptureType),
|
||||
|
|
@ -345,22 +243,6 @@ pub const ThreadContext = struct {
|
|||
return self;
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
pub fn scratch(self: *@This()) std.mem.Allocator {
|
||||
return self.workerInfo.?.scratchArena.allocator();
|
||||
}
|
||||
|
||||
pub fn splitSlice(self: @This(), comptime T: type, slice: []T) struct{ slice: []T, startIndex: usize } {
|
||||
const r = core.p2.splitSliceWork(T, slice, self.threadId, self.threadIdCount);
|
||||
return .{.slice = r.slice, .startIndex = r.startIndex};
|
||||
|
||||
}
|
||||
|
||||
pub fn barrier(self: *@This(), comptime src: std.builtin.SourceLocation) !void {
|
||||
try Barrier(src).sync(self.threadId, self.threadIdCount);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.destroyFunc(self.capture, self.allocator);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,13 +192,13 @@ pub const FileLog = struct {
|
|||
pub const LoggerSys = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.LoggerSys");
|
||||
|
||||
writeOutBuffer: std.ArrayList(u8) = .{},
|
||||
flushBuffer: std.ArrayList(u8) = .{},
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
logFilePath: []const u8 = "none",
|
||||
logFile: std.fs.File = undefined,
|
||||
consoleFile: std.fs.File = undefined,
|
||||
writerBuffer: []u8 = undefined,
|
||||
writeOutBuffer: std.ArrayList(u8),
|
||||
flushBuffer: std.ArrayList(u8),
|
||||
allocator: std.mem.Allocator,
|
||||
logFilePath: []const u8,
|
||||
logFile: std.fs.File,
|
||||
consoleFile: std.fs.File,
|
||||
writerBuffer: []u8,
|
||||
lock: std.Thread.Mutex = .{},
|
||||
flushing: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
|
||||
|
||||
|
|
@ -306,14 +306,12 @@ pub const LoggerSys = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const cwd = std.fs.cwd();
|
||||
const ofile = std.fmt.allocPrint(allocator, core.DefaultSavePath ++ "/{s}", .{"Session_Log.txt"}) catch unreachable;
|
||||
cwd.makePath(core.DefaultSavePath) catch unreachable;
|
||||
|
||||
const self = try allocator.create(@This());
|
||||
self.* = @This(){
|
||||
.allocator = allocator,
|
||||
.writeOutBuffer = std.ArrayList(u8).initCapacity(allocator, LogBufferSize) catch unreachable,
|
||||
|
|
@ -323,6 +321,8 @@ pub const LoggerSys = struct {
|
|||
.logFile = cwd.createFile(ofile, .{}) catch unreachable,
|
||||
.consoleFile = std.fs.File.stdout(),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
|
|
@ -333,6 +333,8 @@ pub const LoggerSys = struct {
|
|||
|
||||
self.writeOutBuffer.deinit(self.allocator);
|
||||
self.flushBuffer.deinit(self.allocator);
|
||||
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn processEvents(self: *@This(), frameNumber: u64) core.EngineDataEventError!void {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
tickables = {}
|
||||
properties = {}
|
||||
|
||||
local function registerTick(object, f)
|
||||
table.insert(tickables, {obj = object, func = f})
|
||||
end
|
||||
|
||||
function __TickScripts(deltaTime)
|
||||
for index, entry in ipairs(tickables) do
|
||||
entry.func(entry.obj, deltaTime)
|
||||
end
|
||||
end
|
||||
|
||||
function __RegisterEntityProperty(userdata)
|
||||
properties[userdata] = {}
|
||||
end
|
||||
|
||||
function SetProperty(entity, property)
|
||||
properties[entity] = property
|
||||
end
|
||||
|
||||
function GetProperty(entity)
|
||||
return properties[entity]
|
||||
end
|
||||
|
||||
function GetObject(entity)
|
||||
return properties[entity]
|
||||
end
|
||||
|
||||
Core = {
|
||||
registerTick = registerTick;
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
print("Hello from lua.")
|
||||
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
pub const StructApi = struct {
|
||||
fields: []const StructField,
|
||||
};
|
||||
|
||||
pub const StructField = struct {
|
||||
name: [:0]const u8,
|
||||
offset: usize,
|
||||
alignment: usize,
|
||||
size: usize,
|
||||
};
|
||||
|
||||
fn generateStructApi(comptime T: type) StructApi {
|
||||
return .{
|
||||
.fields = &generateFieldsList(T),
|
||||
};
|
||||
}
|
||||
|
||||
fn generateFieldsList(comptime T: type) [std.meta.fields(T).len]StructField {
|
||||
var flist: [std.meta.fields(T).len]StructField = undefined;
|
||||
|
||||
const E = std.meta.FieldEnum(T);
|
||||
|
||||
inline for (std.meta.fields(T)) |field| {
|
||||
const fieldIndex = @intFromEnum(@field(E, field.name));
|
||||
|
||||
flist[fieldIndex].name = field.name;
|
||||
flist[fieldIndex].size = @sizeOf(field.type);
|
||||
flist[fieldIndex].alignment = @alignOf(field.type);
|
||||
flist[fieldIndex].offset = @offsetOf(T, field.name);
|
||||
}
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
pub fn ComponentApi(comptime T: type) type {
|
||||
return struct {
|
||||
pub const Api = generateStructApi(T);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn listApi(structApi: StructApi) void {
|
||||
for (structApi.fields) |field| {
|
||||
core.engine_log("field: {s} offset: {d} name: {d} size: {d}", field);
|
||||
}
|
||||
}
|
||||
|
||||
// example function call via api
|
||||
pub fn callInner(func: anytype, args: []const u8) void {
|
||||
const Args = std.meta.ArgsTuple(@typeInfo(@TypeOf(func)).pointer.child);
|
||||
|
||||
const asArgsTuple: *const Args = @ptrCast(@alignCast(args.ptr));
|
||||
@call(.auto, func, asArgsTuple.*);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const core = @import("../core.zig");
|
||||
|
|
@ -1,7 +1,14 @@
|
|||
const std = @import("std");
|
||||
const misc = @import("misc.zig");
|
||||
const zm = @import("zmath");
|
||||
const math = std.math;
|
||||
|
||||
// LUA BEGIN feel like I should mark up the parts of the engine that need to be
|
||||
// sliced out should i ever decide I'm sick of lua
|
||||
const lua = @import("lua");
|
||||
const pod = lua.pod;
|
||||
// LUA END
|
||||
|
||||
// vector versions.
|
||||
pub const f32x4 = @Vector(4, f32); // __m128_int
|
||||
pub const f32x8 = @Vector(8, f32); // __m256_int
|
||||
|
|
@ -65,7 +72,23 @@ pub fn Vector2Type(comptime T: type, comptime typeName: []const u8) type {
|
|||
x: T = 0,
|
||||
y: T = 0,
|
||||
|
||||
pub const VectorTypeName = typeName;
|
||||
// LUA BEGIN
|
||||
pub const PodDataTable: pod.DataTable = .{
|
||||
.name = typeName,
|
||||
.funcs = &.{
|
||||
"fmul",
|
||||
"dot",
|
||||
"length",
|
||||
"normalize",
|
||||
},
|
||||
.operators = .{
|
||||
.add = "add",
|
||||
.sub = "sub",
|
||||
.mul = "vmul",
|
||||
.eq = "equals",
|
||||
},
|
||||
};
|
||||
// LUA END
|
||||
|
||||
pub const Ones = @This(){ .x = 1, .y = 1 };
|
||||
pub const Zeroes = @This(){ .x = 0, .y = 0 };
|
||||
|
|
@ -209,8 +232,6 @@ pub fn Vector3Type(comptime T: type, comptime typeName: []const u8) type {
|
|||
y: T = 0,
|
||||
z: T = 0,
|
||||
|
||||
pub const VectorTypeName = typeName;
|
||||
|
||||
pub const Ones = @This(){ .x = 1, .y = 1, .z = 1 };
|
||||
pub const Zeroes = @This(){ .x = 0, .y = 0, .z = 0 };
|
||||
|
||||
|
|
@ -218,6 +239,24 @@ pub fn Vector3Type(comptime T: type, comptime typeName: []const u8) type {
|
|||
pub const Right = @This(){ .x = 1 };
|
||||
pub const Forward = @This(){ .z = 1 };
|
||||
|
||||
// LUA BEGIN
|
||||
pub const PodDataTable: pod.DataTable = .{
|
||||
.name = typeName,
|
||||
.funcs = &.{
|
||||
"fmul",
|
||||
"dot",
|
||||
"length",
|
||||
"normalize",
|
||||
},
|
||||
.operators = .{
|
||||
.add = "add",
|
||||
.sub = "sub",
|
||||
.mul = "vmul",
|
||||
.eq = "equals",
|
||||
},
|
||||
};
|
||||
// LUA END
|
||||
|
||||
pub inline fn new(x: T, y: T, z: T) @This() {
|
||||
return .{
|
||||
.x = x,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
// Random misc. utilities that aren't /totally/ categorized right now.
|
||||
|
||||
const logging = @import("logging.zig");
|
||||
|
||||
// Creates a for-loopable range between [0, n)
|
||||
pub fn count(comptime n: anytype) [n]u0 {
|
||||
return comptime [1]u0{0} ** n;
|
||||
}
|
||||
|
||||
// Creates a for-loopable range between [start, end).
|
||||
// reccomended for use with inline FORs
|
||||
pub fn range(comptime start: usize, comptime end: anytype) [end - start]@TypeOf(start) {
|
||||
comptime var r = [1]@TypeOf(start){start} ** (end - start);
|
||||
|
||||
comptime {
|
||||
for (r, 0..) |val, i| {
|
||||
r[i] = val + i;
|
||||
}
|
||||
}
|
||||
|
||||
return comptime r;
|
||||
}
|
||||
|
||||
pub fn slice_to_cstr(str: []const u8) ?[*:0]const u8 {
|
||||
return @as(?[*:0]const u8, @ptrCast(str.ptr));
|
||||
}
|
||||
|
||||
pub fn buf_to_cstr(str: anytype) ?[*:0]const u8 {
|
||||
return @as(?[*:0]const u8, @ptrCast(&str[0]));
|
||||
}
|
||||
|
||||
pub const CStr = [*:0]const u8;
|
||||
|
||||
pub fn debug_struct(preamble: []const u8, s: anytype) void {
|
||||
logging.graphics_log("{s}:", .{preamble});
|
||||
logging.graphics_log(" {any}", .{s});
|
||||
}
|
||||
|
||||
pub fn p_to_av(a: anytype) [*]@TypeOf(a.*) {
|
||||
return @as([*]@TypeOf(a.*), @ptrCast(a));
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
const std = @import("std");
|
||||
const spng = @import("spng");
|
||||
|
||||
const core = @import("core");
|
||||
const core = @import("core.zig");
|
||||
|
||||
pub const PngContents = struct {
|
||||
path: []const u8,
|
||||
|
|
@ -27,7 +27,7 @@ pub const PngContents = struct {
|
|||
return pngContents;
|
||||
}
|
||||
|
||||
pub fn initFromFSCooked(fs: *core.PackerFS, allocator: std.mem.Allocator, path: []const u8) !@This() {
|
||||
pub fn initFromFSCooked(fs: *core.FileSystem, allocator: std.mem.Allocator, path: []const u8) !@This() {
|
||||
const mapping = try fs.loadFile(path);
|
||||
defer fs.unmap(mapping);
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ pub const PngContents = struct {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
pub fn initFromFS(fs: *core.PackerFS, allocator: std.mem.Allocator, path: []const u8) !@This() {
|
||||
pub fn initFromFS(fs: *core.FileSystem, allocator: std.mem.Allocator, path: []const u8) !@This() {
|
||||
// 1. check the fs to see if a cooked version of the file exists
|
||||
// 2. load that one if possible
|
||||
// 3. otherwise, load the other one.
|
||||
|
|
@ -56,8 +56,6 @@ pub const SceneObjectRepr = struct {
|
|||
attachmentMode: SceneAttachMode = .relative, // doesn't do anything yet, only support relative right now
|
||||
transformOverride: ?*core.Transform = null,
|
||||
lastUpdate: u32 = 0,
|
||||
|
||||
merge: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), // set to true if we've merged already, false if we havent
|
||||
};
|
||||
|
||||
pub const SceneObject = struct {
|
||||
|
|
@ -296,17 +294,12 @@ fn childAllocator() std.mem.Allocator {
|
|||
pub const SceneSystem = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.SceneSystem");
|
||||
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
allocator: std.mem.Allocator,
|
||||
dynamicObjects: ArrayListUnmanaged(core.ObjectHandle) = .{},
|
||||
childrenArena: std.heap.ArenaAllocator = undefined,
|
||||
childrenArena: std.heap.ArenaAllocator,
|
||||
tickCount: u32 = 0,
|
||||
sceneObjectContainer: *SceneObjectSet = undefined,
|
||||
|
||||
cachedOutputs: std.ArrayList(std.ArrayList(core.Transform)) = .{},
|
||||
writeOutList: std.ArrayList(std.ArrayList(usize)) = .{},
|
||||
|
||||
lastUpdateTransformTime: f64 = 0.0,
|
||||
|
||||
pub const Field = SceneObjectSet.Field;
|
||||
pub const FieldType = SceneObjectSet.FieldType;
|
||||
|
||||
|
|
@ -333,6 +326,16 @@ pub const SceneSystem = struct {
|
|||
} else {}
|
||||
}
|
||||
|
||||
// core.engine_log("final\n{d} {d} {d} {d}\n{d} {d} {d} {d}", .{
|
||||
// final[0][0],
|
||||
// final[0][1],
|
||||
// final[0][2],
|
||||
// final[0][3],
|
||||
// final[1][0],
|
||||
// final[1][1],
|
||||
// final[1][2],
|
||||
// final[1][3],
|
||||
// });
|
||||
repr.transform = core.zm.mul(
|
||||
core.zm.mul(
|
||||
core.zm.mul(
|
||||
|
|
@ -344,52 +347,37 @@ pub const SceneSystem = struct {
|
|||
final,
|
||||
);
|
||||
|
||||
// core.engine_log("final\n{d} {d} {d} {d}\n{d} {d} {d} {d}", .{
|
||||
// repr.transform[0][0],
|
||||
// repr.transform[0][1],
|
||||
// repr.transform[0][2],
|
||||
// repr.transform[0][3],
|
||||
// repr.transform[1][0],
|
||||
// repr.transform[1][1],
|
||||
// repr.transform[1][2],
|
||||
// repr.transform[1][3],
|
||||
// });
|
||||
|
||||
repr.lastUpdate = self.tickCount;
|
||||
}
|
||||
|
||||
const useParallelJob = true;
|
||||
|
||||
pub fn updateTransforms(self: *@This()) void {
|
||||
// todo. calculate a running load factor for the number of movable objects
|
||||
// vs static objects
|
||||
// if we have a small amount of movable vs static AND if we have > 1000 objects,
|
||||
// then iterate over dynamicObjects array instead
|
||||
|
||||
if (useParallelJob) {
|
||||
// we use 6 workers if the last timing scope ran > 2ms
|
||||
// otherwise use 1 worker
|
||||
var scope = core.engineTime.takeProfilingStamp();
|
||||
var workerCount: u32 = 1;
|
||||
|
||||
if (self.lastUpdateTransformTime > 0.001) // 1ms
|
||||
{
|
||||
workerCount = 6;
|
||||
}
|
||||
|
||||
core.parallelJob(UpdateWorldTransformsJob{}, false, workerCount) catch unreachable;
|
||||
scope.end();
|
||||
if (scope.duration()) |duration| {
|
||||
self.lastUpdateTransformTime = duration;
|
||||
}
|
||||
} else {
|
||||
for (Scene.SceneObjectContainer.denseItems(._repr), 0..) |*repr, i| {
|
||||
const settings = Scene.SceneObjectContainer.readDense(i, .settings);
|
||||
if (settings.sceneMode == .moveable or repr.lastUpdate == 0) {
|
||||
const posRot = Scene.SceneObjectContainer.readDense(i, .posRot);
|
||||
self.updateTransform(repr, posRot);
|
||||
}
|
||||
for (Scene.SceneObjectContainer.denseItems(._repr), 0..) |*repr, i| {
|
||||
const settings = Scene.SceneObjectContainer.readDense(i, .settings);
|
||||
if (settings.sceneMode == .moveable or repr.lastUpdate == 0) {
|
||||
const posRot = Scene.SceneObjectContainer.readDense(i, .posRot);
|
||||
self.updateTransform(repr, posRot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const MaxWorkerCount = 24;
|
||||
|
||||
// ----- NeonObject interace ----
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.childrenArena = std.heap.ArenaAllocator.init(allocator),
|
||||
|
|
@ -399,24 +387,7 @@ pub const SceneSystem = struct {
|
|||
Scene.SceneObjectContainer = try SceneObjectSet.create(allocator);
|
||||
self.sceneObjectContainer = Scene.SceneObjectContainer;
|
||||
|
||||
for (0..MaxWorkerCount) |i| {
|
||||
_ = i;
|
||||
try self.cachedOutputs.append(self.allocator, .{});
|
||||
try self.writeOutList.append(self.allocator, .{});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOutputList(self: *@This(), threadId: u32) !*std.ArrayList(usize) {
|
||||
const outputLen = self.sceneObjectContainer.denseItems(._repr).len;
|
||||
try self.writeOutList.items[threadId].ensureTotalCapacity(self.allocator, outputLen);
|
||||
self.writeOutList.items[threadId].shrinkRetainingCapacity(0);
|
||||
return &self.writeOutList.items[threadId];
|
||||
}
|
||||
|
||||
pub fn getOutputForWorker(self: *@This(), threadId: u32) ![]core.Transform {
|
||||
const outputLen = self.sceneObjectContainer.denseItems(._repr).len;
|
||||
try self.cachedOutputs.items[threadId].resize(self.allocator, outputLen);
|
||||
return self.cachedOutputs.items[threadId].items;
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn preTick(self: *@This(), dt: f64) !void {
|
||||
|
|
@ -436,113 +407,10 @@ pub const SceneSystem = struct {
|
|||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
for (self.cachedOutputs.items) |*i| {
|
||||
i.deinit(self.allocator);
|
||||
}
|
||||
for (self.writeOutList.items) |*i| {
|
||||
i.deinit(self.allocator);
|
||||
}
|
||||
self.dynamicObjects.deinit(self.allocator);
|
||||
self.childrenArena.deinit();
|
||||
Scene.SceneObjectContainer.destroy();
|
||||
self.cachedOutputs.deinit(self.allocator);
|
||||
self.writeOutList.deinit(self.allocator);
|
||||
}
|
||||
};
|
||||
|
||||
const UpdateWorldTransformsJob = struct {
|
||||
world: ?*anyopaque = null,
|
||||
|
||||
pub fn func(self: @This(), thread: *core.ThreadContext) void {
|
||||
const z = core.tracy.ZoneN(@src(), "Transform Hierarchy - wide");
|
||||
defer z.End();
|
||||
thread.barrier(@src()) catch unreachable;
|
||||
|
||||
const outputs = core.get(core.SceneSystem).getOutputForWorker(thread.threadId) catch return;
|
||||
const outputList = core.get(core.SceneSystem).getOutputList(thread.threadId) catch return;
|
||||
|
||||
self.updateTransformsHierarchy(thread, outputs, outputList) catch |err| switch (err) {
|
||||
error.OutOfMemory => {
|
||||
unreachable;
|
||||
},
|
||||
// else => {
|
||||
// thread.abort(@src(), "unknown error", err);
|
||||
// return;
|
||||
// },
|
||||
};
|
||||
|
||||
const denseRepr = core.Scene.SceneObjectContainer.denseItems(._repr);
|
||||
|
||||
const z3 = core.tracy.ZoneN(@src(), "Merge Outputs");
|
||||
// merge outputs
|
||||
for (outputList.items) |outIndex| {
|
||||
if (denseRepr[outIndex].merge.cmpxchgStrong(false, true, .seq_cst, .acquire) == null) {
|
||||
denseRepr[outIndex].transform = outputs[outIndex];
|
||||
}
|
||||
}
|
||||
z3.End();
|
||||
thread.barrier(@src()) catch unreachable;
|
||||
}
|
||||
|
||||
fn updateTransform(
|
||||
self: @This(),
|
||||
thread: *core.ThreadContext,
|
||||
index: usize,
|
||||
densePosRot: []core.scene.ScenePosRot,
|
||||
denseRepr: []core.scene.SceneObjectRepr,
|
||||
outputs: []core.math.Transform,
|
||||
outputList: *std.ArrayList(usize),
|
||||
) void {
|
||||
var final: core.Transform = core.zm.identity();
|
||||
|
||||
const repr = denseRepr[index];
|
||||
|
||||
if (repr.parent) |parent| {
|
||||
if (core.Scene.SceneObjectContainer.sparseToDense(parent)) |parentIndex| {
|
||||
self.updateTransform(thread, parentIndex, densePosRot, denseRepr, outputs, outputList);
|
||||
const parentTransform = outputs[parentIndex];
|
||||
final = parentTransform;
|
||||
} else {}
|
||||
}
|
||||
|
||||
const posRot = densePosRot[index];
|
||||
|
||||
outputs[index] = core.zm.mul(
|
||||
core.zm.mul(
|
||||
core.zm.mul(
|
||||
core.zm.scalingV(posRot.scale.toZm()),
|
||||
core.zm.matFromQuat(posRot.rotation.quat),
|
||||
),
|
||||
core.zm.translationV(posRot.position.toZm()),
|
||||
),
|
||||
final,
|
||||
);
|
||||
|
||||
outputList.appendAssumeCapacity(index);
|
||||
}
|
||||
|
||||
pub fn updateTransformsHierarchy(self: @This(), thread: *core.ThreadContext, outputs: []core.math.Transform, outputList: *std.ArrayList(usize)) !void {
|
||||
const z = core.tracy.ZoneN(@src(), "updateTransformsHierarchy");
|
||||
defer z.End();
|
||||
//const dense = self.world.denseScenes();
|
||||
const densePosRot = core.Scene.SceneObjectContainer.denseItems(.posRot);
|
||||
const denseRepr = core.Scene.SceneObjectContainer.denseItems(._repr);
|
||||
|
||||
// everything allocated with thread.scratch is blown away when the thread is complete
|
||||
// try outputs.resize(thread.scratch(), densePosRot.len);
|
||||
|
||||
// scan and mark all root nodes for update
|
||||
const split = thread.splitSlice(core.scene.SceneObjectRepr, denseRepr);
|
||||
|
||||
// const locals:[]core.math.Transform = try thread.scratch().alloc(core.math.Transform, split.slice.len);
|
||||
|
||||
const z2 = core.tracy.ZoneN(@src(), "WalkAndResolve");
|
||||
for (split.slice, 0..) |*repr, i| {
|
||||
repr.merge.store(false, .seq_cst); // reset the merge big
|
||||
const index = split.startIndex + i;
|
||||
self.updateTransform(thread, index, densePosRot, denseRepr, outputs, outputList);
|
||||
}
|
||||
z2.End();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
// scripting integration using lua
|
||||
|
||||
const std = @import("std");
|
||||
pub const lua = @import("lua");
|
||||
const core = @import("core.zig");
|
||||
const startup_script = @embedFile("lua/startup.lua");
|
||||
const core_script = @embedFile("lua/core.lua");
|
||||
const ecs = @import("ecs.zig");
|
||||
const ComponentRef = @import("script/ComponentRef.zig");
|
||||
const ComponentRegistration = @import("script/ComponentRegistration.zig");
|
||||
|
||||
pub const script_bindings = @import("script_bindings.zig");
|
||||
|
||||
const c = lua.c;
|
||||
|
||||
pub var gLuaState: lua.LuaState = undefined;
|
||||
pub var gLuaAllocator: std.mem.Allocator = undefined;
|
||||
|
||||
pub fn setupLuaFromModule(pstate: *anyopaque, p_lua_allocator: *anyopaque) void {
|
||||
gLuaAllocator = core.startup_getAllocator(p_lua_allocator);
|
||||
gLuaState.l = @ptrCast(pstate);
|
||||
}
|
||||
|
||||
const luaRegLibs: []const c.luaL_Reg = &.{
|
||||
.{ .name = "print", .func = printWrapper },
|
||||
.{ .name = null, .func = null },
|
||||
};
|
||||
|
||||
// binds the default print() function in lua to print to the console
|
||||
fn printWrapper(l: ?*lua.c.lua_State) callconv(.c) i32 {
|
||||
const state: lua.LuaState = .{ .l = l };
|
||||
const argc = state.getTop();
|
||||
|
||||
if (core.getLogger() == null) {
|
||||
core.printRaw("> ", .{});
|
||||
}
|
||||
|
||||
if (argc >= 1) {
|
||||
core.printRaw("[SCRIPT ]: ", .{});
|
||||
}
|
||||
|
||||
var i: i32 = 1;
|
||||
while (i <= argc) : (i += 1) {
|
||||
if (state.isString(i)) {
|
||||
if (i > 1) {
|
||||
core.printRaw(" ", .{});
|
||||
}
|
||||
core.printRaw("{s}", .{state.toString(i)});
|
||||
} else if (state.isUserdata(i)) {
|
||||
const s = state.toStringL(i);
|
||||
state.pop(1);
|
||||
core.printRaw("{s}", .{s});
|
||||
}
|
||||
}
|
||||
|
||||
core.printRaw("\n", .{});
|
||||
state.pop(argc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// initialization of the lua scripting interface.
|
||||
// this interface is only threadsafe to operate on from the main systems thread (at this time).
|
||||
pub fn start_lua(allocator: std.mem.Allocator) !void {
|
||||
gLuaAllocator = allocator;
|
||||
gLuaState = try lua.LuaState.init(.{});
|
||||
try lua.pod.setupFormatBuffer(allocator);
|
||||
|
||||
// overload and hook into global functions
|
||||
_ = gLuaState.getGlobal("_G");
|
||||
c.luaL_setfuncs(gLuaState.l, luaRegLibs.ptr, 0);
|
||||
gLuaState.pop(1);
|
||||
|
||||
try lua.pod.registerPodType(&gLuaState, ecs.Entity);
|
||||
try lua.pod.registerPodType(&gLuaState, ComponentRegistration);
|
||||
|
||||
try script_bindings.registerTypes();
|
||||
|
||||
try gLuaState.loadString(startup_script);
|
||||
try gLuaState.pcall();
|
||||
|
||||
try gLuaState.loadString(core_script);
|
||||
try gLuaState.pcall();
|
||||
}
|
||||
|
||||
pub fn createLuaComponentDefinitions(globalName: []const u8, container: ecs.EcsContainerRef, luaNew: anytype) !void {
|
||||
const reg = try gLuaState.newZigUserdata(ComponentRegistration);
|
||||
reg.ref = container;
|
||||
reg.name = globalName;
|
||||
reg.luaNew = luaNew;
|
||||
try gLuaState.setGlobal(globalName);
|
||||
}
|
||||
|
||||
pub fn registerComponent(comptime Component: type, container: ecs.EcsContainerRef) !void {
|
||||
const ReferenceType = ComponentRef.ComponentReferenceType(Component);
|
||||
try createLuaComponentDefinitions(@ptrCast(Component.ComponentName), container, ReferenceType.luaNew);
|
||||
try ReferenceType.registerType(getState());
|
||||
}
|
||||
|
||||
pub fn shutdown_lua() void {
|
||||
lua.pod.shutdownFormatBuffer();
|
||||
gLuaState.deinit();
|
||||
}
|
||||
|
||||
pub fn getState() *lua.LuaState {
|
||||
return &gLuaState;
|
||||
}
|
||||
|
||||
fn findScriptBasepath(s: []const u8) []const u8 {
|
||||
var i: usize = s.len - 1;
|
||||
|
||||
while (i > 0) : (i -= 1) {
|
||||
if (s[i] == '\\' or s[i] == '/') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (s[i] == '\\' or s[i] == '/') {
|
||||
return s[i + 1 ..];
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
pub fn loadTypes(scriptPath: []const u8) !void {
|
||||
// 1. scan filesystem for all files under the script path
|
||||
var fileList = try core.fs().listAllSubpaths(gLuaAllocator, scriptPath);
|
||||
defer fileList.deinit();
|
||||
for (fileList.data.items, 0..) |f, i| {
|
||||
// 2. enforce naming scheme for each script.
|
||||
const basePath = findScriptBasepath(f);
|
||||
|
||||
// core.engine_log("checking script file: {s} path:{s} base {s}", .{ f, fileList.sources.items[i], basePath });
|
||||
// 3. for each one, load the script and assign them based on name.
|
||||
if (std.ascii.isUpper(basePath[0])) {
|
||||
core.engine_log("loading script file: {s} path:{s}", .{ f, fileList.sources.items[i] });
|
||||
const scriptFile = try core.fs().loadFile(f);
|
||||
defer core.fs().unmap(scriptFile);
|
||||
try gLuaState.loadString(scriptFile.bytes);
|
||||
try gLuaState.pcall();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runScriptFile(scriptPath: []const u8) !void {
|
||||
const scriptFile = try core.fs().loadFile(scriptPath);
|
||||
defer core.fs().unmap(scriptFile);
|
||||
try gLuaState.loadString(scriptFile.bytes);
|
||||
try gLuaState.pcall();
|
||||
}
|
||||
|
|
@ -0,0 +1,258 @@
|
|||
// lwdt can't have meta tables so I'm going to create component references
|
||||
//
|
||||
// the way this works is
|
||||
// - entities are created as POD types
|
||||
// - entities can get components added to them via entity:addComponent
|
||||
// - this returns a ComponentReference
|
||||
// - you can also get components from an entity via enity:get()
|
||||
// - this also returns a ComponentReference
|
||||
//
|
||||
// - ComponentReferences allow you to modify data on a component or call functions on them
|
||||
//
|
||||
//
|
||||
// How the registration works
|
||||
//
|
||||
// - define component
|
||||
// - ecs.zig defineComponent
|
||||
// - ComponentRef.zig - ReferenceType
|
||||
// - addComponentRegistration - script.zig
|
||||
// - ComponentRef - ReferenceType.registerType
|
||||
|
||||
// this represents the lua side of the object
|
||||
pub fn ComponentReferenceType(comptime T: type) type {
|
||||
return struct {
|
||||
ptr: *T = undefined,
|
||||
|
||||
// used for resolving deltas.
|
||||
handle: core.ObjectHandle = undefined,
|
||||
stateCount: u32 = 0,
|
||||
containerRef: ecs.EcsContainerRef = undefined,
|
||||
|
||||
pub const MetatableName = T.ComponentName;
|
||||
|
||||
// argc = 1,
|
||||
// 1. a componentRegistration userdata
|
||||
// can only be called from entity.luaAddComponent
|
||||
pub fn luaNew(state: lua.LuaState, handle: core.ObjectHandle, ptr: ?*anyopaque) void {
|
||||
const ud = state.newZigUserdata(@This()) catch return;
|
||||
const containerRef = ecs.getTypeContainer(T);
|
||||
|
||||
ud.* = .{
|
||||
.handle = handle,
|
||||
.containerRef = containerRef,
|
||||
.stateCount = 0,
|
||||
};
|
||||
|
||||
// std.debug.print("luaNew ComponentReferenceType: {x}\n", .{@intFromPtr(ud)});
|
||||
// std.debug.print("luaNew ContainerRef: {x}\n", .{@intFromPtr(containerRef.ptr)});
|
||||
// std.debug.print("luaNew " ++ @typeName(T) ++ " ud.ptr = {x}\n", .{@intFromPtr(ptr)});
|
||||
|
||||
if (ptr) |p| {
|
||||
ud.ptr = @ptrCast(@alignCast(p));
|
||||
ud.stateCount = containerRef.vtable.getStateCount(containerRef.ptr);
|
||||
}
|
||||
|
||||
if (@hasDecl(@TypeOf(T.BaseContainer.*), "IsMultiset")) {
|
||||
// ... there are several things that need to be reworked here...
|
||||
ud.ptr = @ptrCast(@alignCast(@as(*anyopaque, @ptrCast(&ud.handle))));
|
||||
// std.debug.print("luaNew " ++ @typeName(T) ++ " v = {any}\n", .{ud.ptr.getPosition()});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(self: *@This()) void {
|
||||
const ref = self.containerRef;
|
||||
// std.debug.print("self: {x}\n", .{@intFromPtr(self)});
|
||||
// std.debug.print("ptr: {x}\n", .{@intFromPtr(ref.ptr)});
|
||||
if (@hasDecl(@TypeOf(T.BaseContainer.*), "IsMultiset")) {
|
||||
//
|
||||
}
|
||||
if (ref.vtable.getStateCount(ref.ptr) != self.stateCount) {
|
||||
self.ptr = @ptrCast(@alignCast(ref.vtable.get(ref.ptr, self.handle)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self: *@This()) *T {
|
||||
self.resolve();
|
||||
return self.ptr;
|
||||
}
|
||||
|
||||
pub fn luaToString(state: lua.LuaState) i32 {
|
||||
// oh god... this isn't good
|
||||
// I think i've been treating this component ref as the user type
|
||||
// huge failure of type resolution
|
||||
if (state.toUserdata(@This(), 1)) |self| {
|
||||
self.resolve();
|
||||
Buffer.clearRetainingCapacity();
|
||||
|
||||
var writer = Buffer.writer();
|
||||
writer.print("{s}{{", .{MetatableName}) catch return 0;
|
||||
|
||||
inline for (std.meta.fields(T), 0..) |field, i| {
|
||||
if (i == 0) {
|
||||
writer.print(" {s} = ", .{field.name}) catch return 0;
|
||||
} else {
|
||||
writer.print(", {s} = ", .{field.name}) catch return 0;
|
||||
}
|
||||
switch (field.type) {
|
||||
f32, i32, u32, f64, i64, u64 => {
|
||||
writer.print("{d}", .{@field(self.ptr, field.name)}) catch return 0;
|
||||
},
|
||||
[]const u8 => {
|
||||
writer.print("\"{s}\"", .{@field(self.ptr, field.name)}) catch return 0;
|
||||
},
|
||||
else => {
|
||||
writer.print("<unknown type {s}>", .{@typeName(field.type)}) catch return 0;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
writer.print(" }}" ++ "\x00", .{}) catch return 0;
|
||||
state.pop(1);
|
||||
state.pushString(Buffer.items) catch return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn luaIndex(state: lua.LuaState) i32 {
|
||||
if (state.toUserdata(@This(), 1)) |self| {
|
||||
if (state.isString(2)) {
|
||||
_ = self;
|
||||
const argument = state.toString(2);
|
||||
// core.engine_log(@typeName(@This()) ++ " got indexed. 0x{x}", .{self.handle.index});
|
||||
|
||||
// check metatable
|
||||
if (state.getMetafield(1, @ptrCast(argument))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn makeTypeTable() lua.LibSpec {
|
||||
const methods = blk: {
|
||||
comptime var m: lua.LibSpec = &.{};
|
||||
m = m ++ .{lua.luaL_Reg{ .name = "__index", .func = lua.CWrap(luaIndex) }};
|
||||
m = m ++ .{lua.luaL_Reg{ .name = "__tostring", .func = lua.CWrap(luaToString) }};
|
||||
|
||||
inline for (T.ScriptExports) |name| {
|
||||
m = m ++ .{lua.luaL_Reg{ .name = @as([*c]const u8, @ptrCast(name)), .func = ComponentFuncWrapper(@field(T, name), T) }};
|
||||
}
|
||||
|
||||
break :blk m ++ .{lua.luaL_Reg{ .name = null, .func = null }};
|
||||
};
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
pub fn registerType(state: *lua.LuaState) !void {
|
||||
core.engine_log("creating lua metatable {s}", .{MetatableName});
|
||||
|
||||
const methods = comptime makeTypeTable();
|
||||
|
||||
try state.newMetatable(@ptrCast(MetatableName));
|
||||
try state.setFuncs(methods, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn ComponentFuncWrapper(comptime baseFunc: anytype, comptime baseType: type) lua.LuaCFunc {
|
||||
return lua.CWrap(FuncWrapper(baseFunc, baseType).wrapper);
|
||||
}
|
||||
|
||||
pub fn FuncWrapper(comptime baseFunc: anytype, comptime baseType: type) type {
|
||||
return struct {
|
||||
pub fn wrapper(state: lua.LuaState) i32 {
|
||||
const Args = std.meta.ArgsTuple(@TypeOf(baseFunc));
|
||||
|
||||
var args: Args = undefined;
|
||||
inline for (std.meta.fields(Args), 0..) |field, index| {
|
||||
// std.debug.print("typename = {s}\n", .{@typeName(field.type)});
|
||||
switch (field.type) {
|
||||
f32 => {
|
||||
args[index] = @as(f32, @floatCast(state.toNumber(index + 1)));
|
||||
},
|
||||
f64 => {
|
||||
args[index] = state.toNumber(index + 1);
|
||||
},
|
||||
i32 => {
|
||||
args[index] = @intFromFloat(state.toNumber(index + 1));
|
||||
},
|
||||
[]const u8 => {
|
||||
args[index] = state.toString(index + 1);
|
||||
},
|
||||
// I'll be honest how the hell does this work?
|
||||
//
|
||||
// the pointer being passed in here isn't the actual resulting type...
|
||||
// it's the reference type
|
||||
*baseType => {
|
||||
const ref = state.toUserdata(ComponentReferenceType(baseType), index + 1).?;
|
||||
ref.resolve();
|
||||
args[index] = ref.ptr;
|
||||
},
|
||||
baseType => {
|
||||
const ref = state.toUserdata(ComponentReferenceType(baseType), index + 1).?;
|
||||
ref.resolve();
|
||||
args[index] = ref.ptr.*;
|
||||
},
|
||||
else => {
|
||||
// if(isComponentType(field.type)) {
|
||||
// const ref = state.toUserdata(field.type, index + 1).?;
|
||||
// ref.resolve();
|
||||
// args[index] = ref.ptr.*;
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
lua.debugPrints(true);
|
||||
args[index] = (state.toUserdata(field.type, index + 1) orelse {
|
||||
std.debug.print("argument error in index: {d}\n", .{index});
|
||||
state.emitError("something's weird with this argument\n");
|
||||
@panic("lmao");
|
||||
}).*;
|
||||
lua.debugPrints(false);
|
||||
},
|
||||
}
|
||||
}
|
||||
state.pop(@intCast(args.len));
|
||||
|
||||
//const rv = @call(.always_inline, baseFunc, args);
|
||||
const rv = @call(.auto, baseFunc, args);
|
||||
switch (@TypeOf(rv)) {
|
||||
i32, u32, i64, u64 => {
|
||||
state.pushNumber(@floatFromInt(rv));
|
||||
},
|
||||
f32, f64 => {
|
||||
state.pushNumber(@floatCast(rv));
|
||||
},
|
||||
void => {
|
||||
return 0;
|
||||
},
|
||||
else => {
|
||||
const ud = state.newZigUserdata(@TypeOf(rv)) catch @panic("not implemented");
|
||||
ud.* = rv;
|
||||
},
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var Buffer: std.ArrayList(u8) = undefined;
|
||||
|
||||
pub fn setupFormatBuffer(allocator: std.mem.Allocator) !void {
|
||||
Buffer = std.ArrayList(u8).init(allocator);
|
||||
}
|
||||
pub fn shutdownFormatBuffer() void {
|
||||
Buffer.deinit();
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const core = @import("../core.zig");
|
||||
const ecs = @import("../ecs.zig");
|
||||
const lua = @import("lua");
|
||||
const pod = lua.pod;
|
||||
const scene = @import("../scene.zig");
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
ref: ecs.EcsContainerRef = undefined,
|
||||
name: []const u8 = undefined,
|
||||
luaNew: *const fn (state: lua.LuaState, core.ObjectHandle, ?*anyopaque) void = undefined,
|
||||
|
||||
pub const PodDataTable: pod.DataTable = .{
|
||||
.name = "ComponentRegistration",
|
||||
.banInstantiation = true,
|
||||
.toStringOverride = lua.CWrap(toString),
|
||||
};
|
||||
|
||||
// I should really move all the registration to this file.
|
||||
pub fn toString(state: lua.LuaState) i32 {
|
||||
var workBuffer: [256]u8 = undefined;
|
||||
const ud = state.toUserdata(@This(), 1).?;
|
||||
state.pop(1);
|
||||
const str = std.fmt.bufPrintZ(&workBuffer, "Component Data Table: {s} 0x{x}", .{ ud.name, ud.ref.ptr }) catch return 0;
|
||||
state.pushString(str) catch return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
pub fn createComponent(self: @This(), handle: core.ObjectHandle) ?*anyopaque {
|
||||
const ref = self.ref;
|
||||
const rv = ref.vtable.createWithHandle(ref.ptr, handle);
|
||||
// std.debug.print("createComponent: {p}\n", .{rv});
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
const lua = @import("lua");
|
||||
const ecs = @import("../ecs.zig");
|
||||
const std = @import("std");
|
||||
const core = @import("../core.zig");
|
||||
const pod = lua.pod;
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
const luaRegLibs: []const lua.c.luaL_Reg = &.{
|
||||
.{ .name = "registerTick", .func = lua.CWrap(registerTick) },
|
||||
.{ .name = null, .func = null },
|
||||
};
|
||||
|
||||
// register core types and subsystems into the scripting engine
|
||||
pub fn registerTypes() !void {
|
||||
// transform POD type
|
||||
|
||||
const state = script.getState();
|
||||
try lua.pod.registerPodType(state, core.Vector);
|
||||
try lua.pod.registerPodType(state, core.Vectorf);
|
||||
try lua.pod.registerPodType(state, core.Vector2);
|
||||
try lua.pod.registerPodType(state, core.Vector2f);
|
||||
// try lua.pod.registerPodType(state, core.Transform);
|
||||
|
||||
// lua.pod.registerPodType(state, core.Vector4, "Vector4");
|
||||
try state.createLibrary("Systems", luaRegLibs);
|
||||
}
|
||||
|
||||
pub fn registerTick(l: lua.LuaState) i32 {
|
||||
// two arguments first one is going to be userdata entity
|
||||
// second one is going to be a lua function.
|
||||
_ = l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub const ScriptTicks = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.ScriptTicks");
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), deltaTime: f64) void {
|
||||
_ = self;
|
||||
const state = script.getState();
|
||||
_ = state.getGlobal("__TickScripts");
|
||||
state.pushNumber(deltaTime);
|
||||
state.pcallStack(1) catch {
|
||||
core.engine_logs("could not execute lua script");
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const lua = @import("lua");
|
||||
const std = @import("std");
|
||||
const script = @import("script.zig");
|
||||
const core = @import("core.zig");
|
||||
|
|
@ -141,7 +141,7 @@ pub const StackCompactor = struct {
|
|||
var stackCompactor: *core.StackCompactor = undefined;
|
||||
|
||||
pub fn initStackCompactor() void {
|
||||
stackCompactor = core.StackCompactor.create(std.heap.smp_allocator) catch unreachable;
|
||||
stackCompactor = core.StackCompactor.create(std.heap.c_allocator) catch unreachable;
|
||||
}
|
||||
|
||||
pub inline fn pushCallStack() void {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn dupeZ(comptime T: type, allocator: std.mem.Allocator, source: []const T) ![]T {
|
||||
var buff: []T = try allocator.alloc(T, source.len + 1);
|
||||
for (source, 0..source.len) |s, i| {
|
||||
buff[i] = s;
|
||||
}
|
||||
buff[source.len] = 0;
|
||||
return buff;
|
||||
}
|
||||
|
||||
pub fn dupe(comptime T: type, allocator: std.mem.Allocator, source: []const T) ![]T {
|
||||
var buff: []T = try allocator.alloc(T, source.len);
|
||||
for (source, 0..) |s, i| {
|
||||
buff[i] = s;
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn implement_func_for_tagged_union_nonull(
|
||||
self: anytype,
|
||||
comptime funcName: []const u8,
|
||||
comptime returnType: type,
|
||||
args: anytype,
|
||||
) returnType {
|
||||
const Self = @TypeOf(self);
|
||||
inline for (@typeInfo(std.meta.Tag(Self)).Enum.fields) |field| {
|
||||
if (@as(std.meta.Tag(Self), @enumFromInt(field.value)) == self) {
|
||||
if (@hasDecl(@TypeOf(@field(self, field.name)), funcName)) {
|
||||
return @field(@field(self, field.name), funcName)(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// this is generated zig code.
|
||||
pub const SceneAPI = struct {
|
||||
pub const Api: machinery.StructApi = .{
|
||||
.fields = &.{
|
||||
.{
|
||||
.name = "handle",
|
||||
.offset = 0,
|
||||
.alignment = @alignOf(core.ObjectHandle),
|
||||
.size = @sizeOf(core.ObjectHandle),
|
||||
},
|
||||
.{
|
||||
.name = "testField",
|
||||
.offset = 4,
|
||||
.alignment = @alignOf(u32),
|
||||
.size = @sizeOf(u32),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub var setPosition: *const fn (*anyopaque, core.Vectorf) void = undefined;
|
||||
pub const setPositionArgs = std.meta.ArgsTuple(@typeInfo(@TypeOf(setPosition)).pointer.child);
|
||||
};
|
||||
|
||||
const core = @import("core");
|
||||
const machinery = core.machinery;
|
||||
const std = @import("std");
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
pub const Scene = extern struct {
|
||||
pub const ApiGen = machinery.ComponentApi(@This());
|
||||
|
||||
handle: core.ObjectHandle = .{},
|
||||
testField: u32 = 0,
|
||||
position: core.Vectorf = .{},
|
||||
|
||||
pub fn init(self: *@This()) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn setPosition(self: *@This(), vector: core.Vectorf) void {
|
||||
self.position = vector;
|
||||
core.engine_log("position set: x={d} y={d} z={d}", self.position);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
_ = self;
|
||||
}
|
||||
};
|
||||
|
||||
const hand = @import("SceneApiHand.zig");
|
||||
const SceneAPI = hand.SceneAPI;
|
||||
|
||||
pub fn doTest() !void {
|
||||
machinery.listApi(SceneAPI.Api);
|
||||
machinery.listApi(Scene.ApiGen.Api);
|
||||
|
||||
var scene: Scene = .{};
|
||||
|
||||
// from this point on, we're larping like we're a different DLL that
|
||||
// has no direct reference to the 'Scene' type
|
||||
// load function pointers
|
||||
SceneAPI.setPosition = @ptrCast(@alignCast(&Scene.setPosition));
|
||||
|
||||
// takes it as an opaque ptr
|
||||
testmodule.callWithNoDirectLinking(&scene);
|
||||
}
|
||||
|
||||
const testmodule = @import("testmodule.zig");
|
||||
const core = @import("core");
|
||||
const machinery = core.machinery;
|
||||
const std = @import("std");
|
||||
|
|
@ -1,28 +1,20 @@
|
|||
var gAllocator: std.mem.Allocator = undefined;
|
||||
|
||||
pub export fn startup_module(p_allocator: *anyopaque, args: ?*anyopaque) bool {
|
||||
const allocator = core.modulePreamble(p_allocator, args) catch return false;
|
||||
_ = args;
|
||||
|
||||
gAllocator = @as(*std.mem.Allocator, @ptrCast(@alignCast(p_allocator))).*;
|
||||
|
||||
const allocator = gAllocator;
|
||||
|
||||
// do some test allocations and let it leak
|
||||
const t = std.fmt.allocPrint(allocator, "Lmao 2 nova {d}", .{2}) catch unreachable;
|
||||
const t = std.fmt.allocPrint(gAllocator, "Lmao 2 nova {d}", .{2}) catch unreachable;
|
||||
std.debug.print("external module started allocated string: {s}\n", .{t});
|
||||
allocator.free(t);
|
||||
const subsystem = core.get(SampleSubsystem);
|
||||
SceneAPI.setPosition = @ptrCast(@alignCast(subsystem.setPositionPtr));
|
||||
|
||||
const a: SceneAPI.setPositionArgs = .{ subsystem.scene, core.Vectorf{ .y = 12, .z = 13 } };
|
||||
const argsAsBytes = std.mem.asBytes(&a);
|
||||
machinery.callInner(SceneAPI.setPosition, argsAsBytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub export fn shutdown_module() void {}
|
||||
|
||||
const hand = @import("SceneApiHand.zig");
|
||||
const SceneAPI = hand.SceneAPI;
|
||||
const SampleSubsystem = @import("samplesubsystem.zig");
|
||||
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
const machinery = core.machinery;
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "testing.sampleSubsystem");
|
||||
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
scene: *anyopaque = undefined,
|
||||
setPositionPtr: *const anyopaque = undefined,
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
const exampleScene = @import("exampleScene.zig");
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
pub fn callWithNoDirectLinking(scene: *anyopaque) void {
|
||||
const args: SceneAPI.setPositionArgs = .{ scene, core.Vectorf{} };
|
||||
const argsAsBytes = std.mem.asBytes(&args);
|
||||
machinery.callInner(SceneAPI.setPosition, argsAsBytes);
|
||||
}
|
||||
|
||||
const hand = @import("SceneApiHand.zig");
|
||||
const SceneAPI = hand.SceneAPI;
|
||||
|
||||
const core = @import("core");
|
||||
const machinery = core.machinery;
|
||||
const std = @import("std");
|
||||
|
|
@ -5,9 +5,6 @@ const memory = core.MemoryTracker;
|
|||
const engine_log = core.engine_log;
|
||||
const engine_logs = core.engine_logs;
|
||||
|
||||
const exampleScene = @import("exampleScene.zig");
|
||||
const SampleSubsystem = @import("samplesubsystem.zig");
|
||||
|
||||
test "simple systems setup for core" {
|
||||
std.testing.refAllDecls(core.algorithm);
|
||||
|
||||
|
|
@ -25,17 +22,7 @@ test "simple systems setup for core" {
|
|||
try core.start_module(&map, .{ .unitTest = true }, allocator);
|
||||
defer core.shutdown_module(allocator);
|
||||
|
||||
const subsystem = try core.createObject(SampleSubsystem, .{});
|
||||
const scene = try allocator.create(exampleScene.Scene);
|
||||
defer allocator.destroy(scene);
|
||||
|
||||
subsystem.scene = scene;
|
||||
subsystem.setPositionPtr = @ptrCast(&exampleScene.Scene.setPosition);
|
||||
|
||||
engine_logs("systems started, shutting down");
|
||||
|
||||
engine_logs("this is a new print added to the test function");
|
||||
engine_logs("this is a new print added to the test function");
|
||||
memory.MTPrintStatsDelta();
|
||||
|
||||
try test_loadingConfigs();
|
||||
|
|
@ -196,11 +183,7 @@ fn test_gameObjects(allocator: std.mem.Allocator) !void {
|
|||
const spawnFunc = core.GameObjectList.spawnObjectFunction(GameObject);
|
||||
const interface = spawnFunc(objList).?;
|
||||
|
||||
core.engine_log("lol", .{});
|
||||
|
||||
core.engine_log("handle = {x}", .{interface.vtable.getEntity.?(interface.ptr).handle.index});
|
||||
|
||||
objList.tick(0.016);
|
||||
|
||||
try exampleScene.doTest();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@ pub const Impl = struct {
|
|||
};
|
||||
|
||||
// renderer plugin
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
const ImguiArgs = struct { imguiIni: []const u8 = "imgui.ini" };
|
||||
|
|
@ -108,6 +109,11 @@ pub const Impl = struct {
|
|||
_ = ig.dockSpaceOverViewport(ig.getMainViewport(), .{ .passthru_central_node = true }, null);
|
||||
_ = dt;
|
||||
}
|
||||
|
||||
// renderer plugin
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
const c = @import("cimgui").c;
|
||||
const ig = @import("cimgui");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "game.TopBar");
|
||||
pub const Slack = core.SlackStruct(@This(), 256);
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
|
@ -6,10 +7,8 @@ windowsMenu: std.ArrayListUnmanaged(*MenuEntry) = .{},
|
|||
entriesByName: std.AutoHashMapUnmanaged(u32, *MenuEntry) = .{},
|
||||
menuOpen: bool = false,
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try Slack.create(allocator);
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||
|
|
@ -22,6 +21,8 @@ pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
|||
.ctx = self,
|
||||
.windowFunction = windowOpen,
|
||||
});
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn setEntryOpen(self: *@This(), name: []const u8, open: ?bool) void {
|
||||
|
|
@ -107,6 +108,7 @@ pub fn tick(self: *@This(), dt: f64) void {
|
|||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.arena.deinit();
|
||||
self.allocator.destroy(Slack.fromPtr(self));
|
||||
}
|
||||
|
||||
pub const MenuEntry = struct {
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ consolePressedEnter: bool = false,
|
|||
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "extras.consoleWindow");
|
||||
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.buffer = core.logging.LogBuffer.init(allocator),
|
||||
};
|
||||
|
||||
self.setup();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn setup(self: *@This()) void {
|
||||
|
|
@ -65,6 +65,7 @@ pub fn windowOpen(entry: *imgui.utils.MenuEntry, dt: f64) void {
|
|||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.buffer.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
const imgui = @import("../imgui.zig");
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ pub fn main() !void {
|
|||
core.engine_log("Working Dir set: {s}", .{try std.fs.cwd().realpath(".", &BUFFER)});
|
||||
}
|
||||
|
||||
// panickers.attachSegfaultHandler();
|
||||
panickers.attachSegfaultHandler();
|
||||
try realMain.main();
|
||||
}
|
||||
|
||||
|
|
|
|||
161
engine/main2.zig
161
engine/main2.zig
|
|
@ -1,161 +0,0 @@
|
|||
const std = @import("std");
|
||||
const core = @import("core").module;
|
||||
const panickers = core.panickers;
|
||||
|
||||
// const realMain = @import("main");
|
||||
|
||||
pub const gamespec = @import("gamespec");
|
||||
pub const options = @import("BacklogOptions");
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.enable_segfault_handler = true,
|
||||
};
|
||||
|
||||
//pub const build_options = @import("build_options");
|
||||
//pub const tracy_enabled = build_options.tracy_enabled;
|
||||
|
||||
// pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, x: ?usize) noreturn {
|
||||
// core.forceFlush();
|
||||
// core.script.lua.takeDump();
|
||||
// std.debug.defaultPanic(error_return_trace, x, msg);
|
||||
// }
|
||||
|
||||
pub const NwArgs = struct {
|
||||
useGPA: bool = false, // slower zig based memory allocator, provides detailed tracking of leaks and memory violations
|
||||
vulkanValidation: bool = true,
|
||||
fastTest: bool = false,
|
||||
dmt: bool = false, // detailed memory tracking, implements a timeline for tracking all memory allocations
|
||||
fatDump: bool = false, // takes a full fat minidump on crash, very large files are produced
|
||||
};
|
||||
|
||||
pub fn getArgs() !NwArgs {
|
||||
const a = try core.ParseArgs(NwArgs);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
fn fileExists(path: []const u8) bool {
|
||||
std.fs.cwd().access(path, .{}) catch return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn run() !void {
|
||||
core.engine_logs("calling gEngine.run");
|
||||
|
||||
try core.getEngine().run();
|
||||
|
||||
while (!core.getEngine().exitFinished()) {
|
||||
const z = core.tracy.ZoneN(@src(), "shutdown poll");
|
||||
z.End();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn startEngine(spec: *core.SpecVariantMap) bool {
|
||||
const args = getArgs() catch return false;
|
||||
|
||||
var backingAllocator: std.mem.Allocator = std.heap.smp_allocator;
|
||||
var gpa: std.heap.GeneralPurposeAllocator(.{
|
||||
.stack_trace_frames = 20,
|
||||
}) = .{};
|
||||
|
||||
defer {
|
||||
const cleanupStatus = gpa.deinit();
|
||||
if (cleanupStatus == .leak) {
|
||||
std.debug.print("gpa cleanup leaked memory\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.get("useGPA")) |arg| {
|
||||
if (arg.boolean == true) {
|
||||
backingAllocator = gpa.allocator();
|
||||
}
|
||||
}
|
||||
|
||||
const memory = core.MemoryTracker;
|
||||
memory.MTSetup(backingAllocator, .{ .timeline = args.dmt });
|
||||
defer memory.MTShutdown();
|
||||
|
||||
var tracker = memory.MTGet().?;
|
||||
const allocator = tracker.allocator();
|
||||
|
||||
_ = core.createNameRegistry(allocator) catch return false;
|
||||
core.maybeInitPackerFs(allocator) catch return false;
|
||||
|
||||
if (comptime core.BuildOption("static_build")) {
|
||||
core.engine_log("static build, using embedded shaders", .{});
|
||||
const loader = @import("staticShaderLoader");
|
||||
loader.installStaticResources() catch return false;
|
||||
}
|
||||
|
||||
core.start_module(spec, args, allocator) catch return false;
|
||||
|
||||
const moduleName = spec.get("moduleName").?.string;
|
||||
|
||||
if (comptime core.BuildOption("static_build")) {
|
||||
core.engine_logs("static build");
|
||||
const modlaunch = @import("modulelaunch.zig");
|
||||
var modargs = core.externModule.getModuleLoaderArgs(true);
|
||||
var modalloc = allocator;
|
||||
_ = modlaunch.startup_module(&modalloc, &modargs);
|
||||
} else {
|
||||
core.engine_logs("using hot reloading");
|
||||
const platform = @import("platform").module;
|
||||
platform.watchModules();
|
||||
|
||||
//if (comptime @hasDecl(backlog, "platform")) {
|
||||
//backlog.platform.watchModules();
|
||||
// }
|
||||
core.loadModule(moduleName, true) catch return false;
|
||||
}
|
||||
|
||||
// load the shared object and call startup()
|
||||
// core.beginLoading("");
|
||||
|
||||
// if (!start_modules(spec, args, allocator)) return false;
|
||||
// defer shutdown_modules(allocator);
|
||||
|
||||
run() catch return false;
|
||||
|
||||
if (core.getEngine().shutdownModuleFunction) |shutdownFunc| {
|
||||
shutdownFunc();
|
||||
} else {
|
||||
core.shutdown_module(allocator);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
if (!core.BuildOption("RootDeploymentOnly")) {
|
||||
// if we don't see a content/ folder or a
|
||||
// content.pak file in the current directory,
|
||||
// walk up the directory tree until we see one, then change dirs to that before doing anything else
|
||||
|
||||
var iterations: u32 = 0;
|
||||
var BUFFER: [8192]u8 = undefined;
|
||||
|
||||
while (iterations < 8) : (iterations += 1) {
|
||||
if (fileExists("content") or fileExists("content.pak")) {
|
||||
break;
|
||||
}
|
||||
|
||||
var dir = try std.fs.cwd().openDir("..", .{});
|
||||
defer dir.close();
|
||||
core.engine_log("content not found scanning dir {s}", .{try dir.realpath(".", &BUFFER)});
|
||||
try dir.setAsCwd();
|
||||
}
|
||||
core.engine_log("Working Dir set: {s}", .{try std.fs.cwd().realpath(".", &BUFFER)});
|
||||
}
|
||||
|
||||
const spec = try gamespec.getSpec();
|
||||
_ = startEngine(spec);
|
||||
|
||||
// panickers.attachSegfaultHandler();
|
||||
// try realMain.main();
|
||||
|
||||
shutdown_hook();
|
||||
}
|
||||
|
||||
pub fn shutdown_hook() void {
|
||||
std.debug.print("[engine] shutting down now! goodbye!", .{});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue