Add docstrings to gameobject, mesh, light, camera, scene, screen, attributes.
This commit is contained in:
parent
080d9bcc67
commit
893208f4c2
8 changed files with 326 additions and 32 deletions
|
@ -39,6 +39,8 @@ import std/strformat
|
|||
import ./util
|
||||
|
||||
func size*(dtype: DataType): int16 =
|
||||
## Size of a DataType in bytes
|
||||
|
||||
assert dtype != Unknown, "Can't use unknown type"
|
||||
case dtype:
|
||||
of Float, Int, UInt: 4
|
||||
|
@ -47,14 +49,14 @@ func size*(dtype: DataType): int16 =
|
|||
of Unknown: 0
|
||||
|
||||
func stride*(layout: AttributeList, align: int32 = 4): int32 =
|
||||
# Stride of an attribute list, aligned to a byte boundary (default 4)
|
||||
## Stride of an attribute list, aligned to a byte boundary (default 4)
|
||||
for a in layout:
|
||||
result = max(result, a.offset + a.dtype.size * a.count)
|
||||
result = align(result, align)
|
||||
|
||||
proc add*(layout: var AttributeList, attr: Attribute) =
|
||||
# Adds an attribute to a seq setting the approriate location and offset
|
||||
# (aligned to a 4 byte boundary)
|
||||
## Adds an attribute to a seq setting the approriate location and offset
|
||||
## (aligned to a 4 byte boundary)
|
||||
var attr = attr
|
||||
if layout.len != 0:
|
||||
let last = layout[layout.high]
|
||||
|
@ -67,11 +69,13 @@ proc add*(layout: var AttributeList, attr: Attribute) =
|
|||
layout[^1] = attr
|
||||
|
||||
proc contains*(layout: AttributeList, name: string): bool =
|
||||
## Returns whether an attribute with name `name` exists.
|
||||
for attr in layout:
|
||||
if attr.name == name:
|
||||
return true
|
||||
|
||||
proc `[]`*(layout: AttributeList, name: string): Attribute =
|
||||
## Gets an attribute from a seq of attributes from its name.
|
||||
for attr in layout:
|
||||
if attr.name == name:
|
||||
return attr
|
||||
|
|
|
@ -51,12 +51,16 @@ import ./gameobject
|
|||
# import ../graphics/ubo
|
||||
|
||||
method is_camera*(self: GameObject): bool {.base.} =
|
||||
## Return whether a GameObject is a Camera
|
||||
return false
|
||||
method get_camera*(self: GameObject): Camera {.base.} =
|
||||
## Get the Camera object of a GameObject, or nil if it's not a camera
|
||||
return nil
|
||||
method is_camera*(self: Camera): bool =
|
||||
## Return whether a GameObject is a Camera
|
||||
return true
|
||||
method get_camera*(self: Camera): Camera =
|
||||
## Get the Camera object of a GameObject, or nil if it's not a camera
|
||||
return self
|
||||
|
||||
proc newCamera*(engine: MyouEngine, name: string="camera", scene: Scene = nil,
|
||||
|
@ -69,6 +73,8 @@ proc newCamera*(engine: MyouEngine, name: string="camera", scene: Scene = nil,
|
|||
sensor_fit: SensorFit = Auto,
|
||||
shift: Vec2 = vec2(),
|
||||
): Camera =
|
||||
## Create a new Camera object. If you supply `scene` it will be added to that
|
||||
## scene.
|
||||
var self = new Camera
|
||||
discard procCall(self.GameObject.initGameObject(engine, name))
|
||||
self.otype = TCamera
|
||||
|
@ -105,10 +111,16 @@ proc instance_physics*(self: Camera) =
|
|||
discard
|
||||
|
||||
proc get_ray_direction*(self: Camera, x, y: float32): Vec3 =
|
||||
## Converts a viewport coordinate to a vector direction in world space
|
||||
## relative to the camera. The upper left corner of the viewport has
|
||||
## coordinates (0,0), and the lower right corner (1,1).
|
||||
return self.get_world_rotation * (self.projection_matrix_inverse *
|
||||
vec3(x * 2 - 1, 1 - y * 2, 1))
|
||||
|
||||
proc get_ray_direction_local*(self: Camera, x, y: float32): Vec3 =
|
||||
## Converts a viewport coordinate to a vector direction in local space
|
||||
## relative to the camera. The upper left corner of the viewport has
|
||||
## coordinates (0,0), and the lower right corner (1,1).
|
||||
assert self.rotation_order == Quaternion
|
||||
return self.rotation * (self.projection_matrix_inverse *
|
||||
vec3(x * 2 - 1, 1 - y * 2, 1))
|
||||
|
@ -122,6 +134,11 @@ proc get_ray_direction_local*(self: Camera, x, y: float32): Vec3 =
|
|||
# procCall(self.GameObject.look_at(target, options))
|
||||
|
||||
proc is_vertical_fit*(self: Camera): bool =
|
||||
## Returns whether `field_of_view` represents the vertical field of view
|
||||
## according to `sensor_fit`. If it's false, `field_of_view` represents the
|
||||
## horizontal FoV.
|
||||
##
|
||||
## Only applicable when both `field_of_view` and `sensor_fit` are used.
|
||||
return case self.sensor_fit:
|
||||
of Auto: self.aspect_ratio <= 1
|
||||
of Horizontal: false
|
||||
|
@ -130,6 +147,12 @@ proc is_vertical_fit*(self: Camera): bool =
|
|||
of Contain: self.aspect_ratio > self.target_aspect_ratio
|
||||
|
||||
proc set_projection_matrix*(self: Camera, matrix: Mat4, adjust_aspect_ratio: bool = false) =
|
||||
## Sets a custom projection matrix to the camera, and optionally adjust the
|
||||
## aspect ratio if `adjust_aspect_ratio` is set to `true`. If you use this,
|
||||
## `field_of_view` won't be used.
|
||||
##
|
||||
## Note that the projection matrix will be reset if the viewport is resized,
|
||||
## so you may want to call this inside a resize event.
|
||||
self.projection_matrix = matrix
|
||||
if adjust_aspect_ratio:
|
||||
let m_ratio = matrix[0, 0] / matrix[1, 1]
|
||||
|
@ -144,10 +167,19 @@ proc set_projection_matrix*(self: Camera, matrix: Mat4, adjust_aspect_ratio: boo
|
|||
self.calculate_culling_planes()
|
||||
|
||||
proc update_projection*(self: Camera) =
|
||||
## Calculate projection matrices and culling planes from camera parameters.
|
||||
##
|
||||
## You may want to call this if you change any camera parameter.
|
||||
##
|
||||
## Called automatically on viewport creation and on resize events.
|
||||
self.calculate_projection()
|
||||
self.calculate_culling_planes()
|
||||
|
||||
proc calculate_projection*(self: Camera) =
|
||||
## Calculate projection matrices from camera parameters. Called automatically
|
||||
## on viewport creation and on resize events.
|
||||
##
|
||||
## If you change any camera parameters, use `update_projection`_ instead.
|
||||
let near_plane = self.near_plane
|
||||
let far_plane = self.far_plane
|
||||
var top, right, bottom, left: float32
|
||||
|
@ -208,6 +240,10 @@ proc calculate_projection*(self: Camera) =
|
|||
self.projection_matrix_inverse = inverse(self.projection_matrix)
|
||||
|
||||
proc calculate_culling_planes*(self: Camera) =
|
||||
## Calculate culling planes from the projection matrix. Called automatically
|
||||
## on viewport creation, on resize events, and with `set_projection_matrix`_.
|
||||
##
|
||||
## If you change any camera parameters, use `update_projection`_ instead.
|
||||
self.cull_planes = self.projection_matrix.get_culling_planes()
|
||||
|
||||
proc get_oblique_projection_matrix_into*(self: Camera, clip_plane: Vec4): Mat4 =
|
||||
|
|
|
@ -53,12 +53,16 @@ proc render_cubemap*(self: CubemapProbe, use_roughness_prefiltering = false, mip
|
|||
proc render_background_cubemap*(scene: Scene, use_roughness_prefiltering = false, mipmap_shader: Material = nil, world_to_cube_matrix: Mat4 = mat4(), upload_UBO = true)
|
||||
|
||||
method is_cubemap_probe*(self: GameObject): bool {.base.} =
|
||||
## Return whether a GameObject is a CubemapProbe
|
||||
return false
|
||||
method get_cubemap_probe*(self: GameObject): CubemapProbe {.base.} =
|
||||
## Get the CubemapProbe object of a GameObject, or nil if it's not a cubemap probe
|
||||
return nil
|
||||
method is_cubemap_probe*(self: CubemapProbe): bool =
|
||||
## Return whether a GameObject is a CubemapProbe
|
||||
return true
|
||||
method get_cubemap_probe*(self: CubemapProbe): CubemapProbe =
|
||||
## Get the CubemapProbe object of a GameObject, or nil if it's not a cubemap probe
|
||||
return self
|
||||
# End forward declarations and ob type methods
|
||||
|
||||
|
@ -82,6 +86,8 @@ proc newCubemapProbe*(engine: MyouEngine, name: string="camera", scene: Scene =
|
|||
parallax_type: ProbeParallaxType = NoParallax,
|
||||
parallax_distance: float32 = 0.0, # 0 means auto
|
||||
): CubemapProbe =
|
||||
## Create a new Cubemap Probe object. If you supply `scene` it will be added
|
||||
## to that scene.
|
||||
var self = new CubemapProbe
|
||||
discard procCall(self.GameObject.initGameObject(engine, name))
|
||||
self.ubo_index = -1
|
||||
|
@ -148,6 +154,7 @@ template get_roughness_prefilter(engine: MyouEngine): Material =
|
|||
roughness_prefilter
|
||||
|
||||
proc render_cubemap*(self: CubemapProbe, use_roughness_prefiltering = false, mipmap_shader: Material = nil) =
|
||||
## Updates the contents of the cubemap by rendering the scene on each side of the cubemap.
|
||||
if self.ubo_index == -1:
|
||||
self.scene.ensure_cubemaps()
|
||||
let cube2world = self.world_matrix * scale(vec3(self.influence_distance))
|
||||
|
@ -181,6 +188,7 @@ proc render_cubemap*(self: CubemapProbe, use_roughness_prefiltering = false, mip
|
|||
self.cubemap.disable()
|
||||
|
||||
proc render_background_cubemap*(scene: Scene, use_roughness_prefiltering = false, mipmap_shader: Material = nil, world_to_cube_matrix: Mat4 = mat4(), upload_UBO = true) =
|
||||
## Updates the contents of the background cubemap by rendering the world material on each side of the cubemap.
|
||||
if scene.background_cubemap == nil:
|
||||
scene.ensure_cubemaps()
|
||||
if not defined(release):
|
||||
|
@ -204,9 +212,9 @@ proc render_background_cubemap*(scene: Scene, use_roughness_prefiltering = false
|
|||
if upload_UBO:
|
||||
scene.cubemap_UBO.update()
|
||||
|
||||
proc generate_cubemap_mipmaps*(scene: Scene, cubemap: Framebuffer, use_roughness_prefiltering = false, mipmap_shader: Material = nil) =
|
||||
let mipmap_shader = if use_roughness_prefiltering and mipmap_shader == nil:
|
||||
scene.engine.get_roughness_prefilter()
|
||||
else:
|
||||
mipmap_shader
|
||||
cubemap.generate_mipmap(mipmap_shader)
|
||||
proc generate_cubemap_mipmaps_with_roughness_prefiltering*(scene: Scene, cubemap: Framebuffer) =
|
||||
## Generates cubemap.generate_mipmap but using the roughness prefilter. This
|
||||
## is called automatically when rendering cubemaps with
|
||||
## `use_roughness_prefiltering = true`. Use it only when you write data to
|
||||
## the cubemap manually.
|
||||
cubemap.generate_mipmap(scene.engine.get_roughness_prefilter())
|
||||
|
|
|
@ -144,26 +144,40 @@ proc initGameObject*(self: GameObject, engine: MyouEngine, name: string="", scen
|
|||
return self
|
||||
|
||||
proc newGameObject*(engine: MyouEngine, name: string="", scene: Scene=nil): GameObject =
|
||||
result = new GameObject
|
||||
## Create a new GameObject. If you supply `scene` it will be added to that
|
||||
## scene.
|
||||
new(result)
|
||||
return initGameObject(result, engine, name, scene)
|
||||
|
||||
proc show*(self: GameObject, recursive: static[bool] = true) =
|
||||
## Enable visibility of the object, its children, their children and so on.
|
||||
## If you set `recursive = false`, only the visibility of the object will be
|
||||
## changed.
|
||||
self.visible = true
|
||||
when recursive:
|
||||
for c in self.children:
|
||||
c.show(recursive)
|
||||
|
||||
proc hide*(self: GameObject, recursive: static[bool] = true) =
|
||||
## Disable visibility of the object, its children, their children and so on.
|
||||
## If you set `recursive = false`, only the visibility of the object will be
|
||||
## changed.
|
||||
self.visible = false
|
||||
when recursive:
|
||||
for c in self.children:
|
||||
c.hide(recursive)
|
||||
|
||||
proc set_visibility*(self: GameObject, visible: bool, recursive: static[bool] = true) =
|
||||
## Set the visibility of the object and its descendants to the value of the
|
||||
## argument `visible`. If you set `recursive = false`, only the visibility of
|
||||
## the object will be changed.
|
||||
if visible: self.show(recursive)
|
||||
else: self.hide(recursive)
|
||||
|
||||
proc update_matrices*(self: GameObject) =
|
||||
## Calculate the matrices of the object from its position, rotation, scale,
|
||||
## and parent matrix. It assumes the parent object and/or bone has its world
|
||||
## matrix already up to date.
|
||||
var q = if self.rotation_order == Quaternion:
|
||||
self.rotation
|
||||
else:
|
||||
|
@ -214,11 +228,14 @@ proc update_matrices*(self: GameObject) =
|
|||
return
|
||||
|
||||
proc update_matrices_recursive(self: GameObject) =
|
||||
## Calculate the matrices of the object from its position, rotation, scale,
|
||||
## and parent. It will update the parent matrices first if it has a parent.
|
||||
if self != nil:
|
||||
self.parent.update_matrices_recursive()
|
||||
self.update_matrices()
|
||||
|
||||
proc set_rotation_order*(self: GameObject, order: RotationOrder) =
|
||||
## Change the rotation mode and order of the object.
|
||||
if order == self.rotation_order:
|
||||
return
|
||||
var q = self.rotation
|
||||
|
@ -237,11 +254,14 @@ proc set_rotation_order*(self: GameObject, order: RotationOrder) =
|
|||
self.rotation_order = order
|
||||
|
||||
proc get_world_matrix*(self: GameObject): Mat4 =
|
||||
## Calculates and returns the world matrix.
|
||||
# TODO: use dirty flag
|
||||
self.update_matrices_recursive()
|
||||
return self.world_matrix
|
||||
|
||||
proc get_local_matrix*(self: GameObject): Mat4 =
|
||||
## Calculates and returns the transformation matrix in local space. If the
|
||||
## object has no parent, this is equivalent to the world matrix.
|
||||
var q = if self.rotation_order == Quaternion:
|
||||
self.rotation
|
||||
else:
|
||||
|
@ -271,16 +291,19 @@ proc get_local_matrix*(self: GameObject): Mat4 =
|
|||
)
|
||||
|
||||
proc get_world_position*(self: GameObject): Vec3 =
|
||||
## Calculates and returns the position in world space.
|
||||
if self.parent == nil:
|
||||
return self.position
|
||||
return self.get_world_matrix[3].xyz
|
||||
|
||||
proc get_world_rotation*(self: GameObject): Quat =
|
||||
## Calculates and returns the rotation in world space as a quaternion.
|
||||
let wm = self.get_world_matrix
|
||||
# TODO: Calculate rotation matrix more efficiently (dirty flag?)
|
||||
return wm.to_mat3_rotation.to_quat
|
||||
|
||||
proc get_world_position_rotation*(self: GameObject): (Vec3, Quat) =
|
||||
## Calculates and returns the position and rotation in world space.
|
||||
let wm = self.get_world_matrix
|
||||
let position: Vec3 = wm[3].xyz
|
||||
# TODO: Calculate rotation matrix more efficiently (dirty flag?)
|
||||
|
@ -289,6 +312,9 @@ proc get_world_position_rotation*(self: GameObject): (Vec3, Quat) =
|
|||
return (position, rotation)
|
||||
|
||||
proc translate*(self: GameObject, vector: Vec3, relative_object: GameObject=nil): GameObject {.discardable.} =
|
||||
## Translate (move) the object by adding the vector `vector` to its position,
|
||||
## either relative to the scene, or relative to the specified object in
|
||||
## `relative_object`
|
||||
var vector = vector
|
||||
if relative_object != nil:
|
||||
vector = relative_object.get_world_rotation() * vector
|
||||
|
@ -299,12 +325,18 @@ proc translate*(self: GameObject, vector: Vec3, relative_object: GameObject=nil)
|
|||
return self
|
||||
|
||||
proc translate_x*(self: GameObject, x: SomeFloat, relative_object: GameObject=nil): GameObject {.discardable.} =
|
||||
## Translate (move) the object along the X axis of the scene, or the X axis
|
||||
## of the specified object in `relative_object`
|
||||
return self.translate(vec3(x, 0, 0), relative_object)
|
||||
|
||||
proc translate_y*(self: GameObject, y: SomeFloat, relative_object: GameObject=nil): GameObject {.discardable.} =
|
||||
## Translate (move) the object along the Y axis of the scene, or the Y axis
|
||||
## of the specified object in `relative_object`
|
||||
return self.translate(vec3(0, y, 0), relative_object)
|
||||
|
||||
proc translate_z*(self: GameObject, z: SomeFloat, relative_object: GameObject=nil): GameObject {.discardable.} =
|
||||
## Translate (move) the object along the Z axis of the scene, or the Z axis
|
||||
## of the specified object in `relative_object`
|
||||
return self.translate(vec3(0, 0, z), relative_object)
|
||||
|
||||
# proc rotate_euler*(self: GameObject, vector: Vec3, relative_object: GameObject=nil): GameObject =
|
||||
|
@ -317,6 +349,10 @@ proc translate_z*(self: GameObject, z: SomeFloat, relative_object: GameObject=ni
|
|||
# self.rotate_quat(q, relative_object)
|
||||
|
||||
proc rotate_quat*(self: GameObject, q: Quat, relative_object: GameObject=nil): GameObject {.discardable.} =
|
||||
# Rotate the object by applying (multiplying) the quaternion `q`, either
|
||||
# relative to the scene, or relative to the specified object in
|
||||
# `relative_object`
|
||||
|
||||
# TODO: optimize (dirty flag?)
|
||||
var rel = quat()
|
||||
var inv_rel = quat()
|
||||
|
@ -338,28 +374,42 @@ proc rotate_quat*(self: GameObject, q: Quat, relative_object: GameObject=nil): G
|
|||
return self
|
||||
|
||||
proc rotate_x*(self: GameObject, angle: SomeFloat, relative_object: GameObject=nil) =
|
||||
## Rotate the object around the X axis of the scene, or the X axis of the
|
||||
## specified object in `relative_object`. The angle is in radians.
|
||||
self.rotate_quat(rotateX(angle.float32).to_mat3.to_quat, relative_object)
|
||||
|
||||
proc rotate_y*(self: GameObject, angle: SomeFloat, relative_object: GameObject=nil) =
|
||||
## Rotate the object around the Y axis of the scene, or the Y axis of the
|
||||
## specified object in `relative_object`. The angle is in radians.
|
||||
self.rotate_quat(rotateY(angle.float32).to_mat3.to_quat, relative_object)
|
||||
|
||||
proc rotate_z*(self: GameObject, angle: SomeFloat, relative_object: GameObject=nil) =
|
||||
## Rotate the object around the Z axis of the scene, or the Z axis of the
|
||||
## specified object in `relative_object`. The angle is in radians.
|
||||
self.rotate_quat(rotateZ(angle.float32).to_mat3.to_quat, relative_object)
|
||||
|
||||
proc rotate_x_deg*(self: GameObject, angle: SomeFloat, relative_object: GameObject=nil) =
|
||||
## Rotate the object around the X axis of the scene, or the X axis of the
|
||||
## specified object in `relative_object`. The angle is in degrees.
|
||||
self.rotate_quat(rotateX(angle.float32.to_radians).to_mat3.to_quat, relative_object)
|
||||
|
||||
proc rotate_y_deg*(self: GameObject, angle: SomeFloat, relative_object: GameObject=nil) =
|
||||
## Rotate the object around the Y axis of the scene, or the Y axis of the
|
||||
## specified object in `relative_object`. The angle is in degrees.
|
||||
self.rotate_quat(rotateY(angle.float32.to_radians).to_mat3.to_quat, relative_object)
|
||||
|
||||
proc rotate_z_deg*(self: GameObject, angle: SomeFloat, relative_object: GameObject=nil) =
|
||||
## Rotate the object around the Z axis of the scene, or the Z axis of the
|
||||
## specified object in `relative_object`. The angle is in degrees.
|
||||
self.rotate_quat(rotateZ(angle.float32.to_radians).to_mat3.to_quat, relative_object)
|
||||
|
||||
proc set_world_position_rotation*(
|
||||
self: GameObject,
|
||||
position: Vec3,
|
||||
rotation: Quat) =
|
||||
|
||||
## Changes the position and the rotation of the object to match the supplied
|
||||
## position and rotation in world space.
|
||||
|
||||
if self.parent != nil:
|
||||
# TODO: optimize and use less memory?
|
||||
# TODO: does it work well with parents?
|
||||
|
@ -372,9 +422,13 @@ proc set_world_position_rotation*(
|
|||
self.rotation_order = Quaternion
|
||||
|
||||
proc set_world_position*(self: GameObject, position: Vec3) =
|
||||
## Changes the position of the object to match the supplied position in world
|
||||
## space.
|
||||
self.set_world_position_rotation(position, self.get_world_rotation())
|
||||
|
||||
proc set_world_rotation*(self: GameObject, rotation: Quat) =
|
||||
## Changes the rotation of the object to match the supplied rotation in world
|
||||
## space.
|
||||
self.set_world_position_rotation(self.get_world_position(), rotation)
|
||||
|
||||
type LookAtAxis* = enum
|
||||
|
@ -530,6 +584,9 @@ proc destroy*(self: GameObject, recursive: bool = true) =
|
|||
self.object_ubo.destroy()
|
||||
|
||||
proc convert_bone_child_to_bone_parent*(self: GameObject) =
|
||||
## Turns an object that is parented to a bone, into the parent of the same
|
||||
## bone. It disconnects the bone from its former parent, if any. Used
|
||||
## internally to create ragdolls.
|
||||
assert self.parent == nil or self.parent.is_armature, "An armature parent is required"
|
||||
var parent = self.parent.get_armature
|
||||
if not (parent != nil and (self.parent_bone_index) >= 0):
|
||||
|
@ -635,6 +692,7 @@ proc set_world_size*(self: GameObject, size: SomeFloat) =
|
|||
return
|
||||
|
||||
proc set_name*(self: GameObject, name: string) =
|
||||
## Rename the object and update the tables that use it.
|
||||
assert self.scene != nil, "Object has no scene"
|
||||
self.engine.objects.del(self.name)
|
||||
self.scene.objects.del(self.name)
|
||||
|
|
|
@ -55,12 +55,16 @@ proc newLight*(engine: MyouEngine, name: string="",
|
|||
proc instance_physics*(this: Light)
|
||||
|
||||
method is_light*(self: GameObject): bool {.base.} =
|
||||
## Return whether a GameObject is a Light
|
||||
return false
|
||||
method get_light*(self: GameObject): Light {.base.} =
|
||||
## Get the Light object of a GameObject, or nil if it's not a light
|
||||
return nil
|
||||
method is_light*(this: Light): bool =
|
||||
## Return whether a GameObject is a Light
|
||||
return true
|
||||
method get_light*(this: Light): Light =
|
||||
## Get the Light object of a GameObject, or nil if it's not a light
|
||||
return this
|
||||
# End forward declarations and ob type methods
|
||||
|
||||
|
@ -84,6 +88,8 @@ proc newLight*(engine: MyouEngine, name: string="",
|
|||
spot_blend: float32 = 0.15,
|
||||
use_shadow = false,
|
||||
): Light =
|
||||
## Create a new Light object. If you supply `scene` it will be added to that
|
||||
## scene.
|
||||
var this = new Light
|
||||
discard procCall(this.GameObject.initGameObject(engine, name))
|
||||
this.otype = TLight
|
||||
|
@ -108,15 +114,15 @@ proc configure_shadow*(self: Light,
|
|||
camera: Camera = nil,
|
||||
max_distance: float32 = 0.0,
|
||||
objects = self.scene.children) =
|
||||
# Configure a shadow for this light object, either by following a camera
|
||||
# passed as argument, or a static shadow for the specified objects.
|
||||
#
|
||||
# `max_distance` is only used when `camera` is supplied.
|
||||
#
|
||||
# `objects` is only used in static mode (when `camera` is nil). In this
|
||||
# mode, the shadow map will be tailored for the size of the bounding boxes
|
||||
# of the objects. It will exclude flat objects (planes) that are under every
|
||||
# other object because they can't project a shadow to themselves.
|
||||
## Configure a shadow for this light object, either by following a camera
|
||||
## passed as argument, or a static shadow for the specified objects.
|
||||
##
|
||||
## `max_distance` is only used when `camera` is supplied.
|
||||
##
|
||||
## `objects` is only used in static mode (when `camera` is nil). In this
|
||||
## mode, the shadow map will be tailored for the size of the bounding boxes
|
||||
## of the objects. It will exclude flat objects (planes) that are under every
|
||||
## other object because they can't project a shadow to themselves.
|
||||
for s in self.shadows:
|
||||
s.destroy()
|
||||
self.shadows.setLen 0
|
||||
|
@ -162,7 +168,6 @@ 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
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ proc load_from_va_ia*(self: Mesh,
|
|||
index_types: seq[DataType] = @[])
|
||||
proc generate_tangents*(self: MeshData, uv_layer_name, tangent_layer_name: string)
|
||||
proc ensure_capacity*(self: Mesh, extra_elements: int)
|
||||
proc write_vaos*(self: MeshData)
|
||||
# End forward declarations and ob type methods
|
||||
|
||||
|
||||
|
@ -72,20 +71,27 @@ export tables
|
|||
import ../platform/gl
|
||||
|
||||
method is_mesh*(self: GameObject): bool {.base.} =
|
||||
## Return whether a GameObject is a Mesh
|
||||
return false
|
||||
method get_mesh*(self: GameObject): Mesh {.base.} =
|
||||
## Get the Mesh object of a GameObject, or nil if it's not a mesh
|
||||
return nil
|
||||
method is_mesh*(self: Mesh): bool =
|
||||
## Return whether a GameObject is a Mesh
|
||||
return true
|
||||
method get_mesh*(self: Mesh): Mesh =
|
||||
## Get the Mesh object of a GameObject, or nil if it's not a mesh
|
||||
return self
|
||||
|
||||
proc newMeshData(engine: MyouEngine): MeshData =
|
||||
## Creates a new MeshData. It's recommended to use newMesh arguments instead.
|
||||
result = new MeshData
|
||||
result.draw_method = Triangles
|
||||
result.engine = engine
|
||||
|
||||
proc remove*(self: MeshData, ob: Mesh, delete_buffers: bool = true) =
|
||||
## Removes this MeshData from a mesh object, and deletes itself if it doesn't
|
||||
## have any more users.
|
||||
var idx = self.users.find(ob)
|
||||
while idx != -1:
|
||||
self.users.delete(idx)
|
||||
|
@ -94,7 +100,7 @@ proc remove*(self: MeshData, ob: Mesh, delete_buffers: bool = true) =
|
|||
self.engine.mesh_datas.del(self.hash)
|
||||
ob.data = nil
|
||||
|
||||
proc write_vaos*(self: MeshData) =
|
||||
proc write_vaos(self: MeshData) =
|
||||
for i,vao in self.vaos:
|
||||
if vao.int == 0:
|
||||
continue
|
||||
|
@ -209,6 +215,7 @@ proc gpu_buffers_upload*(self: MeshData) =
|
|||
# set loaded to true after that
|
||||
|
||||
proc gpu_buffers_delete*(self: MeshData) =
|
||||
## Removes GPU buffers. For internal use. Use GameObject.destroy() instead.
|
||||
glBindVertexArray(0)
|
||||
self.vaos = @[]
|
||||
self.tf_vaos = @[]
|
||||
|
@ -222,6 +229,8 @@ proc gpu_buffers_delete*(self: MeshData) =
|
|||
self.engine.mesh_datas.del(self.hash)
|
||||
|
||||
proc update_varray*(self: MeshData) =
|
||||
## Update vertex GPU buffers with the contents of vertex arrays. Call this
|
||||
## after updating vertex arrays.
|
||||
if self.vertex_buffers.len == 0:
|
||||
return
|
||||
for i,va in self.varrays:
|
||||
|
@ -229,6 +238,8 @@ proc update_varray*(self: MeshData) =
|
|||
glBufferSubData(GL_ARRAY_BUFFER, 0.GLintptr, cast[GLsizeiptr](va.bytelen), addr va[0])
|
||||
|
||||
proc update_iarray*(self: MeshData) =
|
||||
## Update index GPU buffers with the contents of vertex arrays. Call this
|
||||
## after updating index arrays.
|
||||
if self.index_buffers.len == 0:
|
||||
return
|
||||
for i,ia in self.iarrays:
|
||||
|
@ -241,7 +252,7 @@ proc clone*(self: MeshData): MeshData =
|
|||
d.users = @[]
|
||||
return d
|
||||
|
||||
proc initMesh*(self: Mesh, engine: MyouEngine, name: string, scene: Scene = nil,
|
||||
proc initMesh(self: Mesh, engine: MyouEngine, name: string, scene: Scene = nil,
|
||||
draw_method: MeshDrawMethod = Triangles,
|
||||
common_attributes: CommonMeshAttributes = {vertex, color},
|
||||
layout: AttributeList = @[],
|
||||
|
@ -302,14 +313,42 @@ proc newMesh*(engine: MyouEngine, name: string="mesh", scene: Scene = nil,
|
|||
index_array: seq[uint16] = @[],
|
||||
pass: int32 = 0,
|
||||
): Mesh =
|
||||
result = new Mesh
|
||||
## Create a new Mesh object. If you supply `scene` it will be added to that
|
||||
## scene.
|
||||
##
|
||||
## The most common draw methods are `Triangles` (the default), `Points`,
|
||||
## `Lines`, and `TriangleStrip` (for `add_polygonal_line()`).
|
||||
##
|
||||
## If you supply a layout, it will be used. Otherwise a layout will be
|
||||
## created from `common_attributes`, which by default is `{vertex, color}`.
|
||||
## The available common attributes are `{vertex, color, normal, uv}` and they
|
||||
## will be added in that order.
|
||||
##
|
||||
## You can give a `vertex_count` to allocate a mesh with capacity for that
|
||||
## amount of vertices, but you can always resize it later. Meant to be used
|
||||
## with `add_vertex()` and `add_polygonal_line`
|
||||
##
|
||||
## If you give a vertex array and an index array, they will be used directly
|
||||
## as GPU buffers. If you only supply the vertex array, indices will
|
||||
## implicitely be sequential.
|
||||
|
||||
# TODO: document pass and skip_upload
|
||||
new(result)
|
||||
result.otype = TMesh
|
||||
|
||||
return initMesh(result, engine, name, scene, draw_method, common_attributes, layout,
|
||||
skip_upload, vertex_count, vertex_array, index_array, pass)
|
||||
|
||||
proc add_vertex*(self: Mesh, x, y, z: float32, r, g, b, a: uint8): int {.discardable.} =
|
||||
## Tries to add a vertex to the mesh with the specified position and color.
|
||||
## It will fill an unused vertex if any. Returns the vertex index, or -1 if
|
||||
## the vertex could not be added.
|
||||
##
|
||||
## At the moment you need to call mesh.data.update_varray() after adding
|
||||
## vertices. It's most efficient when only doing it once.
|
||||
|
||||
# TODO: Check it actually has a color attribute!!
|
||||
# TODO: ensure it doesn't have a, index array?
|
||||
var varray = self.data.varrays[0]
|
||||
var varray_byte = varray.to(uint8)
|
||||
let stride = self.data.stride
|
||||
|
@ -331,18 +370,38 @@ proc add_vertex*(self: Mesh, x, y, z: float32, r, g, b, a: uint8): int {.discard
|
|||
return index+1
|
||||
|
||||
proc add_vertex*(self: Mesh, x, y, z, r, g, b, a: SomeFloat): int {.discardable.} =
|
||||
## Tries to add a vertex to the mesh with the specified position and color.
|
||||
## It will fill an unused vertex if any. Returns the vertex index, or -1 if
|
||||
## the vertex could not be added.
|
||||
##
|
||||
## At the moment you need to call mesh.data.update_varray() after adding
|
||||
## vertices. It's most efficient when only doing it once.
|
||||
return self.add_vertex(x, y, z, (r*255).uint8, (g*255).uint8, (b*255).uint8, (a*255).uint8)
|
||||
|
||||
proc add_vertex*(self: Mesh, position: Vec3, color: Vec4): int {.discardable.} =
|
||||
proc add_vertex*(self: Mesh, position: Vec3, color: Vec4 = vec4(1)): int {.discardable.} =
|
||||
## Tries to add a vertex to the mesh with the specified position and color.
|
||||
## It will fill an unused vertex if any. Returns the vertex index, or -1 if
|
||||
## the vertex could not be added.
|
||||
##
|
||||
## At the moment you need to call mesh.data.update_varray() after adding
|
||||
## vertices. It's most efficient when only doing it once.
|
||||
let (x,y,z) = position.to_tuple
|
||||
let (r,g,b,a) = color.to_tuple
|
||||
return self.add_vertex(x, y, z, r, g, b, a)
|
||||
|
||||
proc clear_vertices*(self: Mesh) =
|
||||
## Removes all vertices of the mesh by marking them as unused. You don't need
|
||||
## to call update_varray()
|
||||
self.data.num_indices[0] = 0
|
||||
|
||||
proc remove_vertex*(self: Mesh, index: int) =
|
||||
# TODO: This is only valid for points mode
|
||||
## Removes a vertices of the mesh by marking it as unused. At the moment it
|
||||
## only works in points mode.
|
||||
##
|
||||
## At the moment you need to call mesh.data.update_varray() after adding or
|
||||
## removing vertices. It's most efficient when only doing it once.
|
||||
|
||||
# TODO: Remove a whole line/triangle in line/triangle mode.
|
||||
if not (index >= 0):
|
||||
return # ignore -1
|
||||
assert self.data.num_indices[0] > index, "Invalid index"
|
||||
|
@ -356,6 +415,8 @@ proc remove_vertex*(self: Mesh, index: int) =
|
|||
self.data.num_indices[0] -= 1
|
||||
|
||||
proc ensure_capacity*(self: Mesh, extra_elements: int) =
|
||||
## Checks if there's enough space for the desired number of elements to be
|
||||
## added, and resizes the mesh if it's needed (by a factor of two).
|
||||
if self.data == nil:
|
||||
let stride = self.layout.stride
|
||||
assert stride != 0
|
||||
|
@ -395,6 +456,11 @@ proc load_from_va_ia*(self: Mesh,
|
|||
iarrays: seq[ArrRef[uint16]] | seq[ArrRef[int32]] = newSeq[ArrRef[uint16]](),
|
||||
layout: AttributeList = @[],
|
||||
index_types: seq[DataType] = @[]) =
|
||||
## Loads the mesh to the GPU from the given vertex and index arrays, layout
|
||||
## and index type. It's recommended to use newMesh arguments instead.
|
||||
##
|
||||
## This version takes multiple vertex and index arrays, which can have 16 or
|
||||
## 32 bit indices.
|
||||
|
||||
if self.data != nil:
|
||||
self.data.remove(self)
|
||||
|
@ -446,9 +512,16 @@ proc load_from_va_ia*(self: Mesh,
|
|||
varray: seq[float32],
|
||||
iarray: seq[uint16] = @[],
|
||||
layout: AttributeList = @[]) =
|
||||
## Loads the mesh to the GPU from the given vertex and index arrays and
|
||||
## layout. It's recommended to use newMesh arguments instead.
|
||||
##
|
||||
## This version takes a single vertex array and an optional index array, with
|
||||
## 16 bit indices.
|
||||
self.load_from_va_ia(@[newArrRef varray], @[newArrRef iarray], layout)
|
||||
|
||||
proc add_modifier*(self: Mesh, modifier: VertexModifier) =
|
||||
## Add a vertex modifier to the mesh, and changes the mesh data if necessary.
|
||||
## It's added to the end of the stack.
|
||||
self.vertex_modifiers.add(modifier)
|
||||
if modifier.prepare_mesh.nonNil and self.data.nonNil:
|
||||
echo &"applying modifiers of {self.name} after it was already loaded"
|
||||
|
@ -456,6 +529,8 @@ proc add_modifier*(self: Mesh, modifier: VertexModifier) =
|
|||
# self.update_signature()
|
||||
|
||||
proc insert_modifier*(self: Mesh, index: int, modifier: VertexModifier) =
|
||||
## Insert a vertex modifier to the mesh (at position `index`), and changes
|
||||
## the mesh data if necessary.
|
||||
self.vertex_modifiers.insert(modifier, index)
|
||||
if modifier.prepare_mesh.nonNil and self.data.nonNil:
|
||||
echo &"applying modifiers of {self.name} after it was already loaded"
|
||||
|
@ -463,10 +538,18 @@ proc insert_modifier*(self: Mesh, index: int, modifier: VertexModifier) =
|
|||
# self.update_signature()
|
||||
|
||||
proc remove_modifier*(self: Mesh, index: int) =
|
||||
## Removes a vertex modifier from the mesh at the given index.
|
||||
##
|
||||
## Note that currently if the modifier changed the mesh, it will remain
|
||||
## changed when removing the modifier.
|
||||
self.vertex_modifiers.delete(index)
|
||||
# self.update_signature()
|
||||
|
||||
proc remove_modifier*(self: Mesh, modifier: VertexModifier) =
|
||||
## Removes the given vertex modifier from the mesh.
|
||||
##
|
||||
## Note that currently if the modifier changed the mesh, it will remain
|
||||
## changed when removing the modifier.
|
||||
let index = self.vertex_modifiers.find(modifier)
|
||||
if index != -1:
|
||||
self.remove_modifier index
|
||||
|
@ -491,12 +574,25 @@ proc clone*(self: Mesh,
|
|||
return self.clone_impl(n)
|
||||
|
||||
proc sort_faces*(self: Mesh, camera_position: Vec3) =
|
||||
## TODO: unimplemented
|
||||
return
|
||||
|
||||
# TODO: support multiple index types with a generic?
|
||||
# NOTE: doing it using the triangles and not the original polygons might be
|
||||
# slightly inaccurate, unless mesh is trianglulated when baking
|
||||
proc generate_tangents*(self: MeshData, uv_layer_name, tangent_layer_name: string) =
|
||||
## Fills the given tangent attribute with tangent vectors based on the given
|
||||
## UV layer.
|
||||
##
|
||||
## Intended to be indirectly used by a mesh loader:
|
||||
##
|
||||
## * Reserving an attribute with a name starting by "tg_".
|
||||
## * With the same name as a UV layer minus the initial "uv_". For example
|
||||
## "tg_UVMap" will contain the tangents for "uv_UVMap".
|
||||
## * Then setting `mesh.generate_tangents` to `true`
|
||||
##
|
||||
## If the flag is set, this will be called automatically for each attribute
|
||||
## pair.
|
||||
if self.varrays.len != self.iarrays.len:
|
||||
raise newException(ValueError,
|
||||
"Function generate_tangents() doesn't support meshes without indices at the moment")
|
||||
|
@ -592,6 +688,8 @@ proc generate_tangents*(self: MeshData, uv_layer_name, tangent_layer_name: strin
|
|||
break
|
||||
|
||||
proc update_bounding_box*(self: Mesh) =
|
||||
## Calculates the local bounding box of the mesh and stores it in
|
||||
## `mesh.bound_box`.
|
||||
if self.data == nil or self.layout.len == 0:
|
||||
return
|
||||
# TODO: byte vertices
|
||||
|
@ -611,6 +709,10 @@ proc update_bounding_box*(self: Mesh) =
|
|||
self.center = mix(minv, maxv, 0.5)
|
||||
|
||||
proc debug_print_vertices*(self: Mesh, starts: int = 0, ends: int = 10) =
|
||||
## Debugging helper for mesh format loaders. It prints the values of each
|
||||
## attribute of vertices starting by index `starts` and ending before index
|
||||
## `ends` (not included). If you don't specify range, it will print the first
|
||||
## 10 vertices.
|
||||
if self.data.varrays[0].len == 0:
|
||||
return
|
||||
let varray = cast[ptr UncheckedArray[int8]](self.data.varrays[0][0].addr)
|
||||
|
@ -635,6 +737,11 @@ proc debug_print_vertices*(self: Mesh, starts: int = 0, ends: int = 10) =
|
|||
echo &"{attr.name} {data}"
|
||||
|
||||
proc add_polygonal_line*(self: Mesh, orig, dest: Vec3, width: float) =
|
||||
## Adds a line made of a quad, from `orig` to `dest` with a given width. The
|
||||
## quad will face up the Z axis. If the line starts where the previous line
|
||||
## ended, it will change both ends to match (unless the angle is under ~37°).
|
||||
##
|
||||
## It requires the mesh to have draw method TriangleStrip.
|
||||
let last = self.last_polyline_point
|
||||
let last_left = self.last_polyline_left
|
||||
let has_last = self.data.num_indices[0] != 0
|
||||
|
|
|
@ -119,6 +119,7 @@ proc newScene*(engine: MyouEngine, name: string = "Scene",
|
|||
return initScene(result, engine, name, add_viewport_automatically)
|
||||
|
||||
proc set_ob_name*(self: Scene, ob: GameObject, name: string) =
|
||||
## Rename the object and update the tables that reference it.
|
||||
var n = name
|
||||
while n in self.engine.objects:
|
||||
collision_seq += 1
|
||||
|
@ -131,6 +132,7 @@ proc add_object*(self: Scene, ob: GameObject,
|
|||
name: string = ob.name,
|
||||
parent_name: string = "", parent_bone: string = "",
|
||||
auto_update_matrix: bool = ob.auto_update_matrix): GameObject {.discardable.} =
|
||||
## Add an object to the scene
|
||||
if ob.scene != nil:
|
||||
if ob.scene == self:
|
||||
return
|
||||
|
@ -191,6 +193,7 @@ proc add_object*(self: Scene, ob: GameObject,
|
|||
return ob
|
||||
|
||||
proc remove_object*(self: Scene, ob: GameObject, recursive: bool = true) =
|
||||
## Remove an object from the scene
|
||||
self.children.remove ob
|
||||
if ob.auto_update_matrix:
|
||||
self.auto_updated_children.remove ob
|
||||
|
@ -235,6 +238,8 @@ proc remove_object*(self: Scene, ob: GameObject, recursive: bool = true) =
|
|||
|
||||
proc make_parent*(self: Scene, parent: GameObject, child: GameObject,
|
||||
keep_transform: bool = true) =
|
||||
## Set the parent of an object. It calculates the transformation so it
|
||||
## remains the same in world space unless `keep_transform = false` is passed.
|
||||
assert parent != nil and child != nil, "Arguments 'parent' and 'child' can't be nil."
|
||||
if child.parent.nonNil:
|
||||
self.clear_parent(child, keep_transform)
|
||||
|
@ -281,8 +286,10 @@ proc make_parent*(self: Scene, parent: GameObject, child: GameObject,
|
|||
self.children_are_ordered = false
|
||||
|
||||
proc clear_parent*(self: Scene, child: GameObject, keep_transform = true) =
|
||||
let parent = child.parent
|
||||
if parent != nil:
|
||||
## Disconnects an object from its parent. It calculates the transformation so
|
||||
## it remains the same in world space unless `keep_transform = false` is
|
||||
## passed.
|
||||
if child.parent != nil:
|
||||
if keep_transform:
|
||||
let rotation_order = child.rotation_order
|
||||
let (position, rotation) = child.get_world_position_rotation
|
||||
|
@ -297,13 +304,16 @@ proc clear_parent*(self: Scene, child: GameObject, keep_transform = true) =
|
|||
scale.y = world_matrix[1].xyz.length.copy_sign wm3[1,1]
|
||||
scale.z = world_matrix[2].xyz.length.copy_sign wm3[2,2]
|
||||
child.set_rotation_order(rotation_order)
|
||||
parent.children.remove child
|
||||
child.parent.children.remove child
|
||||
child.parent = nil
|
||||
child.parent_bone_index = -1
|
||||
child.matrix_parent_inverse = mat4()
|
||||
return
|
||||
|
||||
proc reorder_children*(self: Scene) =
|
||||
## Ensures that the order of children objects have parents before children.
|
||||
## To be used by scene loaders that may add objects in any order.
|
||||
|
||||
# TODO: Only the objects marked as unordered need to be resolved here!
|
||||
# (make a new list and append to children)
|
||||
self.auto_updated_children.set_len 0
|
||||
|
@ -320,6 +330,8 @@ proc reorder_children*(self: Scene) =
|
|||
self.children_are_ordered = true
|
||||
|
||||
proc update_all_matrices*(self: Scene) =
|
||||
## Calculates the matrices of all non-static objects and bones. Called
|
||||
## automatically before rendering.
|
||||
if self.children_are_ordered == false:
|
||||
self.reorder_children()
|
||||
# TODO: do this only for visible and modified objects
|
||||
|
@ -337,11 +349,15 @@ proc update_all_matrices*(self: Scene) =
|
|||
ob.update_matrices()
|
||||
|
||||
proc set_objects_auto_update_matrix*(self: Scene, objects: seq[GameObject], auto_update: bool) =
|
||||
## Adds or removes the specified objects from the auto update matrix list.
|
||||
## I.e. those not in the list are static.
|
||||
for ob in objects:
|
||||
ob.auto_update_matrix = auto_update
|
||||
self.children_are_ordered = false
|
||||
|
||||
proc destroy*(self: Scene) =
|
||||
## Destroy the scene and all its resources.
|
||||
|
||||
# This may not be necessary. TODO: test
|
||||
for ob in self.children:
|
||||
if ob.is_mesh:
|
||||
|
@ -378,17 +394,21 @@ proc destroy*(self: Scene) =
|
|||
self.mesh_passes = @[]
|
||||
|
||||
proc enable_render*(self: Scene) =
|
||||
## Enable rendering of the scene
|
||||
self.enabled = true
|
||||
|
||||
proc enable_physics*(self: Scene) =
|
||||
## Enable the phyisics in the scene
|
||||
if self.world != nil:
|
||||
self.world.enabled = true
|
||||
|
||||
proc enable_all*(self: Scene) =
|
||||
## Enable rendering and physics of the scene.
|
||||
self.enable_render()
|
||||
self.enable_physics()
|
||||
|
||||
proc new_gameobject*(self: Scene, name: string): GameObject =
|
||||
## Create a GameObject and add it to the scene.
|
||||
return self.engine.new_gameobject(name=name, scene=self)
|
||||
|
||||
proc new_mesh*(self: Scene, name: string,
|
||||
|
@ -402,10 +422,32 @@ proc new_mesh*(self: Scene, name: string,
|
|||
index_array: seq[uint16] = @[],
|
||||
pass: int32 = 0,
|
||||
): Mesh =
|
||||
## Create a new Mesh object and add it to this scene.
|
||||
##
|
||||
## The most common draw methods are `Triangles` (the default), `Points`,
|
||||
## `Lines`, and `TriangleStrip` (for `add_polygonal_line()`).
|
||||
##
|
||||
## If you supply a layout, it will be used. Otherwise a layout will be
|
||||
## created from `common_attributes`, which by default is `{vertex, color}`.
|
||||
## The available common attributes are `{vertex, color, normal, uv}` and they
|
||||
## will be added in that order.
|
||||
##
|
||||
## You can give a `vertex_count` to allocate a mesh with capacity for that
|
||||
## amount of vertices, but you can always resize it later. Meant to be used
|
||||
## with `add_vertex()` and `add_polygonal_line`
|
||||
##
|
||||
## If you give a vertex array and an index array, they will be used directly
|
||||
## as GPU buffers. If you only supply the vertex array, indices will
|
||||
## implicitely be sequential.
|
||||
self.engine.new_mesh(name, self, draw_method, common_attributes, layout,
|
||||
skip_upload, vertex_count, vertex_array, index_array, pass)
|
||||
|
||||
proc set_active_camera*(self: Scene, camera: Camera) =
|
||||
## Change the active camera of the scene, and if there are no viewports in
|
||||
## the main screen, create one.
|
||||
|
||||
# TODO: change the camera in the viewports that use it as well.
|
||||
# TODO: should only one viewport allowed per camera?
|
||||
self.active_camera = camera
|
||||
if camera.scene != self:
|
||||
let name = if camera.name != "": camera.name else: "Camera"
|
||||
|
@ -416,6 +458,9 @@ proc set_active_camera*(self: Scene, camera: Camera) =
|
|||
return
|
||||
|
||||
proc calculate_max_lights_and_cubemaps*(self: Scene) =
|
||||
## Change the limits for shader lighting data (number of lights of each type,
|
||||
## cubemaps, etc.) depending on the existing objects and settings. It also
|
||||
## resets the shaders.
|
||||
self.max_point_lights = 0
|
||||
self.max_sun_lights = 0
|
||||
self.max_spot_lights = 0
|
||||
|
@ -445,6 +490,10 @@ proc calculate_max_lights_and_cubemaps*(self: Scene) =
|
|||
# echo &"calculated max cubemaps {self.max_cubemaps}"
|
||||
|
||||
proc get_lighting_UBOs*(self: Scene): seq[UBO] =
|
||||
## Get all the Uniform Buffer Objects (UBOs) with lighting information used
|
||||
## in shaders. You need to pass it when creating a custom material that uses
|
||||
## them. You also need to pass the GLSL code returned by `get_lighting_code`_.
|
||||
|
||||
# echo &"getting UBOs {self.max_point_lights} {self.max_sun_lights}"
|
||||
|
||||
if self.lighting_UBOs.len == 0:
|
||||
|
@ -475,6 +524,8 @@ proc get_lighting_UBOs*(self: Scene): seq[UBO] =
|
|||
|
||||
|
||||
proc get_lighting_code_defines*(self: Scene): seq[string] =
|
||||
## Returns the macro defines used in materials with the current limits. Don't
|
||||
## use it. It is added automatically.
|
||||
@[
|
||||
"#define MAX_POINT_LIGHTS " & $(if self.isNil: 0 else: self.max_point_lights),
|
||||
"#define MAX_SUN_LIGHTS " & $(if self.isNil: 0 else: self.max_sun_lights),
|
||||
|
@ -485,6 +536,9 @@ proc get_lighting_code_defines*(self: Scene): seq[string] =
|
|||
]
|
||||
|
||||
proc get_lighting_code*(self: Scene): string =
|
||||
## Returns the GLSL code to be able to use the UBOs given by
|
||||
## `get_lighting_UBOs`_. It includes the definition of the UBOs, texture
|
||||
## uniforms, and shadow map functions.
|
||||
var code: seq[string]
|
||||
code.add dedent """
|
||||
#define light_position pos.xyz
|
||||
|
@ -567,6 +621,7 @@ proc get_lighting_code*(self: Scene): string =
|
|||
return code.join "\n"
|
||||
|
||||
proc update_lights*(self: Scene) =
|
||||
## Update the lighting UBOs. This is called automatically during rendering.
|
||||
if self.lighting_UBOs.len == 0:
|
||||
return
|
||||
var point, sun, spot, area = 0
|
||||
|
@ -627,7 +682,9 @@ proc update_lights*(self: Scene) =
|
|||
self.sun_light_UBO.update()
|
||||
|
||||
proc sort_cubemaps*(self: Scene) =
|
||||
# sort by volume
|
||||
## Sort cubemaps by volume, from largest to smallest. The shaders expect this
|
||||
## order. You don't need to call this, it is called automatically.
|
||||
|
||||
# TODO: avoid this allocation?
|
||||
var volumes = newSeqOfCap[(float32, CubemapProbe)](self.cubemap_probes.len)
|
||||
for probe in self.cubemap_probes:
|
||||
|
@ -643,7 +700,8 @@ proc sort_cubemaps*(self: Scene) =
|
|||
# or share depth buffers?
|
||||
|
||||
proc ensure_cubemaps*(self: Scene) =
|
||||
# echo "ensuring cubemaps"
|
||||
## Creates any missing cube maps. Called automatically.
|
||||
|
||||
let res = self.cubemap_resolution
|
||||
if res == 0:
|
||||
return
|
||||
|
@ -676,10 +734,13 @@ proc ensure_cubemaps*(self: Scene) =
|
|||
# echo &"made {self.cubemaps.len} cubemaps"
|
||||
|
||||
proc render_all_cubemaps*(self: Scene, use_roughness_prefiltering: bool, mipmap_shader: Material = nil) =
|
||||
## Render all cubemaps of the scene regardless of whether they need updating
|
||||
## or not.
|
||||
if self.cubemap_UBO == nil:
|
||||
return
|
||||
if self.world_material != nil:
|
||||
self.render_background_cubemap(use_roughness_prefiltering, mipmap_shader, upload_UBO = false)
|
||||
# TODO: Don't do this if objects haven't changed!
|
||||
self.sort_cubemaps()
|
||||
for probe in self.cubemap_probes:
|
||||
probe.render_cubemap(use_roughness_prefiltering, mipmap_shader)
|
||||
|
|
|
@ -52,6 +52,8 @@ import ./input
|
|||
import ./util
|
||||
|
||||
proc newScreen*(engine: MyouEngine, width, height: int32, title: string): Screen =
|
||||
## Creates a new screen or window. The first one is created by the engine for you.
|
||||
|
||||
result = new Screen
|
||||
result.engine = engine
|
||||
result.width = width
|
||||
|
@ -71,11 +73,17 @@ proc newScreen*(engine: MyouEngine, width, height: int32, title: string): Screen
|
|||
result.frame_interval = 1
|
||||
|
||||
proc destroy*(self: Screen) =
|
||||
## Destroys the screen/window and all its resources. If you destroy the main
|
||||
## screen, the first screen created after it will become the main screen. If
|
||||
## there are no screens left, the engine will exit.
|
||||
|
||||
self.engine.screens.remove self
|
||||
if self.engine.screen == self and self.engine.screens.len != 0:
|
||||
self.engine.screen = self.engine.screens[0]
|
||||
|
||||
proc resize*(self: Screen, width, height: int32, orientation = self.orientation) =
|
||||
## Resize the screen/window and all its viewports.
|
||||
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.orientation = orientation
|
||||
|
@ -95,6 +103,8 @@ proc resize*(self: Screen, width, height: int32, orientation = self.orientation)
|
|||
f(self)
|
||||
|
||||
proc add_viewport*(self: Screen, camera: Camera) =
|
||||
## Add a viewport to the screen with a given camera.
|
||||
|
||||
let vp = new Viewport
|
||||
vp.camera = camera
|
||||
vp.clear_color = true
|
||||
|
@ -104,14 +114,19 @@ proc add_viewport*(self: Screen, camera: Camera) =
|
|||
self.resize(self.width, self.height)
|
||||
|
||||
proc `vsync=`*(self: Screen, vsync: bool) =
|
||||
## Change the vsync setting of the screen/window.
|
||||
cast[Window](self.window).set_vsync vsync
|
||||
|
||||
proc get_ray_direction*(viewport: Viewport, position: Vec2): Vec3 =
|
||||
## Calculates a vector that points from the camera towards the given screen
|
||||
## coordinates, in world space.
|
||||
let x = (position.x - viewport.rect_pix[0].float32) / viewport.rect_pix[2].float32
|
||||
let y = (position.y - viewport.rect_pix[1].float32) / viewport.rect_pix[3].float32
|
||||
return viewport.camera.get_ray_direction(x,y)
|
||||
|
||||
proc get_ray_direction_local*(viewport: Viewport, position: Vec2): Vec3 =
|
||||
## Calculates a vector that points from the camera towards the given screen
|
||||
## coordinates, in camera space.
|
||||
let x = (position.x - viewport.rect_pix[0].float32) / viewport.rect_pix[2].float32
|
||||
let y = (position.y - viewport.rect_pix[1].float32) / viewport.rect_pix[3].float32
|
||||
return viewport.camera.get_ray_direction_local(x,y)
|
||||
|
|
Loading…
Reference in a new issue