multiple GLTF Models, EXT_MSFT_controller_model support
s-ol
1 year, 11 months ago
0 | 0 | usingnamespace @import("xrvk.zig"); |
1 | 1 | const std = @import("std"); |
2 | const glm = @import("glm"); | |
2 | 3 | const gfx = @import("graphics.zig"); |
3 | 4 | const scene = @import("scene.zig"); |
4 | 5 | const renderdoc = @import("renderdoc.zig"); |
11 | 12 | session.deinit(); |
12 | 13 | } |
13 | 14 | |
15 | const Paths = struct { | |
16 | user_hand_right: xr.Path, | |
17 | user_hand_left: xr.Path, | |
18 | user_hand_head: xr.Path, | |
19 | ||
20 | pub fn init(xri: xr.InstanceDispatch, inst: xr.Instance) !Paths { | |
21 | var self: Paths = undefined; | |
22 | ||
23 | inline for (@typeInfo(Paths).Struct.fields) |field| { | |
24 | var buffer: [field.name.len + 2:0]u8 = undefined; | |
25 | _ = std.mem.replace(u8, field.name, "_", "/", buffer[1..]); | |
26 | buffer[0] = '/'; | |
27 | buffer[buffer.len - 1] = 0; | |
28 | @field(self, field.name) = try xri.stringToPath(inst, buffer[0..]); | |
29 | } | |
30 | ||
31 | return self; | |
32 | } | |
33 | }; | |
34 | ||
14 | 35 | const Session = struct { |
15 | 36 | xrb: xr.BaseDispatch, |
16 | 37 | xri: xr.InstanceDispatch, |
23 | 44 | |
24 | 45 | state: xr.SessionState, |
25 | 46 | views: ?ViewInfo, |
47 | paths: Paths, | |
26 | 48 | graphics: gfx.Graphics, |
27 | 49 | layers: []*const xr.CompositionLayerBaseHeader, |
28 | 50 | scene_space: xr.Space, |
41 | 63 | const zero = [_:0]u8{}; |
42 | 64 | const extensions = [_][*:0]const u8{ |
43 | 65 | "XR_KHR_vulkan_enable2", |
66 | "XR_MSFT_controller_model", | |
44 | 67 | }; |
45 | 68 | |
46 | 69 | const xrb = try xr.BaseDispatch.load(xr.getProcAddr); |
61 | 84 | |
62 | 85 | const xri = try xr.InstanceDispatch.load(instance, xr.getProcAddr); |
63 | 86 | errdefer xri.destroyInstance(instance) catch unreachable; |
87 | ||
88 | const paths = try Paths.init(xri, instance); | |
64 | 89 | |
65 | 90 | const system = try xri.getSystem(instance, .{ .form_factor = .head_mounted_display }); |
66 | 91 | |
118 | 143 | |
119 | 144 | .state = .idle, |
120 | 145 | .views = null, |
146 | .paths = paths, | |
121 | 147 | .graphics = graphics, |
122 | 148 | .scene_space = scene_space, |
123 | 149 | .layers = &[_]*const xr.CompositionLayerBaseHeader{}, |
224 | 250 | try self.graphics.createSwapchain(self.session, views.configurations); |
225 | 251 | var gfx_scene = try scene.Scene.init(&self.graphics); |
226 | 252 | |
253 | var helmet = scene.Model.initFile(&gfx_scene, "assets/DamagedHelmet.gltf"); | |
254 | helmet.setTransform(glm.translation(glm.Vec3.init([_]f32{ 0, 1.5, -2 }))); | |
255 | try gfx_scene.models.append(helmet); | |
256 | ||
257 | for ([_]xr.Path{ self.paths.user_hand_left, self.paths.user_hand_right }) |path, i| { | |
258 | var model_state = xr.ControllerModelKeyStateMSFT.empty(); | |
259 | try self.xri.getControllerModelKeyMSFT(self.session, path, &model_state); | |
260 | ||
261 | if (model_state.model_key == 0) | |
262 | continue; | |
263 | ||
264 | const size = try self.xri.loadControllerModelMSFT(self.session, model_state.model_key, 0, null); | |
265 | const buffer = try self.allocator.alloc(u8, size); | |
266 | defer self.allocator.free(buffer); | |
267 | ||
268 | _ = try self.xri.loadControllerModelMSFT(self.session, model_state.model_key, size, buffer.ptr); | |
269 | var model = scene.Model.initMemory(&gfx_scene, buffer); | |
270 | ||
271 | var transform = glm.Mat4.IDENTITY; | |
272 | transform = transform.mul(glm.translation(glm.Vec3.init([_]f32{ 0.6 * (@intToFloat(f32, i) - 0.5), 1.5, -0.5 }))); | |
273 | transform = transform.mul(glm.rotation(0.9, glm.Vec3.init([_]f32{ 1, 0, 0 }))); | |
274 | model.setTransform(transform); | |
275 | try gfx_scene.models.append(model); | |
276 | } | |
277 | ||
227 | 278 | const composition_layer = try self.allocator.create(xr.CompositionLayerProjection); |
228 | 279 | composition_layer.* = .{ |
229 | 280 | .layer_flags = .{ .blend_texture_source_alpha_bit = true }, |
254 | 305 | _ = try self.xri.waitFrame(self.session, null, &frame_state); |
255 | 306 | _ = try self.xri.beginFrame(self.session, null); |
256 | 307 | self.graphics.unlockQueue(); |
308 | ||
309 | const predicted_seconds = @intToFloat(f32, frame_state.predicted_display_time) / 1_000_000_000; | |
310 | ||
311 | var transform = glm.translation(glm.Vec3.init([_]f32{ 0, 1.5, -2 })); | |
312 | transform = transform.mul(glm.rotation(predicted_seconds, glm.Vec3.init([_]f32{ 0, 1, 0 }))); | |
313 | gfx_scene.models.items[0].setTransform(transform); | |
257 | 314 | |
258 | 315 | var layer_count: u32 = 0; |
259 | 316 |
168 | 168 | .MaxLOD = 3.402823466e+38, |
169 | 169 | }; |
170 | 170 | |
171 | pub const Model = struct { | |
172 | model: dg.IGLTFModel_Wrapper, | |
173 | bindings: dg.GLTF_ModelResourceBindings, | |
174 | params: dg.GLTF_RenderInfo, | |
175 | ||
176 | const DEFAULT_PARAMS = dg.GLTF_RenderInfo{ | |
177 | .ModelTransform = @bitCast([16]f32, glm.Mat4.IDENTITY), | |
178 | .AlphaModes = dg.ALPHA_MODE_FLAG_ALL, | |
179 | .DebugView = 0, | |
180 | .OcclusionStrength = 1, | |
181 | .EmissionScale = 1, | |
182 | .IBLScale = 1, | |
183 | .AverageLogLum = 0.3, | |
184 | .MiddleGray = 0.18, | |
185 | .WhitePoint = 3, | |
186 | }; | |
187 | ||
188 | pub fn initMemory(scene: *const Scene, data: []const u8) Model { | |
189 | const model = dg.Diligent_IGLTFModel_Create( | |
190 | @ptrCast(*dg.IRenderDevice, scene.dev.ptr), | |
191 | scene.ctx.ptr, | |
192 | &dg.IGLTFModelCreateInfo{ | |
193 | .FileName = null, | |
194 | .Data = data.ptr, | |
195 | .DataSize = data.len, | |
196 | .FileType = ._BINARY, | |
197 | .pTextureCache = null, | |
198 | .pCacheInfo = null, | |
199 | .LoadAnimationAndSkin = true, | |
200 | }, | |
201 | ); | |
202 | ||
203 | return .{ | |
204 | .model = dg.IGLTFModel_Wrapper.wrap(model), | |
205 | .bindings = scene.renderer.CreateResourceBindings( | |
206 | model, | |
207 | scene.camera_ubo.ptr, | |
208 | scene.light_ubo.ptr, | |
209 | ), | |
210 | .params = DEFAULT_PARAMS, | |
211 | }; | |
212 | } | |
213 | ||
214 | pub fn initFile(scene: *const Scene, path: [:0]const u8) Model { | |
215 | const model = dg.Diligent_IGLTFModel_Create( | |
216 | @ptrCast(*dg.IRenderDevice, scene.dev.ptr), | |
217 | scene.ctx.ptr, | |
218 | &dg.IGLTFModelCreateInfo{ | |
219 | .FileName = path.ptr, | |
220 | .Data = null, | |
221 | .DataSize = 0, | |
222 | .FileType = ._UNKNOWN, | |
223 | .pTextureCache = null, | |
224 | .pCacheInfo = null, | |
225 | .LoadAnimationAndSkin = true, | |
226 | }, | |
227 | ); | |
228 | ||
229 | return .{ | |
230 | .model = dg.IGLTFModel_Wrapper.wrap(model), | |
231 | .bindings = scene.renderer.CreateResourceBindings( | |
232 | model, | |
233 | scene.camera_ubo.ptr, | |
234 | scene.light_ubo.ptr, | |
235 | ), | |
236 | .params = DEFAULT_PARAMS, | |
237 | }; | |
238 | } | |
239 | ||
240 | pub fn deinit(self: *Model) void { | |
241 | self.bindings.Release(); | |
242 | self.model.Release(); | |
243 | } | |
244 | ||
245 | pub fn setTransform(self: *Model, mat: glm.Mat4) void { | |
246 | self.params.ModelTransform = @bitCast([16]f32, mat); | |
247 | } | |
248 | ||
249 | pub fn draw(self: *Model, scene: *Scene) void { | |
250 | scene.renderer.Render( | |
251 | scene.ctx.ptr, | |
252 | self.model.ptr, | |
253 | &self.params, | |
254 | &self.bindings, | |
255 | null, | |
256 | scene.resource_binding.ptr, | |
257 | ); | |
258 | } | |
259 | }; | |
260 | ||
171 | 261 | pub const Scene = struct { |
172 | 262 | viewports: []dg.Viewport, |
173 | 263 | scissors: []dg.Rect, |
179 | 269 | views: []ViewUBO, |
180 | 270 | |
181 | 271 | renderer: dg.IGLTF_PBR_Renderer_Wrapper, |
182 | render_params: dg.GLTF_RenderInfo, | |
183 | model: dg.IGLTFModel_Wrapper, | |
184 | model_bindings: dg.GLTF_ModelResourceBindings, | |
272 | models: std.ArrayList(Model), | |
185 | 273 | envmap: ?*dg.ITextureView, |
186 | 274 | |
187 | 275 | ctx: dg.IDeviceContext_Wrapper, |
376 | 464 | |
377 | 465 | self.renderer.PrecomputeCubemaps(@ptrCast(*dg.IRenderDevice, self.dev.ptr), self.ctx.ptr, self.envmap); |
378 | 466 | |
379 | const model = dg.Diligent_IGLTFModel_Create( | |
380 | @ptrCast(*dg.IRenderDevice, self.dev.ptr), | |
381 | self.ctx.ptr, | |
382 | &dg.IGLTFModelCreateInfo{ | |
383 | .FileName = "assets/DamagedHelmet.gltf", | |
384 | .pTextureCache = null, | |
385 | .pCacheInfo = null, | |
386 | .LoadAnimationAndSkin = true, | |
387 | }, | |
388 | ); | |
389 | self.model = dg.IGLTFModel_Wrapper.wrap(model); | |
390 | ||
391 | self.model_bindings = self.renderer.CreateResourceBindings( | |
392 | self.model.ptr, | |
393 | self.camera_ubo.ptr, | |
394 | self.light_ubo.ptr, | |
395 | ); | |
396 | ||
397 | self.render_params = .{ | |
398 | .ModelTransform = @bitCast([16]f32, glm.translation(glm.Vec3.init([_]f32{ 0, 1.5, -2 }))), | |
399 | .AlphaModes = dg.ALPHA_MODE_FLAG_ALL, | |
400 | .DebugView = 0, | |
401 | .OcclusionStrength = 1, | |
402 | .EmissionScale = 1, | |
403 | .IBLScale = 1, | |
404 | .AverageLogLum = 0.3, | |
405 | .MiddleGray = 0.18, | |
406 | .WhitePoint = 3, | |
407 | }; | |
467 | // model | |
468 | self.models = std.ArrayList(Model).init(gfx.allocator); | |
408 | 469 | |
409 | 470 | return self; |
410 | 471 | } |
411 | 472 | |
412 | 473 | pub fn deinit(self: *const Scene) void { |
413 | self.resource_binding.Release(); | |
414 | 474 | self.views_ubo.Release(); |
415 | 475 | self.light_ubo.Release(); |
416 | 476 | self.camera_ubo.Release(); |
417 | 477 | |
418 | self.model.Release(); | |
478 | for (self.models.items) |*model| { | |
479 | model.deinit(); | |
480 | } | |
481 | self.models.deinit(); | |
419 | 482 | self.renderer.Release(); |
420 | ||
421 | gfx.allocator.free(self.viewports); | |
422 | gfx.allocator.free(self.scissors); | |
423 | gfx.allocator.free(self.views); | |
483 | self.resource_binding.Release(); | |
484 | ||
485 | self.gfx.allocator.free(self.viewports); | |
486 | self.gfx.allocator.free(self.scissors); | |
487 | self.gfx.allocator.free(self.views); | |
424 | 488 | } |
425 | 489 | |
426 | 490 | pub fn render(self: *Scene, views: []const xr.View) !void { |
482 | 546 | |
483 | 547 | // record renderpass |
484 | 548 | self.renderer.Begin(self.ctx.ptr); |
485 | self.renderer.Render(self.ctx.ptr, self.model.ptr, &self.render_params, &self.model_bindings, null, self.resource_binding.ptr); | |
549 | for (self.models.items) |*model| { | |
550 | model.draw(self); | |
551 | } | |
552 | ||
553 | // self.renderer.Render(self.ctx.ptr, self.model.ptr, &self.render_params, &self.model_bindings, null, self.resource_binding.ptr); | |
486 | 554 | } |
487 | 555 | |
488 | 556 | fn createShader( |
51 | 51 | xrDestroyInstance: PfnDestroyInstance, |
52 | 52 | xrGetSystem: PfnGetSystem, |
53 | 53 | xrGetSystemProperties: PfnGetSystemProperties, |
54 | xrStringToPath: PfnStringToPath, | |
54 | 55 | xrCreateSession: PfnCreateSession, |
55 | 56 | xrDestroySession: PfnDestroySession, |
56 | 57 | xrBeginSession: PfnBeginSession, |
72 | 73 | xrAcquireSwapchainImage: PfnAcquireSwapchainImage, |
73 | 74 | xrWaitSwapchainImage: PfnWaitSwapchainImage, |
74 | 75 | xrReleaseSwapchainImage: PfnReleaseSwapchainImage, |
76 | xrGetControllerModelKeyMSFT: PfnGetControllerModelKeyMSFT, | |
77 | xrLoadControllerModelMSFT: PfnLoadControllerModelMSFT, | |
75 | 78 | usingnamespace InstanceWrapper(@This()); |
76 | 79 | }; |
77 | 80 |