v2 / examples / sokol / 03_march_tracing_glsl / rt_glsl.v
424 lines · 376 sloc · 11.35 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1// vtest build: misc-tooling // needs .h files that are produced by `v shader`
2/**********************************************************************
3*
4* Sokol 3d cube demo
5*
6* Copyright (c) 2021 Dario Deledda. All rights reserved.
7* Use of this source code is governed by an MIT license
8* that can be found in the LICENSE file.
9*
10* HOW TO COMPILE SHADERS:
11* Run `v shader .` in this directory to compile the shaders.
12* For more info and help with shader compilation see `docs.md` and `v help shader`.
13*
14* TODO:
15* - frame counter
16**********************************************************************/
17import gg
18import gg.m4
19// import math
20import sokol.sapp
21import sokol.gfx
22import sokol.sgl
23import time
24
25// GLSL Include and functions
26
27#include "@VMODROOT/rt_glsl.h" # It should be generated with `v shader .` (see the instructions at the top of this file)
28
29fn C.rt_shader_desc(gfx.Backend) &gfx.ShaderDesc
30
31const win_width = 800
32const win_height = 800
33const bg_color = gg.white
34
35struct App {
36mut:
37 gg &gg.Context = unsafe { nil }
38 texture gfx.Image
39 sampler gfx.Sampler
40 init_flag bool
41 frame_count int
42
43 mouse_x int = -1
44 mouse_y int = -1
45 // glsl
46 cube_pip_glsl gfx.Pipeline
47 cube_bind gfx.Bindings
48 // time
49 ticks i64
50}
51
52/******************************************************************************
53* Texture functions
54******************************************************************************/
55fn create_texture(w int, h int, buf &u8) (gfx.Image, gfx.Sampler) {
56 sz := w * h * 4
57 mut img_desc := gfx.ImageDesc{
58 width: w
59 height: h
60 num_mipmaps: 0
61 // min_filter: .linear
62 // mag_filter: .linear
63 // usage: .dynamic
64 // wrap_u: .clamp_to_edge
65 // wrap_v: .clamp_to_edge
66 label: &char(unsafe { nil })
67 d3d11_texture: 0
68 }
69 // comment if .dynamic is enabled
70 img_desc.data.subimage[0][0] = gfx.Range{
71 ptr: buf
72 size: usize(sz)
73 }
74
75 sg_img := gfx.make_image(&img_desc)
76
77 mut smp_desc := gfx.SamplerDesc{
78 min_filter: .linear
79 mag_filter: .linear
80 wrap_u: .clamp_to_edge
81 wrap_v: .clamp_to_edge
82 }
83
84 sg_smp := gfx.make_sampler(&smp_desc)
85 return sg_img, sg_smp
86}
87
88fn destroy_texture(sg_img gfx.Image) {
89 gfx.destroy_image(sg_img)
90}
91
92// Use only if usage: .dynamic is enabled
93fn update_text_texture(sg_img gfx.Image, w int, h int, buf &u8) {
94 sz := w * h * 4
95 mut tmp_sbc := gfx.ImageData{}
96 tmp_sbc.subimage[0][0] = gfx.Range{
97 ptr: buf
98 size: usize(sz)
99 }
100 gfx.update_image(sg_img, &tmp_sbc)
101}
102
103/******************************************************************************
104* Draw functions
105******************************************************************************
106Cube vertex buffer with packed vertex formats for color and texture coords.
107 Note that a vertex format which must be portable across all
108 backends must only use the normalized integer formats
109 (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted
110 to floating point formats in the vertex shader inputs.
111 The reason is that D3D11 cannot convert from non-normalized
112 formats to floating point inputs (only to integer inputs),
113 and WebGL2 / GLES2 don't support integer vertex shader inputs.
114*/
115
116struct Vertex_t {
117 x f32
118 y f32
119 z f32
120 color u32
121 u f32
122 v f32
123 // u u16 // for compatibility with D3D11
124 // v u16 // for compatibility with D3D11
125}
126
127fn init_cube_glsl(mut app App) {
128 // cube vertex buffer
129 // d := u16(32767) // for compatibility with D3D11, 32767 stand for 1
130 d := f32(1.0)
131 c := u32(0xFFFFFF_FF) // color RGBA8
132 // vfmt off
133 vertices := [
134 // Face 0
135 Vertex_t{-1.0, -1.0, -1.0, c, 0, 0},
136 Vertex_t{ 1.0, -1.0, -1.0, c, d, 0},
137 Vertex_t{ 1.0, 1.0, -1.0, c, d, d},
138 Vertex_t{-1.0, 1.0, -1.0, c, 0, d},
139 // Face 1
140 Vertex_t{-1.0, -1.0, 1.0, c, 0, 0},
141 Vertex_t{ 1.0, -1.0, 1.0, c, d, 0},
142 Vertex_t{ 1.0, 1.0, 1.0, c, d, d},
143 Vertex_t{-1.0, 1.0, 1.0, c, 0, d},
144 // Face 2
145 Vertex_t{-1.0, -1.0, -1.0, c, 0, 0},
146 Vertex_t{-1.0, 1.0, -1.0, c, d, 0},
147 Vertex_t{-1.0, 1.0, 1.0, c, d, d},
148 Vertex_t{-1.0, -1.0, 1.0, c, 0, d},
149 // Face 3
150 Vertex_t{ 1.0, -1.0, -1.0, c, 0, 0},
151 Vertex_t{ 1.0, 1.0, -1.0, c, d, 0},
152 Vertex_t{ 1.0, 1.0, 1.0, c, d, d},
153 Vertex_t{ 1.0, -1.0, 1.0, c, 0, d},
154 // Face 4
155 Vertex_t{-1.0, -1.0, -1.0, c, 0, 0},
156 Vertex_t{-1.0, -1.0, 1.0, c, d, 0},
157 Vertex_t{ 1.0, -1.0, 1.0, c, d, d},
158 Vertex_t{ 1.0, -1.0, -1.0, c, 0, d},
159 // Face 5
160 Vertex_t{-1.0, 1.0, -1.0, c, 0, 0},
161 Vertex_t{-1.0, 1.0, 1.0, c, d, 0},
162 Vertex_t{ 1.0, 1.0, 1.0, c, d, d},
163 Vertex_t{ 1.0, 1.0, -1.0, c, 0, d},
164 ]
165 // vfmt on
166
167 mut vert_buffer_desc := gfx.BufferDesc{
168 label: c'cube-vertices'
169 }
170 unsafe { vmemset(&vert_buffer_desc, 0, int(sizeof(vert_buffer_desc))) }
171
172 vert_buffer_desc.size = usize(vertices.len * int(sizeof(Vertex_t)))
173 vert_buffer_desc.data = gfx.Range{
174 ptr: vertices.data
175 size: usize(vertices.len * int(sizeof(Vertex_t)))
176 }
177
178 vert_buffer_desc.type = .vertexbuffer
179 vbuf := gfx.make_buffer(&vert_buffer_desc)
180
181 // create an index buffer for the cube
182 // vfmt off
183 indices := [
184 u16(0), 1, 2, 0, 2, 3,
185 6, 5, 4, 7, 6, 4,
186 8, 9, 10, 8, 10, 11,
187 14, 13, 12, 15, 14, 12,
188 16, 17, 18, 16, 18, 19,
189 22, 21, 20, 23, 22, 20,
190 ]
191 // vfmt on
192
193 mut index_buffer_desc := gfx.BufferDesc{
194 label: c'cube-indices'
195 }
196 unsafe { vmemset(&index_buffer_desc, 0, int(sizeof(index_buffer_desc))) }
197
198 index_buffer_desc.size = usize(indices.len * int(sizeof(u16)))
199 index_buffer_desc.data = gfx.Range{
200 ptr: indices.data
201 size: usize(indices.len * int(sizeof(u16)))
202 }
203
204 index_buffer_desc.type = .indexbuffer
205 ibuf := gfx.make_buffer(&index_buffer_desc)
206
207 // create shader
208 shader := gfx.make_shader(voidptr(C.rt_shader_desc(C.sg_query_backend())))
209
210 mut pipdesc := gfx.PipelineDesc{}
211 unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) }
212 pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_t))
213
214 // the constants [C.ATTR_vs_pos, C.ATTR_vs_color0, C.ATTR_vs_texcoord0] are generated by sokol-shdc
215 pipdesc.layout.attrs[C.ATTR_vs_pos].format = .float3 // x,y,z as f32
216 pipdesc.layout.attrs[C.ATTR_vs_color0].format = .ubyte4n // color as u32
217 pipdesc.layout.attrs[C.ATTR_vs_texcoord0].format = .float2 // u,v as f32
218 // pipdesc.layout.attrs[C.ATTR_vs_texcoord0].format = .short2n // u,v as u16
219
220 pipdesc.shader = shader
221 pipdesc.index_type = .uint16
222
223 pipdesc.depth = gfx.DepthState{
224 write_enabled: true
225 compare: .less_equal
226 }
227 pipdesc.cull_mode = .back
228
229 pipdesc.label = c'glsl_shader pipeline'
230
231 app.cube_bind.vertex_buffers[0] = vbuf
232 app.cube_bind.index_buffer = ibuf
233 app.cube_bind.fs.images[C.SLOT_tex] = app.texture
234 app.cube_bind.fs.samplers[C.SLOT_smp] = app.sampler
235 app.cube_pip_glsl = gfx.make_pipeline(&pipdesc)
236 println('GLSL init DONE!')
237}
238
239@[inline]
240fn vec4(x f32, y f32, z f32, w f32) m4.Vec4 {
241 return m4.Vec4{
242 e: [x, y, z, w]!
243 }
244}
245
246fn calc_tr_matrices(w f32, h f32, rx f32, ry f32, in_scale f32) m4.Mat4 {
247 proj := m4.perspective(60, w / h, 0.01, 10.0)
248 view := m4.look_at(vec4(f32(0.0), 0, 6, 0), vec4(f32(0), 0, 0, 0), vec4(f32(0), 1, 0, 0))
249 view_proj := view * proj
250
251 rxm := m4.rotate(m4.rad(rx), vec4(f32(1), 0, 0, 0))
252 rym := m4.rotate(m4.rad(ry), vec4(f32(0), 1, 0, 0))
253
254 model := rym * rxm
255 scale_m := m4.scale(vec4(in_scale, in_scale, in_scale, 1))
256
257 res := (scale_m * model) * view_proj
258 return res
259}
260
261fn draw_cube_glsl(app App) {
262 if app.init_flag == false {
263 return
264 }
265
266 ws := gg.window_size_real_pixels()
267 ratio := f32(ws.width) / ws.height
268 dw := f32(ws.width / 2)
269 dh := f32(ws.height / 2)
270
271 // use the following commented lines to rotate the 3d glsl cube
272 // rot := [f32(app.mouse_y), f32(app.mouse_x)]
273 // calc_tr_matrices(dw, dh, rot[0], rot[1] ,2.3)
274 tr_matrix := calc_tr_matrices(dw, dh, 0, 0, 2.3)
275 gfx.apply_viewport(0, 0, ws.width, ws.height, true)
276
277 // apply the pipeline and bindings
278 gfx.apply_pipeline(app.cube_pip_glsl)
279 gfx.apply_bindings(app.cube_bind)
280
281 // Uniforms
282 // *** vertex shader uniforms ***
283 // passing the view matrix as uniform
284 // res is a 4x4 matrix of f32 thus: 4*16 byte of size
285 vs_uniforms_range := gfx.Range{
286 ptr: &tr_matrix
287 size: usize(4 * 16)
288 }
289 gfx.apply_uniforms(.vs, C.SLOT_vs_params, &vs_uniforms_range)
290
291 // *** fragment shader uniforms ***
292 time_ticks := f32(time.ticks() - app.ticks) / 1000
293 mut tmp_fs_params := [
294 f32(ws.width),
295 ws.height * ratio, // x,y resolution to pass to FS
296 app.mouse_x, // mouse x
297 ws.height - app.mouse_y * 2, // mouse y scaled
298 time_ticks, // time as f32
299 app.frame_count, // frame count
300 0,
301 0, // padding bytes , see "fs_params" struct paddings in rt_glsl.h
302 ]!
303 fs_uniforms_range := gfx.Range{
304 ptr: unsafe { &tmp_fs_params }
305 size: usize(sizeof(tmp_fs_params))
306 }
307 gfx.apply_uniforms(.fs, C.SLOT_fs_params, &fs_uniforms_range)
308
309 // 3 vertices for triangle * 2 triangles per face * 6 faces = 36 vertices to draw
310 gfx.draw(0, (3 * 2) * 6, 1)
311 gfx.end_pass()
312 gfx.commit()
313}
314
315fn frame(mut app App) {
316 // clear
317 mut color_action := gfx.ColorAttachmentAction{
318 load_action: .clear
319 clear_value: gfx.Color{
320 r: 0.0
321 g: 0.0
322 b: 0.0
323 a: 1.0
324 }
325 }
326
327 mut pass_action := gfx.PassAction{}
328 pass_action.colors[0] = color_action
329 pass := sapp.create_default_pass(pass_action)
330 gfx.begin_pass(&pass)
331
332 // glsl cube
333 draw_cube_glsl(app)
334 app.frame_count++
335}
336
337/******************************************************************************
338* Init / Cleanup
339******************************************************************************/
340fn my_init(mut app App) {
341 // set max vertices,
342 // for a large number of the same type of object it is better use the instances!!
343 desc := sapp.create_desc()
344 gfx.setup(&desc)
345 sgl_desc := sgl.Desc{
346 max_vertices: 50 * 65536
347 }
348 sgl.setup(&sgl_desc)
349
350 // create chessboard texture 256*256 RGBA
351 w := 256
352 h := 256
353 sz := w * h * 4
354 tmp_txt := unsafe { malloc(sz) }
355 mut i := 0
356 for i < sz {
357 unsafe {
358 y := (i >> 0x8) >> 5 // 8 cell
359 x := (i & 0xFF) >> 5 // 8 cell
360 // upper left corner
361 if x == 0 && y == 0 {
362 tmp_txt[i + 0] = u8(0xFF)
363 tmp_txt[i + 1] = u8(0)
364 tmp_txt[i + 2] = u8(0)
365 tmp_txt[i + 3] = u8(0xFF)
366 }
367 // low right corner
368 else if x == 7 && y == 7 {
369 tmp_txt[i + 0] = u8(0)
370 tmp_txt[i + 1] = u8(0xFF)
371 tmp_txt[i + 2] = u8(0)
372 tmp_txt[i + 3] = u8(0xFF)
373 } else {
374 col := if ((x + y) & 1) == 1 { 0xFF } else { 128 }
375 tmp_txt[i + 0] = u8(col) // red
376 tmp_txt[i + 1] = u8(col) // green
377 tmp_txt[i + 2] = u8(col) // blue
378 tmp_txt[i + 3] = u8(0xFF) // alpha
379 }
380 i += 4
381 }
382 }
383 unsafe {
384 app.texture, app.sampler = create_texture(w, h, tmp_txt)
385 free(tmp_txt)
386 }
387 // glsl
388 init_cube_glsl(mut app)
389 app.init_flag = true
390}
391
392/******************************************************************************
393* events handling
394******************************************************************************/
395fn my_event_manager(mut ev gg.Event, mut app App) {
396 if ev.typ == .mouse_move {
397 app.mouse_x = int(ev.mouse_x)
398 app.mouse_y = int(ev.mouse_y)
399 }
400 if ev.typ == .touches_began || ev.typ == .touches_moved {
401 if ev.num_touches > 0 {
402 touch_point := ev.touches[0]
403 app.mouse_x = int(touch_point.pos_x)
404 app.mouse_y = int(touch_point.pos_y)
405 }
406 }
407}
408
409fn main() {
410 mut app := &App{}
411 app.gg = gg.new_context(
412 width: win_width
413 height: win_height
414 create_window: true
415 window_title: '3D Ray Marching Cube'
416 user_data: app
417 bg_color: bg_color
418 frame_fn: frame
419 init_fn: my_init
420 event_fn: my_event_manager
421 )
422 app.ticks = time.ticks()
423 app.gg.run()
424}
425