diff options
Diffstat (limited to 'v_windows/v/examples/sokol/06_obj_viewer/show_obj.v')
-rw-r--r-- | v_windows/v/examples/sokol/06_obj_viewer/show_obj.v | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/v_windows/v/examples/sokol/06_obj_viewer/show_obj.v b/v_windows/v/examples/sokol/06_obj_viewer/show_obj.v new file mode 100644 index 0000000..e2e0be3 --- /dev/null +++ b/v_windows/v/examples/sokol/06_obj_viewer/show_obj.v @@ -0,0 +1,338 @@ +/********************************************************************** +* +* .obj viewer +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* Example .obj model of V from SurmanPP +* +* HOW TO COMPILE SHADERS: +* - download the sokol shader convertor tool from https://github.com/floooh/sokol-tools-bin +* +* - compile the .glsl shader with: +* linux : sokol-shdc --input gouraud.glsl --output gouraud.h --slang glsl330 +* windows: sokol-shdc.exe --input gouraud.glsl --output gouraud.h --slang glsl330 +* +* --slang parameter can be: +* - glsl330: desktop GL +* - glsl100: GLES2 / WebGL +* - glsl300es: GLES3 / WebGL2 +* - hlsl4: D3D11 +* - hlsl5: D3D11 +* - metal_macos: Metal on macOS +* - metal_ios: Metal on iOS device +* - metal_sim: Metal on iOS simulator +* - wgpu: WebGPU +* +* you can have multiple platforms at the same time passing parameters like this: --slang glsl330:hlsl5:metal_macos +* for further infos have a look at the sokol shader tool docs. +* +* ALTERNATIVE .OBJ MODELS: +* you can load alternative models putting them in the "assets/model" folder with or without their .mtl file. +* use the program help for further instructions. +* +* TODO: +* - frame counter +**********************************************************************/ +import gg +import gg.m4 +import gx +import math +import sokol.sapp +import sokol.gfx +import sokol.sgl +import time +import os +import obj + +// GLSL Include and functions + +#flag -I @VMODROOT/. +#include "gouraud.h" #Please use sokol-shdc to generate the necessary rt_glsl.h file from rt_glsl.glsl (see the instructions at the top of this file) + +fn C.gouraud_shader_desc(gfx.Backend) &C.sg_shader_desc + +const ( + win_width = 600 + win_height = 600 + bg_color = gx.white +) + +struct App { +mut: + gg &gg.Context + texture C.sg_image + init_flag bool + frame_count int + + mouse_x int = -1 + mouse_y int = -1 + scroll_y int // mouse wheel value + // time + ticks i64 + // model + obj_part &obj.ObjPart + n_vertex u32 + // init parameters + file_name string + single_material_flag bool +} + +/****************************************************************************** +* Draw functions +******************************************************************************/ +[inline] +fn vec4(x f32, y f32, z f32, w f32) m4.Vec4 { + return m4.Vec4{ + e: [x, y, z, w]! + } +} + +fn calc_matrices(w f32, h f32, rx f32, ry f32, in_scale f32, pos m4.Vec4) obj.Mats { + proj := m4.perspective(60, w / h, 0.01, 100.0) // set far plane to 100 fro the zoom function + view := m4.look_at(vec4(f32(0.0), 0, 6, 0), vec4(f32(0), 0, 0, 0), vec4(f32(0), 1, + 0, 0)) + view_proj := view * proj + + rxm := m4.rotate(m4.rad(rx), vec4(f32(1), 0, 0, 0)) + rym := m4.rotate(m4.rad(ry), vec4(f32(0), 1, 0, 0)) + + model_pos := m4.unit_m4().translate(pos) + + model_m := (rym * rxm) * model_pos + scale_m := m4.scale(vec4(in_scale, in_scale, in_scale, 1)) + + mv := scale_m * model_m // model view + nm := mv.inverse().transpose() // normal matrix + mvp := mv * view_proj // model view projection + + return obj.Mats{ + mv: mv + mvp: mvp + nm: nm + } +} + +fn draw_model(app App, model_pos m4.Vec4) u32 { + if app.init_flag == false { + return 0 + } + + ws := gg.window_size_real_pixels() + dw := ws.width / 2 + dh := ws.height / 2 + + mut scale := f32(1) + if app.obj_part.radius > 1 { + scale = 1 / (app.obj_part.radius) + } else { + scale = app.obj_part.radius + } + scale *= 3 + + // *** vertex shader uniforms *** + rot := [f32(app.mouse_y), f32(app.mouse_x)] + mut zoom_scale := scale + f32(app.scroll_y) / (app.obj_part.radius * 4) + mats := calc_matrices(dw, dh, rot[0], rot[1], zoom_scale, model_pos) + + mut tmp_vs_param := obj.Tmp_vs_param{ + mv: mats.mv + mvp: mats.mvp + nm: mats.nm + } + + // *** fragment shader uniforms *** + time_ticks := f32(time.ticks() - app.ticks) / 1000 + radius_light := f32(app.obj_part.radius) + x_light := f32(math.cos(time_ticks) * radius_light) + z_light := f32(math.sin(time_ticks) * radius_light) + + mut tmp_fs_params := obj.Tmp_fs_param{} + tmp_fs_params.ligth = m4.vec3(x_light, radius_light, z_light) + + sd := obj.Shader_data{ + vs_data: unsafe { &tmp_vs_param } + vs_len: int(sizeof(tmp_vs_param)) + fs_data: unsafe { &tmp_fs_params } + fs_len: int(sizeof(tmp_fs_params)) + } + + return app.obj_part.bind_and_draw_all(sd) +} + +fn frame(mut app App) { + ws := gg.window_size_real_pixels() + + // clear + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 0.0 + g: 0.0 + b: 0.0 + a: 1.0 + } + } + + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + gfx.begin_default_pass(&pass_action, ws.width, ws.height) + + // render the data + draw_start_glsl(app) + draw_model(app, m4.Vec4{}) + // uncoment if you want a raw benchmark mode + /* + mut n_vertex_drawn := u32(0) + n_x_obj := 20 + + for x in 0..n_x_obj { + for z in 0..30 { + for y in 0..4 { + n_vertex_drawn += draw_model(app, m4.Vec4{e:[f32((x-(n_x_obj>>1))*3),-3 + y*3,f32(-6*z),1]!}) + } + } + } + */ + draw_end_glsl(app) + + // println("v:$n_vertex_drawn") + app.frame_count++ +} + +fn draw_start_glsl(app App) { + if app.init_flag == false { + return + } + ws := gg.window_size_real_pixels() + gfx.apply_viewport(0, 0, ws.width, ws.height, true) +} + +fn draw_end_glsl(app App) { + gfx.end_pass() + gfx.commit() +} + +/****************************************************************************** +* Init / Cleanup +******************************************************************************/ +fn my_init(mut app App) { + mut object := &obj.ObjPart{} + obj_file_lines := obj.read_lines_from_file(app.file_name) + object.parse_obj_buffer(obj_file_lines, app.single_material_flag) + object.summary() + app.obj_part = object + + // set max vertices, + // for a large number of the same type of object it is better use the instances!! + desc := sapp.create_desc() + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{ + max_vertices: 128 * 65536 + } + sgl.setup(&sgl_desc) + + // 1x1 pixel white, default texture + unsafe { + tmp_txt := malloc(4) + tmp_txt[0] = byte(0xFF) + tmp_txt[1] = byte(0xFF) + tmp_txt[2] = byte(0xFF) + tmp_txt[3] = byte(0xFF) + app.texture = obj.create_texture(1, 1, tmp_txt) + free(tmp_txt) + } + // glsl + app.obj_part.init_render_data(app.texture) + app.init_flag = true +} + +fn cleanup(mut app App) { + gfx.shutdown() + /* + for _, mat in app.obj_part.texture { + obj.destroy_texture(mat) + } + */ +} + +/****************************************************************************** +* events handling +******************************************************************************/ +fn my_event_manager(mut ev gg.Event, mut app App) { + if ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } + + if ev.scroll_y != 0 { + app.scroll_y += int(ev.scroll_y) + } + + if ev.typ == .touches_began || ev.typ == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.mouse_x = int(touch_point.pos_x) + app.mouse_y = int(touch_point.pos_y) + } + } +} + +/****************************************************************************** +* Main +******************************************************************************/ +// is needed for easier diagnostics on windows +[console] +fn main() { + /* + obj.tst() + exit(0) + */ + + // App init + mut app := &App{ + gg: 0 + obj_part: 0 + } + + app.file_name = 'v.obj_' // default object is the v logo + + app.single_material_flag = false + $if !android { + if os.args.len > 3 || (os.args.len >= 2 && os.args[1] in ['-h', '--help', '\\?', '-?']) { + eprintln('Usage:\nshow_obj [file_name:string] [single_material_flag:(true|false)]\n') + eprintln('file_name : name of the .obj file, it must be in the folder "assets/models"') + eprintln(' if no file name is passed the default V logo will be showed.') + eprintln(' if you want custom models you can put them in the folder "assets/models".') + eprintln("single_material_flag: if true the viewer use for all the model's parts the default material\n") + exit(0) + } + + if os.args.len >= 2 { + app.file_name = os.args[1] + } + if os.args.len >= 3 { + app.single_material_flag = os.args[2].bool() + } + println('Loading model: $app.file_name') + println('Using single material: $app.single_material_flag') + } + + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: 'V Wavefront OBJ viewer - Use the mouse wheel to zoom' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: my_init + cleanup_fn: cleanup + event_fn: my_event_manager + ) + + app.ticks = time.ticks() + app.gg.run() +} |