refactor: move world concepts into engine
This commit is contained in:
parent
eeedcb8e48
commit
2d15c7ab8d
52 changed files with 3298 additions and 1558 deletions
|
|
@ -1,15 +1,28 @@
|
|||
import { Terrain } from "../../Terrain"
|
||||
import type { Mesh } from "../../../engine/scene/Mesh"
|
||||
import type { Mob } from "../Mob"
|
||||
import type { Entity } from "../../../engine/scene/Actor"
|
||||
import { Terrain, type Terrain as Ground } from "../../../engine/world/Terrain"
|
||||
import { Mesh, type Mesh as Geometry } from "../../../engine/scene/Mesh"
|
||||
import type { Material } from "../../../engine/render/Material"
|
||||
import type { ActorDefinition } from "../../../engine/scene/Actor"
|
||||
import { Mob, type MobState } from "../Mob"
|
||||
import { ellipsoid, nextRand, ovoidZ, wanderHeading, wing } from "./mobkit"
|
||||
|
||||
export const bee: Entity<Mob, Terrain> = {
|
||||
name: "bee",
|
||||
build,
|
||||
update,
|
||||
boundingRadius: 0.5,
|
||||
bodyHeight: 0.5,
|
||||
export type Bee = ActorDefinition<MobState, Ground>
|
||||
|
||||
export namespace Bee {
|
||||
export function create(material: Material): Bee {
|
||||
const mesh = Mesh.create()
|
||||
build(mesh)
|
||||
return {
|
||||
prototype: {
|
||||
groups: [{ mesh, material }],
|
||||
radius: 0.6,
|
||||
minY: -0.5,
|
||||
maxY: 1,
|
||||
},
|
||||
update,
|
||||
transform: Mob.transform,
|
||||
collider: (state) => Mob.collider(state, 0.5, 0.5, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LEASH = 6
|
||||
|
|
@ -20,7 +33,7 @@ const HOVER = 1.1
|
|||
const BOB_AMP = 0.18
|
||||
const BOB_FREQ = 3
|
||||
|
||||
function build(mesh: Mesh): void {
|
||||
function build(mesh: Geometry): void {
|
||||
// Fore-aft ovoid body striped along its length, a dark head at the front, two
|
||||
// pale wings. UVs: bee texture is stripe bands (left), head-dark (mid), wing-pale
|
||||
// (right); the body maps v along z so the stripes band across it.
|
||||
|
|
@ -30,7 +43,7 @@ function build(mesh: Mesh): void {
|
|||
wing(mesh, -1, 0.83, 0.99, 0, 1)
|
||||
}
|
||||
|
||||
function update(mob: Mob, dt: number, terrain: Terrain): void {
|
||||
function update(mob: MobState, dt: number, terrain: Ground): void {
|
||||
mob.phase += dt
|
||||
mob.timer -= dt
|
||||
if (mob.timer <= 0) {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,30 @@
|
|||
import { Terrain } from "../../Terrain"
|
||||
import type { Mesh } from "../../../engine/scene/Mesh"
|
||||
import type { Mob } from "../Mob"
|
||||
import type { Entity } from "../../../engine/scene/Actor"
|
||||
import { Terrain, type Terrain as Ground } from "../../../engine/world/Terrain"
|
||||
import { Mesh, type Mesh as Geometry } from "../../../engine/scene/Mesh"
|
||||
import type { Material } from "../../../engine/render/Material"
|
||||
import type { ActorDefinition } from "../../../engine/scene/Actor"
|
||||
import { Mob, type MobState } from "../Mob"
|
||||
import { ellipsoid, nextRand, wanderHeading } from "./mobkit"
|
||||
|
||||
// Everything about the frog: squat, ground-bound, sits then springs a ballistic hop.
|
||||
|
||||
export const frog: Entity<Mob, Terrain> = {
|
||||
name: "frog",
|
||||
build,
|
||||
update,
|
||||
boundingRadius: 0.7,
|
||||
bodyHeight: 0.6,
|
||||
export type Frog = ActorDefinition<MobState, Ground>
|
||||
|
||||
export namespace Frog {
|
||||
export function create(material: Material): Frog {
|
||||
const mesh = Mesh.create()
|
||||
build(mesh)
|
||||
return {
|
||||
prototype: {
|
||||
groups: [{ mesh, material }],
|
||||
radius: 0.7,
|
||||
minY: -0.7,
|
||||
maxY: 1.3,
|
||||
},
|
||||
update,
|
||||
transform: Mob.transform,
|
||||
collider: (state) => Mob.collider(state, 0.7, 0.6, state.grounded),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LEASH = 5
|
||||
|
|
@ -21,7 +34,7 @@ const HOP_SPEED = 1.6
|
|||
const HOP_IMPULSE = 3.2
|
||||
const GRAVITY = 14
|
||||
|
||||
function build(mesh: Mesh): void {
|
||||
function build(mesh: Geometry): void {
|
||||
// Wide squat body, two eye bumps on the top-front, two hind haunches. UVs:
|
||||
// the frog texture is green skin on the left, a dark eye tone on the right.
|
||||
ellipsoid(mesh, 0, 0.26, 0, 0.5, 0.28, 0.52, 6, 4, 0, 0.68, 0, 1)
|
||||
|
|
@ -31,7 +44,7 @@ function build(mesh: Mesh): void {
|
|||
ellipsoid(mesh, -0.3, 0.2, -0.26, 0.2, 0.2, 0.26, 4, 3, 0, 0.68, 0, 1)
|
||||
}
|
||||
|
||||
function update(mob: Mob, dt: number, terrain: Terrain): void {
|
||||
function update(mob: MobState, dt: number, terrain: Ground): void {
|
||||
if (mob.grounded) {
|
||||
mob.timer -= dt
|
||||
mob.position.y = Terrain.height(terrain, mob.position.x, mob.position.z)
|
||||
|
|
|
|||
|
|
@ -1,18 +1,31 @@
|
|||
import { Terrain } from "../../Terrain"
|
||||
import type { Mesh } from "../../../engine/scene/Mesh"
|
||||
import type { Mob } from "../Mob"
|
||||
import type { Entity } from "../../../engine/scene/Actor"
|
||||
import { Terrain, type Terrain as Ground } from "../../../engine/world/Terrain"
|
||||
import { Mesh, type Mesh as Geometry } from "../../../engine/scene/Mesh"
|
||||
import type { Material } from "../../../engine/render/Material"
|
||||
import type { ActorDefinition } from "../../../engine/scene/Actor"
|
||||
import { Mob, type MobState } from "../Mob"
|
||||
import { ellipsoid, nextRand, wanderHeading } from "./mobkit"
|
||||
|
||||
// Everything about the robin: round red-breasted bird that mostly hops like a frog
|
||||
// but now and then takes a short powered flight to a new perch.
|
||||
|
||||
export const robin: Entity<Mob, Terrain> = {
|
||||
name: "robin",
|
||||
build,
|
||||
update,
|
||||
boundingRadius: 0.45,
|
||||
bodyHeight: 0.55,
|
||||
export type Robin = ActorDefinition<MobState, Ground>
|
||||
|
||||
export namespace Robin {
|
||||
export function create(material: Material): Robin {
|
||||
const mesh = Mesh.create()
|
||||
build(mesh)
|
||||
return {
|
||||
prototype: {
|
||||
groups: [{ mesh, material }],
|
||||
radius: 0.5,
|
||||
minY: -0.5,
|
||||
maxY: 1,
|
||||
},
|
||||
update,
|
||||
transform: Mob.transform,
|
||||
collider: (state) => Mob.collider(state, 0.45, 0.55, state.grounded),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LEASH = 6
|
||||
|
|
@ -27,7 +40,7 @@ const FLY_IMPULSE = 3.5
|
|||
const CRUISE = 0.8
|
||||
const GRAVITY = 14
|
||||
|
||||
function build(mesh: Mesh): void {
|
||||
function build(mesh: Geometry): void {
|
||||
// Round European robin: plump brown body, an orange-red breast bulging on the
|
||||
// front, a round brown head with two dark eyes + a small dark beak, short tail.
|
||||
// UVs: robin texture is brown (left), orange breast (mid), dark eye/beak (right).
|
||||
|
|
@ -40,7 +53,7 @@ function build(mesh: Mesh): void {
|
|||
ellipsoid(mesh, 0, 0.26, -0.32, 0.09, 0.05, 0.16, 4, 2, 0, 0.38, 0, 1) // tail (brown)
|
||||
}
|
||||
|
||||
function update(mob: Mob, dt: number, terrain: Terrain): void {
|
||||
function update(mob: MobState, dt: number, terrain: Ground): void {
|
||||
if (mob.grounded) {
|
||||
mob.timer -= dt
|
||||
mob.position.y = Terrain.height(terrain, mob.position.x, mob.position.z)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import type { Mob } from "../Mob"
|
||||
import type { MobState } from "../Mob"
|
||||
import { STRIDE, type Mesh } from "../../../engine/scene/Mesh"
|
||||
|
||||
// Shared building blocks for the per-kind mob definitions (Frog/Bee/Robin): the
|
||||
// faceted geometry primitives and the deterministic wander helpers. Kept in its own
|
||||
// module (no runtime import of `Mob`, only its type) so the per-kind files and the
|
||||
// `Mob` registry don't form an import cycle.
|
||||
// Shared building blocks for concrete mob definitions: faceted geometry primitives
|
||||
// and deterministic wander helpers.
|
||||
|
||||
export const TAU = Math.PI * 2
|
||||
|
||||
|
|
@ -13,7 +11,11 @@ export const TAU = Math.PI * 2
|
|||
/** A new heading: free wander when inside the leash, else biased back toward home
|
||||
* so the mob never drifts off into the peaks (`jitter` = the random cone half-width
|
||||
* in radians layered on top of the homeward bearing). */
|
||||
export function wanderHeading(mob: Mob, leash: number, jitter: number): number {
|
||||
export function wanderHeading(
|
||||
mob: MobState,
|
||||
leash: number,
|
||||
jitter: number,
|
||||
): number {
|
||||
const dx = mob.home.x - mob.position.x
|
||||
const dz = mob.home.z - mob.position.z
|
||||
if (dx * dx + dz * dz > leash * leash) {
|
||||
|
|
@ -24,7 +26,7 @@ export function wanderHeading(mob: Mob, leash: number, jitter: number): number {
|
|||
|
||||
/** mulberry32 step over the mob's own `seed` (mutated), so a mob's motion is
|
||||
* deterministic and needs no external RNG object to clone. */
|
||||
export function nextRand(mob: Mob): number {
|
||||
export function nextRand(mob: MobState): number {
|
||||
const a = (mob.seed + 0x6D2B79F5) | 0
|
||||
mob.seed = a
|
||||
let t = Math.imul(a ^ (a >>> 15), 1 | a)
|
||||
|
|
@ -33,7 +35,7 @@ export function nextRand(mob: Mob): number {
|
|||
}
|
||||
|
||||
// --- Geometry primitives --------------------------------------------------
|
||||
// Mobs are drawn double-sided (see renderScene), so winding is not load-bearing --
|
||||
// Mobs are drawn double-sided, so winding is not load-bearing --
|
||||
// these only need to place faceted, flat-shaded surfaces.
|
||||
|
||||
/** A UV-rected ellipsoid (pole on Y), faceted like the boulders. */
|
||||
|
|
@ -61,7 +63,13 @@ export function ellipsoid(
|
|||
for (let is = 0; is <= seg; is++) {
|
||||
const theta = (is / seg) * TAU
|
||||
const u = u0 + (u1 - u0) * (is / seg)
|
||||
mesh.verts.push(cx + crv * Math.cos(theta) * rx, cy + cyv * ry, cz + crv * Math.sin(theta) * rz, u, v)
|
||||
mesh.verts.push(
|
||||
cx + crv * Math.cos(theta) * rx,
|
||||
cy + cyv * ry,
|
||||
cz + crv * Math.sin(theta) * rz,
|
||||
u,
|
||||
v,
|
||||
)
|
||||
}
|
||||
}
|
||||
quadGrid(mesh, start, seg, rings)
|
||||
|
|
@ -97,13 +105,36 @@ export function ovoidZ(
|
|||
}
|
||||
|
||||
/** One flat wing quad on `side` (+1 right / -1 left), swept up and out. */
|
||||
export function wing(mesh: Mesh, side: number, u0: number, u1: number, v0: number, v1: number): void {
|
||||
export function wing(
|
||||
mesh: Mesh,
|
||||
side: number,
|
||||
u0: number,
|
||||
u1: number,
|
||||
v0: number,
|
||||
v1: number,
|
||||
): void {
|
||||
const base = mesh.verts.length / STRIDE
|
||||
mesh.verts.push(
|
||||
side * 0.06, 0.12, 0.14, u0, v0,
|
||||
side * 0.42, 0.24, 0.1, u1, v0,
|
||||
side * 0.42, 0.24, -0.12, u1, v1,
|
||||
side * 0.06, 0.12, -0.1, u0, v1,
|
||||
side * 0.06,
|
||||
0.12,
|
||||
0.14,
|
||||
u0,
|
||||
v0,
|
||||
side * 0.42,
|
||||
0.24,
|
||||
0.1,
|
||||
u1,
|
||||
v0,
|
||||
side * 0.42,
|
||||
0.24,
|
||||
-0.12,
|
||||
u1,
|
||||
v1,
|
||||
side * 0.06,
|
||||
0.12,
|
||||
-0.1,
|
||||
u0,
|
||||
v1,
|
||||
)
|
||||
mesh.indices.push(base, base + 1, base + 2, base, base + 2, base + 3)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue