v / examples / sokol / 06_obj_viewer / show_obj.v
308 lines · 266 sloc · 7.69 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1// vtest build: misc-tooling // needs .h files that are produced by `v shader`
2/**********************************************************************
3*
4* .obj viewer
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* Example .obj model of V from SurmanPP
11*
12* HOW TO COMPILE SHADERS:
13* Run `v shader .` in this directory to compile the shaders.
14* For more info and help with shader compilation see `docs.md` and `v help shader`.
15*
16* ALTERNATIVE .OBJ MODELS:
17* you can load alternative models putting them in the "assets/model" folder with or without their .mtl file.
18* use the program help for further instructions.
19*
20* TODO:
21* - frame counter
22**********************************************************************/
23import gg
24import gg.m4
25import math
26import sokol.sapp
27import sokol.gfx
28import sokol.sgl
29import time
30import os
31import obj
32
33// GLSL Include and functions
34
35#include "@VMODROOT/gouraud.h" # It should be generated with `v shader .` (see the instructions at the top of this file)
36
37fn C.gouraud_shader_desc(gfx.Backend) &gfx.ShaderDesc
38
39const win_width = 600
40const win_height = 600
41const bg_color = gg.white
42
43struct App {
44mut:
45 gg &gg.Context = unsafe { nil }
46 texture gfx.Image
47 sampler gfx.Sampler
48 init_flag bool
49 frame_count int
50
51 mouse_x int = -1
52 mouse_y int = -1
53 scroll_y int // mouse wheel value
54 // time
55 ticks i64
56 // model
57 obj_part &obj.ObjPart = unsafe { nil }
58 n_vertex u32
59 // init parameters
60 file_name string
61 single_material_flag bool
62}
63
64/******************************************************************************
65* Draw functions
66******************************************************************************/
67@[inline]
68fn vec4(x f32, y f32, z f32, w f32) m4.Vec4 {
69 return m4.Vec4{
70 e: [x, y, z, w]!
71 }
72}
73
74fn calc_matrices(w f32, h f32, rx f32, ry f32, in_scale f32, pos m4.Vec4) obj.Mats {
75 proj := m4.perspective(60, w / h, 0.01, 100.0) // set far plane to 100 fro the zoom function
76 view := m4.look_at(vec4(f32(0.0), 0, 6, 0), vec4(f32(0), 0, 0, 0), vec4(f32(0), 1, 0, 0))
77 view_proj := view * proj
78
79 rxm := m4.rotate(m4.rad(rx), vec4(f32(1), 0, 0, 0))
80 rym := m4.rotate(m4.rad(ry), vec4(f32(0), 1, 0, 0))
81
82 model_pos := m4.unit_m4().translate(pos)
83
84 model_m := (rym * rxm) * model_pos
85 scale_m := m4.scale(vec4(in_scale, in_scale, in_scale, 1))
86
87 mv := scale_m * model_m // model view
88 nm := mv.inverse().transpose() // normal matrix
89 mvp := mv * view_proj // model view projection
90
91 return obj.Mats{
92 mv: mv
93 mvp: mvp
94 nm: nm
95 }
96}
97
98fn draw_model(app App, model_pos m4.Vec4) u32 {
99 if app.init_flag == false {
100 return 0
101 }
102
103 ws := gg.window_size_real_pixels()
104 dw := ws.width / 2
105 dh := ws.height / 2
106
107 mut scale := f32(1)
108 if app.obj_part.radius > 1 {
109 scale = 1 / (app.obj_part.radius)
110 } else {
111 scale = app.obj_part.radius
112 }
113 scale *= 3
114
115 // *** vertex shader uniforms ***
116 rot := [f32(app.mouse_y), f32(app.mouse_x)]
117 mut zoom_scale := scale + f32(app.scroll_y) / (app.obj_part.radius * 4)
118 mats := calc_matrices(dw, dh, rot[0], rot[1], zoom_scale, model_pos)
119
120 mut tmp_vs_param := obj.Tmp_vs_param{
121 mv: mats.mv
122 mvp: mats.mvp
123 nm: mats.nm
124 }
125
126 // *** fragment shader uniforms ***
127 time_ticks := f32(time.ticks() - app.ticks) / 1000
128 radius_light := f32(app.obj_part.radius)
129 x_light := f32(math.cos(time_ticks) * radius_light)
130 z_light := f32(math.sin(time_ticks) * radius_light)
131
132 mut tmp_fs_params := obj.Tmp_fs_param{}
133 tmp_fs_params.light = m4.vec3(x_light, radius_light, z_light)
134
135 sd := obj.Shader_data{
136 vs_data: unsafe { &tmp_vs_param }
137 vs_len: int(sizeof(tmp_vs_param))
138 fs_data: unsafe { &tmp_fs_params }
139 fs_len: int(sizeof(tmp_fs_params))
140 }
141
142 return app.obj_part.bind_and_draw_all(sd)
143}
144
145fn frame(mut app App) {
146 // clear
147 mut color_action := gfx.ColorAttachmentAction{
148 load_action: .clear
149 clear_value: gfx.Color{
150 r: 0.0
151 g: 0.0
152 b: 0.0
153 a: 1.0
154 }
155 }
156
157 mut pass_action := gfx.PassAction{}
158 pass_action.colors[0] = color_action
159 pass := sapp.create_default_pass(pass_action)
160 gfx.begin_pass(&pass)
161
162 // render the data
163 draw_start_glsl(app)
164 draw_model(app, m4.Vec4{})
165 // uncomment if you want a raw benchmark mode
166 /*
167 mut n_vertex_drawn := u32(0)
168 n_x_obj := 20
169
170 for x in 0..n_x_obj {
171 for z in 0..30 {
172 for y in 0..4 {
173 n_vertex_drawn += draw_model(app, m4.Vec4{e:[f32((x-(n_x_obj>>1))*3),-3 + y*3,f32(-6*z),1]!})
174 }
175 }
176 }
177 */
178 draw_end_glsl(app)
179
180 // println("v:${n_vertex_drawn}")
181 app.frame_count++
182}
183
184fn draw_start_glsl(app App) {
185 if app.init_flag == false {
186 return
187 }
188 ws := gg.window_size_real_pixels()
189 gfx.apply_viewport(0, 0, ws.width, ws.height, true)
190}
191
192fn draw_end_glsl(app App) {
193 gfx.end_pass()
194 gfx.commit()
195}
196
197/******************************************************************************
198* Init / Cleanup
199******************************************************************************/
200fn my_init(mut app App) {
201 mut object := &obj.ObjPart{}
202 obj_file_lines := obj.read_lines_from_file(app.file_name)
203 object.parse_obj_buffer(obj_file_lines, app.single_material_flag)
204 object.summary()
205 app.obj_part = object
206
207 // set max vertices,
208 // for a large number of the same type of object it is better use the instances!!
209 desc := sapp.create_desc()
210 gfx.setup(&desc)
211 sgl_desc := sgl.Desc{
212 max_vertices: 128 * 65536
213 }
214 sgl.setup(&sgl_desc)
215
216 // 1x1 pixel white, default texture
217 unsafe {
218 tmp_txt := malloc(4)
219 tmp_txt[0] = u8(0xFF)
220 tmp_txt[1] = u8(0xFF)
221 tmp_txt[2] = u8(0xFF)
222 tmp_txt[3] = u8(0xFF)
223 app.texture, app.sampler = obj.create_texture(1, 1, tmp_txt)
224 free(tmp_txt)
225 }
226 // glsl
227 app.obj_part.init_render_data(app.texture, app.sampler)
228 app.init_flag = true
229}
230
231fn cleanup(mut app App) {
232 /*
233 for _, mat in app.obj_part.texture {
234 obj.destroy_texture(mat)
235 }
236 */
237}
238
239/******************************************************************************
240* events handling
241******************************************************************************/
242fn my_event_manager(mut ev gg.Event, mut app App) {
243 if ev.typ == .mouse_move {
244 app.mouse_x = int(ev.mouse_x)
245 app.mouse_y = int(ev.mouse_y)
246 }
247
248 if ev.scroll_y != 0 {
249 app.scroll_y += int(ev.scroll_y)
250 }
251
252 if ev.typ == .touches_began || ev.typ == .touches_moved {
253 if ev.num_touches > 0 {
254 touch_point := ev.touches[0]
255 app.mouse_x = int(touch_point.pos_x)
256 app.mouse_y = int(touch_point.pos_y)
257 }
258 }
259}
260
261fn main() {
262 /*
263 obj.tst()
264 exit(0)
265 */
266 // App init
267 mut app := &App{}
268
269 // app.file_name = 'v.obj' // default object is the v logo
270 app.file_name = 'utahTeapot.obj' // default object is the v logo
271
272 app.single_material_flag = false
273 $if !android {
274 if os.args.len > 3 || (os.args.len >= 2 && os.args[1] in ['-h', '--help', '\\?', '-?']) {
275 eprintln('Usage:\nshow_obj [file_name:string] [single_material_flag:(true|false)]\n')
276 eprintln('file_name : name of the .obj file.')
277 eprintln(' If no file name is passed the default V logo will be showed.')
278 eprintln(' Try one of the .obj files in the "assets/models/" folder.')
279 eprintln("single_material_flag: if true the viewer use for all the model's parts the default material")
280 exit(0)
281 }
282
283 if os.args.len >= 2 {
284 app.file_name = os.args[1]
285 }
286 if os.args.len >= 3 {
287 app.single_material_flag = os.args[2].bool()
288 }
289 println('Loading model: ${app.file_name}')
290 println('Using single material: ${app.single_material_flag}')
291 }
292
293 app.gg = gg.new_context(
294 width: win_width
295 height: win_height
296 create_window: true
297 window_title: 'V Wavefront OBJ viewer - Use the mouse wheel to zoom'
298 user_data: app
299 bg_color: bg_color
300 frame_fn: frame
301 init_fn: my_init
302 cleanup_fn: cleanup
303 event_fn: my_event_manager
304 )
305
306 app.ticks = time.ticks()
307 app.gg.run()
308}
309