GameObject: Document many of its functions.

Also rename `get_dimensions` to `get_local_dimensions`, add
`get_world_dimensions` for consistency, and add `children_recursive_and_self`.
This commit is contained in:
Alberto Torres 2024-09-21 02:19:44 +02:00
parent f71ae37821
commit 40b70003ec

View file

@ -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
@ -392,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
@ -440,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)
@ -452,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:
@ -483,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
@ -518,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)
@ -550,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
@ -602,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