refactor: move world concepts into engine
This commit is contained in:
parent
eeedcb8e48
commit
2d15c7ab8d
52 changed files with 3298 additions and 1558 deletions
123
engine/world/Level.ts
Normal file
123
engine/world/Level.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import {
|
||||
Actor,
|
||||
type Actor as RuntimeActor,
|
||||
} from "../scene/Actor"
|
||||
import { Collider, type Collider as CollisionShape } from "./Collider"
|
||||
import {
|
||||
CollisionWorld,
|
||||
type CollisionWorld as Collision,
|
||||
} from "./CollisionWorld"
|
||||
import type { Terrain } from "./Terrain"
|
||||
import {
|
||||
RenderScene,
|
||||
type Billboard,
|
||||
type RenderInstance,
|
||||
type RenderPrototype,
|
||||
type RenderScene as Scene,
|
||||
} from "../render/RenderScene"
|
||||
import type { Chunk } from "../render/Chunk"
|
||||
import type { DrawGroup } from "../render/Material"
|
||||
import type { SkyConfig } from "../render/Sky"
|
||||
import type { Mat4 } from "../math/Mat4"
|
||||
import type { Vec3 } from "../math/Vec3"
|
||||
|
||||
export type LevelDefinition<World> = {
|
||||
terrain: Terrain
|
||||
actorWorld: World
|
||||
actors: readonly RuntimeActor<World>[]
|
||||
staticColliders: CollisionShape[]
|
||||
staticGroups: DrawGroup[]
|
||||
chunks: Chunk[]
|
||||
billboards: Billboard[]
|
||||
sky: SkyConfig
|
||||
}
|
||||
|
||||
/** Live engine world. Behavior-bearing actors stay here on the main thread; only
|
||||
* `render` is clone-safe and sent to workers. */
|
||||
export type Level<World> = {
|
||||
readonly terrain: Terrain
|
||||
readonly actorWorld: World
|
||||
readonly actors: readonly RuntimeActor<World>[]
|
||||
readonly collision: Collision
|
||||
readonly render: Scene
|
||||
}
|
||||
|
||||
const prototypeIndexes = new WeakMap<object, ReadonlyMap<object, number>>()
|
||||
|
||||
export namespace Level {
|
||||
export function create<World>(definition: LevelDefinition<World>): Level<World> {
|
||||
const actors = Object.freeze([...definition.actors])
|
||||
const prototypes: RenderPrototype[] = []
|
||||
const prototypeIndex = new Map<object, number>()
|
||||
for (const actor of actors) {
|
||||
if (!prototypeIndex.has(actor.definition)) {
|
||||
prototypeIndex.set(actor.definition, prototypes.length)
|
||||
prototypes.push(actor.prototype)
|
||||
}
|
||||
}
|
||||
const level: Level<World> = {
|
||||
terrain: definition.terrain,
|
||||
actorWorld: definition.actorWorld,
|
||||
actors,
|
||||
collision: CollisionWorld.create(
|
||||
definition.terrain,
|
||||
definition.staticColliders,
|
||||
),
|
||||
render: Object.freeze({
|
||||
chunks: Object.freeze([...definition.chunks]),
|
||||
staticGroups: Object.freeze([...definition.staticGroups]),
|
||||
billboards: Object.freeze([...definition.billboards]),
|
||||
prototypes: Object.freeze(prototypes),
|
||||
maxInstances: actors.length,
|
||||
sky: definition.sky,
|
||||
}),
|
||||
}
|
||||
prototypeIndexes.set(level, prototypeIndex)
|
||||
return level
|
||||
}
|
||||
|
||||
export function update<World>(level: Level<World>, dt: number): void {
|
||||
for (const actor of level.actors) {
|
||||
Actor.update(actor, dt, level.actorWorld)
|
||||
}
|
||||
}
|
||||
|
||||
export function visibleInstances<World>(
|
||||
level: Level<World>,
|
||||
viewProjection: Mat4,
|
||||
): RenderInstance[] {
|
||||
const prototypeIndex = prototypeIndexes.get(level)
|
||||
if (prototypeIndex === undefined) {
|
||||
throw new Error("level was not created by Level.create")
|
||||
}
|
||||
const instances: RenderInstance[] = []
|
||||
for (const actor of level.actors) {
|
||||
const prototype = prototypeIndex.get(actor.definition)
|
||||
if (prototype !== undefined) {
|
||||
instances.push({ prototype, ...Actor.transform(actor) })
|
||||
}
|
||||
}
|
||||
return RenderScene.visibleInstances(level.render, instances, viewProjection)
|
||||
}
|
||||
|
||||
export function refreshActorColliders<World>(
|
||||
level: Level<World>,
|
||||
focus: Vec3,
|
||||
range: number,
|
||||
): void {
|
||||
const dynamic = level.collision.dynamicColliders
|
||||
dynamic.length = 0
|
||||
const rangeSquared = range * range
|
||||
for (const actor of level.actors) {
|
||||
const collider = Actor.collider(actor, level.actorWorld)
|
||||
if (collider === null) {
|
||||
continue
|
||||
}
|
||||
const dx = Collider.centerX(collider) - focus.x
|
||||
const dz = Collider.centerZ(collider) - focus.z
|
||||
if (dx * dx + dz * dz <= rangeSquared) {
|
||||
dynamic.push(collider)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue