v2 / examples / sokol / 06_obj_viewer / modules / obj / rend.v
305 lines · 264 sloc · 8.57 KB · 2332ecff4811b8c97dfda8e825170e9397962519
Raw
1/**********************************************************************
2*
3* .obj loader
4*
5* Copyright (c) 2021 Dario Deledda. All rights reserved.
6* Use of this source code is governed by an MIT license
7* that can be found in the LICENSE file.
8*
9* TODO:
10**********************************************************************/
11module obj
12
13import sokol.gfx
14import gg.m4
15import math
16import stbi
17
18/******************************************************************************
19* Texture functions
20******************************************************************************/
21pub fn create_texture(w int, h int, buf &u8) (gfx.Image, gfx.Sampler) {
22 sz := w * h * 4
23 mut img_desc := gfx.ImageDesc{
24 width: w
25 height: h
26 num_mipmaps: 0
27 // min_filter: .linear
28 // mag_filter: .linear
29 // usage: .dynamic
30 // wrap_u: .clamp_to_edge
31 // wrap_v: .clamp_to_edge
32 label: &char(unsafe { nil })
33 d3d11_texture: 0
34 }
35 // comment if .dynamic is enabled
36 img_desc.data.subimage[0][0] = gfx.Range{
37 ptr: buf
38 size: usize(sz)
39 }
40
41 sg_img := gfx.make_image(&img_desc)
42
43 mut smp_desc := gfx.SamplerDesc{
44 min_filter: .linear
45 mag_filter: .linear
46 wrap_u: .clamp_to_edge
47 wrap_v: .clamp_to_edge
48 }
49
50 sg_smp := gfx.make_sampler(&smp_desc)
51 return sg_img, sg_smp
52}
53
54pub fn destroy_texture(sg_img gfx.Image) {
55 gfx.destroy_image(sg_img)
56}
57
58pub fn load_texture(file_name string) (gfx.Image, gfx.Sampler) {
59 buffer := read_bytes_from_file(file_name)
60 stbi.set_flip_vertically_on_load(true)
61 img := stbi.load_from_memory(buffer.data, buffer.len) or {
62 eprintln('Texture file: [${file_name}] ERROR!')
63 exit(0)
64 }
65 sg_img, sg_smp := create_texture(int(img.width), int(img.height), img.data)
66 img.free()
67 return sg_img, sg_smp
68}
69
70/******************************************************************************
71* Pipeline
72******************************************************************************/
73pub fn (mut obj_part ObjPart) create_pipeline(in_part []int, shader gfx.Shader, texture gfx.Image, sampler gfx.Sampler) Render_data {
74 mut res := Render_data{}
75 obj_buf := obj_part.get_buffer(in_part)
76 res.n_vert = obj_buf.n_vertex
77 res.material = obj_part.part[in_part[0]].material
78
79 // vertex buffer
80 mut vert_buffer_desc := gfx.BufferDesc{}
81 unsafe { vmemset(&vert_buffer_desc, 0, int(sizeof(vert_buffer_desc))) }
82
83 vert_buffer_desc.size = usize(obj_buf.vbuf.len * int(sizeof(Vertex_pnct)))
84 vert_buffer_desc.data = gfx.Range{
85 ptr: obj_buf.vbuf.data
86 size: usize(obj_buf.vbuf.len * int(sizeof(Vertex_pnct)))
87 }
88
89 vert_buffer_desc.type = .vertexbuffer
90 vert_buffer_desc.label = &char('vertbuf_part_${in_part:03}'.str)
91 vbuf := gfx.make_buffer(&vert_buffer_desc)
92
93 // index buffer
94 mut index_buffer_desc := gfx.BufferDesc{}
95 unsafe { vmemset(&index_buffer_desc, 0, int(sizeof(index_buffer_desc))) }
96
97 index_buffer_desc.size = usize(obj_buf.ibuf.len * int(sizeof(u32)))
98 index_buffer_desc.data = gfx.Range{
99 ptr: obj_buf.ibuf.data
100 size: usize(obj_buf.ibuf.len * int(sizeof(u32)))
101 }
102
103 index_buffer_desc.type = .indexbuffer
104 index_buffer_desc.label = &char('indbuf_part_${in_part:03}'.str)
105 ibuf := gfx.make_buffer(&index_buffer_desc)
106
107 mut pipdesc := gfx.PipelineDesc{}
108 unsafe { vmemset(&pipdesc, 0, int(sizeof(pipdesc))) }
109 pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_pnct))
110
111 // the constants [C.ATTR_vs_a_Position, C.ATTR_vs_a_Color, C.ATTR_vs_a_Texcoord0] are generated by sokol-shdc
112 pipdesc.layout.attrs[C.ATTR_vs_a_Position].format = .float3 // x,y,z as f32
113 pipdesc.layout.attrs[C.ATTR_vs_a_Normal].format = .float3 // x,y,z as f32
114 // pipdesc.layout.attrs[C.ATTR_vs_a_Color].format = .ubyte4n // color as u32
115 pipdesc.layout.attrs[C.ATTR_vs_a_Texcoord0].format = .float2 // u,v as f32
116 // pipdesc.layout.attrs[C.ATTR_vs_a_Texcoord0].format = .short2n // u,v as u16
117 pipdesc.index_type = .uint32
118
119 color_state := gfx.ColorTargetState{
120 blend: gfx.BlendState{
121 enabled: true
122 src_factor_rgb: .src_alpha
123 dst_factor_rgb: .one_minus_src_alpha
124 }
125 }
126 pipdesc.colors[0] = color_state
127
128 pipdesc.depth = gfx.DepthState{
129 write_enabled: true
130 compare: .less_equal
131 }
132 pipdesc.cull_mode = .front
133
134 pipdesc.label = &char('pip_part_${in_part:03}'.str)
135
136 // shader
137 pipdesc.shader = shader
138
139 res.bind.vertex_buffers[0] = vbuf
140 res.bind.index_buffer = ibuf
141 res.bind.fs.images[C.SLOT_tex] = texture
142 res.bind.fs.samplers[C.SLOT_smp] = sampler
143 res.pipeline = gfx.make_pipeline(&pipdesc)
144 // println('Buffers part [${in_part}] init done!')
145
146 return res
147}
148
149/******************************************************************************
150* Render functions
151******************************************************************************/
152// aggregate all the part by materials
153pub fn (mut obj_part ObjPart) init_render_data(texture gfx.Image, sampler gfx.Sampler) {
154 // create shader
155 // One shader for all the model
156 shader := gfx.make_shader(voidptr(C.gouraud_shader_desc(gfx.query_backend())))
157
158 mut part_dict := map[string][]int{}
159 for i, p in obj_part.part {
160 if p.faces.len > 0 {
161 part_dict[p.material] << i
162 }
163 }
164 obj_part.rend_data.clear()
165 // println("Material dict: ${obj_part.mat_map.keys()}")
166
167 for k, v in part_dict {
168 // println("${k} => Parts ${v}")
169
170 mut txt := texture
171 mut smp := sampler
172
173 if k in obj_part.mat_map {
174 mat_map := obj_part.mat[obj_part.mat_map[k]]
175 if 'map_Kd' in mat_map.maps {
176 file_name := mat_map.maps['map_Kd']
177 if file_name in obj_part.texture {
178 txt = obj_part.texture[file_name]
179 // println("Texture [${file_name}] => from CACHE")
180 } else {
181 txt, smp = load_texture(file_name)
182 obj_part.texture[file_name] = txt
183 obj_part.sampler[file_name] = smp
184 // println("Texture [${file_name}] => LOADED")
185 }
186 }
187 }
188 // key := obj_part.texture.keys()[0]
189 // obj_part.rend_data << obj_part.create_pipeline(v, shader, obj_part.texture[key])
190 obj_part.rend_data << obj_part.create_pipeline(v, shader, txt, smp)
191 }
192 // println("Texture array len: ${obj_part.texture.len}")
193 // println("Calc bounding box.")
194 obj_part.calc_bbox()
195 println('init_render_data DONE!')
196}
197
198pub fn (obj_part ObjPart) bind_and_draw(rend_data_index int, in_data Shader_data) u32 {
199 // apply the pipeline and bindings
200 mut part_render_data := obj_part.rend_data[rend_data_index]
201
202 // pass light position
203 mut tmp_fs_params := Tmp_fs_param{}
204 tmp_fs_params.light = in_data.fs_data.light
205
206 if part_render_data.material in obj_part.mat_map {
207 mat_index := obj_part.mat_map[part_render_data.material]
208 mat := obj_part.mat[mat_index]
209
210 // ambient
211 tmp_fs_params.ka = in_data.fs_data.ka
212 if 'Ka' in mat.ks {
213 tmp_fs_params.ka = mat.ks['Ka']
214 }
215
216 // specular
217 tmp_fs_params.ks = in_data.fs_data.ks
218 if 'Ks' in mat.ks {
219 tmp_fs_params.ks = mat.ks['Ks']
220 }
221
222 // specular exponent Ns
223 if 'Ns' in mat.ns {
224 tmp_fs_params.ks.e[3] = mat.ns['Ns'] / 1000.0
225 } else {
226 // default value is 10
227 tmp_fs_params.ks.e[3] = f32(10) / 1000.0
228 }
229
230 // diffuse
231 tmp_fs_params.kd = in_data.fs_data.kd
232 if 'Kd' in mat.ks {
233 tmp_fs_params.kd = mat.ks['Kd']
234 }
235
236 // alpha/transparency
237 if 'Tr' in mat.ns {
238 tmp_fs_params.kd.e[3] = mat.ns['Tr']
239 }
240 }
241
242 gfx.apply_pipeline(part_render_data.pipeline)
243 gfx.apply_bindings(part_render_data.bind)
244
245 vs_uniforms_range := gfx.Range{
246 ptr: in_data.vs_data
247 size: usize(in_data.vs_len)
248 }
249 fs_uniforms_range := gfx.Range{
250 ptr: unsafe { &tmp_fs_params }
251 size: usize(in_data.fs_len)
252 }
253
254 gfx.apply_uniforms(.vs, C.SLOT_vs_params, &vs_uniforms_range)
255 gfx.apply_uniforms(.fs, C.SLOT_fs_params, &fs_uniforms_range)
256 gfx.draw(0, int(part_render_data.n_vert), 1)
257 return part_render_data.n_vert
258}
259
260pub fn (obj_part ObjPart) bind_and_draw_all(in_data Shader_data) u32 {
261 mut n_vert := u32(0)
262 // println("Parts: ${obj_part.rend_data.len}")
263 for i, _ in obj_part.rend_data {
264 n_vert += obj_part.bind_and_draw(i, in_data)
265 }
266 return n_vert
267}
268
269pub fn (mut obj_part ObjPart) calc_bbox() {
270 obj_part.max = m4.Vec4{
271 e: [f32(-math.max_f32), -math.max_f32, -math.max_f32, 0]!
272 }
273 obj_part.min = m4.Vec4{
274 e: [f32(math.max_f32), math.max_f32, math.max_f32, 0]!
275 }
276 for v in obj_part.v {
277 if v.e[0] > obj_part.max.e[0] {
278 obj_part.max.e[0] = v.e[0]
279 }
280 if v.e[1] > obj_part.max.e[1] {
281 obj_part.max.e[1] = v.e[1]
282 }
283 if v.e[2] > obj_part.max.e[2] {
284 obj_part.max.e[2] = v.e[2]
285 }
286
287 if v.e[0] < obj_part.min.e[0] {
288 obj_part.min.e[0] = v.e[0]
289 }
290 if v.e[1] < obj_part.min.e[1] {
291 obj_part.min.e[1] = v.e[1]
292 }
293 if v.e[2] < obj_part.min.e[2] {
294 obj_part.min.e[2] = v.e[2]
295 }
296 }
297 val1 := obj_part.max.mod3()
298 val2 := obj_part.min.mod3()
299 if val1 > val2 {
300 obj_part.radius = f32(val1)
301 } else {
302 obj_part.radius = f32(val2)
303 }
304 // println("BBox: ${obj_part.min} <=> ${obj_part.max}\nRadius: ${obj_part.radius}")
305}
306