Game engine built on top of signal-gl
- Components
<Scene/>
root-component ofsignal-gl/world
-tree<Group/>
JSX to group and transform simultaneously itssignal-gl/world
-children<Camera/>
JSX representing the scene's clip view<Shape/>
<Program/>
with default shaders<Cube/>
<Shape/>
with hard-coded cube vertices<ColliderProvider/>
dependency injection of collider-management<AxisAlignedBoxCollider/>
AABB colider
- Hooks
useScene
context-hook, only available inside<Scene/>
root-component of
signal-gl/world
import { Scene } from '@bigmistqke/signal-gl/world'
return (
<Scene>
...
</Scene>
)
type CanvasProps
see
create a parent-child relationship by cascading model-view's matrix transformations aka grouping
signal-gl/world
-objects in cyberspace
import { Scene, Group } from '@bigmistqke/signal-gl/world'
return (
<Scene>
<Group position={[0, 0, 5]}>
...
</Group>
</Scene>
)
type Pose = {
position: Vector3 | vec3
rotation: Vector3 | vec3
scale: Vector3 | vec3
matrix: mat4
}
type GroupProps = ParentProps<Pose>
control the scene's model- and projection-view aka a camera in cyberspace
import { Scene, Group } from '@bigmistqke/signal-gl/world'
return (
<Scene>
<Camera position={[0, 0, 5]} fov={90}/>
...
</Scene>
)
type CameraProps = ParentProps<Pose & {
active: boolean
fov: number
near: number
far: number
realMatrix: mat4
}>
a
<Program/>
with a default vertex- and fragment-shader wrapped with a<Group/>
aka a shape in cyberspace
import { Shape } from '@bigmistqke/signal-gl/world'
return (
<Scene>
<Shape vertices={new UInt16Array(...)} position={[0, 20, 0]}/>
...
</Scene>
)
type ShapeProps = ParentProps<Pose & {
fragment?: ShaderToken
vertex?: ShaderToken
indices: number[]
color?: Vector3 | vec3 // [1, 1, 1]
opacity?: number // 1
vertices: Float32Array
mode?: RenderMode // 'TRIANGLES'
}>
glsl`#version 300 es
precision mediump float;
out vec4 model;
out vec4 view;
out vec4 clip;
void main(void) {
model = ${scene.model.uniform} * vec4(${attribute.vec3(() => props.vertices)}, 1.);
view = ${scene.view.uniform} * model;
clip = ${scene.projection.uniform} * view;
gl_Position = clip;
}`
in the fragment-shader you have access to the following varying
model
model-spaceview
view-spaceclip
clip-space
glsl`#version 300 es
precision mediump float;
out vec4 color_out;
void main(void) {
color_out = vec4(${uniform.vec3(() => props.color)}, ${uniform.float(() => props.opacity)});
}`
a
<Shape/>
with vertices and indices describing a cube aka a cube in cyberspace
import { Shape } from '@bigmistqke/signal-gl/world'
return (
<Scene>
<Shape vertices={new UInt16Array(...)} position={[0, 20, 0]}/>
...
</Scene>
)
type ShapeProps = ParentProps<Pose & {
fragment?: ShaderToken
vertex?: ShaderToken
indices: number[]
color?: Vector3 | vec3 // [1, 1, 1]
opacity?: number // 1
vertices: Float32Array
mode?: RenderMode // 'TRIANGLES'
}>
dependency inject collider-functionality into your
signal-gl/world
-scene
import { ColliderProvider, createRaycaster, Shape } from '@bigmistqke/signal-gl/world'
const raycaster = createRaycaster()
return (
<Scene>
<ColliderProvider plugins={[raycaster]}>
...
</ColliderProvider>
</Scene>
)
type ColliderPlugin = Raycaster
type ColliderProviderProps = ParentProps<{
plugins: ColliderPlugin[]
}>
an
AABB
-collider see
import { Shape, AxisAlignedBoxCollider } from '@bigmistqke/signal-gl/world'
return (
<Scene>
<ColliderProvider>
<AxisAlignedBoxCollider onEvent={({hit}) => console.log('hit?', hit)}>
<Cube>
</AxisAlignedBoxCollider>
</ColliderProvider>
</Scene>
)
you can also visualise AxisAlignedBoxCollider
by assigning it a color
import { Shape, AxisAlignedBoxCollider } from '@bigmistqke/signal-gl/world'
return (
<Scene>
<ColliderProvider>
<AxisAlignedBoxCollider color={[0, 2, 0]} />
</ColliderProvider>
</Scene>
)
type AxisAlignedBoxColliderProps = ParentProps<{
scale?: Vector3 | vec3
position?: Vector3 | vec3
color?: Vector3 | vec3
onEvent?: (event: { type: string; hit: boolean }) => void
mode?: RenderMode
}>
- access
signal-gl/world
state through context - only available inside scope of a
<Scene/>
import { useScene } from "@bigmistqke/signal-gl/world"
const Object = () => {
const scene = useScene()
if(!scene) throw 'scene is not defined'
console.log(scene.model.matrix)
...
}
type SceneContext = SignalGLContext & {
projection: {
uniform: ReturnType<typeof uniform.mat4>
matrix: mat4
invertedMatrix: mat4
},
view: {
uniform: ReturnType<typeof uniform.mat4>
matrix: mat4
invertedMatrix: mat4
},
model: {
uniform: ReturnType<typeof uniform.mat4>
matrix: mat4
},
setView(view: mat4) : void
setProjection(view: mat4) : void
}
- factory-function for a
ColliderPlugin
import { createRaycaster } from "@bigmistqke/signal-gl/world"
const raycaster = createRaycaster()
if(!scene) throw 'scene is not defined'
const raycaster = createRaycaster()
// raycast direction of the cursor
// whenever the camera or any of the collider's update
createEffect(raycaster.castCursor)
return (
// raycast from the center of the camera
<Scene onMouseDown={raycaster.castCenter}>
<ColliderProvider plugins={[raycaster]}>
<AxisAlignedBoxCollider onEvent={({type, hit}) => console.log(type, hit)}/>
</ColliderProvider>
</Scene>
)