auto-git:
[add] src/foliage/foliage-render-batches.ts
This commit is contained in:
151
src/foliage/foliage-render-batches.ts
Normal file
151
src/foliage/foliage-render-batches.ts
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import { Matrix4, Quaternion, Vector3 } from "three";
|
||||||
|
|
||||||
|
import type { Vec3 } from "../core/vector";
|
||||||
|
import type { FoliagePrototype, FoliagePrototypeRegistry } from "./foliage";
|
||||||
|
import type {
|
||||||
|
DerivedFoliageInstance,
|
||||||
|
FoliageScatterResult
|
||||||
|
} from "./foliage-scatter";
|
||||||
|
|
||||||
|
export const FOLIAGE_RENDER_LOD_LEVEL = 0 as const;
|
||||||
|
|
||||||
|
export interface FoliageRenderBatch {
|
||||||
|
key: string;
|
||||||
|
terrainId: string;
|
||||||
|
layerId: string;
|
||||||
|
prototypeId: string;
|
||||||
|
lodLevel: typeof FOLIAGE_RENDER_LOD_LEVEL;
|
||||||
|
bundledPath: string;
|
||||||
|
castShadow: boolean;
|
||||||
|
instances: DerivedFoliageInstance[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const IDENTITY_SOURCE_MATRIX = new Matrix4();
|
||||||
|
const UP_VECTOR = new Vector3(0, 1, 0);
|
||||||
|
|
||||||
|
function clamp(value: number, min: number, max: number): number {
|
||||||
|
return Math.min(max, Math.max(min, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createVector3(vector: Vec3): Vector3 {
|
||||||
|
return new Vector3(vector.x, vector.y, vector.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFoliagePrototypeRenderLod(
|
||||||
|
prototype: FoliagePrototype
|
||||||
|
): {
|
||||||
|
bundledPath: string;
|
||||||
|
castShadow: boolean;
|
||||||
|
} | null {
|
||||||
|
const lod = prototype.lods.find(
|
||||||
|
(candidate) => candidate.level === FOLIAGE_RENDER_LOD_LEVEL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lod === undefined || lod.source !== "bundled") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
bundledPath: lod.bundledPath,
|
||||||
|
castShadow: lod.castShadow
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createFoliageRenderBatchKey(options: {
|
||||||
|
terrainId: string;
|
||||||
|
layerId: string;
|
||||||
|
prototypeId: string;
|
||||||
|
bundledPath: string;
|
||||||
|
}): string {
|
||||||
|
return [
|
||||||
|
options.terrainId,
|
||||||
|
options.layerId,
|
||||||
|
options.prototypeId,
|
||||||
|
FOLIAGE_RENDER_LOD_LEVEL,
|
||||||
|
options.bundledPath
|
||||||
|
].join("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createFoliageRenderBatches(
|
||||||
|
scatter: FoliageScatterResult,
|
||||||
|
prototypeRegistry: FoliagePrototypeRegistry
|
||||||
|
): FoliageRenderBatch[] {
|
||||||
|
const batches = new Map<string, FoliageRenderBatch>();
|
||||||
|
|
||||||
|
for (const chunk of scatter.chunks) {
|
||||||
|
for (const instance of chunk.instances) {
|
||||||
|
const prototype = prototypeRegistry[instance.prototypeId];
|
||||||
|
|
||||||
|
if (prototype === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderLod = getFoliagePrototypeRenderLod(prototype);
|
||||||
|
|
||||||
|
if (renderLod === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = createFoliageRenderBatchKey({
|
||||||
|
terrainId: instance.terrainId,
|
||||||
|
layerId: instance.layerId,
|
||||||
|
prototypeId: instance.prototypeId,
|
||||||
|
bundledPath: renderLod.bundledPath
|
||||||
|
});
|
||||||
|
let batch = batches.get(key);
|
||||||
|
|
||||||
|
if (batch === undefined) {
|
||||||
|
batch = {
|
||||||
|
key,
|
||||||
|
terrainId: instance.terrainId,
|
||||||
|
layerId: instance.layerId,
|
||||||
|
prototypeId: instance.prototypeId,
|
||||||
|
lodLevel: FOLIAGE_RENDER_LOD_LEVEL,
|
||||||
|
bundledPath: renderLod.bundledPath,
|
||||||
|
castShadow: renderLod.castShadow,
|
||||||
|
instances: []
|
||||||
|
};
|
||||||
|
batches.set(key, batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.instances.push(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...batches.values()].sort((left, right) =>
|
||||||
|
left.key.localeCompare(right.key)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createFoliageInstanceMatrix(
|
||||||
|
instance: Pick<
|
||||||
|
DerivedFoliageInstance,
|
||||||
|
"position" | "normal" | "yawRadians" | "scale" | "alignToNormal"
|
||||||
|
>,
|
||||||
|
sourceMatrix: Matrix4 = IDENTITY_SOURCE_MATRIX
|
||||||
|
): Matrix4 {
|
||||||
|
const normal = createVector3(instance.normal);
|
||||||
|
|
||||||
|
if (normal.lengthSq() <= 0) {
|
||||||
|
normal.copy(UP_VECTOR);
|
||||||
|
} else {
|
||||||
|
normal.normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
const tilt = new Quaternion().setFromUnitVectors(UP_VECTOR, normal);
|
||||||
|
const tiltAmount = clamp(instance.alignToNormal, 0, 1);
|
||||||
|
const partialTilt = new Quaternion().slerpQuaternions(
|
||||||
|
new Quaternion(),
|
||||||
|
tilt,
|
||||||
|
tiltAmount
|
||||||
|
);
|
||||||
|
const yaw = new Quaternion().setFromAxisAngle(normal, instance.yawRadians);
|
||||||
|
const rotation = yaw.multiply(partialTilt);
|
||||||
|
const instanceMatrix = new Matrix4().compose(
|
||||||
|
createVector3(instance.position),
|
||||||
|
rotation,
|
||||||
|
new Vector3(instance.scale, instance.scale, instance.scale)
|
||||||
|
);
|
||||||
|
|
||||||
|
return instanceMatrix.multiply(sourceMatrix);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user