Add tests: triangle, lines, texture, vertex_modifier, armature, msaa1, msaa2.

This commit is contained in:
Alberto Torres 2025-03-18 20:42:32 +01:00
parent bde930692d
commit a4007a36b0
8 changed files with 432 additions and 0 deletions

109
tests/armature.nim Normal file
View file

@ -0,0 +1,109 @@
import myou_engine
import std/sequtils
# Create engine, scene, camera
let engine = newMyouEngine(1024, 768)
let scene = engine.newScene()
let cam = scene.newCamera()
# Move the camera upwards (the default rotation looks down)
cam.position.z = 10
let armature = engine.newArmature(scene=scene)
let b0 = armature.add_bone("Bone", vec3(), quat(), 0, "", 0.5)
let b1 = armature.add_bone("Bone1", vec3(0,0.5,0), quat(), 1, "Bone", 0.5)
let b2 = armature.add_bone("Bone2", vec3(0,0.5,0), quat(), 2, "Bone1", 0.5)
let b3 = armature.add_bone("Bone3", vec3(0,0.5,0), quat(), 3, "Bone2", 0.5)
armature.update_rest_matrices
armature.position.y = -1
var layout: AttributeList
layout.add Attribute(name: "vertex", dtype: Float, count: 3)
layout.add Attribute(name: "weights", dtype: UByte, count: 4)
layout.add Attribute(name: "b_indices", dtype: UByte, count: 4)
var vertices: seq[(Vec3, array[4, uint8], array[4, uint8])]
var indices: seq[uint16]
const faces_x = 2
const faces_y = 16
for y in 0 .. faces_y:
for x in 0 .. faces_x:
let pos = vec3(0.5 * (x/faces_x - 0.5), 2f * (y/faces_y), -0.1)
let p = vec3(0, pos.y, 0)
var weights = [
clamp(0.5-(b0.dist_to_sphere(p) * 3), 0, 1),
clamp(0.5-(b1.dist_to_sphere(p) * 3), 0, 1),
clamp(0.5-(b2.dist_to_sphere(p) * 3), 0, 1),
clamp(0.5-(b3.dist_to_sphere(p) * 3), 0, 1),
]
let sum = weights.foldl a+b
let w_u8 = weights.mapIt uint8(255 * it/sum)
let w_u8a = [w_u8[0], w_u8[1], w_u8[2], w_u8[3]]
vertices.add (pos, w_u8a, [0'u8,1,2,3])
for y in 0'u16 ..< faces_y.uint16:
let p = y * (faces_x.uint16 + 1)
for x in p ..< p + faces_x.uint16:
let x2 = x + faces_x.uint16 + 1
indices.add @[
x, x+1, x2,
x2, x+1, x2+1,
]
let grid = scene.newMesh("grid",
layout = layout,
vertex_array = vertices,
index_array = indices,
)
grid.parent_to armature, keep_transform=false
grid.armature = armature
grid.add_modifier engine.newArmatureModifier(4)
grid.materials.add engine.newSolidMaterial("blue", vec4(0.1,0.3,1,1))
# TODO: Move visualization of armature wireframe to the engine
let wireframe = scene.newMesh("wireframe", draw_method=Lines, vertex_count=8)
wireframe.parent_to armature, keep_transform=false
wireframe.materials.add engine.newSolidMaterial("white", vec4(1))
const OCTAEDRON = @[
vec3(0), vec3(-0.1, 0.1, -0.1),
vec3(0), vec3(-0.1, 0.1, 0.1),
vec3(0), vec3(0.1, 0.1, -0.1),
vec3(0), vec3(0.1, 0.1, 0.1),
vec3(0.1, 0.1, 0.1), vec3(-0.1, 0.1, -0.1),
vec3(-0.1, 0.1, -0.1), vec3(-0.1, 0.1, 0.1),
vec3(-0.1, 0.1, 0.1), vec3(0.1, 0.1, -0.1),
vec3(0.1, 0.1, -0.1), vec3(0.1, 0.1, 0.1),
vec3(-0.1, 0.1, -0.1), vec3(0,1,0),
vec3(-0.1, 0.1, 0.1), vec3(0,1,0),
vec3(0.1, 0.1, -0.1), vec3(0,1,0),
vec3(0.1, 0.1, 0.1), vec3(0,1,0),
]
proc update_wireframe() =
armature.recalculate_bone_matrices() # only needed here for visualization
wireframe.clear_vertices()
wireframe.ensure_capacity(OCTAEDRON.len * armature.bone_list.len)
for bone in armature.bone_list:
let m = bone.matrix
for point in OCTAEDRON:
wireframe.add_vertex(m * (bone.blength * point))
wireframe.data.update_varray()
update_wireframe()
var time = -1.0
scene.pre_draw_callbacks.add proc(scene: Scene, dt: float) =
time += dt
if time < 0:
return
let f = time * 2f
const w = 1.0
armature.bones["Bone"].rotation = quat().rotateZ(-sin(f) * w)
armature.bones["Bone1"].rotation = quat().rotateZ(-sin(f*1.1) * w)
armature.bones["Bone2"].rotation = quat().rotateZ(-sin(f*1.2) * w)
armature.bones["Bone3"].rotation = quat().rotateZ(-sin(f*1.4) * w)
update_wireframe()
scene.enable_render()
engine.run()

27
tests/lines.nim Normal file
View file

@ -0,0 +1,27 @@
import myou_engine
import random
# NOT calling this to have always the same results
#randomize()
let engine = newMyouEngine(1024, 768)
let scene = engine.newScene()
let cam = scene.newCamera(cam_type = Orthographic, ortho_scale = 2)
cam.position.z = 10
# vertex_count is the initial capacity and is not necessary
let lines = scene.newMesh("lines", draw_method=Lines, vertex_count=64)
lines.materials.add engine.newSolidMaterial("white", vec4(1))
scene.pre_draw_callbacks.add proc(scene: Scene, dt: float) =
# ensure_capacity enlarges the size of the mesh if the next N vertices
# don't fit, by making it at least twice the size
lines.ensure_capacity(1)
lines.add_vertex(vec3(rand(1f), rand(1f), 0) * 2f - 1f)
# call update_varray() only once after making changes
lines.data.update_varray()
scene.enable_render()
engine.run()

25
tests/msaa1.nim Normal file
View file

@ -0,0 +1,25 @@
import myou_engine
import random
# Create engine, scene, camera
let engine = newMyouEngine(1024, 768, context_msaa_samples=4)
let scene = engine.newScene()
let cam = scene.newCamera(cam_type = Orthographic, ortho_scale = 2)
cam.position.z = 10
# vertex_count is the initial capacity and is not necessary
let lines = scene.newMesh("lines", draw_method=Lines, vertex_count=64)
lines.materials.add engine.newSolidMaterial("white", vec4(1))
scene.pre_draw_callbacks.add proc(scene: Scene, dt: float) =
# ensure_capacity enlarges the size of the mesh if the next N vertices
# don't fit, by making it at least twice the size
lines.ensure_capacity(1)
lines.add_vertex(vec3(rand(1f), rand(1f), 0) * 2f - 1f)
# call update_varray() only once after making changes
lines.data.update_varray()
scene.enable_render()
engine.run()

30
tests/msaa2.nim Normal file
View file

@ -0,0 +1,30 @@
import myou_engine
import random
# Create engine, scene, camera
let engine = newMyouEngine(1024, 768)
let scene = engine.newScene()
let cam = scene.newCamera(cam_type = Orthographic, ortho_scale = 2)
# This method allows changing the amount of antialiasing samples at any point,
# and it will resize, add or remove the multisampled framebuffer
engine.screen.set_MSAA_samples 4
# Move the camera upwards (the default rotation looks down)
cam.position.z = 10
# vertex_count is the initial capacity and is not necessary
let lines = scene.newMesh("lines", draw_method=Lines, vertex_count=64)
lines.materials.add engine.newSolidMaterial("white", vec4(1))
scene.pre_draw_callbacks.add proc(scene: Scene, dt: float) =
# ensure_capacity enlarges the size of the mesh if the next N vertices
# don't fit, by making it at least twice the size
lines.ensure_capacity(1)
lines.add_vertex(vec3(rand(1f), rand(1f), 0) * 2f - 1f)
# call update_varray() only once after making changes
lines.data.update_varray()
scene.enable_render()
engine.run()

2
tests/nim.cfg Normal file
View file

@ -0,0 +1,2 @@
path:"../libs/packages/"
debugger: native

74
tests/texture.nim Normal file
View file

@ -0,0 +1,74 @@
import myou_engine
# Create engine, scene, camera
let engine = newMyouEngine(1024, 768)
let scene = engine.newScene()
let cam = scene.newCamera() # this sets the active camera if there's none
scene.background_color = vec4(0.1, 0.1, 0.1, 1)
# Move the camera upwards (the default rotation looks down)
cam.position.z = 10
# Create a simple square mesh with UV
let quad = scene.newMesh("quad",
common_attributes = {vertex, uv},
vertex_array = @[
# x, y, z, U, V,
-1f, -1, 0, 0, 0,
1, -1, 0, 1, 0,
-1, 1, 0, 0, 1,
1, 1, 0, 1, 1,
],
index_array = @[0'u16, 1, 2, 2, 1, 3],
)
# Make the object wider with a 3:2 aspect ratio
quad.scale.x = 1.5
# This example texture is 1 pixel wide and 8 pixels high,
# to make a stripped flag
# NOTE: The colors are SRGB_u8, not RGB, but we're skipping output encoding
# in the shader for simplicity
let width = 1
let height = 8
let format = RGB_u8
# The texture goes from bottom to top
# (unless we invert the Y coordinate of the UV)
let pixels = @[
115, 41, 130,
36, 64, 142,
0, 128, 38,
255, 237, 0,
255, 140, 0,
228, 3, 3,
120, 79, 23,
0, 0, 0,
]
# Create the texture, use Nearest filter
# to have a sharp bands instead of a gradient
doAssert width * height * 3 == pixels.len
let texture = engine.newTexture("test",
width = width, height = height,
format = format, filter = Nearest,
pixels = newArrRef[uint8](pixels).to float32)
# Minimal example of a material with a UV and a texture uniform
quad.materials.add engine.newMaterial("shadeless texture",
fragment = """
uniform sampler2D tex;
in vec2 uv;
out vec4 glOutColor;
void main(){
glOutColor = texture(tex, uv);
}""",
varyings = @[Varying(vtype: Uv, varname: "uv")],
textures = {
"tex": texture,
}.toOrderedTable,
)
scene.enable_render()
engine.run()

28
tests/triangle.nim Normal file
View file

@ -0,0 +1,28 @@
import myou_engine
# Create engine, scene, camera
let engine = newMyouEngine(1024, 768)
let scene = engine.newScene()
let cam = scene.newCamera() # this sets the active camera if there's none
# Move the camera upwards (the default rotation looks down)
cam.position.z = 10
# The default layout is vertex position (vec3) and color (uint8 RGBA).
# This seq can be of any type as long as it matches the underlying mesh layout.
let vertices = @[
(vec3(-1.2, -1, 0), [255'u8, 255, 0, 255]),
(vec3( 1.2, -1, 0), [0'u8, 255, 255, 255]),
(vec3( 0, 1, 0), [255'u8, 0, 255, 255]),
]
let triangle = scene.newMesh("triangle", vertex_array=vertices)
# A predefined material that just draws the vertex colors
triangle.materials.add engine.newVertexColorMaterial()
# A new scene is disabled by default, we need to enable it
scene.enable_render()
# This should be the last line of code of the main module
engine.run()

137
tests/vertex_modifier.nim Normal file
View file

@ -0,0 +1,137 @@
import myou_engine
# A vertex modifier allows us to inject vertex shader code
# while using the autogenerated vertex shader
proc newWaveModifier*(engine: MyouEngine): VertexModifier =
# get_code is called when a shader is created for the mesh
result.get_code = proc(v: seq[Varying]): VertexModifierCodeLines =
# uniform_lines go in the global scope, for uniforms
# and global functions
result.uniform_lines = @[
"layout(std140) uniform WaveModifier {",
" float wave_time;",
"};",
"vec3 waveFlag(vec3 pos, float time) {",
" float waveIntensity = (pos.x + 1.0) / 2.0;",
" float wave = sin(pos.x * 4.0 - time * 5.0 + pos.y * 3.0) * 0.5;",
" wave += sin(pos.x * 6.0 + time * 3.5) * 0.3;",
" pos.z += wave * waveIntensity * 0.9;",
" pos.y -= waveIntensity * waveIntensity * 0.3;",
" ",
" return pos;",
"}",
]
# body lines are inserted after reading the local vertex position
# and normal, called co (vec4) and normal (vec3) respectively
result.body_lines = @[
"co.xyz = waveFlag(co.xyz, wave_time);",
]
# Size is 4 because of std140 requirements
let ubo = engine.renderer.newUBO("WaveModifier", float32, 4)
result.ubo = ubo
result.update = proc(m: Mesh) =
var data = ubo.storage(float32)
data[0] = m.scene.time
ubo.update()
# Create engine, scene, camera
let engine = newMyouEngine(1024, 768)
let scene = engine.newScene()
let cam = scene.newCamera() # this sets the active camera if there's none
scene.background_color = vec4(0.1, 0.1, 0.1, 1)
# Move the camera upwards (the default rotation looks down)
cam.position.z = 10
# Create a grid mesh with UV
# The dimensions are the amount of faces,
# or the amount of vertices minus one
const grid_x = 64
const grid_y = 48
var vertices: seq[float32]
for y in 0 ..< grid_y + 1:
for x in 0 ..< grid_x + 1:
vertices.add @[
-1f + x.float32 * (2f / grid_x),
-1f + y.float32 * (2f / grid_y),
0,
x / grid_x, y / grid_y,
]
var indices: seq[uint16]
for y in 0'u16 ..< grid_y:
let p = y * (grid_x + 1)
for x in p ..< p + grid_x:
let x2 = x + grid_x + 1
indices.add @[
x, x+1, x2,
x2, x+1, x2+1,
]
let grid = scene.newMesh("grid",
common_attributes = {vertex, uv},
vertex_array = vertices,
index_array = indices,
)
# Make the object wider with a 3:2 aspect ratio
grid.scale.x = 1.5
# Add the vertex modifier
grid.get_mesh.add_modifier newWaveModifier(engine)
# This example texture is 1 pixel wide and 8 pixels high,
# to make a stripped flag
# NOTE: The colors are SRGB_u8, not RGB, but we're skipping output encoding
# in the shader for simplicity
let width = 1
let height = 8
let format = RGB_u8
# The texture goes from bottom to top
# (unless we invert the Y coordinate of the UV)
let pixels = @[
115, 41, 130,
36, 64, 142,
0, 128, 38,
255, 237, 0,
255, 140, 0,
228, 3, 3,
120, 79, 23,
0, 0, 0,
]
# Create the texture, use Nearest filter
# to have a sharp bands instead of a gradient
doAssert width * height * 3 == pixels.len
let texture = engine.newTexture("test",
width = width, height = height,
format = format, filter = Nearest,
pixels = newArrRef[uint8](pixels).to float32)
# Minimal example of a material with a UV and a texture uniform
grid.materials.add engine.newMaterial("shadeless texture",
fragment = """
uniform sampler2D tex;
in vec2 uv;
in vec3 worldpos;
out vec4 glOutColor;
void main(){
glOutColor = texture(tex, uv) + vec4(worldpos.z * 0.5);
}""",
varyings = @[
Varying(vtype: Uv, varname: "uv"),
Varying(vtype: WorldPosition, varname: "worldpos"),
],
textures = {
"tex": texture,
}.toOrderedTable,
)
scene.enable_render()
engine.run()