feat: 1995
This commit is contained in:
commit
fb89263930
69 changed files with 3359 additions and 0 deletions
35
engine/scene/Camera.ts
Normal file
35
engine/scene/Camera.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { Mat4 } from "../math/Mat4"
|
||||
import { Vec3 } from "../math/Vec3"
|
||||
|
||||
/** First-person camera. Orientation is Euler yaw/pitch (no roll), which is all
|
||||
* an FPS needs and avoids gimbal bookkeeping. */
|
||||
export type Camera = {
|
||||
position: Vec3
|
||||
/** Rotation around +Y, radians. 0 looks toward -Z; increasing turns right. */
|
||||
yaw: number
|
||||
/** Look up/down, radians. Positive looks up. Clamp near +-pi/2 to avoid flip. */
|
||||
pitch: number
|
||||
/** Vertical field of view, radians. */
|
||||
fov: number
|
||||
}
|
||||
|
||||
export namespace Camera {
|
||||
/** Unit forward direction implied by yaw/pitch. */
|
||||
export function forward(cam: Camera): Vec3 {
|
||||
const cp = Math.cos(cam.pitch)
|
||||
return {
|
||||
x: cp * Math.sin(cam.yaw),
|
||||
y: Math.sin(cam.pitch),
|
||||
z: -cp * Math.cos(cam.yaw),
|
||||
}
|
||||
}
|
||||
|
||||
/** Combined projection * view matrix for the given viewport aspect ratio.
|
||||
* Near/far are fixed for now; far only needs to exceed the fog distance. */
|
||||
export function viewProjection(cam: Camera, aspect: number): Mat4 {
|
||||
const eye = cam.position
|
||||
const view = Mat4.lookAt(eye, Vec3.add(eye, forward(cam)), { x: 0, y: 1, z: 0 })
|
||||
const proj = Mat4.perspective(cam.fov, aspect, 0.05, 100)
|
||||
return Mat4.multiply(proj, view)
|
||||
}
|
||||
}
|
||||
11
engine/scene/Mesh.ts
Normal file
11
engine/scene/Mesh.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import type { Vec2 } from "../math/Vec2"
|
||||
import type { Vec3 } from "../math/Vec3"
|
||||
|
||||
/** One mesh vertex: a world-space position and its texture coordinate. uv is in
|
||||
* tile units, not 0..1, so values >1 repeat the texture (see Texture.sample). */
|
||||
export type Vertex = { pos: Vec3; uv: Vec2 }
|
||||
|
||||
/** Indexed triangle mesh in world space. `indices` holds three entries per
|
||||
* triangle, each indexing into `vertices`; sharing vertices between triangles
|
||||
* keeps seams welded and shrinks the data. */
|
||||
export type Mesh = { vertices: Vertex[]; indices: number[] }
|
||||
45
engine/scene/Sprite.ts
Normal file
45
engine/scene/Sprite.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import type { Vec2 } from "../math/Vec2"
|
||||
import type { Vec3 } from "../math/Vec3"
|
||||
import type { Texture } from "../render/Texture"
|
||||
import { Camera } from "./Camera"
|
||||
import type { Mesh } from "./Mesh"
|
||||
|
||||
/** A flat image standing in the world, always turned to face the camera --
|
||||
* how the PS1 drew most enemies and props instead of 3D models. */
|
||||
export type Sprite = {
|
||||
/** World anchor at the base (feet) center. */
|
||||
position: Vec3
|
||||
/** World-space width and height. */
|
||||
size: Vec2
|
||||
texture: Texture
|
||||
}
|
||||
|
||||
export namespace Sprite {
|
||||
/**
|
||||
* Build the sprite's quad as a Y-axis billboard: it spins around vertical to
|
||||
* face the camera but stays upright, so characters never tilt. Draw the
|
||||
* result with Rasterizer.draw (its alpha cutout hides transparent texels).
|
||||
*/
|
||||
export function billboard(sprite: Sprite, camera: Camera): Mesh {
|
||||
const forward = Camera.forward(camera)
|
||||
// Camera right projected onto the ground plane (== normalize(-fz, 0, fx)).
|
||||
const len = Math.hypot(forward.x, forward.z) || 1
|
||||
const rx = -forward.z / len
|
||||
const rz = forward.x / len
|
||||
const hw = sprite.size.x / 2
|
||||
const p = sprite.position
|
||||
const y0 = p.y
|
||||
const y1 = p.y + sprite.size.y
|
||||
const lx = p.x - rx * hw
|
||||
const lz = p.z - rz * hw
|
||||
const gx = p.x + rx * hw
|
||||
const gz = p.z + rz * hw
|
||||
const vertices = [
|
||||
{ pos: { x: lx, y: y0, z: lz }, uv: { x: 0, y: 1 } },
|
||||
{ pos: { x: gx, y: y0, z: gz }, uv: { x: 1, y: 1 } },
|
||||
{ pos: { x: gx, y: y1, z: gz }, uv: { x: 1, y: 0 } },
|
||||
{ pos: { x: lx, y: y1, z: lz }, uv: { x: 0, y: 0 } },
|
||||
]
|
||||
return { vertices, indices: [0, 1, 2, 0, 2, 3] }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue