untitled-game-engine/examples/cube/index.js
2025-03-28 18:06:18 -05:00

205 lines
6 KiB
JavaScript

import { addComponent, addEntity, createWorld, query } from '/src/ecs.js'
import { Engine, Startup, Update, World } from '/src/core/engine.js'
import { DefaultPlugins } from '/src/plugins/index.js'
import { WebGLContext } from '/src/plugins/renderer/webgl-context.js'
import { Camera, Mesh, Renderable, Transform } from '/src/components/index.js'
import * as twgl from '/public/vendor/twgl/twgl-full.module.js'
import { Assets, AssetType } from '/src/core/assets.js'
import { Geometry, Material, Materials, Meshes } from '/src/plugins/renderer/index.js'
/** @param {number} degrees */
const deg2rad = degrees => degrees * Math.PI / 180
// INFO: does a camera need a Transform, specifically
// a scale??
/** @param {World} world */
const createCamera = world => {
const entity = addEntity(world)
addComponent(world, entity, [Camera, Transform])
const transform = twgl.m4.identity()
twgl.m4.translate(transform, [0, 0, 10], transform)
Transform.matrix[entity] = transform
Camera.projectionMatrix[entity] = []
Camera.viewMatrix[entity] = []
Camera.up[entity] = new Float32Array([0, 1, 0])
Camera.fov[entity] = deg2rad(30)
Camera.zNear[entity] = 0.1
Camera.zFar[entity] = 1000
return entity
}
const Cube = () => {
const position = [
// Front face
-0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
// Back face
-0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
// Top face
-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5,
// Bottom face
-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5,
// Right face
0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5,
// Left face
-0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
]
const indices = [
// Front face
0, 1, 2, 0, 2, 3,
// Back face
4, 5, 6, 4, 6, 7,
// Top face
8, 9, 10, 8, 10, 11,
// Bottom face
12, 13, 14, 12, 14, 15,
// Right face
16, 17, 18, 16, 18, 19,
// Left face
20, 21, 22, 20, 22, 23,
]
const color = [
// Front face (red)
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
// Back face (green)
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0,
// Top face (blue)
0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
// Bottom face (yellow)
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0,
// Right face (magenta)
1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1,
// Left face (cyan)
0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
]
return {
position,
indices,
color: { numComponents: 3, data: color }
}
}
const Quad = () => {
const position = [
-0.5, -0.5, 0,
0.5, -0.5, 0,
0.5, 0.5, 0,
-0.5, 0.5, 0,
]
const indices = [
0, 1, 2,
2, 3, 0,
]
const color = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 1, 0,
]
return { position, indices, color: { numComponents: 3, data: color } }
}
const Triangle = () => {
return {
position: [-0.5, -0.5, 0, 0.5, -0.5, 0, 0, 0.5, 0],
color: { numComponents: 3, data: [1, 0, 0, 0, 1, 0, 0, 0, 1] },
indices: [0, 1, 2]
}
}
const createObject = (world, mesh, material, position) => {
const entity = addEntity(world)
addComponent(world, entity, [Renderable, Mesh, Transform])
const transform = twgl.m4.identity()
twgl.m4.translate(transform, position, transform)
Transform.matrix[entity] = transform
Mesh.geometry[entity] = mesh
Mesh.material[entity] = material
return entity
}
/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('main canvas')
if (canvas.getContext) {
const world = createWorld(new World())
let app = new Engine(world, canvas)
.addResource(new WebGLContext(canvas))
.addResource(new Assets())
.addPlugin(DefaultPlugins)
.addSystem(Startup, async world => {
const gl = world.getResource(WebGLContext).context
const meshes = world.getResource(Meshes)
const materials = world.getResource(Materials)
const assets = world.getResource(Assets)
const camera = createCamera(world)
let sources = ['simple-vert.wgsl', 'simple-frag.wgsl']
.map(url => `/public/shaders/${url}`)
.map(import.meta.resolve)
.map(url => assets.load(url, AssetType.Text))
sources = await Promise.all(sources)
const mesh = Cube()
const geometry = new Geometry(mesh.position, mesh.indices).createBuffers()
const meshHandle = meshes.add(buffer)
const materialHandle = materials.add(new Material(
programInfo,
{ u_color: [1, 1, 1, 1] }
))
const one = createObject(world, meshHandle, materialHandle, [0, -1.5, 0])
let transform = Transform.matrix[one]
twgl.m4.rotateX(transform, deg2rad(45), transform)
twgl.m4.rotateY(transform, deg2rad(45), transform)
const two = createObject(world, meshHandle, materialHandle, [2, 1, 0])
transform = Transform.matrix[two]
twgl.m4.rotateX(transform, deg2rad(135), transform)
twgl.m4.rotateY(transform, deg2rad(135), transform)
const three = createObject(world, meshHandle, materialHandle, [-2, 1, 0])
transform = Transform.matrix[three]
twgl.m4.rotateX(transform, deg2rad(225), transform)
twgl.m4.rotateY(transform, deg2rad(225), transform)
})
.addSystem(Update, world => {
const meshes = query(world, [Mesh, Transform])
for (const mesh of meshes) {
const transform = Transform.matrix[mesh]
twgl.m4.rotateZ(transform, 0.01, transform)
}
})
.run()
} else {
// canvas not supported
}