animations working again
This commit is contained in:
parent
1b8488b411
commit
5b63271f5a
|
|
@ -391,15 +391,7 @@ pub const Program = struct {
|
|||
return;
|
||||
}
|
||||
|
||||
// const b = self.buildSystem.b;
|
||||
//const fmt = b.fmt("content/{s}", .{path});
|
||||
//const p = b.path(fmt);
|
||||
|
||||
//const absolute = p.getPath(b);
|
||||
|
||||
self.iconPath = self.buildSystem.generateRc(self.opts.name, path) catch return;
|
||||
// std.debug.print("absolute path: {s}", .{absolute});
|
||||
//std.debug.print("\nabsolute path: {s}\n", .{absolute});
|
||||
}
|
||||
|
||||
pub fn setModuleEnabled(self: *@This(), module: []const u8, enable: bool) void {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me
|
|||
memory.MTPrintStatsDelta();
|
||||
}
|
||||
|
||||
pub fn initCooking() !void {
|
||||
cooking = true;
|
||||
try cook.startup(core.getEngineObject(AssetReferenceSys).?.allocator);
|
||||
}
|
||||
|
||||
pub fn shutdown_module(allocator: std.mem.Allocator) void {
|
||||
if (cooking) {
|
||||
cook.shutdown();
|
||||
|
|
|
|||
|
|
@ -197,12 +197,16 @@ pub fn generateAllCookFiles(allocator: std.mem.Allocator, dir: std.fs.Dir) !void
|
|||
try generateCookFile(allocator, dir, next.name);
|
||||
},
|
||||
.directory => {
|
||||
if (!std.mem.eql(u8, "_cooked", next.name)) {
|
||||
var subDir = try dir.openDir(next.name, .{ .iterate = true });
|
||||
defer subDir.close();
|
||||
|
||||
try generateAllCookFiles(allocator, subDir);
|
||||
if (std.mem.eql(u8, "_cooked", next.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, ".gitignore", next.name)) {}
|
||||
|
||||
var subDir = try dir.openDir(next.name, .{ .iterate = true });
|
||||
defer subDir.close();
|
||||
|
||||
try generateAllCookFiles(allocator, subDir);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
// I just reuse this one for the shadow mapping right?
|
||||
|
||||
uint getCharFromUintArray(uint color, uint index)
|
||||
{
|
||||
return uint((color >> 8 * index) & 0xFF);
|
||||
}
|
||||
|
||||
struct Input
|
||||
{
|
||||
float3 Position : TEXCOORD0;
|
||||
|
|
@ -31,13 +36,21 @@ struct Scene
|
|||
uint textureMode; // 0 = regular triple,
|
||||
// 0x10 = video yuv,
|
||||
|
||||
uint pad0; // 0 = regular triple,
|
||||
uint pad1; // 0 = regular triple,
|
||||
uint pad2; // 0 = regular triple,
|
||||
int animation; // -1 means no animation, any non-zero value means an index into the animationBuffer
|
||||
uint flags; // 0 -> 0: always in front
|
||||
// 1 -> 1: useAltCamera
|
||||
uint pad2;
|
||||
};
|
||||
|
||||
StructuredBuffer<Scene> scene: register(t0, space0);
|
||||
|
||||
struct BoneTransform
|
||||
{
|
||||
float4x4 final;
|
||||
};
|
||||
|
||||
StructuredBuffer<BoneTransform> animationBuffer: register(t1, space0);
|
||||
|
||||
cbuffer Uniforms : register(b0, space1)
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
|
|
@ -73,11 +86,33 @@ Output main(Input input)
|
|||
Output output;
|
||||
output.TexCoord = input.UV;
|
||||
|
||||
float4 pos = float4(input.Position, 1.0);
|
||||
int animation = scene[input.Instance].animation;
|
||||
|
||||
float3 vertexPos = input.Position;
|
||||
|
||||
if(animation != -1)
|
||||
{
|
||||
vertexPos = float3(0.0, 0.0, 0.0);
|
||||
for(int i = 0; i < 4; i += 1)
|
||||
{
|
||||
uint boneIndex = getCharFromUintArray(input.Bones, i);
|
||||
|
||||
float weight = float(getCharFromUintArray(input.weights, i)) / 255;
|
||||
|
||||
// this will depend on ozz's finals format
|
||||
BoneTransform boneTransform = animationBuffer[uint(animation) + boneIndex];
|
||||
vertexPos += weight * ( mul(boneTransform.final, float4(input.Position, 1.0)) ).xyz;
|
||||
}
|
||||
}
|
||||
|
||||
// float4 pos = float4(input.Position, 1.0);
|
||||
float4 pos = float4(vertexPos, 1.0);
|
||||
|
||||
// pos.x += 0.2 * sin(time * 0.5 ) * pos.y * 0.5;
|
||||
// pos.y += 0.2 * cos(time * 0.5 ) * pos.z * 0.5;
|
||||
|
||||
// todo. maybe all world position values should be
|
||||
// transformed into screen space
|
||||
float4 WorldPos = mul(scene[input.Instance].Model, pos);
|
||||
output.Position = mul(ViewProjection, mul(scene[input.Instance].Model, pos));
|
||||
output.WorldPos = WorldPos.xyz;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
}
|
||||
],
|
||||
"types" : {
|
||||
"_15" : {
|
||||
"_17" : {
|
||||
"name" : "Scene",
|
||||
"members" : [
|
||||
{
|
||||
|
|
@ -22,12 +22,12 @@
|
|||
"offset" : 64
|
||||
},
|
||||
{
|
||||
"name" : "pad0",
|
||||
"type" : "uint",
|
||||
"name" : "animation",
|
||||
"type" : "int",
|
||||
"offset" : 68
|
||||
},
|
||||
{
|
||||
"name" : "pad1",
|
||||
"name" : "flags",
|
||||
"type" : "uint",
|
||||
"offset" : 72
|
||||
},
|
||||
|
|
@ -38,12 +38,12 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"_14" : {
|
||||
"_16" : {
|
||||
"name" : "type.StructuredBuffer.Scene",
|
||||
"members" : [
|
||||
{
|
||||
"name" : "_m0",
|
||||
"type" : "_15",
|
||||
"type" : "_17",
|
||||
"array" : [
|
||||
0
|
||||
],
|
||||
|
|
@ -55,7 +55,36 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"_17" : {
|
||||
"_20" : {
|
||||
"name" : "BoneTransform",
|
||||
"members" : [
|
||||
{
|
||||
"name" : "final",
|
||||
"type" : "mat4",
|
||||
"offset" : 0,
|
||||
"matrix_stride" : 16,
|
||||
"row_major" : true
|
||||
}
|
||||
]
|
||||
},
|
||||
"_19" : {
|
||||
"name" : "type.StructuredBuffer.BoneTransform",
|
||||
"members" : [
|
||||
{
|
||||
"name" : "_m0",
|
||||
"type" : "_20",
|
||||
"array" : [
|
||||
0
|
||||
],
|
||||
"array_size_is_literal" : [
|
||||
true
|
||||
],
|
||||
"offset" : 0,
|
||||
"array_stride" : 64
|
||||
}
|
||||
]
|
||||
},
|
||||
"_22" : {
|
||||
"name" : "type.Uniforms",
|
||||
"members" : [
|
||||
{
|
||||
|
|
@ -102,6 +131,16 @@
|
|||
"type" : "vec2",
|
||||
"name" : "in.var.TEXCOORD3",
|
||||
"location" : 3
|
||||
},
|
||||
{
|
||||
"type" : "uint",
|
||||
"name" : "in.var.TEXCOORD4",
|
||||
"location" : 4
|
||||
},
|
||||
{
|
||||
"type" : "uint",
|
||||
"name" : "in.var.TEXCOORD5",
|
||||
"location" : 5
|
||||
}
|
||||
],
|
||||
"outputs" : [
|
||||
|
|
@ -138,17 +177,25 @@
|
|||
],
|
||||
"ssbos" : [
|
||||
{
|
||||
"type" : "_14",
|
||||
"type" : "_16",
|
||||
"name" : "scene",
|
||||
"readonly" : true,
|
||||
"block_size" : 0,
|
||||
"set" : 0,
|
||||
"binding" : 0
|
||||
},
|
||||
{
|
||||
"type" : "_19",
|
||||
"name" : "animationBuffer",
|
||||
"readonly" : true,
|
||||
"block_size" : 0,
|
||||
"set" : 0,
|
||||
"binding" : 1
|
||||
}
|
||||
],
|
||||
"ubos" : [
|
||||
{
|
||||
"type" : "_17",
|
||||
"type" : "_22",
|
||||
"name" : "type.Uniforms",
|
||||
"block_size" : 196,
|
||||
"set" : 1,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
pub const AnimResolverRef = core.Reference(AnimResolverInterface);
|
||||
pub const AnimResolverInterface = core.MakeInterface("AnimResolverVTable", struct {
|
||||
// this tick function should evaluate the current state of the resolver
|
||||
// and then update the animator's finals[] matrix list.
|
||||
resolve: *const fn (*anyopaque, f64, *Animator) void,
|
||||
onSkeletonSet: ?*const fn (*anyopaque, *Animator) void = null,
|
||||
|
||||
create: *const fn (std.mem.Allocator) core.EngineDataEventError!*anyopaque,
|
||||
destroy: *const fn (*anyopaque) void,
|
||||
|
||||
pub fn Implement(comptime TargetType: type) @This() {
|
||||
const Wrap = struct {
|
||||
pub fn create(allocator: std.mem.Allocator) core.EngineDataEventError!*anyopaque {
|
||||
const new = TargetType.create(allocator) catch return core.EngineDataEventError.BadInit;
|
||||
return @ptrCast(new);
|
||||
}
|
||||
|
||||
pub fn destroy(p: *anyopaque) void {
|
||||
const ptr: *TargetType = @ptrCast(@alignCast(p));
|
||||
ptr.destroy();
|
||||
}
|
||||
|
||||
pub fn onSkeletonSet(p: *anyopaque, a: *Animator) void {
|
||||
const ptr: *TargetType = @ptrCast(@alignCast(p));
|
||||
ptr.onSkeletonSet(a) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn resolve(p: *anyopaque, dt: f64, a: *Animator) void {
|
||||
const ptr: *TargetType = @ptrCast(@alignCast(p));
|
||||
ptr.resolve(dt, a) catch unreachable;
|
||||
}
|
||||
};
|
||||
return .{
|
||||
.destroy = Wrap.destroy,
|
||||
.create = Wrap.create,
|
||||
.resolve = Wrap.resolve,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
pub const AnimSampler = struct {
|
||||
name: ?core.Name = null,
|
||||
track: ?*AnimationTrack = null,
|
||||
playbackRate: f32 = 1.0,
|
||||
time: f32 = 0.0,
|
||||
outputLocals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
|
||||
|
||||
// other features
|
||||
// paused: bool = false,
|
||||
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
|
||||
self.outputLocals.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn getOutput(self: *@This()) []ozz.SoaTransform {
|
||||
return self.outputLocals.items;
|
||||
}
|
||||
|
||||
pub fn sampleAndAdvance(self: *@This(), allocator: std.mem.Allocator, dt: f64, animator: *Animator) void {
|
||||
self.sample(allocator, animator);
|
||||
self.advance(dt);
|
||||
}
|
||||
|
||||
pub fn advance(self: *@This(), dt: f64) void {
|
||||
if (self.track == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FloatHelpers.updateTrackTime(&self.time, dt, self.playbackRate, self.track.?.endTime);
|
||||
}
|
||||
|
||||
pub fn setName(self: *@This(), name: core.Name) void {
|
||||
self.name = name;
|
||||
self.track = null;
|
||||
}
|
||||
|
||||
pub fn sample(self: *@This(), allocator: std.mem.Allocator, animator: *Animator) void {
|
||||
if (self.name == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.track == null) {
|
||||
self.track = animation_system.gAnimationSys.animTracks.get(self.name.?.handle());
|
||||
}
|
||||
|
||||
self.outputLocals.resize(allocator, animator.jointLength) catch return;
|
||||
|
||||
if (self.track) |track| {
|
||||
if (track.endTime < 0.01) {
|
||||
return;
|
||||
}
|
||||
|
||||
animator.sampleAnimation(self.time, track, self.outputLocals.items);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const BlenderList = struct {
|
||||
backing: std.mem.Allocator,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
||||
jobLayers: std.ArrayListUnmanaged(ozz.Layer) = .{},
|
||||
jobLayersAdditive: std.ArrayListUnmanaged(ozz.Layer) = .{},
|
||||
useAdditive: bool = false,
|
||||
|
||||
threshold: f32 = 0.01,
|
||||
jointLength: usize = 0,
|
||||
blendingJob: ozz.BlendingJob = .{},
|
||||
|
||||
pub fn create(backingAllocator: std.mem.Allocator) !*@This() {
|
||||
const self = try backingAllocator.create(@This());
|
||||
self.* = .{
|
||||
.backing = backingAllocator,
|
||||
.arena = std.heap.ArenaAllocator.init(backingAllocator),
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.arena.deinit();
|
||||
self.backing.destroy(self);
|
||||
}
|
||||
|
||||
pub fn updateRestPose(self: *@This(), animator: *Animator) !void {
|
||||
if (animator.skeleton) |skeleton| {
|
||||
self.jointLength = skeleton.sk.numJoints();
|
||||
self.blendingJob.rest_pose = skeleton.sk.getRestPoseModel();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clearLayers(self: *@This()) void {
|
||||
self.jobLayersAdditive.clearRetainingCapacity();
|
||||
self.jobLayers.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn addLayer(self: *@This(), transform: []ozz.SoaTransform, weight: f32, settings: anytype) void {
|
||||
const layer = self.jobLayers.addOne(self.arena.allocator()) catch unreachable;
|
||||
layer.* = .{
|
||||
.weight = weight,
|
||||
.transform = ozz.makeSpan(transform),
|
||||
};
|
||||
_ = settings;
|
||||
}
|
||||
|
||||
pub fn updateAndRun(self: *@This(), output: []ozz.SoaTransform) void {
|
||||
self.updateBlendingJob();
|
||||
self.runBlendingJob(output) catch return;
|
||||
}
|
||||
|
||||
pub fn updateBlendingJob(self: *@This()) void {
|
||||
self.blendingJob.threshold = self.threshold;
|
||||
self.blendingJob.layers = ozz.makeSpan(self.jobLayers.items);
|
||||
//self.blendingJob.additive_layers = if (self.useAdditive) ozz.makeSpan(self.jobLayersAdditive.items) else .{};
|
||||
self.blendingJob.additive_layers = .{};
|
||||
}
|
||||
|
||||
pub fn runBlendingJob(self: *@This(), output: []ozz.SoaTransform) !void {
|
||||
self.blendingJob.output = ozz.makeSpan(output);
|
||||
if (!self.blendingJob.run()) {
|
||||
core.engine_logs("blending job failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// resolver helpers
|
||||
pub const FloatHelpers = struct {
|
||||
pub inline fn updateTrackTime(target: *f32, dt: f64, rate: f32, endTime: f32) void {
|
||||
target.* += @as(f32, @floatCast(dt)) * rate;
|
||||
while (target.* > endTime) {
|
||||
target.* -= endTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// samples a single animation, same as the default behaviour.
|
||||
// used as a test for the resolver system
|
||||
pub const SingleAnimationResolver = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
|
||||
|
||||
track: ?*AnimationTrack = null,
|
||||
playback: f32 = 0.0,
|
||||
playbackRate: f32 = 1.0,
|
||||
|
||||
pub const AnimResolverVTable = AnimResolverInterface.Implement(@This());
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn onSkeletonSet(self: *@This(), animator: *Animator) !void {
|
||||
if (animator.skeleton) |skeleton| {
|
||||
try self.locals.resize(self.allocator, skeleton.sk.numSoaJoints());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(self: *@This(), dt: f64, animator: *Animator) !void {
|
||||
self.track = animator.track;
|
||||
if (self.track == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const track = self.track.?;
|
||||
if (track.endTime < 0.01) {
|
||||
return;
|
||||
}
|
||||
|
||||
FloatHelpers.updateTrackTime(&self.playback, dt, self.playbackRate, track.endTime);
|
||||
animator.sampleAnimation(self.playback, track, self.locals.items);
|
||||
animator.commitLocalToModel(self.locals.items);
|
||||
animator.modelToFinal();
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.locals.deinit(self.allocator);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
const animation_system = @import("animationSystem.zig");
|
||||
const Animator = animation_system.Animator;
|
||||
const AnimationTrack = animation_system.AnimationTrack;
|
||||
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
const ozz = @import("ozz");
|
||||
|
|
@ -0,0 +1,486 @@
|
|||
// big main sy.itemsstem for animation
|
||||
|
||||
const ozz = @import("ozz");
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
|
||||
pub const BoneHandle = enum(u8) { _ };
|
||||
|
||||
pub const Skeleton = struct {
|
||||
sk: *ozz.Skeleton,
|
||||
inverseBinds: std.ArrayListUnmanaged(core.Mat) = .{},
|
||||
jointMapping: std.StringHashMapUnmanaged(u8) = .{},
|
||||
|
||||
pub fn buildJointMap(self: *@This(), allocator: std.mem.Allocator) !void {
|
||||
for (self.sk.getJointsList(), 0..) |jointName, i| {
|
||||
// std.debug.print("jointName {d} {s}\n", .{ i, jointName });
|
||||
const str = std.mem.span(jointName);
|
||||
try self.jointMapping.put(allocator, str, @intCast(i));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getBoneHandleByName(self: @This(), string: []const u8) ?BoneHandle {
|
||||
if (self.jointMapping.get(string)) |x| {
|
||||
return @enumFromInt(x);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.sk.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
pub const AnimationTrack = struct {
|
||||
animation: *ozz.Animation,
|
||||
endTime: f32 = 1.0,
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.animation.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
pub const PlaybackTrack = struct {
|
||||
track: ?*AnimationTrack = null,
|
||||
playback: f32 = 0.0,
|
||||
playbackRate: f32 = 1.0,
|
||||
};
|
||||
|
||||
pub const Animator = struct {
|
||||
jointRemap: ?[]u8 = null,
|
||||
animationName: ?core.Name = null,
|
||||
skeleton: ?*Skeleton = null,
|
||||
skeletonName: ?core.Name = null,
|
||||
sjc: *ozz.SamplingJobContext = undefined,
|
||||
|
||||
track: ?*AnimationTrack = null,
|
||||
playback: f32 = 0.0,
|
||||
playbackRate: f32 = 1.0,
|
||||
|
||||
// todo.. implement blending
|
||||
// animations: [4]*ozz.Animation = undefined,
|
||||
// timelines: [4]f32 = .{ 0, 0, 0, 0 },
|
||||
animationCount: u32 = 0,
|
||||
|
||||
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
|
||||
models: std.ArrayListUnmanaged(ozz.Float4x4) = .{},
|
||||
finals: std.ArrayListUnmanaged(core.Mat) = .{},
|
||||
finalsSpan: core.Span = undefined,
|
||||
|
||||
entity: core.Entity = undefined,
|
||||
jointLength: usize = 0,
|
||||
|
||||
resolverRef: ?AnimResolverRef = null,
|
||||
|
||||
pub var allocator: std.mem.Allocator = undefined;
|
||||
// oh god if I want to support multiple animation blending...
|
||||
// maybe the kernel should contain a fixed amount of animations?
|
||||
|
||||
pub fn initECS(self: *@This(), handle: core.SetHandle) void {
|
||||
// get the mesh component
|
||||
self.entity = core.Entity{ .handle = handle };
|
||||
|
||||
if (self.entity.fetch(rend.MeshComponent)) |mesh| {
|
||||
mesh.animated = true; //todo
|
||||
mesh.animator = self;
|
||||
self.sjc = ozz.SamplingJobContext.createMaxTracks(256);
|
||||
} else {
|
||||
@panic("animator added to an entity that does not have a mesh component");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getBoneTransform(self: *@This(), handle: BoneHandle) core.Mat {
|
||||
return @bitCast(self.models.items[@intFromEnum(handle)]);
|
||||
}
|
||||
|
||||
pub fn setSkeletonByName(self: *@This(), skName: core.Name) !void {
|
||||
if (self.skeleton != null) {
|
||||
// return the previous span and allocate a new one.
|
||||
gAnimationSys.slots.removeSpan(self.finalsSpan);
|
||||
}
|
||||
|
||||
self.skeletonName = skName;
|
||||
self.skeleton = gAnimationSys.skeletons.get(self.skeletonName.?.handle()).?;
|
||||
const numJoints = self.skeleton.?.sk.numJoints();
|
||||
self.jointLength = numJoints;
|
||||
|
||||
self.sjc.resize(@intCast(numJoints));
|
||||
try self.locals.resize(allocator, self.skeleton.?.sk.numSoaJoints());
|
||||
try self.models.resize(allocator, numJoints);
|
||||
try self.finals.resize(allocator, numJoints);
|
||||
self.finalsSpan = try gAnimationSys.slots.allocate(@intCast(numJoints));
|
||||
|
||||
if (self.resolverRef) |ref| {
|
||||
if (ref.vtable.onSkeletonSet) |f| {
|
||||
f(ref.ptr, self);
|
||||
}
|
||||
}
|
||||
|
||||
self.jointRemap = null;
|
||||
}
|
||||
|
||||
pub fn setSkeleton(self: *@This(), skeleton: []const u8) void {
|
||||
self.setSkeletonByName(core.MakeName(skeleton)) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn addResolver(self: *@This(), comptime Resolver: type) !*Resolver {
|
||||
const resolver = try Resolver.create(allocator);
|
||||
try resolver.onSkeletonSet(self);
|
||||
|
||||
self.resolverRef = core.refFromPtr(AnimResolverInterface, resolver);
|
||||
|
||||
return resolver;
|
||||
}
|
||||
|
||||
pub fn removeResolver(self: *@This()) void {
|
||||
if (self.resolverRef) |ref| {
|
||||
ref.vtable.destroy(ref.ptr);
|
||||
self.resolverRef = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(self: *@This(), dt: f64) void {
|
||||
if (self.skeleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if a resolver is present, use that to update my the finals instead of the default function below
|
||||
if (self.resolverRef) |ref| {
|
||||
ref.vtable.resolve(ref.ptr, dt, self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.defaultSample(dt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.modelToFinal();
|
||||
}
|
||||
|
||||
fn defaultSample(self: *@This(), dt: f64) bool {
|
||||
if (self.track == null)
|
||||
return false;
|
||||
|
||||
const track = self.track.?;
|
||||
if (track.endTime < 0.01)
|
||||
return false;
|
||||
|
||||
const skeleton = self.skeleton.?;
|
||||
self.playback += @as(f32, @floatCast(dt)) * self.playbackRate;
|
||||
|
||||
while (self.playback > track.endTime) {
|
||||
self.playback -= track.endTime;
|
||||
}
|
||||
|
||||
var samplingJob: ozz.SamplingJob = .{
|
||||
.ratio = self.playback / track.endTime,
|
||||
.animation = track.animation,
|
||||
.context = self.sjc,
|
||||
.output = ozz.makeSpan(self.locals.items),
|
||||
};
|
||||
|
||||
if (!samplingJob.run()) {
|
||||
core.engine_errs("sampling job failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
var ltmJob: ozz.LocalToModelJob = .{
|
||||
.skeleton = skeleton.sk,
|
||||
.input = ozz.makeSpan(self.locals.items),
|
||||
.output = ozz.makeSpan(self.models.items),
|
||||
};
|
||||
|
||||
if (!ltmJob.run()) {
|
||||
core.engine_errs("local to model job failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn commitLocalToModel(self: *@This(), input: []ozz.SoaTransform) void {
|
||||
self.localToModel(input, self.models.items);
|
||||
}
|
||||
|
||||
pub fn localToModel(self: *@This(), input: []ozz.SoaTransform, output: []ozz.Float4x4) void {
|
||||
var ltmJob: ozz.LocalToModelJob = .{
|
||||
.skeleton = self.skeleton.?.sk,
|
||||
.input = ozz.makeSpan(input),
|
||||
.output = ozz.makeSpan(output),
|
||||
};
|
||||
|
||||
if (!ltmJob.run()) {
|
||||
core.engine_errs("local to model job failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sampleAnimation(self: *@This(), time: f32, track: *AnimationTrack, output: []ozz.SoaTransform) void {
|
||||
var samplingJob: ozz.SamplingJob = .{
|
||||
.ratio = time / track.endTime,
|
||||
.animation = track.animation,
|
||||
.context = self.sjc,
|
||||
.output = ozz.makeSpan(output),
|
||||
};
|
||||
|
||||
if (!samplingJob.run()) {
|
||||
core.engine_errs("sampling job failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modelToFinal(self: *@This()) void {
|
||||
if (self.jointRemap == null) {
|
||||
if (self.entity.fetch(rend.MeshComponent)) |meshComponent| {
|
||||
if (meshComponent.mesh) |mesh| {
|
||||
self.jointRemap = mesh.jointRemap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const skeleton = self.skeleton.?;
|
||||
for (self.models.items, 0..) |model, i| {
|
||||
const transform: core.Mat = @bitCast(model);
|
||||
|
||||
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
|
||||
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, transform)), 0.03, .{
|
||||
// .color = if (i == 15) .{ .x = 1 } else .{ .y = 1 },
|
||||
// });
|
||||
|
||||
const final = core.zm.mul(skeleton.inverseBinds.items[i], transform);
|
||||
// joint remap ozz -> gltf
|
||||
if (self.jointRemap) |jr| {
|
||||
// core.engine_log("{d} xx {d}", .{ i, jr[i] });
|
||||
self.finals.items[@intCast(jr[i])] = final;
|
||||
} else {
|
||||
self.finals.items[i] = final;
|
||||
}
|
||||
// core.engine_log(
|
||||
// "[{d}] {d} {d} {d} {d}, {d} {d} {d} {d}",
|
||||
// .{ i, transform[0][0], transform[0][1], transform[0][2], transform[0][3], transform[1][0], transform[1][1], transform[1][2], transform[1][3] },
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setAnimationByName(self: *@This(), _name: core.Name) !void {
|
||||
var name = _name;
|
||||
self.track = gAnimationSys.animTracks.get(name.handle());
|
||||
}
|
||||
|
||||
pub fn setAnimation(self: *@This(), path: []const u8) void {
|
||||
const name = core.MakeName(path);
|
||||
self.setAnimationByName(name) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
self.sjc.destroy();
|
||||
self.removeResolver();
|
||||
self.finals.deinit(allocator);
|
||||
self.locals.deinit(allocator);
|
||||
self.models.deinit(allocator);
|
||||
}
|
||||
|
||||
pub var BaseContainer: *core.SparseMap(@This()) = undefined;
|
||||
pub const ComponentName = "Animator";
|
||||
pub const ScriptExports: []const []const u8 = &.{};
|
||||
};
|
||||
|
||||
pub const AnimationSystem = struct {
|
||||
backingAllocator: std.mem.Allocator,
|
||||
|
||||
arena: std.heap.ArenaAllocator,
|
||||
slots: MergedSpans,
|
||||
|
||||
// Only AnimationTrack and Skeletons are made using the ArenaAllocator
|
||||
animTracks: std.AutoHashMapUnmanaged(u32, *AnimationTrack) = .{},
|
||||
skeletons: std.AutoHashMapUnmanaged(u32, *Skeleton) = .{},
|
||||
|
||||
sharedArena: [2]std.heap.ArenaAllocator, // could be a good usecase for a fat bump arena
|
||||
shared: [2]std.ArrayListUnmanaged(MatrixUploads) = .{ .{}, .{} },
|
||||
sharedLocks: [2]std.Thread.Mutex = .{ .{}, .{} }, // could be a good usecase for a fat bump arena
|
||||
|
||||
pub const MatrixUploads = struct {
|
||||
offset: u32,
|
||||
matrices: std.ArrayListUnmanaged(core.Mat) = .{},
|
||||
};
|
||||
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "rend.AnimationSystem");
|
||||
|
||||
pub fn preTick(self: *@This(), dt: f64) !void {
|
||||
_ = self;
|
||||
|
||||
var z1 = core.tracy.ZoneN(@src(), "animation system tick");
|
||||
defer z1.End();
|
||||
|
||||
for (Animator.BaseContainer.list.items) |animator| {
|
||||
animator.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn newAnimTrack(self: *@This(), _name: core.Name, anim: *ozz.Animation) !void {
|
||||
var name = _name;
|
||||
const new = try self.arenaAllocator().create(AnimationTrack);
|
||||
new.* = .{
|
||||
.animation = anim,
|
||||
.endTime = anim.getDuration(),
|
||||
};
|
||||
try self.animTracks.put(self.backingAllocator, name.handle(), new);
|
||||
}
|
||||
|
||||
pub fn newSkeleton(self: *@This(), _name: core.Name, sk: *ozz.Skeleton) !void {
|
||||
const new = try self.arenaAllocator().create(Skeleton);
|
||||
new.* = .{
|
||||
.sk = sk,
|
||||
};
|
||||
var name = _name;
|
||||
|
||||
try new.buildJointMap(self.arenaAllocator());
|
||||
|
||||
try new.inverseBinds.resize(self.arenaAllocator(), new.sk.numJoints());
|
||||
|
||||
if (new.inverseBinds.items.len > 256) {
|
||||
@panic("too many bones in skeleton, not supported");
|
||||
}
|
||||
|
||||
var bindModels = std.ArrayList(ozz.Float4x4).init(self.backingAllocator);
|
||||
defer bindModels.deinit();
|
||||
|
||||
try bindModels.resize(new.sk.numJoints());
|
||||
|
||||
var ltmJob: ozz.LocalToModelJob = .{
|
||||
.skeleton = new.sk,
|
||||
.input = new.sk.getRestPoseModel(),
|
||||
.output = ozz.makeSpan(bindModels.items),
|
||||
};
|
||||
|
||||
core.engine_log("creating bind pose {d} joints", .{new.inverseBinds.items.len});
|
||||
|
||||
if (!ltmJob.run()) {
|
||||
core.engine_logs("unable to get bind pose");
|
||||
return error.UnableToLoad;
|
||||
}
|
||||
|
||||
for (bindModels.items, 0..) |bind, i| {
|
||||
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
|
||||
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, @as(core.Mat, @bitCast(bind)))), 0.1, .{ .duration = 100 });
|
||||
|
||||
new.inverseBinds.items[i] = core.zm.inverse(@as(core.Mat, @bitCast(bind)));
|
||||
}
|
||||
|
||||
try self.skeletons.put(self.backingAllocator, name.handle(), new);
|
||||
}
|
||||
|
||||
pub fn arenaAllocator(self: *@This()) std.mem.Allocator {
|
||||
return self.arena.allocator();
|
||||
}
|
||||
|
||||
pub fn getShared(self: @This(), fi: u32) []const MatrixUploads {
|
||||
return self.shared[fi].items;
|
||||
}
|
||||
|
||||
pub fn sendShared(self: *@This(), frameIndex: u32) void {
|
||||
const fi: usize = @intCast(frameIndex);
|
||||
|
||||
self.sharedLocks[fi].lock();
|
||||
defer self.sharedLocks[fi].unlock();
|
||||
|
||||
_ = self.sharedArena[fi].reset(.retain_capacity);
|
||||
|
||||
const allocator = self.sharedArena[fi].allocator();
|
||||
const shared = &self.shared[fi];
|
||||
shared.* = .{};
|
||||
|
||||
for (Animator.BaseContainer.list.items) |animator| {
|
||||
var upload: MatrixUploads = .{ .offset = animator.finalsSpan.start };
|
||||
// core.engine_log(
|
||||
// "finalsSpan size offset{d} {d} animator finals {d}\n",
|
||||
// .{
|
||||
// animator.finalsSpan.start,
|
||||
// animator.finalsSpan.size,
|
||||
// animator.finals.items.len
|
||||
// });
|
||||
|
||||
upload.matrices.resize(allocator, animator.finalsSpan.size) catch unreachable;
|
||||
|
||||
for (animator.finals.items, 0..) |final, i| {
|
||||
upload.matrices.items[i] = final;
|
||||
}
|
||||
|
||||
shared.append(allocator, upload) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) !*@This() {
|
||||
const self = try alloc.create(@This());
|
||||
self.* = .{
|
||||
.backingAllocator = alloc,
|
||||
.arena = std.heap.ArenaAllocator.init(alloc),
|
||||
.sharedArena = .{
|
||||
std.heap.ArenaAllocator.init(alloc),
|
||||
std.heap.ArenaAllocator.init(alloc),
|
||||
},
|
||||
.slots = try MergedSpans.init(alloc, max_skin_slots),
|
||||
};
|
||||
|
||||
gAnimationSys = self;
|
||||
Animator.allocator = alloc;
|
||||
|
||||
core.engine_logs("Animation System initialized");
|
||||
try core.defineComponent(Animator, alloc);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *@This()) void {
|
||||
core.engine_logs("deinitializing animation system");
|
||||
{
|
||||
core.engine_log("skeleton count {d}", .{self.skeletons.count()});
|
||||
var iter = self.skeletons.iterator();
|
||||
while (iter.next()) |i| {
|
||||
i.value_ptr.*.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
core.engine_log("animTracks count {d}", .{self.animTracks.count()});
|
||||
var iter = self.animTracks.iterator();
|
||||
while (iter.next()) |i| {
|
||||
i.value_ptr.*.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
for (self.sharedArena) |arena| {
|
||||
arena.deinit();
|
||||
}
|
||||
|
||||
for (Animator.BaseContainer.list.items) |animator| {
|
||||
animator.deinit();
|
||||
}
|
||||
self.slots.deinit();
|
||||
core.undefineComponent(Animator);
|
||||
self.arena.deinit();
|
||||
self.skeletons.deinit(self.backingAllocator);
|
||||
self.animTracks.deinit(self.backingAllocator);
|
||||
self.backingAllocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub var gAnimationSys: *AnimationSystem = undefined;
|
||||
|
||||
pub fn getAnimationSystem() *AnimationSystem {
|
||||
return gAnimationSys;
|
||||
}
|
||||
|
||||
pub fn getSkeletonByName(_name: core.Name) ?*Skeleton {
|
||||
var name = _name;
|
||||
return gAnimationSys.skeletons.get(name.handle());
|
||||
}
|
||||
|
||||
const rend = @import("../rend.zig");
|
||||
const MergedSpans = core.MergedSpans;
|
||||
|
||||
pub const max_skin_slots = 100_000;
|
||||
|
||||
const anim_resolver = @import("animationResolver.zig");
|
||||
const AnimResolverRef = anim_resolver.AnimResolverRef;
|
||||
const AnimResolverInterface = anim_resolver.AnimResolverInterface;
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
pub const AnimationLoader = struct {
|
||||
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Animation", @This());
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "rend.AnimationLoader");
|
||||
|
||||
sys: *animation_system.AnimationSystem,
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
pub fn discardAll(self: *@This()) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
|
||||
const animation = ozz.Animation.create();
|
||||
|
||||
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
|
||||
defer core.fs().unmap(mapping);
|
||||
animation.loadFromBytes(mapping.bytes);
|
||||
|
||||
self.sys.newAnimTrack(assetRef.name, animation) catch return error.UnableToLoad;
|
||||
}
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.sys = animation_system.gAnimationSys,
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const SkeletonLoader = struct {
|
||||
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Skeleton", @This());
|
||||
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "rend.SkeletonLoader");
|
||||
|
||||
sys: *animation_system.AnimationSystem,
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
pub fn discardAll(self: *@This()) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
|
||||
const sk = ozz.Skeleton.create();
|
||||
|
||||
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
|
||||
defer core.fs().unmap(mapping);
|
||||
sk.loadFromBytes(mapping.bytes);
|
||||
|
||||
self.sys.newSkeleton(assetRef.name, sk) catch return error.UnableToLoad;
|
||||
}
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.sys = animation_system.gAnimationSys,
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub var gSkeletonLoader: *SkeletonLoader = undefined;
|
||||
pub var gAnimationLoader: *AnimationLoader = undefined;
|
||||
|
||||
pub fn initLoaders() !void {
|
||||
gSkeletonLoader = try core.createObject(SkeletonLoader, .{});
|
||||
gAnimationLoader = try core.createObject(AnimationLoader, .{});
|
||||
|
||||
try assets.getAssets().registerLoader(gSkeletonLoader);
|
||||
try assets.getAssets().registerLoader(gAnimationLoader);
|
||||
}
|
||||
|
||||
const animation_system = @import("animationSystem.zig");
|
||||
const assets = @import("assets");
|
||||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
const ozz = @import("ozz");
|
||||
|
|
@ -9,6 +9,9 @@ texture: ?*Texture = null,
|
|||
|
||||
entity: core.Entity = undefined,
|
||||
|
||||
animated: bool = false,
|
||||
animator: ?*Animator = null,
|
||||
|
||||
textureMode: ComponentTextureMode = .{}, // bit
|
||||
// DEBUG for-fun function, completely override what happens when the renderer tries to render this mesh.
|
||||
cmrf: ?rend.renderer.CustomMeshRenderFunc = null,
|
||||
|
|
@ -91,3 +94,6 @@ const core = @import("core");
|
|||
const std = @import("std");
|
||||
const rend = @import("../rend.zig");
|
||||
const Texture = rend.Texture;
|
||||
|
||||
const animationSystem = rend.animationSystem;
|
||||
const Animator = animationSystem.Animator;
|
||||
|
|
|
|||
|
|
@ -269,6 +269,8 @@ pub fn loadIndexedMeshForPoolingGltf(allocator: std.mem.Allocator, meshName: cor
|
|||
},
|
||||
};
|
||||
|
||||
// grab the animation system and generate a joint remap
|
||||
|
||||
core.graphics_log("[{s}] gltf loaded vertex count vertices={d} indices={d}", .{ path, rv.new.vertices.len, rv.new.indices.len });
|
||||
|
||||
// try gMeshPoolBuffer.updateRequests.pushLocked(rv);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ pub const MeshVertex = extern struct {
|
|||
normal: core.Vectorf = .{},
|
||||
color: core.colors.Color = .{},
|
||||
uv: core.Vector2f = .{},
|
||||
bones: [4]u8 = .{ 0, 0, 0, 0 },
|
||||
weights: [4]u8 = .{ 0, 0, 0, 0 },
|
||||
bones: u8vec4 = .{ 0, 0, 0, 0 },
|
||||
weights: u8vec4 = .{ 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
pub const MeshUpdate = union(enum(u8)) {
|
||||
|
|
@ -95,3 +95,5 @@ const rend = @import("../rend.zig");
|
|||
const renderer = rend.renderer;
|
||||
const std = @import("std");
|
||||
const zgltf = @import("zgltf");
|
||||
const shaderTypes = @import("sdl3").shaderTypes;
|
||||
const u8vec4 = shaderTypes.u8vec4;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ pub const textureExists = renderer.textureExists;
|
|||
pub const texture = @import("texture/texture.zig");
|
||||
pub const Texture = texture.Texture;
|
||||
|
||||
pub const animationSystem = @import("animations/animationSystem.zig");
|
||||
pub const animationResolver = @import("animations/animationResolver.zig");
|
||||
const animationLoaders = @import("animations/loaders.zig");
|
||||
|
||||
pub const Animator = animationSystem.Animator;
|
||||
|
||||
pub const setSkyboxTexture = renderer.setSkyboxTexture;
|
||||
|
||||
var rendAllocator: std.mem.Allocator = undefined;
|
||||
|
|
@ -55,9 +61,13 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me
|
|||
_ = spec;
|
||||
|
||||
rendAllocator = allocator;
|
||||
|
||||
try renderer.createInstance();
|
||||
try renderer.start();
|
||||
|
||||
_ = try context().createRendererEngineObject(animationSystem.AnimationSystem);
|
||||
|
||||
try animationLoaders.initLoaders();
|
||||
try core.defineComponentList(ComponentList, allocator);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ fn createPipeline(self: *@This()) !void {
|
|||
pci.vertex_shader = vertex;
|
||||
pci.fragment_shader = fragment;
|
||||
|
||||
var attributes = try ctx.generateVertexAttributeList();
|
||||
//var attributes = try ctx.generateVertexAttributeList();
|
||||
|
||||
var attributes = try ctx.addVertexAttributes(&pci);
|
||||
defer attributes.deinit();
|
||||
|
||||
pci.vertex_input_state = .{
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ pub const MeshPool = struct {
|
|||
self.destroyList.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub const JointMap = std.AutoHashMapUnmanaged(u32, u32);
|
||||
|
||||
pub fn onUploadInner(self: *@This(), copyPass: *gpu.GPUCopyPass) !void {
|
||||
self.meshUpdates.lock();
|
||||
defer self.meshUpdates.unlock();
|
||||
|
|
@ -93,12 +95,48 @@ pub const MeshPool = struct {
|
|||
|
||||
core.graphics_log("uploading {d} vertices and {d} indices", .{ vertexSpan.size, indexSpan.size });
|
||||
|
||||
var maybeJointRemap: ?[]u8 = null;
|
||||
|
||||
if (new.skeletonName) |skName| {
|
||||
if (animationSystem.getSkeletonByName(skName)) |sk| {
|
||||
var jointMap: JointMap = .{};
|
||||
|
||||
for (new.jointNames) |entry| {
|
||||
var entryName = core.MakeName(entry.name);
|
||||
// core.engine_log("gtlf bone found {s} -> {d}", .{ entry.name, entry.index });
|
||||
try jointMap.put(self.allocator, entryName.handle(), entry.index);
|
||||
}
|
||||
// build the joint remap
|
||||
// this is a map from ozz's index to gltf's index
|
||||
var iter = sk.jointMapping.iterator();
|
||||
var jointRemap = try self.allocator.alloc(u8, sk.jointMapping.count());
|
||||
|
||||
while (iter.next()) |i| {
|
||||
const jointName = i.key_ptr.*;
|
||||
const ozzIndex = i.value_ptr.*;
|
||||
var jn = core.MakeName(jointName);
|
||||
var gltfIndex = jointMap.get(jn.handle());
|
||||
if (gltfIndex == null) {
|
||||
gltfIndex = 0;
|
||||
// core.engine_log("ERROR REMAPPING BONE setting to zero {s}", .{jointName});
|
||||
}
|
||||
|
||||
// core.engine_log("remapping bone from {s} ozz {d} -> {d} gltf", .{ jointName, ozzIndex, gltfIndex.? });
|
||||
|
||||
jointRemap[ozzIndex] = @intCast(gltfIndex.?);
|
||||
}
|
||||
|
||||
maybeJointRemap = jointRemap;
|
||||
// todo.. self.jointMaps.put(new.name().handle(), jointMap);
|
||||
}
|
||||
}
|
||||
|
||||
core.engine_log("install mesh by name {d}", .{name.handle()});
|
||||
try self.installedMeshes.put(self.allocator, name.handle(), .{
|
||||
.vertex = vertexSpan,
|
||||
.index = indexSpan,
|
||||
.name = new.name,
|
||||
.jointRemap = null, // joint remaps... TODO parse the installed skeletal meshes to generate this remap
|
||||
.jointRemap = maybeJointRemap, // joint remaps... TODO parse the installed skeletal meshes to generate this remap
|
||||
});
|
||||
},
|
||||
.free => |free| {
|
||||
|
|
@ -199,6 +237,7 @@ const MeshAssetLoader = @import("MeshAssetLoader.zig");
|
|||
const sdl3 = @import("sdl3");
|
||||
const gpu = sdl3.gpu;
|
||||
const rend = @import("../rend.zig");
|
||||
const animationSystem = rend.animationSystem;
|
||||
const std = @import("std");
|
||||
const core = @import("core");
|
||||
const assets = @import("assets");
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ pub const Renderer = struct {
|
|||
ssboScene: *gpu.GPUBuffer = undefined,
|
||||
ssboSceneUpload: *gpu.GPUTransferBuffer = undefined,
|
||||
|
||||
ssboAnimation: *gpu.GPUBuffer = undefined,
|
||||
ssboAnimationUpload: *gpu.GPUTransferBuffer = undefined,
|
||||
|
||||
meshPool: *MeshPool = undefined,
|
||||
|
||||
uploads: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCopyPass) void }) = .{},
|
||||
|
|
@ -219,7 +222,8 @@ pub const Renderer = struct {
|
|||
}
|
||||
|
||||
pub fn addVertexAttributes(self: *@This(), pci: *gpu.GPUGraphicsPipelineCreateInfo) !std.ArrayList(gpu.GPUVertexAttribute) {
|
||||
const attributes = try self.generateVertexAttributeList();
|
||||
_ = self;
|
||||
const attributes = try addVertexAttributesFromStruct(rend.MeshVertex, pci);
|
||||
|
||||
pci.vertex_input_state = .{
|
||||
.num_vertex_buffers = 1,
|
||||
|
|
@ -241,7 +245,10 @@ pub const Renderer = struct {
|
|||
pci.vertex_shader = vertex;
|
||||
pci.fragment_shader = fragment;
|
||||
|
||||
var attributes = try self.generateVertexAttributeList();
|
||||
//return rend.renderer.addVertexAttributesFromStruct(TextMeshVertex, pci);
|
||||
//var attributes = try self.generateVertexAttributeList();
|
||||
|
||||
var attributes = try addVertexAttributesFromStruct(rend.MeshVertex, &pci);
|
||||
defer attributes.deinit();
|
||||
|
||||
pci.vertex_input_state = .{
|
||||
|
|
@ -334,7 +341,9 @@ pub const Renderer = struct {
|
|||
|
||||
pub fn createRendererEngineObject(self: *@This(), T: type) !*T {
|
||||
const object = try core.createObject(T, .{});
|
||||
try object.setup(self.device);
|
||||
if (@hasDecl(T, "setup")) {
|
||||
try object.setup(self.device);
|
||||
}
|
||||
|
||||
try self.registerRendererObject(T, object);
|
||||
|
||||
|
|
@ -405,7 +414,7 @@ pub const Renderer = struct {
|
|||
offset.* = offset.* + size;
|
||||
}
|
||||
|
||||
pub fn generateVertexAttributeList(self: *@This()) !std.ArrayList(gpu.GPUVertexAttribute) {
|
||||
pub fn generateVertexAttributeList_deprecated(self: *@This()) !std.ArrayList(gpu.GPUVertexAttribute) {
|
||||
var list = std.ArrayList(gpu.GPUVertexAttribute).init(self.allocator);
|
||||
var offset: u32 = 0;
|
||||
{
|
||||
|
|
@ -427,7 +436,8 @@ pub const Renderer = struct {
|
|||
pci.vertex_shader = vertex;
|
||||
pci.fragment_shader = fragment;
|
||||
|
||||
var attributes = try self.generateVertexAttributeList();
|
||||
// var attributes = try self.generateVertexAttributeList();
|
||||
var attributes = try self.addVertexAttributes(&pci);
|
||||
defer attributes.deinit();
|
||||
|
||||
pci.vertex_input_state = .{
|
||||
|
|
@ -465,7 +475,8 @@ pub const Renderer = struct {
|
|||
pci.vertex_shader = vertex;
|
||||
pci.fragment_shader = fragment;
|
||||
|
||||
var attributes = try self.generateVertexAttributeList();
|
||||
//var attributes = try self.generateVertexAttributeList();
|
||||
var attributes = try self.addVertexAttributes(&pci);
|
||||
defer attributes.deinit();
|
||||
|
||||
pci.vertex_input_state = .{
|
||||
|
|
@ -531,6 +542,21 @@ pub const Renderer = struct {
|
|||
.size = MaxObjectCount * @sizeOf(meshes_vert.Scene),
|
||||
.props = 0,
|
||||
});
|
||||
|
||||
self.ssboAnimation = self.device.createGPUBuffer(&.{
|
||||
.usage = .{
|
||||
.bufferusageGraphicsStorageRead = true,
|
||||
.bufferusageVertex = true,
|
||||
},
|
||||
.size = MaxObjectCount * @sizeOf(meshes_vert.BoneTransform),
|
||||
.props = 0,
|
||||
});
|
||||
|
||||
self.ssboAnimationUpload = self.device.createGPUTransferBuffer(&.{
|
||||
.usage = .transferbufferusageUpload,
|
||||
.size = MaxObjectCount * @sizeOf(meshes_vert.BoneTransform),
|
||||
.props = 0,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn uploadSSBOs(self: *@This(), copyPass: *gpu.GPUCopyPass) !void {
|
||||
|
|
@ -551,6 +577,30 @@ pub const Renderer = struct {
|
|||
}
|
||||
uploadMapped[i].Model = @bitCast(transform);
|
||||
uploadMapped[i].textureMode = @bitCast(object.textureMode);
|
||||
if (container.dense.items[i].value.animator) |animator| {
|
||||
uploadMapped[i].animation = @intCast(animator.finalsSpan.start);
|
||||
} else {
|
||||
uploadMapped[i].animation = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var animationHighest: u32 = 0;
|
||||
|
||||
// upload animation buffers
|
||||
{
|
||||
const animationMapped: [*]meshes_vert.BoneTransform = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(self.ssboAnimationUpload, true)));
|
||||
defer self.device.unmapGPUTransferBuffer(self.ssboAnimationUpload);
|
||||
// const as = animationSystem.getAnimationSystem();
|
||||
|
||||
for (animationSystem.Animator.BaseContainer.list.items) |animator| {
|
||||
const span = animator.finalsSpan;
|
||||
|
||||
for (0..span.size) |i| {
|
||||
animationMapped[span.start + i].final = animator.finals.items[i];
|
||||
}
|
||||
if (span.start + span.size > animationHighest)
|
||||
animationHighest = span.start + span.size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -562,6 +612,15 @@ pub const Renderer = struct {
|
|||
.offset = 0,
|
||||
.size = @intCast(uploadCount * @sizeOf(meshes_vert.Scene)),
|
||||
}, true);
|
||||
|
||||
if (animationHighest == 0)
|
||||
return;
|
||||
|
||||
copyPass.uploadToGPUBuffer(&.{ .transfer_buffer = self.ssboAnimationUpload, .offset = 0 }, &.{
|
||||
.buffer = self.ssboAnimation,
|
||||
.offset = 0,
|
||||
.size = @intCast(animationHighest * @sizeOf(meshes_vert.BoneTransform)),
|
||||
}, true);
|
||||
}
|
||||
|
||||
pub fn loadShader(
|
||||
|
|
@ -764,6 +823,7 @@ pub const Renderer = struct {
|
|||
const renderpass = cmd.beginGPURenderPass(null, 0, &depthTarget);
|
||||
renderpass.bindGPUGraphicsPipeline(self.shadowCastingPipeline);
|
||||
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1);
|
||||
renderpass.bindGPUVertexStorageBuffers(1, &self.ssboAnimation, 1);
|
||||
renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1);
|
||||
renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
|
||||
|
||||
|
|
@ -822,6 +882,7 @@ pub const Renderer = struct {
|
|||
renderpass.bindGPUFragmentSamplers(2, &.{ .sampler = self.blockySampler, .texture = self.defaultTexture.texture }, 1);
|
||||
|
||||
renderpass.bindGPUVertexStorageBuffers(0, &self.ssboScene, 1);
|
||||
renderpass.bindGPUVertexStorageBuffers(1, &self.ssboAnimation, 1);
|
||||
renderpass.bindGPUFragmentStorageBuffers(0, &self.ssboScene, 1);
|
||||
renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1);
|
||||
renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
|
||||
|
|
@ -1068,5 +1129,7 @@ const texture_sample_png align(8) = @embedFile("embedded/texture_sample.png").*;
|
|||
const primitive_box_obj align(8) = @embedFile("embedded/primitive_box.obj").*;
|
||||
const tracy = core.tracy;
|
||||
|
||||
const animationSystem = rend.animationSystem;
|
||||
|
||||
const builtin = @import("builtin");
|
||||
pub const SsaoSystem = @import("ssao.zig");
|
||||
|
|
|
|||
|
|
@ -8,15 +8,16 @@ pub fn getVertexFormatFromType(comptime T: type) gpu.GPUVertexElementFormat {
|
|||
u32, u8vec4 => {
|
||||
return gpu.GPUVertexElementFormat.vertexelementformatUint;
|
||||
},
|
||||
vec2 => {
|
||||
vec2, core.Vector2f => {
|
||||
return gpu.GPUVertexElementFormat.vertexelementformatFloat2;
|
||||
},
|
||||
vec3 => {
|
||||
vec3, core.Vectorf => {
|
||||
return gpu.GPUVertexElementFormat.vertexelementformatFloat3;
|
||||
},
|
||||
vec4 => {
|
||||
vec4, core.colors.Color => {
|
||||
return gpu.GPUVertexElementFormat.vertexelementformatFloat4;
|
||||
},
|
||||
|
||||
else => {
|
||||
@compileError("unable to get vertex format from subtype" ++ @typeName(T));
|
||||
},
|
||||
|
|
@ -31,6 +32,7 @@ pub fn addVertexAttributesFromStruct(comptime T: type, pci: *gpu.GPUGraphicsPipe
|
|||
inline for (@typeInfo(T).@"struct".fields) |field| {
|
||||
try Renderer.addAttribute(&list, &offset, @sizeOf(field.type), getVertexFormatFromType(field.type));
|
||||
}
|
||||
core.engine_log("# of attributes added : {d}", .{list.items.len});
|
||||
|
||||
pci.vertex_input_state = .{
|
||||
.num_vertex_buffers = 1,
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ pub fn createRectPipeline(self: *@This()) !void {
|
|||
pci.vertex_shader = vertex;
|
||||
pci.fragment_shader = fragment;
|
||||
|
||||
var attributes = try ctx.generateVertexAttributeList();
|
||||
var attributes = try ctx.addVertexAttributes(&pci);
|
||||
defer attributes.deinit();
|
||||
|
||||
pci.vertex_input_state = .{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
pub const SortMode = enum {
|
||||
unordered,
|
||||
name,
|
||||
dateModified,
|
||||
};
|
||||
|
||||
pub fn Browser(comptime T: type) type {
|
||||
return struct {
|
||||
list: ?*[]T = null,
|
||||
|
||||
display: std.ArrayListUnmanaged(u32) = .{},
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
sameWindow: bool = false,
|
||||
windowOpen: bool = false,
|
||||
|
||||
browserName: []u8,
|
||||
|
||||
sortMode: SortMode = .unordered,
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator) !*@This() {
|
||||
const self = try allocator.create(@This());
|
||||
|
||||
self.* = .{
|
||||
.browserName = try std.fmt.allocPrintZ(allocator, "Browser", .{}),
|
||||
.allocator = allocator,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn setList(self: *@This(), list: *[]T) void {
|
||||
self.list = list;
|
||||
}
|
||||
|
||||
fn sortList(self: *@This()) void {
|
||||
if (self.list) |list| {
|
||||
self.display.clearRetainingCapacity();
|
||||
if (self.sortMode == .unordered) {
|
||||
for (list.*, 0..) |x, i| {
|
||||
_ = x;
|
||||
self.display.append(self.allocator, @intCast(i)) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tickDisplay(self: *@This()) void {
|
||||
self.sortList();
|
||||
|
||||
if (!self.sameWindow) {
|
||||
if (!ig.begin(self.browserName.ptr, &self.windowOpen, .{})) {
|
||||
ig.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ig.beginTable("FileBrowserTable", 2, .{ .resizable = true }, .{}, 0)) {
|
||||
ig.tableSetupColumn("File Name", .{ .width_stretch = true }, 0, 0);
|
||||
ig.tableSetupColumn("Modified Time", .{ .width_fixed = true }, 150, 0);
|
||||
ig.tableHeadersRow();
|
||||
|
||||
if (self.list) |list| {
|
||||
for (self.display.items) |i| {
|
||||
const item = &list.*[i];
|
||||
|
||||
ig.tableNextRow(.{}, 0);
|
||||
_ = ig.tableSetColumnIndex(0);
|
||||
if (@hasDecl(T, "fileName")) {
|
||||
const filename_str = item.fileName();
|
||||
ig.textSlice(filename_str);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
_ = ig.tableSetColumnIndex(1);
|
||||
if (@hasDecl(T, "modtime")) {
|
||||
const modtime_str = item.modtime();
|
||||
ig.textSlice(modtime_str);
|
||||
} else {
|
||||
ig.textSlice("N/A");
|
||||
}
|
||||
}
|
||||
} else {}
|
||||
|
||||
ig.endTable();
|
||||
}
|
||||
|
||||
if (!self.sameWindow) {
|
||||
ig.end();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.display.deinit(self.allocator);
|
||||
self.allocator.free(self.browserName);
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const backlog = @import("Backlog");
|
||||
const core = backlog.core;
|
||||
const rend = backlog.rend;
|
||||
const ig = backlog.imgui.api;
|
||||
const igutils = backlog.imgui.utils;
|
||||
|
|
@ -6,3 +6,5 @@ pub const ObjectSpawner = @import("debuggers/objectSpawner.zig");
|
|||
pub const RendererDebug = @import("debuggers/RendererDebug.zig");
|
||||
pub const EngineTool = @import("debuggers/EngineTool.zig");
|
||||
pub const PhysicsObjectList = @import("debuggers/PhysicsObjectList.zig");
|
||||
|
||||
pub const browser = @import("debuggers/fileBrowser.zig");
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ pub const TableFlags = packed struct(c_int) {
|
|||
scroll_y: bool = false, // ImGuiTableFlags_ScrollY = 1 << 25,
|
||||
sort_multi: bool = false, // ImGuiTableFlags_SortMulti = 1 << 26,
|
||||
sort_tristate: bool = false, // ImGuiTableFlags_SortTristate = 1 << 27,
|
||||
reserved: u4 = 0,
|
||||
reserved: u3 = 0,
|
||||
|
||||
pub const borders_h = .{ .borders_inner_h = true, .borders_outer_h = true };
|
||||
pub const borders_v = .{ .borders_inner_v = true, .borders_outer_v = true };
|
||||
|
|
@ -325,6 +325,7 @@ pub const TableColumnFlags = packed struct(c_int) {
|
|||
prefer_sort_descending: bool = false, // ImGuiTableColumnFlags_PreferSortDescending = 1 << 15,
|
||||
indent_enable: bool = false, // ImGuiTableColumnFlags_IndentEnable = 1 << 16,
|
||||
indent_disable: bool = false, // ImGuiTableColumnFlags_IndentDisable = 1 << 17,
|
||||
reserved0: u7 = 0,
|
||||
is_enabled: bool = false, // ImGuiTableColumnFlags_IsEnabled = 1 << 24,
|
||||
is_visible: bool = false, // ImGuiTableColumnFlags_IsVisible = 1 << 25,
|
||||
is_sorted: bool = false, // ImGuiTableColumnFlags_IsSorted = 1 << 26,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ const utils = @import("structures/utils.zig");
|
|||
pub const getFileExtension = utils.getFileExtension;
|
||||
pub const getBasePath = utils.getBasePath;
|
||||
pub const getFolder = utils.getFolder;
|
||||
pub const getDir = utils.getFolder;
|
||||
|
||||
pub const BumpArena = @import("structures/bump-arena.zig").BumpArena;
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ pub fn assert(eval: anytype) !void {
|
|||
}
|
||||
}
|
||||
|
||||
// gets the basename of the file/path
|
||||
pub fn getBasePath(path: []const u8) []const u8 {
|
||||
if (path.len == 0) {
|
||||
return "";
|
||||
|
|
@ -119,6 +120,7 @@ pub fn getBasePath(path: []const u8) []const u8 {
|
|||
return path;
|
||||
}
|
||||
|
||||
// gets the folder name of the dir/path
|
||||
pub fn getFolder(path: []const u8) []const u8 {
|
||||
if (path.len == 0) {
|
||||
unreachable;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ pub fn build(b: *std.Build) void {
|
|||
toolbox.setModuleEnabled("imgui", true);
|
||||
toolbox.setModuleEnabled("audio", false);
|
||||
toolbox.setModuleEnabled("sys", true);
|
||||
toolbox.addExtraModule("gameExtras");
|
||||
|
||||
const toolboxExe = toolbox.compileInstall();
|
||||
// Add Windows icon resource for toolbox
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -7,8 +7,8 @@ struct Scene
|
|||
{
|
||||
float4x4 Model;
|
||||
uint textureMode;
|
||||
uint pad0;
|
||||
uint pad1;
|
||||
int animation;
|
||||
uint flags;
|
||||
uint pad2;
|
||||
};
|
||||
|
||||
|
|
@ -17,6 +17,16 @@ struct type_StructuredBuffer_Scene
|
|||
Scene _m0[1];
|
||||
};
|
||||
|
||||
struct BoneTransform
|
||||
{
|
||||
float4x4 final;
|
||||
};
|
||||
|
||||
struct type_StructuredBuffer_BoneTransform
|
||||
{
|
||||
BoneTransform _m0[1];
|
||||
};
|
||||
|
||||
struct type_Uniforms
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
|
|
@ -41,22 +51,42 @@ 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)]], uint gl_InstanceIndex [[instance_id]])
|
||||
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 = {};
|
||||
float4 _53 = float4(in.in_var_TEXCOORD0, 1.0);
|
||||
float4 _56 = scene._m0[gl_InstanceIndex].Model * _53;
|
||||
float4x4 _91 = 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 _95 = float4(in.in_var_TEXCOORD1, 1.0);
|
||||
float3 _103;
|
||||
if (scene._m0[gl_InstanceIndex].animation != (-1))
|
||||
{
|
||||
float3 _75;
|
||||
_75 = float3(0.0);
|
||||
for (int _78 = 0; _78 < 4; )
|
||||
{
|
||||
uint _84 = (8u * uint(_78)) & 31u;
|
||||
_75 += ((animationBuffer._m0[uint(scene._m0[gl_InstanceIndex].animation) + ((in.in_var_TEXCOORD4 >> _84) & 255u)].final * float4(in.in_var_TEXCOORD0, 1.0)).xyz * (float((in.in_var_TEXCOORD5 >> _84) & 255u) * 0.0039215688593685626983642578125));
|
||||
_78++;
|
||||
continue;
|
||||
}
|
||||
_103 = _75;
|
||||
}
|
||||
else
|
||||
{
|
||||
_103 = in.in_var_TEXCOORD0;
|
||||
}
|
||||
float4 _107 = float4(_103, 1.0);
|
||||
float4 _110 = scene._m0[gl_InstanceIndex].Model * _107;
|
||||
float4x4 _145 = 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 _149 = float4(in.in_var_TEXCOORD1, 1.0);
|
||||
out.out_var_TEXCOORD0 = in.in_var_TEXCOORD3;
|
||||
out.out_var_TEXCOORD1 = _56.xyz;
|
||||
out.out_var_TEXCOORD2 = fast::normalize(_95 * _91).xyz;
|
||||
out.out_var_TEXCOORD3 = Uniforms.ShadowMapProjection * _56;
|
||||
out.out_var_TEXCOORD1 = _110.xyz;
|
||||
out.out_var_TEXCOORD2 = fast::normalize(_149 * _145).xyz;
|
||||
out.out_var_TEXCOORD3 = Uniforms.ShadowMapProjection * _110;
|
||||
out.out_var_TEXCOORD4 = gl_InstanceIndex;
|
||||
out.out_var_TEXCOORD5 = (_95 * (_91 * transpose(Uniforms.NoTranslateView))).xyz;
|
||||
out.gl_Position = Uniforms.ViewProjection * (scene._m0[gl_InstanceIndex].Model * _53);
|
||||
out.out_var_TEXCOORD5 = (_149 * (_145 * transpose(Uniforms.NoTranslateView))).xyz;
|
||||
out.gl_Position = Uniforms.ViewProjection * (scene._m0[gl_InstanceIndex].Model * _107);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -86,6 +86,11 @@ pub const ExternGameObject = struct {
|
|||
_ = fireInput.data.addListener(self, onFire);
|
||||
fireInput.activate();
|
||||
|
||||
const altFireInput = try core.ActionBinding.create(core.MakeName("altFire"));
|
||||
altFireInput.addKey(.Mouse3, .keyDown);
|
||||
_ = altFireInput.data.addListener(self, onAltFire);
|
||||
altFireInput.activate();
|
||||
|
||||
const settings: physics.ShapeSettings = .{ .box = try physics.BoxShapeSettings.create(.{ 1.0, 1.0, 1.0 }) };
|
||||
defer settings.release();
|
||||
try physics.addShape("ph_small_box", settings);
|
||||
|
|
@ -135,7 +140,7 @@ pub const ExternGameObject = struct {
|
|||
});
|
||||
}
|
||||
|
||||
fn onFire(ctx: ?*anyopaque, _: core.ActionEvent) void {
|
||||
fn onAltFire(ctx: ?*anyopaque, _: core.ActionEvent) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(ctx));
|
||||
|
||||
if (platform.context().isCursorEnabled()) {
|
||||
|
|
@ -145,8 +150,6 @@ pub const ExternGameObject = struct {
|
|||
if (r.normal) |n| {
|
||||
const p = r.point.add(n.fmul(0.2));
|
||||
core.debugSphere(p, 0.2, .{ .color = .{ .y = 1.0 }, .duration = 10.0 });
|
||||
|
||||
addBox(p.add(n.fmul(1.0))) catch unreachable;
|
||||
rend.context().lightPosition = p;
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +159,22 @@ pub const ExternGameObject = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn onFire(ctx: ?*anyopaque, _: core.ActionEvent) void {
|
||||
const self: *@This() = @ptrCast(@alignCast(ctx));
|
||||
|
||||
if (platform.context().isCursorEnabled()) {
|
||||
core.engine_logs("moving light");
|
||||
const ray = rend.context().activeCamera.?.getNormalRayFromScreen(platform.getCursorPosition());
|
||||
if (physics.traceLine(ray.start, ray.dir.fmul(10000), .{ .bodyFilter = &self.fireTraceFilter, .getNormalsSlow = true })) |r| {
|
||||
if (r.normal) |n| {
|
||||
const p = r.point.add(n.fmul(0.2));
|
||||
|
||||
addBox(p.add(n.fmul(1.0))) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This(), dt: f64) void {
|
||||
const rctx = rend.context();
|
||||
_ = rctx;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,16 @@ const assetReferences = [_]assets.AssetImportReference{
|
|||
"m_skybox",
|
||||
.{ .path = "meshes/skybox.obj" },
|
||||
),
|
||||
assets.MakeImportRefOptions(
|
||||
"Skeleton",
|
||||
"sk_fox",
|
||||
.{ .path = "gltf-samples/Fox/glTF/skeleton.ozz" },
|
||||
),
|
||||
assets.MakeImportRefOptions(
|
||||
"Animation",
|
||||
"a_fox_survey",
|
||||
.{ .path = "gltf-samples/Fox/glTF/Survey.ozz" },
|
||||
),
|
||||
assets.MakeImportRefOptions(
|
||||
"Mesh",
|
||||
"m_fox",
|
||||
|
|
@ -96,6 +106,9 @@ pub const FoxObject = struct {
|
|||
const mesh = fox.addComponent(rend.MeshComponent).?;
|
||||
mesh.setMesh("m_fox");
|
||||
mesh.setTexture("t_fox");
|
||||
const animator = fox.addComponent(rend.Animator).?;
|
||||
animator.setSkeleton("sk_fox");
|
||||
animator.setAnimation("a_fox_survey");
|
||||
|
||||
self.entity = fox;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ pub fn init(allocator: std.mem.Allocator) !*@This() {
|
|||
|
||||
pub fn prepare(self: *@This()) !void {
|
||||
core.engine_log("program ready", .{});
|
||||
try assets.initCooking();
|
||||
|
||||
if (core.getEngineObject(imgui.utils.TopBar)) |topbar| {
|
||||
topbar.menuOpen = true;
|
||||
}
|
||||
|
|
@ -42,6 +44,7 @@ pub fn tick(self: *@This(), dt: f64) void {
|
|||
std.time.sleep(10 * 1000 * 1000);
|
||||
|
||||
self.tickTasks() catch {};
|
||||
self.animationStore.tick();
|
||||
|
||||
if (self.activeCommand != null or self.commandQueue.count() > 0) {
|
||||
//ig.setNextWindowPos(.{ .x = 20, .y = 20 }, .{}, .{});
|
||||
|
|
@ -71,8 +74,6 @@ pub fn tick(self: *@This(), dt: f64) void {
|
|||
|
||||
// Create a docked window
|
||||
if (ig.begin("Toolbox Window", null, .{})) {
|
||||
ig.textf("This is a docked toolbox window", .{});
|
||||
|
||||
if (ig.button("Compile Shaders", .{})) {
|
||||
start_CompileShaders() catch {};
|
||||
}
|
||||
|
|
@ -82,7 +83,9 @@ pub fn tick(self: *@This(), dt: f64) void {
|
|||
}
|
||||
|
||||
ig.separator();
|
||||
ig.textf("Additional toolbox content can go here", .{});
|
||||
ig.textf("animation store", .{});
|
||||
|
||||
self.animationStore.tickDisplay();
|
||||
|
||||
if (self.activeCommand != null or self.commandQueue.count() > 0) {
|
||||
ig.textf("outstanding tasks: {d}", .{self.commandQueue.count() + 1});
|
||||
|
|
@ -292,6 +295,7 @@ const builtin = @import("builtin");
|
|||
const std = @import("std");
|
||||
const api = @import("backlog");
|
||||
const imgui = api.imgui;
|
||||
const assets = api.assets;
|
||||
const ig = api.imgui.api;
|
||||
const core = api.core;
|
||||
const sys = api.sys;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,25 @@ stringArena: std.heap.ArenaAllocator,
|
|||
|
||||
filesToProcess: std.ArrayListUnmanaged(FileToProcess) = .{},
|
||||
|
||||
animations: std.ArrayListUnmanaged(AnimationData) = .{},
|
||||
|
||||
browser: *AnimationDataBrowser,
|
||||
|
||||
const AnimationDataBrowser = extras.browser.Browser(AnimationData);
|
||||
|
||||
pub const AnimationData = struct {
|
||||
name: []const u8,
|
||||
|
||||
pub fn fileName(self: *@This()) []const u8 {
|
||||
return self.name;
|
||||
}
|
||||
|
||||
pub fn getModTime(self: *@This()) []const u8 {
|
||||
_ = self;
|
||||
return "0";
|
||||
}
|
||||
};
|
||||
|
||||
pub const FileToProcess = struct {
|
||||
path: []const u8,
|
||||
};
|
||||
|
|
@ -23,8 +42,12 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
|
|||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.stringArena = std.heap.ArenaAllocator.init(allocator),
|
||||
.browser = try AnimationDataBrowser.create(allocator),
|
||||
};
|
||||
|
||||
self.browser.sameWindow = true;
|
||||
self.browser.setList(&self.animations.items);
|
||||
|
||||
try self.setupCallbacks();
|
||||
try self.initialLoad();
|
||||
|
||||
|
|
@ -52,7 +75,8 @@ pub fn scanAll(self: *@This(), dir_path: []const u8) !void {
|
|||
try self.scanAll(full_path); // Recursive call for subdirectories
|
||||
} else if (entry.kind == .file) {
|
||||
// core.engine_log("processing file: {s}", .{entry.name});
|
||||
try self.filesToProcess.append(self.allocator, .{ .path = entry.name });
|
||||
var pathAsName = core.MakeName(full_path);
|
||||
try self.filesToProcess.append(self.allocator, .{ .path = pathAsName.utf8() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +89,9 @@ pub fn fileChangedCallback(path: []const u8, ctx: ?*anyopaque) void {
|
|||
const self: *@This() = @ptrCast(@alignCast(ctx.?));
|
||||
// core.engine_log("file change seen {s}", .{path});
|
||||
//
|
||||
self.filesToProcess.append(self.allocator, .{ .path = path }) catch {};
|
||||
var name = core.MakeName(path);
|
||||
|
||||
self.filesToProcess.append(self.allocator, .{ .path = name.utf8() }) catch {};
|
||||
// self.updatePath(path) catch return;
|
||||
}
|
||||
|
||||
|
|
@ -85,21 +111,28 @@ pub fn updatePath(self: *@This(), path: []const u8) !void {
|
|||
const pathstr = try std.fmt.allocPrint(self.salloc(), "{s}.cook", .{path[0 .. path.len - 5]});
|
||||
defer self.salloc().free(pathstr);
|
||||
|
||||
core.engine_log("checking path: {s}", pathstr);
|
||||
core.engine_log("checking path: {s}", .{pathstr});
|
||||
|
||||
if (!core.fs().fileExists(pathstr)) {
|
||||
// create a cook file under the pathstr, the file watcher should see it..
|
||||
core.engine_log("creating cooker {s}", .{pathstr});
|
||||
const dir = try std.fs.cwd().openDir(p2.getDir(pathstr), .{ .iterate = true });
|
||||
try cook.generateAllCookFiles(self.allocator, dir);
|
||||
}
|
||||
|
||||
try self.animations.append(self.allocator, .{ .name = path });
|
||||
|
||||
//var name = core.MakeName(pathstr);
|
||||
// if there is a cook file
|
||||
// var mapping = try core.fs().loadFile(path);
|
||||
// zgltf.init(self.allocator);
|
||||
// core.fs().unmap(mapping);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tickDisplay(self: *@This()) void {
|
||||
self.browser.tickDisplay();
|
||||
}
|
||||
|
||||
pub fn tick(self: *@This()) void {
|
||||
const start = core.getEngineTime();
|
||||
|
||||
|
|
@ -109,11 +142,12 @@ pub fn tick(self: *@This()) void {
|
|||
break;
|
||||
}
|
||||
|
||||
self.updatePath(pop.path) catch {};
|
||||
self.updatePath(pop.path) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(self: *@This()) void {
|
||||
self.browser.destroy();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +155,10 @@ const std = @import("std");
|
|||
const api = @import("backlog");
|
||||
const imgui = api.imgui;
|
||||
const ig = api.imgui.api;
|
||||
const p2 = core.algorithm;
|
||||
const core = api.core;
|
||||
const sys = api.sys;
|
||||
const zgltf = core.zgltf;
|
||||
const assets = api.assets;
|
||||
const cook = assets.cook;
|
||||
const extras = @import("gameExtras");
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
zig build -Dstatic_build --watch --prominent-compile-errors -freference-trace -p binaries install
|
||||
zig build -Dstatic_build --watch --prominent-compile-errors -freference-trace -p binaries %* install
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
bl_info = {
|
||||
"name": "Hello World Addon",
|
||||
"author": "BacklogEngine",
|
||||
"version": (1, 0),
|
||||
"blender": (2, 80, 0),
|
||||
"location": "View3D > Sidebar > Hello World",
|
||||
"description": "Adds a button that prints Hello World",
|
||||
"category": "Development",
|
||||
}
|
||||
|
||||
import bpy
|
||||
|
||||
class HELLO_OT_print_hello_world(bpy.types.Operator):
|
||||
"""Print Hello World to console"""
|
||||
bl_idname = "hello.print_hello_world"
|
||||
bl_label = "Print Hello World"
|
||||
|
||||
def execute(self, context):
|
||||
print("Hello World")
|
||||
self.report({'INFO'}, "Hello World printed to console!")
|
||||
return {'FINISHED'}
|
||||
|
||||
class HELLO_PT_main_panel(bpy.types.Panel):
|
||||
"""Creates a Panel in the 3D Viewport N-Panel"""
|
||||
bl_label = "Hello World"
|
||||
bl_idname = "HELLO_PT_main_panel"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Hello World"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator("hello.print_hello_world")
|
||||
|
||||
classes = (
|
||||
HELLO_OT_print_hello_world,
|
||||
HELLO_PT_main_panel,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
Loading…
Reference in New Issue