Skip to content

Commit

Permalink
Add basic fetch wrapper for loading obj, mtl and texture files
Browse files Browse the repository at this point in the history
  • Loading branch information
thetarnav committed Jan 11, 2025
1 parent 22386a4 commit efeac69
Show file tree
Hide file tree
Showing 6 changed files with 576 additions and 308 deletions.
88 changes: 76 additions & 12 deletions example/setup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as wasm from "../wasm/runtime.js"
import * as mem from "../wasm/memory.js"

import {IS_DEV, RELOAD_URL, WASM_FILENAME} from "./_config.js"

Expand Down Expand Up @@ -103,36 +104,99 @@ Wasm instance
* @property {Example_Start } start
* @property {Example_Frame } frame
* @property {Example_On_Window_Resize} on_window_resize
* @property {Fetch_Alloc} fetch_alloc
*
* @typedef {wasm.OdinExports & Example_Exports} Wasm_Exports
*
* @callback Example_Start
* @param {Example_Kind} example_type
* @param {wasm.rawptr } ctx
* @returns {wasm.bool }
* @param {mem.rawptr } ctx
* @returns {mem.bool }
*
* @callback Example_Frame
* @param {wasm.f32 } delta
* @param {wasm.rawptr} ctx
* @param {mem.f32 } delta
* @param {mem.rawptr} ctx
* @returns {void }
*
* @callback Example_On_Window_Resize
* @param {wasm.f32} window_w
* @param {wasm.f32} window_h
* @param {wasm.f32} canvas_w
* @param {wasm.f32} canvas_h
* @param {wasm.f32} canvas_x
* @param {wasm.f32} canvas_y
* @param {wasm.rawptr} ctx
* @param {mem.f32} window_w
* @param {mem.f32} window_h
* @param {mem.f32} canvas_w
* @param {mem.f32} canvas_h
* @param {mem.f32} canvas_x
* @param {mem.f32} canvas_y
* @param {mem.rawptr} ctx
* @returns {void}
*
* @callback Fetch_Alloc
* @param {mem.rawptr} res_ptr
* @param {mem.int} data_len
*/

/*
Fetch_Status :: enum u8 {
Idle,
Loading,
Error,
Done,
}
Fetch_Resource :: struct {
status: Fetch_Status, // u8 4 4
data: []byte, // [ptr, len] 8 12
url: string, // [ptr, len] 8 20
allocator: runtime.Allocator, // [ptr, ptr] 8 28
}
*/

const wasm_state = wasm.makeWasmState()
const webgl_state = wasm.webgl.makeWebGLState()
const ctx2d_state = new wasm.ctx2d.Ctx2d_State()

const src_instance = await wasm.fetchInstanciateWasm(WASM_FILENAME, {
env: {}, // TODO
env: {
/**
@param {mem.rawptr} res_ptr
@returns {void} */
fetch(res_ptr) {

let data = new DataView(exports.memory.buffer)
let url = mem.load_string(data, res_ptr+12)
mem.store_u8(data, res_ptr, 1)

;(async () => {
try {
let r = await fetch(url)
let bytes = await r.arrayBuffer()

exports.fetch_alloc(res_ptr, bytes.byteLength)
data = new DataView(exports.memory.buffer)
mem.store_bytes(
exports.memory.buffer,
mem.load_ptr(data, res_ptr+mem.REG_SIZE),
new Uint8Array(bytes))
mem.store_u8(data, res_ptr, 3)
// if (r.body == null) {
// throw new Error('No response body')
// }
// let reader = r.body.getReader()
// for (;;) {
// let res = await reader.read()
// if (res.value != null) {
// console.log('data', res.value)
// exports.fetch_alloc(res_ptr, res.value.byteLength)
// }
// if (res.done) {
// break
// }
// }
} catch (err) {
console.error('Fetch error:', err)
let data = new DataView(exports.memory.buffer)
mem.store_u8(data, res_ptr, 2)
}
})()
},
},
odin_env: wasm.env .makeOdinEnv (wasm_state),
odin_dom: wasm.dom .makeOdinDOM (wasm_state),
webgl : wasm.webgl.makeOdinWebGL (wasm_state, webgl_state),
Expand Down
136 changes: 100 additions & 36 deletions example/windmill.odin
Original file line number Diff line number Diff line change
@@ -1,14 +1,62 @@
#+private file
package example

import "base:runtime"
import glm "core:math/linalg/glsl"
import gl "../wasm/webgl"
import "../obj"

foreign import "env"

@(default_calling_convention = "contextless")
foreign env {
@(link_name="fetch")
_fetch :: proc (resource: ^Fetch_Resource) ---
}

fetch :: proc (resource: ^Fetch_Resource, url: string, allocator := context.allocator) {
resource.allocator = allocator
resource.url = url
_fetch(resource)
}

Fetch_Status :: enum u8 {
Idle,
Loading,
Error,
Done,
}
Fetch_Resource :: struct {
status: Fetch_Status,
data: []byte,
url: string,
allocator: runtime.Allocator,
}
#assert(size_of(Fetch_Resource) == 28)

@export
fetch_alloc :: proc (resource: ^Fetch_Resource, len: int) {
data, err := make([]byte, len, resource.allocator)
if err != nil {
panic("Alloc error")
}
resource.data = data
}

Load_State :: enum {
Init,
Obj_Done,
Mtl_Done,
}

@private
State_Windmill :: struct {
program: gl.Program,
rotation: mat4,
shapes: []Shape,
obj_res: Fetch_Resource,
mtl_res: Fetch_Resource,
load: Load_State,
}

Shape :: struct {
Expand All @@ -19,60 +67,76 @@ Shape :: struct {
material: obj.Material,
}

chair_obj_src := #load("./public/windmill.obj", string)
chair_mtl_src := #load("./public/windmill.mtl", string)

@private
setup_windmill :: proc(s: ^State_Windmill, program: gl.Program) {

s.program = program

gl.Enable(gl.CULL_FACE) // don't draw back faces
gl.Enable(gl.DEPTH_TEST) // draw only closest faces

obj_data, obj_parse_err := obj.parse_file(chair_obj_src, context.temp_allocator)
if obj_parse_err != nil {
fmt.eprintf("Obj parse error: %v", obj_parse_err)
}

materials, mtl_parse_err := obj.parse_mtl_file(chair_mtl_src)
if obj_parse_err != nil {
fmt.eprintf("Mtl parse error: %v", mtl_parse_err)
}
fetch(&s.obj_res, "./public/windmill.obj")

extent_min, extent_max := get_extents(obj_data.positions[:])

s.shapes = make([]Shape, len(obj_data.objects))
/* Init rotation */
s.rotation = 1
}

for &shape, i in s.shapes {
o := obj_data.objects[i]
@private
frame_windmill :: proc(s: ^State_Windmill, delta: f32) {

shape.vao = gl.CreateVertexArray()
if s.obj_res.status == .Done && s.load == .Init {
s.load = .Obj_Done

shape.vertices = convert_obj_vertices(o.vertices[:])
obj_data, obj_parse_err := obj.parse_file(string(s.obj_res.data), context.temp_allocator)
if obj_parse_err != nil {
fmt.eprintf("Obj parse error: %v", obj_parse_err)
}

correct_extents(shape.vertices.position[:len(shape.vertices)], extent_min, extent_max, -200, 200)
extent_min, extent_max := get_extents(obj_data.positions[:])

s.shapes = make([]Shape, len(obj_data.objects))

for m in materials {
if m.name == o.material {
shape.material = m
break
}
}
for &shape, i in s.shapes {
o := obj_data.objects[i]

gl.BindVertexArray(shape.vao)
shape.material.name = o.material
shape.material.opacity = 1 // display it even before material is loaded}

input_locations_chair(&shape.locations, program)
shape.vao = gl.CreateVertexArray()

attribute(shape.locations.a_position, gl.CreateBuffer(), shape.vertices.position[:len(shape.vertices)])
attribute(shape.locations.a_color, gl.CreateBuffer(), shape.vertices.color[:len(shape.vertices)])
attribute(shape.locations.a_normal, gl.CreateBuffer(), shape.vertices.normal[:len(shape.vertices)])
shape.vertices = convert_obj_vertices(o.vertices[:])

correct_extents(shape.vertices.position[:len(shape.vertices)], extent_min, extent_max, -200, 200)

gl.BindVertexArray(shape.vao)

input_locations_chair(&shape.locations, s.program)

attribute(shape.locations.a_position, gl.CreateBuffer(), shape.vertices.position[:len(shape.vertices)])
attribute(shape.locations.a_color, gl.CreateBuffer(), shape.vertices.color[:len(shape.vertices)])
attribute(shape.locations.a_normal, gl.CreateBuffer(), shape.vertices.normal[:len(shape.vertices)])
}

fetch(&s.mtl_res, obj_data.mtllibs[0])
}

/* Init rotation */
s.rotation = 1
}
if s.mtl_res.status == .Done && s.load == .Obj_Done {
s.load = .Mtl_Done

@private
frame_windmill :: proc(s: ^State_Windmill, delta: f32) {
materials, mtl_parse_err := obj.parse_mtl_file(string(s.mtl_res.data))
if mtl_parse_err != nil {
fmt.eprintf("Mtl parse error: %v", mtl_parse_err)
}

for &shape in s.shapes {
for m in materials {
if m.name == shape.material.name {
shape.material = m
break
}
}
}
}

gl.Viewport(0, 0, canvas_res.x, canvas_res.y)
gl.ClearColor(0, 0, 0, 0)
Expand Down
Loading

0 comments on commit efeac69

Please sign in to comment.