Compare commits
42 Commits
dev/upgrad
...
dev/varian
| Author | SHA1 | Date |
|---|---|---|
|
|
2d59109994 | |
|
|
bae150305d | |
|
|
6300ef0e35 | |
|
|
a518a89798 | |
|
|
e69a8d13ec | |
|
|
ef91c4599b | |
|
|
9f9f40abd9 | |
|
|
81aaaa81b7 | |
|
|
c36cae7db9 | |
|
|
a06b0e6394 | |
|
|
17b3a4c23d | |
|
|
81132ac97b | |
|
|
97c8de24b9 | |
|
|
bb99e9025e | |
|
|
b4fa25106a | |
|
|
75a9ae1b39 | |
|
|
18b5537826 | |
|
|
b248573930 | |
|
|
3648615829 | |
|
|
8a30c78260 | |
|
|
cc00fb076e | |
|
|
ffc84ce44c | |
|
|
a727aedd97 | |
|
|
fb131ab6c4 | |
|
|
1572f96429 | |
|
|
5fbc11e1a1 | |
|
|
0fc379cbc3 | |
|
|
55e7c955c1 | |
|
|
35562ab6d7 | |
|
|
7aa70eb5d5 | |
|
|
2e2ab68178 | |
|
|
054b41da95 | |
|
|
60dfe0ae60 | |
|
|
180badad39 | |
|
|
08bad270e6 | |
|
|
c7cfa18ce9 | |
|
|
b5dad13bd7 | |
|
|
2e4f7d98e4 | |
|
|
6bf5b3516f | |
|
|
cf0f27cc92 | |
|
|
338954aa8a | |
|
|
efc11fcba9 |
|
|
@ -0,0 +1,169 @@
|
|||
# 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,18 +1,100 @@
|
|||
# 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
|
||||
|
||||
/// --------------------------------------------------------
|
||||
void* malloc(size_t size); // gives you a pointer to a memory buffer of size
|
||||
void free(void* ptr); // releases a pointer to memory
|
||||
/// --------------------------------------------------------
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,662 @@
|
|||
// 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,696 @@
|
|||
// 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,42 +1,27 @@
|
|||
// 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,
|
||||
bEngine: *std.Build, // used to resolve executable paths from within the engine's root directory
|
||||
opts: bh.Options,
|
||||
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) = .{},
|
||||
|
||||
const engineDepList = [_][]const u8{
|
||||
"assets",
|
||||
"audio",
|
||||
"core",
|
||||
"net",
|
||||
"papyrus",
|
||||
"platform",
|
||||
"rend",
|
||||
"ui",
|
||||
"imgui",
|
||||
"physics",
|
||||
"sys",
|
||||
};
|
||||
|
||||
const BuildSystem = @This();
|
||||
pub const BuildSystem = @This();
|
||||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const LazyPath = Build.LazyPath;
|
||||
|
|
@ -46,41 +31,47 @@ const ozz = @import("ozz");
|
|||
pub const InitOptions = struct {
|
||||
import_name: []const u8 = "Backlog",
|
||||
backlogRoot: []const u8 = "./BacklogEngine",
|
||||
staticBuild: bool = false,
|
||||
target: Build.ResolvedTarget,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
};
|
||||
|
||||
pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
|
||||
var buildOpts = declareBuildOptions(b);
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
const nwdep = b.dependency(opts.import_name, .{
|
||||
const nwdep = b.dependency(initOptions.import_name, .{
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.static_build = buildOpts.static_build,
|
||||
.static_build = opts.static_build,
|
||||
});
|
||||
|
||||
var self = BuildSystem{
|
||||
.b = b,
|
||||
.nw_builder = nwdep.builder,
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.bEngine = nwdep.builder,
|
||||
.opts = opts,
|
||||
.nw_mod = nwdep.module("Backlog"),
|
||||
.backlogRoot = opts.backlogRoot,
|
||||
.options = createGameOptions(b, buildOpts),
|
||||
.backlogRoot = initOptions.backlogRoot,
|
||||
.options = createGameOptions(b, buildOpts, opts),
|
||||
.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"),
|
||||
};
|
||||
|
|
@ -110,135 +101,55 @@ pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
|
|||
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 {
|
||||
// Build Options for the engine,
|
||||
// should generally be written as false = default
|
||||
// true = enabling something
|
||||
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 {
|
||||
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 {
|
||||
fn createGameOptions(b: *std.Build, options: BuildOptions, bhopts: bh.Options) *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",
|
||||
// );
|
||||
// forward the options to the build options
|
||||
opts.addOption(bool, "tracy", bhopts.tracy);
|
||||
opts.addOption(bool, "static_build", bhopts.static_build);
|
||||
|
||||
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 });
|
||||
const dep = self.b.dependency(moduleName, .{ .target = self.opts.target, .optimize = self.opts.optimize, .static_build = self.opts.static_build });
|
||||
mod.addImport(moduleName, dep.module(moduleName));
|
||||
}
|
||||
|
||||
pub fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
|
||||
//if (self.staticBuild) {
|
||||
//return;
|
||||
// }
|
||||
fn addDependencyInstalls(self: *BuildSystem, b: *std.Build, optimize: std.builtin.OptimizeMode) void {
|
||||
if (self.opts.static_build) {
|
||||
return;
|
||||
}
|
||||
|
||||
inline for (DynamicDepList) |d| {
|
||||
b.installArtifact(b.dependency(
|
||||
d.dep,
|
||||
.{ .target = self.target, .optimize = optimize, .static_build = self.staticBuild },
|
||||
.{ .target = self.opts.target, .optimize = optimize, .static_build = self.opts.static_build },
|
||||
).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 } = &.{
|
||||
|
|
@ -251,365 +162,19 @@ const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } =
|
|||
.{ .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 {
|
||||
pub fn addProgram(
|
||||
self: *@This(),
|
||||
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,
|
||||
) *Program {
|
||||
const p = self.b.allocator.create(Program) catch unreachable;
|
||||
p.* = .{
|
||||
.name = name,
|
||||
.opts = self.opts,
|
||||
.buildSystem = self,
|
||||
.allocator = self.b.allocator,
|
||||
};
|
||||
|
||||
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;
|
||||
return p;
|
||||
}
|
||||
|
||||
pub fn generateRc(self: *@This(), programName: []const u8, iconPath: []const u8) !std.Build.LazyPath {
|
||||
|
|
@ -621,35 +186,6 @@ 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});
|
||||
|
|
@ -658,8 +194,8 @@ pub fn generateInstallStaticResources(self: *@This(), programName: []const u8, s
|
|||
|
||||
const b = self.b;
|
||||
const mod = self.b.addModule(pfile, .{
|
||||
.target = self.target,
|
||||
.optimize = self.optimize,
|
||||
.target = self.opts.target,
|
||||
.optimize = self.opts.optimize,
|
||||
.root_source_file = output,
|
||||
});
|
||||
mod.addImport("core", self.nwdep.module("core"));
|
||||
|
|
@ -684,3 +220,15 @@ 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,28 +1,23 @@
|
|||
.{
|
||||
.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" },
|
||||
},
|
||||
.paths = .{
|
||||
"",
|
||||
},
|
||||
.fingerprint = 0xcf9bab998abe37e3
|
||||
}
|
||||
.sdl3 = .{ .path = "lib/sdl3" },
|
||||
.enet = .{ .path = "lib/enet" },
|
||||
.bh = .{ .path = "lib/bh" },
|
||||
}, .paths = .{
|
||||
"",
|
||||
}, .fingerprint = 0xcf9bab998abe37e3 }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
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;
|
||||
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
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");
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
// 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
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");
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
const std = @import("std");
|
||||
const Build = std.Build;
|
||||
const LazyPath = Build.LazyPath;
|
||||
125
build/utils.zig
125
build/utils.zig
|
|
@ -1,19 +1,114 @@
|
|||
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 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 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");
|
||||
|
|
|
|||
|
|
@ -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.c_allocator;
|
||||
\\ var backingAllocator: std.mem.Allocator = std.heap.smp_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.c_allocator); }}\n", .{});
|
||||
try writer.print(allocator, "}}, std.heap.smp_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);
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// 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 {}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
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");
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.fontcache/
|
||||
Saved/
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
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();
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
.{
|
||||
.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.
|
|
@ -0,0 +1,198 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#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.
|
|
@ -0,0 +1,3 @@
|
|||
# studio games depot
|
||||
|
||||
!! not for open source.
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
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");
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
# 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
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
# 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,11 +120,6 @@ 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),
|
||||
|
|
@ -159,15 +154,15 @@ pub const AssetReferenceSys = struct {
|
|||
|
||||
pub var NeonObjectTable = core.EngineObjectVTable.from(@This(), "AssetReference");
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
self.* = @This(){
|
||||
.loaders = .{},
|
||||
.allocator = allocator,
|
||||
.outstandingAssetJobs = std.atomic.Value(i32).init(0),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn registerLoader(self: *@This(), loader: anytype) !void {
|
||||
|
|
@ -210,6 +205,5 @@ pub const AssetReferenceSys = struct {
|
|||
// i.destroy(self.allocator);
|
||||
// }
|
||||
self.loaders.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -73,8 +73,10 @@ pub const SoundEngine = struct {
|
|||
allocator: std.mem.Allocator,
|
||||
volume: f32 = 1.0,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
self.* = @This(){
|
||||
.engine = allocator.create(ma.ma_engine) catch unreachable,
|
||||
.sounds = .{},
|
||||
|
|
@ -83,8 +85,6 @@ 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.c_allocator;
|
||||
var backingAllocator: std.mem.Allocator = std.heap.smp_allocator;
|
||||
var gpa: std.heap.GeneralPurposeAllocator(.{
|
||||
.stack_trace_frames = 20,
|
||||
}) = .{};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
pub const std = @import("std");
|
||||
const core = @import("core");
|
||||
const impl = core;
|
||||
|
||||
pub const inputs = struct {
|
||||
pub const getInputStack = inputs.getInputStack;
|
||||
};
|
||||
|
|
@ -2,14 +2,9 @@ const std = @import("std");
|
|||
|
||||
const dependencyList = [_][]const u8{
|
||||
"p2",
|
||||
"tracy",
|
||||
"tracy", // usually doesnt have C deps... unless we're compiled with it on, in which case we only support static builds
|
||||
"zmath",
|
||||
"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",
|
||||
"packer", // packer no longer has C deps.
|
||||
};
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
|
|
@ -33,8 +28,10 @@ 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(.{
|
||||
|
|
@ -45,8 +42,10 @@ 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.c_allocator) catch null else null;
|
||||
const stackCompactor = if (EnableMemoryTimeline) core.StackCompactor.create(std.heap.smp_allocator) catch null else null;
|
||||
|
||||
return .{
|
||||
.backingAllocator = backingAllocator,
|
||||
.stackCompactor = stackCompactor,
|
||||
.timeline = if (EnableMemoryTimeline) EventTimeline.init(std.heap.c_allocator) catch null else null,
|
||||
.timeline = if (EnableMemoryTimeline) EventTimeline.init(std.heap.smp_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.c_allocator);
|
||||
var iter = try std.process.argsWithAllocator(std.heap.smp_allocator);
|
||||
var args: T = .{};
|
||||
|
||||
const shortBuf: [32]u8 = undefined;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
pub const ConfigRegistry = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.ConfigRegistry");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
configMap: ?ConfigMap = null,
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// by convention this should be in the root
|
||||
|
|
@ -38,11 +37,10 @@ pub const ConfigRegistry = struct {
|
|||
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
pub fn deinit(self: *@This()) void {
|
||||
if (self.configMap) |*map| {
|
||||
map.deinit();
|
||||
}
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,21 +8,22 @@ pub const ConsoleCommand = struct {
|
|||
pub const Console = struct {
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "core.Console");
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
allocator: std.mem.Allocator = undefined,
|
||||
arena: std.heap.ArenaAllocator = undefined,
|
||||
|
||||
commandMap: std.StringHashMapUnmanaged(ConsoleCommand) = .{},
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
@ -56,10 +57,9 @@ pub const Console = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.arena.deinit();
|
||||
self.commandMap.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// 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;
|
||||
|
|
@ -25,9 +27,10 @@ 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;
|
||||
|
|
@ -38,16 +41,13 @@ pub const EngineObjectDelegate = engineObject.EngineObjectDelegate;
|
|||
pub const FieldInfo = engineObject.FieldInfo;
|
||||
|
||||
pub const jobs = @import("jobs.zig");
|
||||
pub const JobContext = jobs.JobContext;
|
||||
pub const JobContext = jobs.ThreadContext;
|
||||
pub const ThreadContext = jobs.ThreadContext;
|
||||
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,8 +140,6 @@ 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";
|
||||
|
|
@ -172,14 +170,16 @@ 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;
|
||||
|
||||
var staticsInitialized = false;
|
||||
pub 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,8 +216,6 @@ 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;
|
||||
|
||||
|
|
@ -236,10 +234,6 @@ 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,
|
||||
|
|
@ -280,8 +274,6 @@ 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");
|
||||
|
|
@ -294,15 +286,9 @@ 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| {
|
||||
|
|
@ -311,8 +297,7 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All
|
|||
|
||||
gEngine = try allocator.create(Engine);
|
||||
gEngine.* = try Engine.init(allocator);
|
||||
_ = try createObject(ModuleLoader, .{});
|
||||
try console.start();
|
||||
staticsInitialized = true;
|
||||
|
||||
const engineName = try std.fmt.allocPrint(allocator, "{s}Engine.ini", .{name});
|
||||
defer allocator.free(engineName);
|
||||
|
|
@ -323,16 +308,15 @@ pub fn start_module_(map: *SpecVariantMap, args: anytype, allocator: std.mem.All
|
|||
try logging.setupLogging(gEngine);
|
||||
}
|
||||
|
||||
try ecs.setup(allocator);
|
||||
|
||||
_ = try gEngine.createObject(scene.SceneSystem, .{ .can_tick = true });
|
||||
|
||||
try algorithm.string_pool.setup(allocator);
|
||||
gEngine.stringContext = algorithm.string_pool.gStringContext;
|
||||
|
||||
// _ = 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 inputs.initInputStack();
|
||||
_ = try createObject(inputs.InputStack, .{});
|
||||
_ = try createObject(console.Console, .{});
|
||||
|
||||
// components define
|
||||
try ecs.defineComponentList(ComponentList, allocator);
|
||||
|
|
@ -343,8 +327,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;
|
||||
|
||||
|
|
@ -366,18 +350,36 @@ 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);
|
||||
try gEngine.jobManager.newJob(capture, .{ .threadId = 0, .threadCount = 1 }); //0, 1, null);
|
||||
}
|
||||
|
||||
pub fn createObject(comptime T: type, params: engine.NeonObjectParams) !*T {
|
||||
|
|
@ -524,7 +526,11 @@ 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);
|
||||
try setupFromModule(args);
|
||||
|
||||
if (comptime !BuildOption("static_build")) {
|
||||
try setupFromModule(args);
|
||||
}
|
||||
|
||||
return allocator;
|
||||
}
|
||||
|
||||
|
|
@ -608,3 +614,7 @@ 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(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
pub fn init(self: *@This(), allocator: std.mem.Allocator, first: bool) !void {
|
||||
if (!first) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.baseSet = BaseSet.init(allocator),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn registerContainer(self: *@This(), ref: EcsContainerRef, _containerName: core.Name) !void {
|
||||
|
|
@ -279,13 +279,11 @@ pub const EcsRegistry = struct {
|
|||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.destroy();
|
||||
}
|
||||
// this should never work... wtf?
|
||||
// for (self.containers.items) |ref| {
|
||||
// ref.vtable.evictFromRegistry(ref.ptr);
|
||||
// }
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -300,7 +298,6 @@ pub const EcsRegistry = struct {
|
|||
self.containers.deinit(self.allocator);
|
||||
self.containerNames.deinit(self.allocator);
|
||||
self.containersByName.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -377,7 +374,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,6 +5,8 @@ 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");
|
||||
|
|
@ -84,8 +86,15 @@ 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,
|
||||
|
|
@ -96,8 +105,10 @@ 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),
|
||||
};
|
||||
|
|
@ -113,14 +124,13 @@ 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))];
|
||||
if (item.vtable.deinit_func) |deinitFn| {
|
||||
deinitFn(item.ptr);
|
||||
}
|
||||
self.destroyObject(item);
|
||||
}
|
||||
}
|
||||
self.destroyListCore.deinit(self.allocator);
|
||||
|
|
@ -151,6 +161,10 @@ 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) {
|
||||
|
|
@ -162,7 +176,17 @@ pub const Engine = struct {
|
|||
self.createObjectLock = true;
|
||||
defer self.createObjectLock = false;
|
||||
const newIndex = self.engineObjects.items.len;
|
||||
const newObjectPtr = try vtable.init_func(self.allocator); // call this thing with the special allocator that adds vtable. slackSize to it.
|
||||
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 newObjectRef = EngineObjectRef{
|
||||
.ptr = @as(*anyopaque, @ptrCast(newObjectPtr)),
|
||||
|
|
@ -230,6 +254,7 @@ pub const Engine = struct {
|
|||
}
|
||||
|
||||
pub fn tick(self: *@This()) !void {
|
||||
// ------------ frame updates ---------
|
||||
tracy.FrameMark();
|
||||
tracy.FrameMarkStart("frame");
|
||||
defer tracy.FrameMarkEnd("frame");
|
||||
|
|
@ -238,14 +263,18 @@ 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,
|
||||
|
|
@ -253,6 +282,16 @@ 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();
|
||||
|
||||
|
|
@ -263,7 +302,9 @@ 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);
|
||||
|
|
@ -274,12 +315,14 @@ 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) {
|
||||
|
|
@ -293,6 +336,7 @@ 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);
|
||||
|
|
@ -328,14 +372,29 @@ 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))];
|
||||
if (item.vtable.deinit_func) |deinitFn| {
|
||||
deinitFn(item.ptr);
|
||||
}
|
||||
self.destroyObject(item);
|
||||
}
|
||||
}
|
||||
self.dependentsDestroyed.store(true, .seq_cst);
|
||||
|
|
|
|||
|
|
@ -68,10 +68,6 @@ 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 {
|
||||
|
|
@ -167,7 +163,8 @@ pub const EngineObjectVTable = struct {
|
|||
|
||||
singletonName: ?[]const u8 = null,
|
||||
|
||||
init_func: *const fn (std.mem.Allocator) EngineDataEventError!*anyopaque,
|
||||
// new init_function passes in an already created object
|
||||
init_func: *const fn (*anyopaque, std.mem.Allocator, bool) EngineDataEventError!void,
|
||||
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,
|
||||
|
|
@ -271,11 +268,10 @@ pub const EngineObjectVTable = struct {
|
|||
|
||||
if (@hasDecl(TargetType, "init")) {
|
||||
const wrappedInit = struct {
|
||||
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));
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -284,11 +280,10 @@ pub const EngineObjectVTable = struct {
|
|||
|
||||
if (@hasDecl(TargetType, "create")) {
|
||||
const wrappedInit = struct {
|
||||
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));
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue