v / examples / sokol / 05_instancing_glsl / rt_glsl.v
511 lines · 461 sloc · 14.3 KB · bbb61ab3687afe512a1fa12492c876d011626107
Raw
1// vtest build: misc-tooling // needs .h files that are produced by `v shader`
2/**********************************************************************
3*
4* Sokol 3d cube multishader 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
19import math
20import sokol.gfx
21// import sokol.sgl
22import time
23
24const win_width = 800
25const win_height = 800
26const bg_color = gg.white
27const num_inst = 16384
28
29struct App {
30mut:
31 gg &gg.Context = unsafe { nil }
32 texture gfx.Image
33 sampler gfx.Sampler
34 init_flag bool
35 frame_count int
36
37 mouse_x int = 903
38 mouse_y int = 638
39 mouse_down bool
40 // glsl
41 cube_pip_glsl gfx.Pipeline
42 cube_bind gfx.Bindings
43
44 pipe map[string]gfx.Pipeline
45 bind map[string]gfx.Bindings
46 // time
47 ticks i64
48 // instances
49 inst_pos [num_inst]m4.Vec4
50 // camera
51 camera_x f32 = -8
52 camera_z f32 = 47
53}
54
55/******************************************************************************
56* GLSL Include and functions
57******************************************************************************/
58#include "@VMODROOT/rt_glsl_instancing.h" # It should be generated with `v shader .` (see the instructions at the top of this file)
59
60fn C.instancing_shader_desc(gfx.Backend) &gfx.ShaderDesc
61
62/******************************************************************************
63* Texture functions
64******************************************************************************/
65fn create_texture(w int, h int, buf byteptr) (gfx.Image, gfx.Sampler) {
66 sz := w * h * 4
67 // vfmt off
68 mut img_desc := gfx.ImageDesc{
69 width: w
70 height: h
71 num_mipmaps: 0
72// min_filter: .linear
73// mag_filter: .linear
74 //usage: .dynamic
75// wrap_u: .clamp_to_edge
76// wrap_v: .clamp_to_edge
77 label: &char(unsafe { nil })
78 d3d11_texture: 0
79 }
80 // vfmt on
81 // comment if .dynamic is enabled
82 img_desc.data.subimage[0][0] = gfx.Range{
83 ptr: buf
84 size: usize(sz)
85 }
86
87 sg_img := gfx.make_image(&img_desc)
88
89 mut smp_desc := gfx.SamplerDesc{
90 min_filter: .linear
91 mag_filter: .linear
92 wrap_u: .clamp_to_edge
93 wrap_v: .clamp_to_edge
94 }
95
96 sg_smp := gfx.make_sampler(&smp_desc)
97 return sg_img, sg_smp
98}
99
100fn destroy_texture(sg_img gfx.Image) {
101 gfx.destroy_image(sg_img)
102}
103
104// Use only if usage: .dynamic is enabled
105fn update_text_texture(sg_img gfx.Image, w int, h int, buf byteptr) {
106 sz := w * h * 4
107 mut tmp_sbc := gfx.ImageData{}
108 tmp_sbc.subimage[0][0] = gfx.Range{
109 ptr: buf
110 size: usize(sz)
111 }
112 gfx.update_image(sg_img, &tmp_sbc)
113}
114
115/******************************************************************************
116* Draw functions
117******************************************************************************
118 Cube vertex buffer with packed vertex formats for color and texture coords.
119 Note that a vertex format which must be portable across all
120 backends must only use the normalized integer formats
121 (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted
122 to floating point formats in the vertex shader inputs.
123 The reason is that D3D11 cannot convert from non-normalized
124 formats to floating point inputs (only to integer inputs),
125 and WebGL2 / GLES2 don't support integer vertex shader inputs.
126*/
127
128struct Vertex_t {
129 x f32
130 y f32
131 z f32
132 color u32
133 // u u16 // for compatibility with D3D11
134 // v u16 // for compatibility with D3D11
135 u f32
136 v f32
137}
138
139// march shader init
140fn init_cube_glsl_i(mut app App) {
141 // cube vertex buffer
142 // d := u16(32767) // for compatibility with D3D11, 32767 stand for 1
143 d := f32(1.0)
144 c := u32(0xFFFFFF_FF) // color RGBA8
145 // vfmt off
146 vertices := [
147 // Face 0
148 Vertex_t{-1.0, -1.0, -1.0, c, 0, 0},
149 Vertex_t{ 1.0, -1.0, -1.0, c, d, 0},
150 Vertex_t{ 1.0, 1.0, -1.0, c, d, d},
151 Vertex_t{-1.0, 1.0, -1.0, c, 0, d},
152 // Face 1
153 Vertex_t{-1.0, -1.0, 1.0, c, 0, 0},
154 Vertex_t{ 1.0, -1.0, 1.0, c, d, 0},
155 Vertex_t{ 1.0, 1.0, 1.0, c, d, d},
156 Vertex_t{-1.0, 1.0, 1.0, c, 0, d},
157 // Face 2
158 Vertex_t{-1.0, -1.0, -1.0, c, 0, 0},
159 Vertex_t{-1.0, 1.0, -1.0, c, d, 0},
160 Vertex_t{-1.0, 1.0, 1.0, c, d, d},
161 Vertex_t{-1.0, -1.0, 1.0, c, 0, d},
162 // Face 3
163 Vertex_t{ 1.0, -1.0, -1.0, c, 0, 0},
164 Vertex_t{ 1.0, 1.0, -1.0, c, d, 0},
165 Vertex_t{ 1.0, 1.0, 1.0, c, d, d},
166 Vertex_t{ 1.0, -1.0, 1.0, c, 0, d},
167 // Face 4
168 Vertex_t{-1.0, -1.0, -1.0, c, 0, 0},
169 Vertex_t{-1.0, -1.0, 1.0, c, d, 0},
170 Vertex_t{ 1.0, -1.0, 1.0, c, d, d},
171 Vertex_t{ 1.0, -1.0, -1.0, c, 0, d},
172 // Face 5
173 Vertex_t{-1.0, 1.0, -1.0, c, 0, 0},
174 Vertex_t{-1.0, 1.0, 1.0, c, d, 0},
175 Vertex_t{ 1.0, 1.0, 1.0, c, d, d},
176 Vertex_t{ 1.0, 1.0, -1.0, c, 0, d},
177 ]
178 // vfmt on
179
180 mut vert_buffer_desc := gfx.BufferDesc{
181 label: c'cube-vertices'
182 }
183 unsafe { vmemset(&vert_buffer_desc, 0, int(sizeof(vert_buffer_desc))) }
184 vert_buffer_desc.size = usize(vertices.len * int(sizeof(Vertex_t)))
185 vert_buffer_desc.data = gfx.Range{
186 ptr: vertices.data
187 size: usize(vertices.len * int(sizeof(Vertex_t)))
188 }
189 vert_buffer_desc.type = .vertexbuffer
190 vbuf := gfx.make_buffer(&vert_buffer_desc)
191
192 // create an instance buffer for the cube
193 mut inst_buffer_desc := gfx.BufferDesc{
194 label: c'instance-data'
195 }
196 unsafe { vmemset(&inst_buffer_desc, 0, int(sizeof(inst_buffer_desc))) }
197
198 inst_buffer_desc.size = usize(num_inst * int(sizeof(m4.Vec4)))
199 inst_buffer_desc.type = .vertexbuffer
200 inst_buffer_desc.usage = .stream
201 inst_buf := gfx.make_buffer(&inst_buffer_desc)
202
203 // create an index buffer for the cube
204 // vfmt off
205 indices := [
206 u16(0), 1, 2, 0, 2, 3,
207 6, 5, 4, 7, 6, 4,
208 8, 9, 10, 8, 10, 11,
209 14, 13, 12, 15, 14, 12,
210 16, 17, 18, 16, 18, 19,
211 22, 21, 20, 23, 22, 20,
212 ]
213 // vfmt on
214
215 mut index_buffer_desc := gfx.BufferDesc{
216 label: c'cube-indices'
217 }
218 unsafe { vmemset(&index_buffer_desc, 0, int(sizeof(index_buffer_desc))) }
219 index_buffer_desc.size = usize(indices.len * int(sizeof(u16)))
220 index_buffer_desc.data = gfx.Range{
221 ptr: indices.data
222 size: usize(indices.len * int(sizeof(u16)))
223 }
224 index_buffer_desc.type = .indexbuffer
225 ibuf := gfx.make_buffer(&index_buffer_desc)
226
227 // create shader
228 shader := gfx.make_shader(voidptr(C.instancing_shader_desc(C.sg_query_backend())))
229
230 mut pipdesc := gfx.PipelineDesc{}
231 unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) }
232 pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_t))
233
234 // vfmt off
235 // the constants [C.ATTR_vs_m_pos, C.ATTR_vs_m_color0, C.ATTR_vs_m_texcoord0] are generated by sokol-shdc
236 pipdesc.layout.attrs[C.ATTR_vs_i_pos ].format = .float3 // x,y,z as f32
237 pipdesc.layout.attrs[C.ATTR_vs_i_pos ].buffer_index = 0
238 pipdesc.layout.attrs[C.ATTR_vs_i_color0 ].format = .ubyte4n // color as u32
239 pipdesc.layout.attrs[C.ATTR_vs_i_pos ].buffer_index = 0
240 pipdesc.layout.attrs[C.ATTR_vs_i_texcoord0].format = .float2 // u,v as f32
241 pipdesc.layout.attrs[C.ATTR_vs_i_pos ].buffer_index = 0
242
243 // instancing
244 // the constant ATTR_vs_i_inst_pos is generated by sokol-shdc
245 pipdesc.layout.buffers[1].stride = int(sizeof(m4.Vec4))
246 pipdesc.layout.buffers[1].step_func = .per_instance // we will pass a single parameter for each instance!!
247 pipdesc.layout.attrs[C.ATTR_vs_i_inst_pos ].format = .float4
248 pipdesc.layout.attrs[C.ATTR_vs_i_inst_pos ].buffer_index = 1
249 // vfmt on
250
251 pipdesc.shader = shader
252 pipdesc.index_type = .uint16
253
254 pipdesc.depth = gfx.DepthState{
255 write_enabled: true
256 compare: .less_equal
257 }
258 pipdesc.cull_mode = .back
259
260 pipdesc.label = c'glsl_shader pipeline'
261
262 mut bind := gfx.Bindings{}
263 unsafe { vmemset(&bind, 0, int(sizeof(bind))) }
264 bind.vertex_buffers[0] = vbuf // vertex buffer
265 bind.vertex_buffers[1] = inst_buf // instance buffer
266 bind.index_buffer = ibuf
267 bind.fs.images[C.SLOT_tex] = app.texture
268 bind.fs.samplers[C.SLOT_smp] = app.sampler
269 app.bind['inst'] = bind
270 app.pipe['inst'] = gfx.make_pipeline(&pipdesc)
271
272 println('GLSL March init DONE!')
273}
274
275fn calc_tr_matrices(w f32, h f32, rx f32, ry f32, in_scale f32) m4.Mat4 {
276 // vfmt off
277 proj := m4.perspective(60, w/h, 0.01, 4000.0)
278 view := m4.look_at(m4.Vec4{e:[f32(0.0),100,6,0]!}, m4.Vec4{e:[f32(0),0,0,0]!}, m4.Vec4{e:[f32(0),1.0,0,0]!})
279 view_proj := view * proj
280
281 rxm := m4.rotate(m4.rad(rx), m4.Vec4{e:[f32(1),0,0,0]!})
282 rym := m4.rotate(m4.rad(ry), m4.Vec4{e:[f32(0),1,0,0]!})
283 // vfmt on
284
285 model := rym * rxm
286 scale_m := m4.scale(m4.Vec4{ e: [in_scale, in_scale, in_scale, 1]! })
287
288 res := (scale_m * model) * view_proj
289 return res
290}
291
292// triangles draw
293fn draw_cube_glsl_i(mut app App) {
294 if app.init_flag == false {
295 return
296 }
297
298 ws := gg.window_size_real_pixels()
299 // ratio := f32(ws.width) / ws.height
300 dw := f32(ws.width / 2)
301 dh := f32(ws.height / 2)
302
303 rot := [f32(app.mouse_y), f32(app.mouse_x)]
304 tr_matrix := calc_tr_matrices(dw, dh, rot[0], rot[1], 2.3)
305
306 gfx.apply_pipeline(app.pipe['inst'])
307 gfx.apply_bindings(app.bind['inst'])
308
309 //***************
310 // Instancing
311 //***************
312 // passing the instancing to the vs
313 time_ticks := f32(time.ticks() - app.ticks) / 1000
314 cube_size := 2
315 sz := 128 // field size dimension
316 cx := 64 // x center for the cubes
317 cz := 64 // z center for the cubes
318 // frame := (app.frame_count/4) % 100
319 for index in 0 .. num_inst {
320 x := f32(index % sz)
321 z := f32(index / sz)
322 // simply waves
323 y := f32(math.cos((x + time_ticks) / 2.0) * math.sin(z / 2.0)) * 2
324 // sombrero function
325 // r := ((x-cx)*(x-cx)+(z-cz)*(z-cz))/(sz/2)
326 // y := f32(math.sin(r+time_ticks)*4.0)
327 spare_param := f32(index % 10)
328 // vfmt off
329 app.inst_pos[index] = m4.Vec4{e:[f32((x - cx - app.camera_x) * cube_size),y ,f32( (z - cz - app.camera_z) * cube_size),spare_param]!}
330 // vfmt on
331 }
332 range := gfx.Range{
333 ptr: unsafe { &app.inst_pos }
334 size: usize(num_inst * int(sizeof(m4.Vec4)))
335 }
336 gfx.update_buffer(app.bind['inst'].vertex_buffers[1], &range)
337
338 // Uniforms
339 // *** vertex shadeer uniforms ***
340 // passing the view matrix as uniform
341 // res is a 4x4 matrix of f32 thus: 4*16 byte of size
342 vs_uniforms_range := gfx.Range{
343 ptr: unsafe { &tr_matrix }
344 size: usize(4 * 16)
345 }
346 gfx.apply_uniforms(.vs, C.SLOT_vs_params_i, &vs_uniforms_range)
347
348 /*
349 // *** fragment shader uniforms ***
350 time_ticks := f32(time.ticks() - app.ticks) / 1000
351 // vfmt off
352 mut tmp_fs_params := [
353 f32(ws.width), ws.height * ratio, // x,y resolution to pass to FS
354 0,0, // dont send mouse position
355 //app.mouse_x, // mouse x
356 //ws.height - app.mouse_y*2, // mouse y scaled
357 time_ticks, // time as f32
358 app.frame_count, // frame count
359 0,0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h
360 ]!
361 // vfmt on
362 fs_uniforms_range := gfx.Range{
363 ptr: unsafe { &tmp_fs_params }
364 size: usize(sizeof(tmp_fs_params))
365 }
366 gfx.apply_uniforms(.fs, C.SLOT_fs_params, &fs_uniforms_range)
367 */
368 // 3 vertices for triangle * 2 triangles per face * 6 faces = 36 vertices to draw for num_inst times
369 gfx.draw(0, (3 * 2) * 6, num_inst)
370}
371
372fn draw_start_glsl(app App) {
373 if app.init_flag == false {
374 return
375 }
376
377 ws := gg.window_size_real_pixels()
378 // ratio := f32(ws.width) / ws.height
379 // dw := f32(ws.width / 2)
380 // dh := f32(ws.height / 2)
381
382 gfx.apply_viewport(0, 0, ws.width, ws.height, true)
383}
384
385fn draw_end_glsl(app App) {
386 gfx.end_pass()
387 gfx.commit()
388}
389
390fn frame(mut app App) {
391 // clear
392 mut color_action := gfx.ColorAttachmentAction{
393 load_action: .clear
394 clear_value: gfx.Color{
395 r: 0.0
396 g: 0.0
397 b: 0.0
398 a: 1.0
399 }
400 }
401 mut pass_action := gfx.PassAction{}
402 pass_action.colors[0] = color_action
403 pass := gg.create_default_pass(pass_action)
404 gfx.begin_pass(&pass)
405
406 draw_start_glsl(app)
407 draw_cube_glsl_i(mut app)
408 draw_end_glsl(app)
409 app.frame_count++
410}
411
412/******************************************************************************
413* Init / Cleanup
414******************************************************************************/
415fn my_init(mut app App) {
416 // create chessboard texture 256*256 RGBA
417 w := 256
418 h := 256
419 sz := w * h * 4
420 tmp_txt := unsafe { malloc(sz) }
421 mut i := 0
422 for i < sz {
423 unsafe {
424 y := (i >> 0x8) >> 5 // 8 cell
425 x := (i & 0xFF) >> 5 // 8 cell
426 // upper left corner
427 if x == 0 && y == 0 {
428 tmp_txt[i + 0] = u8(0xFF)
429 tmp_txt[i + 1] = u8(0)
430 tmp_txt[i + 2] = u8(0)
431 tmp_txt[i + 3] = u8(0xFF)
432 }
433 // low right corner
434 else if x == 7 && y == 7 {
435 tmp_txt[i + 0] = u8(0)
436 tmp_txt[i + 1] = u8(0xFF)
437 tmp_txt[i + 2] = u8(0)
438 tmp_txt[i + 3] = u8(0xFF)
439 } else {
440 col := if ((x + y) & 1) == 1 { 0xFF } else { 128 }
441 tmp_txt[i + 0] = u8(col) // red
442 tmp_txt[i + 1] = u8(col) // green
443 tmp_txt[i + 2] = u8(col) // blue
444 tmp_txt[i + 3] = u8(0xFF) // alpha
445 }
446 i += 4
447 }
448 }
449 unsafe {
450 app.texture, app.sampler = create_texture(w, h, tmp_txt)
451 free(tmp_txt)
452 }
453 // glsl
454 init_cube_glsl_i(mut app)
455 app.init_flag = true
456}
457
458/******************************************************************************
459* events handling
460******************************************************************************/
461fn my_event_manager(mut ev gg.Event, mut app App) {
462 if ev.typ == .mouse_down {
463 app.mouse_down = true
464 }
465 if ev.typ == .mouse_up {
466 app.mouse_down = false
467 }
468 if app.mouse_down == true && ev.typ == .mouse_move {
469 app.mouse_x = int(ev.mouse_x)
470 app.mouse_y = int(ev.mouse_y)
471 }
472 if ev.typ == .touches_began || ev.typ == .touches_moved {
473 if ev.num_touches > 0 {
474 touch_point := ev.touches[0]
475 app.mouse_x = int(touch_point.pos_x)
476 app.mouse_y = int(touch_point.pos_y)
477 }
478 }
479
480 // keyboard
481 if ev.typ == .key_down {
482 step := f32(1.0)
483 match ev.key_code {
484 .w { app.camera_z += step }
485 .s { app.camera_z -= step }
486 .a { app.camera_x -= step }
487 .d { app.camera_x += step }
488 else {}
489 }
490 }
491 eprintln('>> app.camera_x: ${app.camera_x} , app.camera_z: ${app.camera_z}, app.mouse_x: ${app.mouse_x}, app.mouse_y: ${app.mouse_y}')
492}
493
494fn main() {
495 mut app := &App{}
496 // vfmt off
497 app.gg = gg.new_context(
498 width: win_width
499 height: win_height
500 create_window: true
501 window_title: 'Instancing Cube'
502 user_data: app
503 bg_color: bg_color
504 frame_fn: frame
505 init_fn: my_init
506 event_fn: my_event_manager
507 )
508 // vfmt on
509 app.ticks = time.ticks()
510 app.gg.run()
511}
512