UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
renderable_trianglelist.hpp
1
2#ifndef UFO_VIZ_RENDERABLE_TRIANGLELIST_HPP
3#define UFO_VIZ_RENDERABLE_TRIANGLELIST_HPP
4
5// UFO
6#include <ufo/compute/compute.hpp>
7#include <ufo/vision/color.hpp>
8#include <ufo/viz/renderable.hpp>
9#include <ufo/viz/renderable/triangulate.hpp>
10
11namespace ufo
12{
14{
15 public:
16 RenderableTrianglelist(ufo::Color color) : color_(color)
17 {
18 // TODO or should they all come with and load their own shader?
19 }
20
21 ~RenderableTrianglelist() { release(); }
22
23 void init(WGPUDevice device)
24 {
25 initBuffers(device);
26 initBindGroups(device);
27 initPipeline(device);
28 }
29
30 void release()
31 {
32 // TODO: Implement
33 }
34
35 template <typename Camera>
36 void update(WGPUDevice device, WGPUCommandEncoder encoder,
37 WGPUTextureView render_texture, WGPUTextureView depth_texture,
38 Camera const& camera)
39 {
40 // Camera
41 uniform_.projection = camera.projection();
42 uniform_.view = ufo::Mat4x4f(camera.pose);
43 uniform_.color = color_;
44 wgpuQueueWriteBuffer(compute::queue(device), uniform_buffer_, 0, &uniform_,
45 sizeof(uniform_));
46
47 // Render
48 auto render_pass_desc = renderPassDesc(render_texture, depth_texture);
49 WGPURenderPassEncoder render_pass =
50 wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc);
51
52 // Select which render pipeline to use
53 wgpuRenderPassEncoderSetPipeline(render_pass, pipeline_);
54
55 // Set binding group here!
56 wgpuRenderPassEncoderSetBindGroup(render_pass, 0, bind_group_, 0, nullptr);
57
58 // Bind the vertex buffer
59 wgpuRenderPassEncoderSetVertexBuffer(render_pass, 0, vertex_buffer_, 0,
60 WGPU_WHOLE_SIZE);
61
62 // Bind the index buffer
63 wgpuRenderPassEncoderSetIndexBuffer(render_pass, index_buffer_,
64 WGPUIndexFormat_Uint32, 0, WGPU_WHOLE_SIZE);
65
66 wgpuRenderPassEncoderDrawIndexed(render_pass, triangles_.indices.size(), 1, 0, 0, 0);
67
68 wgpuRenderPassEncoderEnd(render_pass);
69 wgpuRenderPassEncoderRelease(render_pass);
70
71 // TODO Not sure if this should be here or outside?
72 // Encode and submit the render pass
73 // WGPUCommandBufferDescriptor cmdBufferDescriptor = {};
74 // cmdBufferDescriptor.nextInChain = nullptr;
75 // cmdBufferDescriptor.label = "Command buffer";
76 // WGPUCommandBuffer command = wgpuCommandEncoderFinish(encoder,
77 // &cmdBufferDescriptor); wgpuCommandEncoderRelease(encoder);
78
79 // // std::cout << "Submitting command..." << std::endl;
80 // wgpuQueueSubmit(Compute::queue(), 1, &command);
81 // wgpuCommandBufferRelease(command);
82 // // std::cout << "Command submitted." << std::endl;
83
84 // wgpuTextureViewRelease(render_texture);
85
86 // #ifndef __EMSCRIPTEN__
87 // wgpuSurfacePresent(Compute::surface());
88 // #endif
89
90 // #if defined(WEBGPU_BACKEND_DAWN)
91 // wgpuDeviceTick(device);
92 // #elif defined(WEBGPU_BACKEND_WGPU)
93 // wgpuDevicePoll(device, false, nullptr);
94 // #endif
95 }
96
97 protected:
98 virtual WGPUBindGroupLayout bindGroupLayout(WGPUDevice device)
99 {
100 std::array<WGPUBindGroupLayoutEntry, 1> binding_layout{};
101
102 // Uniform
103 compute::setDefault(binding_layout[0]);
104 binding_layout[0].binding = 0;
105 binding_layout[0].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
106 binding_layout[0].buffer.type = WGPUBufferBindingType_Uniform;
107 binding_layout[0].buffer.minBindingSize = sizeof(uniform_);
108
109 // Create a bind group layout
110 bind_group_layout_desc = {};
111 bind_group_layout_desc.label = "Compute Bind Group Layout";
112 bind_group_layout_desc.nextInChain = nullptr;
113 bind_group_layout_desc.entryCount = binding_layout.size();
114 bind_group_layout_desc.entries = binding_layout.data();
115
116 return wgpuDeviceCreateBindGroupLayout(device, &bind_group_layout_desc);
117 }
118
119 virtual void initBindGroups(WGPUDevice device)
120 {
121 std::array<WGPUBindGroupEntry, 1> binding{};
122
123 // Uniforms
124 binding[0].nextInChain = nullptr;
125 binding[0].binding = 0;
126 binding[0].buffer = uniform_buffer_;
127 binding[0].offset = 0;
128 binding[0].size = sizeof(uniform_);
129
130 // A bind group contains one or multiple bindings
131 bind_group_desc = {};
132 bind_group_desc.nextInChain = nullptr;
133 bind_group_layout_ = bindGroupLayout(device);
134 bind_group_desc.layout = bind_group_layout_;
135 // There must be as many bindings as declared in the layout!
136 bind_group_desc.entryCount = binding.size();
137 bind_group_desc.entries = binding.data();
138
139 bind_group_ = wgpuDeviceCreateBindGroup(device, &bind_group_desc);
140 }
141
142 virtual WGPURenderPassColorAttachment renderPassColorAttachment(
143 WGPUTextureView render_texture)
144 {
145 WGPURenderPassColorAttachment color_attachment = {};
146
147 color_attachment.view = render_texture;
148 color_attachment.resolveTarget = nullptr;
149 color_attachment.loadOp = WGPULoadOp_Load;
150 color_attachment.storeOp = WGPUStoreOp_Store;
151 color_attachment.clearValue = WGPUColor{0.5, 0.5, 0.5, 1.0};
152
153 return color_attachment;
154 }
155
156 virtual WGPURenderPassDepthStencilAttachment depthAttachment(
157 WGPUTextureView depth_texture)
158 {
159 WGPURenderPassDepthStencilAttachment depth_attachment = {};
160
161 depth_attachment.view = depth_texture;
162 depth_attachment.depthClearValue = 1.0;
163 depth_attachment.depthLoadOp = WGPULoadOp_Load;
164 depth_attachment.depthStoreOp = WGPUStoreOp_Store;
165
166 depth_attachment.depthReadOnly = false;
167 // Stencil setup, mandatory but unused
168 depth_attachment.stencilClearValue = 0.0;
169 depth_attachment.stencilLoadOp = WGPULoadOp_Clear;
170 depth_attachment.stencilStoreOp = WGPUStoreOp_Store;
171 depth_attachment.stencilReadOnly = true;
172
173 return depth_attachment;
174 }
175
176 virtual WGPURenderPassDescriptor renderPassDesc(WGPUTextureView render_texture,
177 WGPUTextureView depth_texture)
178 {
179 WGPURenderPassDescriptor render_pass_desc = {};
180
181 render_pass_desc.nextInChain = nullptr;
182 render_pass_desc.depthStencilAttachment = nullptr;
183 render_pass_desc.colorAttachmentCount = 1;
184 color_attachment = renderPassColorAttachment(render_texture);
185 render_pass_desc.colorAttachments = &color_attachment;
186 render_pass_desc.timestampWrites = nullptr;
187 depth_attachment = depthAttachment(depth_texture);
188 render_pass_desc.depthStencilAttachment = &depth_attachment;
189
190 return render_pass_desc;
191 }
192
193 virtual WGPUPipelineLayout pipelineLayout(WGPUDevice device)
194 {
195 pipeline_layout_desc = {};
196
197 pipeline_layout_desc.nextInChain = nullptr;
198 pipeline_layout_desc.bindGroupLayoutCount = 1;
199 pipeline_layout_desc.bindGroupLayouts = &bind_group_layout_;
200
201 return wgpuDeviceCreatePipelineLayout(device, &pipeline_layout_desc);
202 }
203
204 virtual WGPURenderPipelineDescriptor pipelineDesc(WGPUDevice device)
205 {
206 WGPURenderPipelineDescriptor pipeline_desc;
207 pipeline_desc.layout = pipelineLayout(device);
208 // The face orientation is defined by assuming that when looking
209 // from the front of the face, its corner vertices are enumerated
210 // in the counter-clockwise (CCW) order.
211 pipeline_desc.primitive.frontFace = WGPUFrontFace_CCW;
212
213 // But the face orientation does not matter much because we do not
214 // cull (i.e. "hide") the faces pointing away from us (which is often
215 // used for optimization).
216 pipeline_desc.primitive.cullMode = WGPUCullMode_None;
217 // Samples per pixel
218 pipeline_desc.multisample.count = 1;
219
220 // Default value for the mask, meaning "all bits on"
221 pipeline_desc.multisample.mask = ~0u;
222
223 // Default value as well (irrelevant for count = 1 anyways)
224 pipeline_desc.multisample.alphaToCoverageEnabled = false;
225
226 // shader
227 depth_stencil_state = {};
228 depth_stencil_state.format = WGPUTextureFormat_Depth32Float;
229 depth_stencil_state.depthWriteEnabled = true;
230 depth_stencil_state.depthCompare = WGPUCompareFunction_Less;
231 depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
232 depth_stencil_state.stencilBack.compare = WGPUCompareFunction_Always;
233 depth_stencil_state.stencilReadMask = 0xFFFFFFFF;
234 depth_stencil_state.stencilWriteMask = 0xFFFFFFFF;
235
236 pipeline_desc.depthStencil = &depth_stencil_state;
237
238 // Set the vertex state
239 WGPUVertexState vertexState = {};
240 vertexState.module = shader_module_;
241 vertexState.entryPoint = "vertMain";
242 vertexState.bufferCount = 1;
243 vertexState.buffers = &vertex_buffer_layout; // Attach the vertex buffer layout
244
245 pipeline_desc.vertex = vertexState;
246
247 // Set the fragment state
248 color_target = {};
249 color_target.format =
250 WGPUTextureFormat_BGRA8UnormSrgb; // Match the swap chain format
251 color_target.writeMask = WGPUColorWriteMask_All;
252
253 fragment_state = {};
254 fragment_state.module = shader_module_;
255 fragment_state.entryPoint = "fragMain";
256 fragment_state.constantCount = 0;
257 fragment_state.constants = nullptr;
258 fragment_state.targetCount = 1;
259 fragment_state.targets = &color_target;
260
261 pipeline_desc.fragment = &fragment_state;
262 pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
263 pipeline_desc.multisample.count = 1;
264 pipeline_desc.multisample.mask = ~0u;
265 pipeline_desc.multisample.alphaToCoverageEnabled = false;
266
267 return pipeline_desc;
268 }
269
270 virtual void initPipeline(WGPUDevice device)
271 {
272 // Needs:
273 // - pipelineLayout
274 // - bind_group_layout_ (uniform)
275 pipeline_desc = pipelineDesc(device);
276 pipeline_ = wgpuDeviceCreateRenderPipeline(device, &pipeline_desc);
277 }
278
279 virtual void initBuffers(WGPUDevice device)
280 {
281 initVertexBuffer(device);
282 initIndexBuffer(device);
283 initUniformBuffer(device);
284 }
285
286 virtual void initVertexBuffer(WGPUDevice device)
287 {
288 vertex_buffer_desc = {};
289 vertex_buffer_desc.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst;
290 vertex_buffer_desc.size = triangles_.vertices.size() * sizeof(Vertex);
291 vertex_buffer_desc.mappedAtCreation = true;
292
293 vertex_buffer_ = wgpuDeviceCreateBuffer(device, &vertex_buffer_desc);
294
295 // Copy the vertex data into the buffer
296 void* mapped_data =
297 wgpuBufferGetMappedRange(vertex_buffer_, 0, vertex_buffer_desc.size);
298 memcpy(mapped_data, triangles_.vertices.data(), vertex_buffer_desc.size);
299 wgpuBufferUnmap(vertex_buffer_);
300
301 // Layout
302 // Position attribute
303 WGPUVertexAttribute position_attr = {};
304 position_attr.shaderLocation = 0; // Matches @location(0) in the vertex shader
305 position_attr.format = WGPUVertexFormat_Float32x3; // vec3<f32> in WGSL
306 position_attr.offset = offsetof(Vertex, pos);
307
308 // Normal attribute
309 WGPUVertexAttribute normal_attr = {};
310 normal_attr.shaderLocation = 1; // Matches @location(1) in the vertex shader
311 normal_attr.format = WGPUVertexFormat_Float32x3; // vec3<f32> in WGSL
312 normal_attr.offset = offsetof(Vertex, normal);
313
314 vertex_buffer_layout = {};
315 vertex_buffer_layout.arrayStride = sizeof(Vertex);
316 vertex_buffer_layout.attributeCount = 2;
317 vertex_attributes = {position_attr, normal_attr};
318 vertex_buffer_layout.attributes = vertex_attributes.data();
319 }
320
321 virtual void initIndexBuffer(WGPUDevice device)
322 {
323 index_buffer_desc = {};
324 index_buffer_desc.usage = WGPUBufferUsage_Index | WGPUBufferUsage_CopyDst;
325 index_buffer_desc.size = triangles_.indices.size() * sizeof(uint32_t);
326 index_buffer_desc.mappedAtCreation = true;
327
328 index_buffer_ = wgpuDeviceCreateBuffer(device, &index_buffer_desc);
329
330 // Copy the index data into the buffer
331 void* mapped_data =
332 wgpuBufferGetMappedRange(index_buffer_, 0, index_buffer_desc.size);
333 memcpy(mapped_data, triangles_.indices.data(), index_buffer_desc.size);
334 wgpuBufferUnmap(index_buffer_);
335 }
336
337 virtual void initUniformBuffer(WGPUDevice device)
338 {
339 uniform_buffer_desc = {};
340 uniform_buffer_desc.mappedAtCreation = false;
341 uniform_buffer_desc.size = sizeof(uniform_);
342 uniform_buffer_desc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
343 uniform_buffer_desc.mappedAtCreation = false;
344 uniform_buffer_ = wgpuDeviceCreateBuffer(device, &uniform_buffer_desc);
345 }
346
347 protected:
348 struct alignas(16) UBO {
349 ufo::Mat4x4f projection;
350 ufo::Mat4x4f view;
351 ufo::Color color;
352 } uniform_;
353 // Have the compiler check byte alignment
354 static_assert(sizeof(UBO) % 16 == 0);
355
356 ufo::Color color_;
357
358 WGPURenderPipeline pipeline_ = nullptr;
359 WGPURenderPipelineDescriptor pipeline_desc = {};
360
361 // TODO release the buffers somewhere
362 WGPUBuffer vertex_buffer_ = nullptr;
363 WGPUBufferDescriptor vertex_buffer_desc = {};
364 WGPUBuffer index_buffer_ = nullptr;
365 WGPUBufferDescriptor index_buffer_desc{};
366 WGPUBuffer uniform_buffer_ = nullptr;
367 WGPUBufferDescriptor uniform_buffer_desc{};
368
369 WGPUBindGroupLayout bind_group_layout_ = nullptr;
370 WGPUBindGroup bind_group_ = nullptr;
371
372 // TODO deal with these
373 WGPURenderPassColorAttachment color_attachment;
374 WGPURenderPassDepthStencilAttachment depth_attachment;
375 WGPUDepthStencilState depth_stencil_state;
376 WGPUVertexBufferLayout vertex_buffer_layout;
377 WGPUFragmentState fragment_state{};
378 std::vector<WGPUVertexAttribute> vertex_attributes;
379 WGPUColorTargetState color_target;
380 WGPUBindGroupLayoutDescriptor bind_group_layout_desc{};
381 WGPUBindGroupDescriptor bind_group_desc{};
382 WGPUPipelineLayoutDescriptor pipeline_layout_desc = {};
383
384 public:
385 WGPUShaderModule shader_module_ = nullptr;
386 TriangleList<Vertex> triangles_;
387};
388
389} // namespace ufo
390
391#endif // UFO_VIZ_RENDERABLE_PATH_HPP
Identifies any UFO color instantiation (Gray, Lab, Lch, Lrgb, Rgb).
Definition concepts.hpp:73
All vision-related classes and functions.
Definition cloud.hpp:49