Compare commits
12 commits
6180767bf2
...
29e4735626
Author | SHA1 | Date | |
---|---|---|---|
29e4735626 | |||
40b70003ec | |||
f71ae37821 | |||
f03d1929cb | |||
1abd60e48f | |||
61c1986c92 | |||
55d0c859ab | |||
dca0123f13 | |||
1640040f93 | |||
01229cc2b5 | |||
c1291c0412 | |||
7efd3bb160 |
16 changed files with 252 additions and 137 deletions
|
@ -406,7 +406,7 @@ when compileOption("threads"):
|
|||
proc workerThreadProc() {.thread.} =
|
||||
# TODO: handle errors
|
||||
while true:
|
||||
let to_decode = decode_chan.recv()
|
||||
var to_decode = decode_chan.recv()
|
||||
if to_decode.tex == nil:
|
||||
break
|
||||
proc cb(tex: Texture, data: SliceMem[byte]) =
|
||||
|
@ -415,7 +415,7 @@ when compileOption("threads"):
|
|||
compressed_return_chan.send((callback: to_decode.callback_compressed, tex: tex, data: data, refdata: refdata))
|
||||
let cb_out = if to_decode.callback_uncompressed != nil: cb else: nil
|
||||
let cbc_out = if to_decode.callback_compressed != nil: cbc else: nil
|
||||
loadOptimized(to_decode.tex, to_decode.slices, cb_out, cbc_out,
|
||||
loadOptimized(move to_decode.tex, to_decode.slices, cb_out, cbc_out,
|
||||
to_decode.flip, to_decode.min_channels)
|
||||
|
||||
decode_chan.open()
|
||||
|
|
|
@ -208,7 +208,7 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
|
|||
@[
|
||||
&"precision {precision} float;",
|
||||
&"precision {precision} int;",
|
||||
&"precision {precision} sampler2DShadow;",
|
||||
&"precision {precision} sampler2DArrayShadow;",
|
||||
]
|
||||
else:
|
||||
@[]
|
||||
|
@ -438,6 +438,7 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
|
|||
if success == 0:
|
||||
let gl_log = get_shader_info_log(fragment_shader)
|
||||
let error_msg = &"Error compiling fragment shader of material {material.name}\n{gl_log}"
|
||||
echo error_msg
|
||||
# console_error fragment
|
||||
let lines = fragment.split("\n")
|
||||
if "ERROR: 0:" in gl_log:
|
||||
|
@ -452,8 +453,8 @@ proc initShader*(self: Shader, engine: MyouEngine, material: Material,
|
|||
for i in max(1, line - 1000) ..< min(line + 4, lines.len):
|
||||
console_error(&"{i} {lines[i - 1]}")
|
||||
else:
|
||||
for i in 1 ..< lines.len:
|
||||
console_error(&"{i} {lines[i - 1]}")
|
||||
for i,line in lines:
|
||||
console_error(&"{i+1} {line}")
|
||||
console_error(error_msg)
|
||||
return
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, addr success)
|
||||
|
|
|
@ -260,7 +260,7 @@ proc bind_it*(texture: Texture, reserve_slot: static[int32] = -1, needs_active_t
|
|||
# if old_tex.sampler_object != 0:
|
||||
# glBindSampler(cast[GLuint](bound_unit), 0)
|
||||
bound_textures[bound_unit] = nil
|
||||
glBindTexture(texture.storage.target, texture.storage.tex.GLuint)
|
||||
glBindTexture(texture.storage.target, GLuint(texture.storage.tex))
|
||||
# if texture.sampler_object != 0:
|
||||
# glBindSampler(bound_unit.GLuint, texture.sampler_object)
|
||||
bound_textures[bound_unit] = texture
|
||||
|
@ -554,8 +554,8 @@ proc getTexturePixels*(self: Texture): TexturePixels =
|
|||
proc setMipmapRange*(self: Texture, first = 0, last = 1000) {.gcsafe.} =
|
||||
self.bind_it(needs_active_texture=true)
|
||||
self.mipmap_range = (first, last)
|
||||
glTexParameteri(self.storage.target.GLenum, GL_TEXTURE_BASE_LEVEL, first.GLint);
|
||||
glTexParameteri(self.storage.target.GLenum, GL_TEXTURE_MAX_LEVEL, last.GLint);
|
||||
glTexParameteri(self.storage.target, GL_TEXTURE_BASE_LEVEL, first.GLint);
|
||||
glTexParameteri(self.storage.target, GL_TEXTURE_MAX_LEVEL, last.GLint);
|
||||
|
||||
func vec3size*(self: Texture, mip_level = -1): Vec3 =
|
||||
let depth = if self.tex_type == Tex2DArray: 1 else: self.depth
|
||||
|
|
|
@ -44,7 +44,7 @@ import std/strutils
|
|||
import std/strformat
|
||||
import std/bitops
|
||||
import std/options
|
||||
# import std/tables
|
||||
import std/json
|
||||
import vmath except Quat
|
||||
import ../quat
|
||||
|
||||
|
@ -101,9 +101,9 @@ method openAssetFile*(self: BlendLoader, path: string) =
|
|||
# self.on_destroy = OnDestroy(destructor: proc() = self.close())
|
||||
self.blend_file_path = path
|
||||
|
||||
proc loadAsync(self: BlendLoader, callback: proc()) =
|
||||
proc loadAsync(self: BlendLoader, callback: proc(err: string)) =
|
||||
if self.blend_file != nil:
|
||||
callback()
|
||||
callback("")
|
||||
else:
|
||||
self.close()
|
||||
self.resource = loadUri(self.blend_file_path,
|
||||
|
@ -111,9 +111,7 @@ proc loadAsync(self: BlendLoader, callback: proc()) =
|
|||
self.resource = nil
|
||||
if ok:
|
||||
self.blend_file = openBlendFile(self.blend_file_path, data.data, data.byte_len)
|
||||
callback()
|
||||
else:
|
||||
raise IOError.newException err
|
||||
callback(err)
|
||||
)
|
||||
|
||||
type BlenderObTypes = enum
|
||||
|
@ -265,6 +263,31 @@ proc makeMaterialAndTextures(self: BlendLoader;
|
|||
echo "Material: ", bmat.id.name.str.strip2
|
||||
raise e
|
||||
|
||||
proc idPropertiesToJsonTable(prop: FNode): Table[string, JsonNode] =
|
||||
var prop = prop
|
||||
while prop.valid:
|
||||
let name = prop.name.str
|
||||
let val = prop.data.val.i32[0]
|
||||
var cstr: cstring = ""
|
||||
if prop.data["pointer"].valid:
|
||||
var p = prop.data["pointer"]
|
||||
p.set_type("char")
|
||||
cstr = p.cstr
|
||||
case prop["type"].i8[0]
|
||||
of 0: # string
|
||||
result[name] = %($cstr)
|
||||
of 1: # int
|
||||
result[name] = %val
|
||||
of 5: # array
|
||||
let subtype = prop.subtype.i8[0]
|
||||
of 8: # float
|
||||
let f = cast[ptr float64](prop.data.val.i32[0].addr)[]
|
||||
result[name] = %f
|
||||
of 10: # bool
|
||||
result[name] = %bool(val)
|
||||
else: discard
|
||||
prop = prop.next
|
||||
|
||||
proc loadObjectImpl(self: BlendLoader, scene: Scene, obn: FNode): (GameObject, string) =
|
||||
let name = obn.id.name.str.strip2
|
||||
let data = obn.data
|
||||
|
@ -482,6 +505,10 @@ proc loadObjectImpl(self: BlendLoader, scene: Scene, obn: FNode): (GameObject, s
|
|||
|
||||
ob.object_color = obn.col.f32.vec4
|
||||
|
||||
let prop = obn.id.properties
|
||||
if prop.valid:
|
||||
ob.properties = idPropertiesToJsonTable(prop.data.group.first)
|
||||
|
||||
return (ob, parent_name)
|
||||
|
||||
proc loadSceneImpl(self: BlendLoader, scn: FNode, scene: Scene) =
|
||||
|
@ -573,9 +600,12 @@ proc get_active_scene_name(self: BlendLoader): string =
|
|||
if scene.valid:
|
||||
return scene.id.name.str[2 .. ^1]
|
||||
|
||||
method loadScene*(self: BlendLoader, name: string="", scene: Scene=nil, callback: proc(scene: Scene)) =
|
||||
method loadScene*(self: BlendLoader, name: string="", scene: Scene=nil, callback: proc(err: string, scene: Scene)) =
|
||||
assert self.blend_file_path != "", "Blend file is not loaded"
|
||||
self.loadAsync proc() =
|
||||
self.loadAsync proc(err: string) =
|
||||
if err != "":
|
||||
callback(err, nil)
|
||||
return
|
||||
assert self.blend_file != nil, "Error loading blend file " & self.blend_file_path
|
||||
var name = name
|
||||
if name == "":
|
||||
|
@ -588,8 +618,19 @@ method loadScene*(self: BlendLoader, name: string="", scene: Scene=nil, callback
|
|||
let was_first_scene = self.engine.scenes.len == 0
|
||||
if scene == nil:
|
||||
scene = self.engine.new_scene(name=name)
|
||||
self.loadSceneImpl(node, scene)
|
||||
callback(scene)
|
||||
try:
|
||||
self.loadSceneImpl(node, scene)
|
||||
except Exception as e:
|
||||
for line in e.getStackTrace.split '\n':
|
||||
echo line
|
||||
echo getCurrentExceptionMsg()
|
||||
scene.destroy()
|
||||
callback(getCurrentExceptionMsg(), nil)
|
||||
return
|
||||
callback("", scene)
|
||||
if scene.name notin self.engine.new_scenes:
|
||||
# it was deleted
|
||||
return
|
||||
# TODO: when loading is async, move this stuff after loading has
|
||||
# finished
|
||||
if was_first_scene and not scene.enabled:
|
||||
|
|
|
@ -122,7 +122,7 @@ proc hash*(n: FNode): Hash =
|
|||
|
||||
const BASIC_TYPE_LENGTHS = [1, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 0, 1]
|
||||
|
||||
template `[]`*(blocks: NamedBlocks, s: string): untyped = blocks[[s[0],s[1]]]
|
||||
template `[]`*(blocks: NamedBlocks, s: string): untyped = blocks.getOrDefault([s[0],s[1]])
|
||||
# iterator items*(s: NamedBlocks): seq[FNode] {.borrow.}
|
||||
# iterator pairs*(s: NamedBlocks): (array[2, char], seq[FNode]) {.borrow.}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ when defined(nimdoc):
|
|||
method openAssetFile*(self: Loader, path: string) {.base.} =
|
||||
discard
|
||||
method loadScene*(self: Loader, name: string="", scene: Scene=nil,
|
||||
callback: proc(scene: Scene)) {.base.} =
|
||||
callback: proc(err: string, scene: Scene)) {.base.} =
|
||||
discard
|
||||
method loadImageImpl*(self: Loader) {.base.} =
|
||||
discard
|
||||
|
|
|
@ -201,7 +201,7 @@ proc run*(self: MyouEngine) =
|
|||
when not defined(nimdoc):
|
||||
start_platform_main_loop(self, myou_main_loop)
|
||||
|
||||
proc loadScene*(self: MyouEngine, uri: string, callback: proc(scene: Scene), name = "", ext = "") =
|
||||
proc loadScene*(self: MyouEngine, uri: string, callback: proc(err: string, scene: Scene), name = "", ext = "") =
|
||||
let ext = if ext == "":
|
||||
uri.rsplit('.', 1)[1]
|
||||
else:
|
||||
|
|
|
@ -92,7 +92,8 @@ proc get_local_Z_vector*(self: GameObject): Vec3
|
|||
proc get_world_X_vector*(self: GameObject): Vec3
|
||||
proc get_world_Y_vector*(self: GameObject): Vec3
|
||||
proc get_world_Z_vector*(self: GameObject): Vec3
|
||||
proc get_dimensions*(self: GameObject): Vec3
|
||||
proc get_local_dimensions*(self: GameObject): Vec3
|
||||
proc get_world_dimensions*(self: GameObject): Vec3
|
||||
proc get_world_scale*(self: GameObject): float
|
||||
proc get_world_scale_vector*(self: GameObject): Vec3
|
||||
proc get_local_size*(self: GameObject): float
|
||||
|
@ -241,10 +242,12 @@ proc get_world_matrix*(self: GameObject): Mat4 =
|
|||
return self.world_matrix
|
||||
|
||||
proc get_local_matrix*(self: GameObject): Mat4 =
|
||||
var (x,y,z,w) = self.rotation.toTuple
|
||||
# if self.rotation_order != Quaternion:
|
||||
# let q = fromEulerOrder(self.rotation, self.rotation_order)
|
||||
# let (x,y,z,w) = q.toTuple
|
||||
var q = if self.rotation_order == Quaternion:
|
||||
self.rotation
|
||||
else:
|
||||
to_quat(self.rotation.xyz, self.rotation_order)
|
||||
q = normalize(q)
|
||||
var (x,y,z,w) = q.toTuple
|
||||
let scl = self.scale
|
||||
# TODO: test negative scales
|
||||
let pos = self.position
|
||||
|
@ -390,6 +393,9 @@ proc clone*(self: GameObject,
|
|||
scene: Scene=self.scene,
|
||||
instance_body: bool=true
|
||||
): GameObject =
|
||||
## Makes a clone of the object and its children (unless `recursive` is
|
||||
## false).
|
||||
|
||||
case self.otype:
|
||||
of TGameObject:
|
||||
var n = new GameObject
|
||||
|
@ -438,9 +444,17 @@ proc clone_impl*(self: GameObject, n: var GameObject, recursive: bool,
|
|||
return n
|
||||
|
||||
proc set_parent*(self: GameObject, parent: GameObject, keep_transform: bool=true) =
|
||||
## Sets the parent of the object, while keeping the same transform in world
|
||||
## space (unless you set `keep_transform` to false).
|
||||
|
||||
# TODO: there should be no distinction between this and parent_to
|
||||
self.scene.make_parent(parent, self, keep_transform=keep_transform)
|
||||
|
||||
proc parent_to*(self: GameObject, parent: GameObject, keep_transform: bool=true) =
|
||||
## Sets the parent of the object, while adding it to the same scene if
|
||||
## needed, as well as keeping the same transform in world space (unless you
|
||||
## set `keep_transform` to false).
|
||||
|
||||
# TODO move this to make_parent
|
||||
if self.scene == nil:
|
||||
parent.scene.add_object(self)
|
||||
|
@ -450,12 +464,17 @@ proc clear_parent*(self: GameObject, keep_transform: bool=true) =
|
|||
self.scene.clear_parent(self, keep_transform=keep_transform)
|
||||
|
||||
proc get_top_ancestor*(self: GameObject, top_level_parents: seq[GameObject] = @[]): GameObject =
|
||||
## Find the topmost ancestor (e.g. parent of parent of parent...) or an
|
||||
## object contained in `top_level_parents`, whatever happens first.
|
||||
var ob = self
|
||||
while ob.parent != nil and ob.parent notin top_level_parents:
|
||||
ob = ob.parent
|
||||
return ob
|
||||
|
||||
iterator children_recursive*(self: GameObject, include_self: static[bool] = false): GameObject =
|
||||
## Iterate all the descendants of the object, i.e. its children, the
|
||||
## children's children an so on.
|
||||
|
||||
# NOTE: This was written before the knowledge of closure iterators
|
||||
# (by default iterators are inline and don't allow recursion)
|
||||
when include_self:
|
||||
|
@ -481,11 +500,20 @@ iterator children_recursive*(self: GameObject, include_self: static[bool] = fals
|
|||
else:
|
||||
break
|
||||
|
||||
template children_recursive_and_self*(self: GameObject): GameObject =
|
||||
## Iterate all the descendants of the object, and include itself in the
|
||||
## iteration.
|
||||
children_recursive(self, include_self = true)
|
||||
|
||||
proc remove*(self: GameObject, recursive: bool = true) =
|
||||
## Remove the object from the scene it is in, without destroying it.
|
||||
## It will also remove its children unless you set `recursive` to false.
|
||||
if self.scene.nonNil:
|
||||
self.scene.remove_object(self, recursive)
|
||||
|
||||
proc destroy*(self: GameObject, recursive: bool = true) =
|
||||
## Destroy the object and free all unused resources previously used by it.
|
||||
## It will also destroy its children unless you set `recursive` to false.
|
||||
if self.is_mesh and self.get_mesh.data.nonNil:
|
||||
self.get_mesh.data.gpu_buffers_delete()
|
||||
self.get_mesh.materials.setLen 0
|
||||
|
@ -516,27 +544,41 @@ proc convert_bone_child_to_bone_parent*(self: GameObject) =
|
|||
bone.parent_object = self
|
||||
|
||||
proc get_local_X_vector*(self: GameObject): Vec3 =
|
||||
## Get the vector of the X axis of the object in local space.
|
||||
return self.get_local_matrix[0].xyz
|
||||
|
||||
proc get_local_Y_vector*(self: GameObject): Vec3 =
|
||||
## Get the vector of the Y axis of the object in local space.
|
||||
return self.get_local_matrix[1].xyz
|
||||
|
||||
proc get_local_Z_vector*(self: GameObject): Vec3 =
|
||||
## Get the vector of the Z axis of the object in local space.
|
||||
return self.get_local_matrix[2].xyz
|
||||
|
||||
proc get_world_X_vector*(self: GameObject): Vec3 =
|
||||
## Get the vector of the X axis of the object in world space.
|
||||
return self.get_world_matrix[0].xyz
|
||||
|
||||
proc get_world_Y_vector*(self: GameObject): Vec3 =
|
||||
## Get the vector of the Y axis of the object in world space.
|
||||
return self.get_world_matrix[1].xyz
|
||||
|
||||
proc get_world_Z_vector*(self: GameObject): Vec3 =
|
||||
## Get the vector of the Z axis of the object in world space.
|
||||
return self.get_world_matrix[2].xyz
|
||||
|
||||
proc get_dimensions*(self: GameObject): Vec3 =
|
||||
proc get_local_dimensions*(self: GameObject): Vec3 =
|
||||
## Calculate the local dimensions of the object, i.e. the size of the
|
||||
## bounding box with the scale of the object.
|
||||
let (a,b) = self.bound_box
|
||||
return (b-a) * self.scale
|
||||
|
||||
proc get_world_dimensions*(self: GameObject): Vec3 =
|
||||
## Calculate the world dimensions of the object, i.e. the size of the
|
||||
## bounding box in world scale.
|
||||
let (a,b) = self.bound_box
|
||||
return (b-a) * self.get_world_scale_vector
|
||||
|
||||
proc get_world_scale*(self: GameObject): float =
|
||||
# TODO: use the diagonal of a sphere instead?
|
||||
# i.e. length((wm*vec3(1).normalize,0).xyz)
|
||||
|
@ -548,46 +590,46 @@ proc get_world_scale*(self: GameObject): float =
|
|||
return s
|
||||
|
||||
proc get_world_scale_vector*(self: GameObject): Vec3 =
|
||||
## Gets the world scale vector in local coordinates (i.e. relative to the
|
||||
## current object orientation).
|
||||
let rotation = self.get_world_rotation
|
||||
let wm = self.world_matrix
|
||||
let rot_inv = inverse(rotation)
|
||||
var wm3 = wm.to_mat3
|
||||
let wm3_b = rot_inv.to_mat3
|
||||
wm3 = wm3_b * wm3
|
||||
let scale_x_sign = copy_sign(1.0, wm3[0,0])
|
||||
let scale_y_sign = copy_sign(1.0, wm3[1,1])
|
||||
let scale_z_sign = copy_sign(1.0, wm3[2,2])
|
||||
return vec3(
|
||||
wm[0].xyz.length * scale_x_sign,
|
||||
wm[1].xyz.length * scale_y_sign,
|
||||
wm[2].xyz.length * scale_z_sign,
|
||||
copy_sign(wm[0].xyz.length, wm3[0,0]),
|
||||
copy_sign(wm[1].xyz.length, wm3[1,1]),
|
||||
copy_sign(wm[2].xyz.length, wm3[2,2]),
|
||||
)
|
||||
|
||||
proc get_local_size*(self: GameObject): float =
|
||||
let d = self.get_dimensions()
|
||||
## Calculates the size of the largest dimension of the object in local
|
||||
## coordinates.
|
||||
let d = self.get_local_dimensions()
|
||||
return max(max(d.x, d.y), d.z)
|
||||
|
||||
proc set_local_size*(self: GameObject, size: SomeFloat) =
|
||||
## Sets the size of the object in local cordinates, given by the desired
|
||||
## largest dimension of the object. The other two dimensions are scaled
|
||||
## proportionally. If the object dimensions are zero, no action is taken.
|
||||
let current_size = self.get_local_size
|
||||
if abs(current_size) > 1e-8:
|
||||
self.scale = self.scale * size / current_size
|
||||
return
|
||||
|
||||
proc get_world_size*(self: GameObject): float =
|
||||
let d = self.get_dimensions()
|
||||
let size = max(max(d.x, d.y), d.z)
|
||||
if self.parent != nil:
|
||||
let parent_z_scale = self.parent.get_world_Z_vector.length
|
||||
return size * parent_z_scale
|
||||
return size
|
||||
## Calculates the size of the largest dimension of the object in world
|
||||
## coordinates.
|
||||
let d = self.get_world_dimensions()
|
||||
return max(max(d.x, d.y), d.z)
|
||||
|
||||
proc set_world_size*(self: GameObject, size: SomeFloat) =
|
||||
var size = size
|
||||
if self.parent != nil:
|
||||
let parent_z_scale = self.parent.get_world_Z_vector.length
|
||||
if abs(parent_z_scale) > 1e-8:
|
||||
size /= parent_z_scale
|
||||
let current_size = self.get_local_size
|
||||
## Sets the size of the object in world cordinates, given by the desired
|
||||
## largest dimension of the object. The other two dimensions are scaled
|
||||
## proportionally. If the object dimensions are zero, no action is taken.
|
||||
let current_size = self.get_world_size
|
||||
if abs(current_size) > 1e-8:
|
||||
self.scale = self.scale * size / current_size
|
||||
return
|
||||
|
@ -600,11 +642,14 @@ proc set_name*(self: GameObject, name: string) =
|
|||
self.scene.set_ob_name(self, name)
|
||||
|
||||
proc local_to_world*(self: GameObject, point: Vec3): Vec3 =
|
||||
## Transforms a point in local coordinates to world coordinates.
|
||||
return self.get_world_matrix * point
|
||||
|
||||
proc world_to_local*(self: GameObject, point: Vec3): Vec3 =
|
||||
# TODO: check that determinant is not zero?
|
||||
# does the compiler optimize multiple calls to .determinant?
|
||||
let wm = self.get_world_matrix.inverse
|
||||
return wm * point
|
||||
## Transforms a point in world coordinates to local coordinates. If the
|
||||
## object has a scale of 0, the point is not transformed.
|
||||
let wm = self.get_world_matrix
|
||||
if wm.determinant != 0.0:
|
||||
return wm.inverse * point
|
||||
return point
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
import ../types
|
||||
import vmath except Quat
|
||||
import ../util
|
||||
import ../graphics/material
|
||||
|
||||
when defined(nimdoc):
|
||||
type TYPES* = LightType | Light
|
||||
|
@ -100,6 +101,9 @@ proc newLight*(engine: MyouEngine, name: string="",
|
|||
scene.add_object(this, name=name)
|
||||
return this
|
||||
|
||||
when defined(myouDebugShadows):
|
||||
var debug_mesh: Mesh
|
||||
|
||||
proc configure_shadow*(self: Light,
|
||||
camera: Camera = nil,
|
||||
max_distance: float32 = 0.0,
|
||||
|
@ -131,13 +135,16 @@ proc configure_shadow*(self: Light,
|
|||
# TODO: instead of a ground plane, we can just exclude the largest
|
||||
# convex object, as long as we can clamp the polygons outside the
|
||||
# frustum. Can we avoid clipping by setting W?
|
||||
|
||||
self.scene.update_all_matrices()
|
||||
var casters: seq[Vec3]
|
||||
for ob in self.scene.children:
|
||||
for ob in objects:
|
||||
if not (ob.is_mesh and ob.visible):
|
||||
continue
|
||||
let me = ob.get_mesh
|
||||
let bb = me.bound_box
|
||||
if bb[0] == bb[1]:
|
||||
continue
|
||||
let world_dim = (ob.world_matrix * vec4(bb[1] - bb[0], 0.0)).xyz
|
||||
var casts = true
|
||||
if world_dim.z < 0.00001:
|
||||
|
@ -155,9 +162,29 @@ proc configure_shadow*(self: Light,
|
|||
break
|
||||
casts = not all_above
|
||||
if casts:
|
||||
echo "adding casting ", ob.name
|
||||
for v in box_corners(bb):
|
||||
casters.add ob.world_matrix * v
|
||||
|
||||
if casters.len == 0:
|
||||
return
|
||||
when defined(myouDebugShadows):
|
||||
if debug_mesh.nonNil:
|
||||
debug_mesh.destroy()
|
||||
debug_mesh = self.engine.newMesh(vertex_count = 100, draw_method = Lines)
|
||||
debug_mesh.materials.add self.engine.newSolidMaterial("green", vec4(0,1,0,1))
|
||||
debug_mesh.visible = true
|
||||
self.scene.add_object debug_mesh
|
||||
|
||||
let hull = quickhull(casters)
|
||||
for f in hull:
|
||||
debug_mesh.add_vertex f.points[0], vec4(1)
|
||||
for v in f.points[1..^1]:
|
||||
debug_mesh.add_vertex v, vec4(1)
|
||||
debug_mesh.add_vertex v, vec4(1)
|
||||
debug_mesh.add_vertex f.points[0], vec4(1)
|
||||
debug_mesh.data.update_varray()
|
||||
|
||||
casters = quickhull_points(casters)
|
||||
let shadow = newSimpleShadowManager(self, use_camera = false)
|
||||
shadow.caster_bounding_points = casters
|
||||
|
|
|
@ -342,16 +342,18 @@ proc clear_vertices*(self: Mesh) =
|
|||
self.data.num_indices[0] = 0
|
||||
|
||||
proc remove_vertex*(self: Mesh, index: int) =
|
||||
# TODO: This is only valid for points mode
|
||||
if not (index >= 0):
|
||||
return
|
||||
# -1 or undefined
|
||||
let num_indices = self.data.num_indices
|
||||
return # ignore -1
|
||||
assert self.data.num_indices[0] > index, "Invalid index"
|
||||
let stride = self.data.stride
|
||||
# move the last vertex to the one in index
|
||||
let pos0 = num_indices[0] * stride
|
||||
let pos0 = self.data.num_indices[0] * stride
|
||||
let pos1 = index * stride
|
||||
var bytes = self.data.varrays[0].to(int8)
|
||||
bytes.copyWithin(pos1, pos0, pos0 + stride)
|
||||
for i in 0 ..< stride:
|
||||
bytes[pos1+i] = bytes[pos0+i]
|
||||
self.data.num_indices[0] -= 1
|
||||
|
||||
proc ensure_capacity*(self: Mesh, extra_elements: int) =
|
||||
if self.data == nil:
|
||||
|
@ -371,7 +373,7 @@ proc ensure_capacity*(self: Mesh, extra_elements: int) =
|
|||
while fpos >= cap:
|
||||
cap *= 2
|
||||
var va = newArrRef[float32](cap)
|
||||
varray.copy_bytes_to va
|
||||
copyMem(va.toPointer, varray.toPointer, bytelen)
|
||||
self.load_from_va_ia @[va]
|
||||
self.data.num_indices[0] = current_index
|
||||
|
||||
|
@ -381,8 +383,8 @@ proc ensure_capacity*(self: Mesh, extra_elements: int) =
|
|||
# let ilen = self.offsets[self.offsets.len - 1]
|
||||
# let offset = (self.pack_offset or 0) + buffer_offset
|
||||
# try:
|
||||
# let va = makeSeq[float32](data, offset, vlen)
|
||||
# let ia = makeSeq[uint16](data, offset + vlen * 4, ilen)
|
||||
# let va = newSeq[float32](data, offset, vlen)
|
||||
# let ia = newSeq[uint16](data, offset + vlen * 4, ilen)
|
||||
# except Exception as e:
|
||||
# let e = Error(&"Mesh {self.name} is corrupt")
|
||||
# raise e
|
||||
|
@ -518,7 +520,7 @@ proc generate_tangents*(self: MeshData, uv_layer_name, tangent_layer_name: strin
|
|||
for idx in ia.to int32:
|
||||
maxi = max(maxi, idx)
|
||||
# dump (max_len, maxi)
|
||||
var tangents = makeSeq[Vec3](max_len div stride_f.int)
|
||||
var tangents = newSeq[Vec3](max_len div stride_f.int)
|
||||
# for va, ia in zip(self.varrays, self.iarrays):
|
||||
for i in 0 ..< self.varrays.len:
|
||||
let va = self.varrays[i]
|
||||
|
@ -627,7 +629,7 @@ proc debug_print_vertices*(self: Mesh, starts: int = 0, ends: int = 10) =
|
|||
of Short:
|
||||
($cast[ptr array[16, uint16]](varray[offset].addr)[]).split(',')[0 ..< attr.count].join(",") & "]"
|
||||
# of HalfFloat:
|
||||
# let a = makeSeq[uint16](varray.buffer, offset, attr.count)
|
||||
# let a = newSeq[uint16](varray.buffer, offset, attr.count)
|
||||
# @[read_f16(a[0]), read_f16(a[1]), read_f16(a[2])]
|
||||
else: ""
|
||||
echo &"{attr.name} {data}"
|
||||
|
|
|
@ -20,10 +20,12 @@ proc get_program_info_log*(program: GLuint): string =
|
|||
glGetProgramInfoLog(program, logSize.GLsizei, nil, s.cstring)
|
||||
return s
|
||||
|
||||
let main_thread_id = getThreadID()
|
||||
when compileOption("threads"):
|
||||
let main_thread_id = getThreadID()
|
||||
|
||||
template handleError(x: untyped, t: string) =
|
||||
assert main_thread_id == getThreadID()
|
||||
when compileOption("threads"):
|
||||
assert main_thread_id == getThreadID()
|
||||
try:
|
||||
x
|
||||
except:
|
||||
|
|
|
@ -342,6 +342,13 @@ proc set_objects_auto_update_matrix*(self: Scene, objects: seq[GameObject], auto
|
|||
self.children_are_ordered = false
|
||||
|
||||
proc destroy*(self: Scene) =
|
||||
# This may not be necessary. TODO: test
|
||||
for ob in self.children:
|
||||
if ob.is_mesh:
|
||||
for m in ob.get_mesh.materials:
|
||||
if m != nil:
|
||||
for tex in m.textures.mvalues:
|
||||
tex.destroy()
|
||||
for ob in reversed(self.children):
|
||||
ob.destroy(recursive=false)
|
||||
self.world.destroy()
|
||||
|
@ -351,7 +358,13 @@ proc destroy*(self: Scene) =
|
|||
self.shadow_maps.destroy()
|
||||
for ubo in self.lighting_UBOs:
|
||||
ubo.destroy()
|
||||
if self.background_cubemap != nil:
|
||||
self.background_cubemap.destroy()
|
||||
for cube in self.cubemaps:
|
||||
cube.destroy()
|
||||
self.cubemaps = @[]
|
||||
self.engine.scenes.del(self.name)
|
||||
self.engine.new_scenes.del(self.name)
|
||||
# bound textures can linger
|
||||
unbindAllTextures()
|
||||
# probably none of this is actually necessary
|
||||
|
|
|
@ -116,6 +116,15 @@ proc get_ray_direction_local*(viewport: Viewport, position: Vec2): Vec3 =
|
|||
let y = (position.y - viewport.rect_pix[1].float32) / viewport.rect_pix[3].float32
|
||||
return viewport.camera.get_ray_direction_local(x,y)
|
||||
|
||||
proc get_pixels_at_depth*(viewport: Viewport, depth: float32): float32 =
|
||||
## Returns the length of one world unit (usually one meter) in screen pixels,
|
||||
## at a given depth.
|
||||
|
||||
# TODO: shift_x
|
||||
let p = vec4(1.0, 0.0, -depth, 1.0)
|
||||
let s = viewport.camera.projection_matrix * p
|
||||
return viewport.rect_pix[2].float32 * 0.5 * s.x/s.w
|
||||
|
||||
proc clear_all_callbacks*(self: Screen) =
|
||||
self.key_callbacks.setLen 0
|
||||
self.mouse_button_callbacks.setLen 0
|
||||
|
|
|
@ -5,7 +5,7 @@ import ../graphics/framebuffer
|
|||
import ../graphics/texture
|
||||
import ../scene
|
||||
|
||||
const USE_SHADOW_SAMPLERS* = false
|
||||
const USE_SHADOW_SAMPLERS* = true
|
||||
|
||||
when defined(nimdoc):
|
||||
type TYPES* = ShadowManager | ShadowMapUniform
|
||||
|
@ -38,6 +38,12 @@ proc updateShadowStorage*(scene: Scene) =
|
|||
let fb_size = scene.shadow_map_resolution
|
||||
assert color_channels <= 4
|
||||
if scene.shadow_maps != nil:
|
||||
# TODO: we're assuming that if it exists it has all the same parameters
|
||||
# and that only resolution and number of layers will change
|
||||
if scene.shadow_maps.width == fb_size and
|
||||
scene.shadow_maps.layer_count == count:
|
||||
scene.calculate_max_lights_and_cubemaps()
|
||||
return
|
||||
scene.shadow_maps.destroy()
|
||||
scene.shadow_maps = scene.engine.newFramebuffer(fb_size, fb_size,
|
||||
if color_channels != 0:
|
||||
|
|
|
@ -97,6 +97,8 @@ proc newSimpleShadowManager*(light: Light;
|
|||
self.uses_depth = false
|
||||
|
||||
when defined(myouDebugShadows):
|
||||
if debug_mesh != nil:
|
||||
debug_mesh.destroy()
|
||||
debug_mesh = self.engine.newMesh(vertex_count = 100, draw_method = Lines)
|
||||
debug_mesh.materials.add self.engine.newSolidMaterial("green", vec4(0,1,0,1))
|
||||
debug_mesh.visible = false
|
||||
|
@ -105,6 +107,7 @@ proc newSimpleShadowManager*(light: Light;
|
|||
method destroy*(self: SimpleShadowManager) {.locks:"unknown".} =
|
||||
if self.material != nil:
|
||||
self.material.destroy()
|
||||
debug_mesh.destroy()
|
||||
|
||||
when defined(myouDebugShadows):
|
||||
proc show_mat(mat: Mat4) =
|
||||
|
|
110
src/util.nim
110
src/util.nim
|
@ -75,96 +75,55 @@ func remove_scale_skew*[T](m: GMat4[T]): GMat4[T] =
|
|||
result[3,1] = m[3,1]
|
||||
result[3,2] = m[3,2]
|
||||
|
||||
proc remove*[T](s: var seq[T], element: T) =
|
||||
proc remove*[T](s: var seq[T], element: T): bool {.raises:[],discardable.} =
|
||||
## Removes a value from a seq if it exists. Returns whether an item was
|
||||
## removed. Preserves the order of the other elements. If the element is
|
||||
## repeated, only one instance is removed.
|
||||
let index = s.find element
|
||||
if index != -1:
|
||||
s.delete index
|
||||
# TODO: option to warn or break when not present
|
||||
# and a version or an argument to not warn
|
||||
# Since we only remove an existing element, it should never raise,
|
||||
# but we have to use try/except to satisfy raises:[]
|
||||
try: s.delete index
|
||||
except: discard
|
||||
return true
|
||||
|
||||
proc remove_unordered*[T](s: seq[T], element: T) =
|
||||
proc remove_unordered*[T](s: seq[T], element: T): bool {.raises:[],discardable.} =
|
||||
## Removes a value from a seq if it exists. Returns whether an item was
|
||||
## removed. It's quicker than `remove` by moving the last element to the slot
|
||||
## of the removed one. If the element is repeated, only one instance is
|
||||
## removed.
|
||||
let index = s.find element
|
||||
if index != -1:
|
||||
s.del index
|
||||
# Since we only remove an existing element, it should never raise,
|
||||
# but we have to use try/except to satisfy raises:[]
|
||||
try: s.del index
|
||||
except: discard
|
||||
return true
|
||||
|
||||
func align4*[T: SomeInteger](n: T): T = n + ((4-(n and 3)) and 3)
|
||||
func align4*[T: SomeInteger](n: T): T =
|
||||
# Increments the number to the nearest multiplier of 4 if it's not already
|
||||
# aligned.
|
||||
n + ((4-(n and 3)) and 3)
|
||||
|
||||
func align*[T: SomeInteger](n, align: T): T =
|
||||
## Increments the number to the nearest multiplier of the `align` parameter,
|
||||
## if it's not already aligned. `align` must be a power of two.
|
||||
let mask = align - 1
|
||||
assert((align and mask) == 0, "align must be a power of two")
|
||||
return n + ((align-(n and mask)) and mask)
|
||||
|
||||
func bytelen*[T](s: seq[T]): int = s.len * sizeof(T)
|
||||
func bytelen*[T](s: seq[T]): int =
|
||||
## Provides the size of the seq in bytes.
|
||||
s.len * sizeof(T)
|
||||
|
||||
func get_or_default*[T](s: seq[T], i: int, default: T = T.default): T =
|
||||
func getOrDefault*[T](s: seq[T], i: int, default: T = T.default): T =
|
||||
## Returns an element of the seq if the index is within bounds, otherwise it
|
||||
## returns a default value, optionally given as argument.
|
||||
if i >= s.low and i <= s.high:
|
||||
return s[i]
|
||||
return default
|
||||
|
||||
|
||||
when defined(js):
|
||||
import jsffi
|
||||
proc newArrayBufferView(size: uint8): seq[uint8] {.importjs: "(new Uint8Array(#))".}
|
||||
proc newArrayBufferView(size: uint16): seq[uint16] {.importjs: "(new Uint16Array(#))".}
|
||||
proc newArrayBufferView(size: uint32): seq[uint32] {.importjs: "(new Uint32Array(#))".}
|
||||
proc newArrayBufferView(size: int8): seq[int8] {.importjs: "(new Int8Array(#))".}
|
||||
proc newArrayBufferView(size: int16): seq[int16] {.importjs: "(new Int16Array(#))".}
|
||||
proc newArrayBufferView(size: int32): seq[int32] {.importjs: "(new Int32Array(#))".}
|
||||
proc newArrayBufferView(size: float32): seq[float32] {.importjs: "(new Float32Array(#))".}
|
||||
proc newArrayBufferView(size: float64): seq[float64] {.importjs: "(new Float64Array(#))".}
|
||||
template makeSeq*[T](size: int): seq[T] =
|
||||
# incredibly cursed hack, but it works
|
||||
newArrayBufferView(size.tojs.to(typeof T))
|
||||
|
||||
proc as_byte_array*[T](arr: seq[T]): seq[int8] {.importjs: "(new Int8Array((#).buffer))".}
|
||||
proc as_ubyte_array*[T](arr: seq[T]): seq[uint8] {.importjs: "(new Uint8Array((#).buffer))".}
|
||||
proc set*[T](arr, arr2: seq[T]) {.importjs: "((#).set(#))"}
|
||||
template copy_bytes_to*[T](arr, arr2: seq[T]) =
|
||||
arr2.as_byte_array.set arr.as_byte_array
|
||||
proc copyWithin*[T](arr: seq[T], target, start, ends: int) {.importjs: "#.copyWithin(#,#,#)".}
|
||||
|
||||
else:
|
||||
template makeSeq*[T](size: int): seq[T] = newSeq[T](size)
|
||||
template as_byte_array*[T](arr: openArray[T]): ptr UncheckedArray[int8] =
|
||||
cast[ptr UncheckedArray[int8]](addr arr[0])
|
||||
template as_ubyte_array*[T](arr: openArray[T]): ptr UncheckedArray[uint8] =
|
||||
cast[ptr UncheckedArray[uint8]](addr arr[0])
|
||||
template as_byte_array*[T](arr: ArrRef[T]): ArrRef[int8] =
|
||||
arr.to(int8)
|
||||
template as_ubyte_array*[T](arr: ArrRef[T]): ArrRef[uint8] =
|
||||
arr.to(uint8)
|
||||
|
||||
template set*[T](arr, arr2: seq[T]) =
|
||||
for i,v in arr2:
|
||||
arr[i] = v
|
||||
|
||||
template copy_bytes_to*[T](arr, arr2: seq[T]|ArrRef[T]) =
|
||||
let src = arr.as_byte_array
|
||||
let dst = arr2.as_byte_array
|
||||
for i in 0 .. min(arr.byte_len, arr2.byte_len):
|
||||
dst[i] = src[i]
|
||||
|
||||
# TODO: version of arrays with len, that clamps bounds and can use negative indices
|
||||
proc copyWithin*[T](arr: ptr UncheckedArray[T], target, start, ends: int) =
|
||||
let len = ends-start
|
||||
let target_end = target + len
|
||||
if start < target and target < ends:
|
||||
# reverse (#TODO: only the overlapping section?)
|
||||
var i = ends - 1
|
||||
var o = target_end - 1
|
||||
while i != start:
|
||||
arr[o] = arr[i]
|
||||
i -= 1; o -= 1
|
||||
else:
|
||||
var i = start
|
||||
var o = target
|
||||
while i != ends:
|
||||
arr[o] = arr[i]
|
||||
i += 1; o += 1
|
||||
|
||||
template copyWithin*[T](arr: ArrRef[T], target, start, ends: int) =
|
||||
copyWithin(cast[ptr UncheckedArray[T]](arr[0].addr), target, start, ends)
|
||||
|
||||
template rotate_cw*[T](v: GVec3[T]): GVec3[T] = gvec3[T](v.y, -v.x, v.z)
|
||||
|
||||
template rotate_ccw*[T](v: GVec3[T]): GVec3[T] = gvec3[T](-v.y, v.x, v.z)
|
||||
|
@ -243,6 +202,13 @@ template high*[T](x: typedesc[GVec2[T]]): GVec2[T] = gvec2[T](T.high,T.high)
|
|||
template high*[T](x: typedesc[GVec3[T]]): GVec3[T] = gvec3[T](T.high,T.high,T.high)
|
||||
template high*[T](x: typedesc[GVec4[T]]): GVec4[T] = gvec4[T](T.high,T.high,T.high,T.high)
|
||||
|
||||
proc min*[T](v: GVec2[T]): T {.inline.} = min(v.x, v.y)
|
||||
proc min*[T](v: GVec3[T]): T {.inline.} = min(min(v.x, v.y), v.z)
|
||||
proc min*[T](v: GVec4[T]): T {.inline.} = min(min(min(v.x, v.y), v.z), v.w)
|
||||
proc max*[T](v: GVec2[T]): T {.inline.} = max(v.x, v.y)
|
||||
proc max*[T](v: GVec3[T]): T {.inline.} = max(max(v.x, v.y), v.z)
|
||||
proc max*[T](v: GVec4[T]): T {.inline.} = max(max(max(v.x, v.y), v.z), v.w)
|
||||
|
||||
# bounding box operations
|
||||
|
||||
template `&`*[T](a, b: (GVec3[T], GVec3[T])): (GVec3[T], GVec3[T]) =
|
||||
|
|
Loading…
Reference in a new issue