myou-engine/tests/vertex_modifier.nim

137 lines
4 KiB
Nim

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()