@aethervtt/shard-render 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/assets.d.ts +104 -0
- package/dist/assets.js +213 -0
- package/dist/atmosphere-model.d.ts +96 -0
- package/dist/atmosphere-model.js +370 -0
- package/dist/atmosphere-nodes.d.ts +55 -0
- package/dist/atmosphere-nodes.js +780 -0
- package/dist/atmosphere-plugin.d.ts +5 -0
- package/dist/atmosphere-plugin.js +42 -0
- package/dist/atmosphere-shaders.d.ts +7 -0
- package/dist/atmosphere-shaders.js +595 -0
- package/dist/atmosphere-state.d.ts +72 -0
- package/dist/atmosphere-state.js +18 -0
- package/dist/atmosphere.d.ts +191 -0
- package/dist/atmosphere.js +950 -0
- package/dist/camera.d.ts +83 -0
- package/dist/camera.js +163 -0
- package/dist/clusters.d.ts +68 -0
- package/dist/clusters.js +288 -0
- package/dist/culling-shaders.d.ts +2 -0
- package/dist/culling-shaders.js +203 -0
- package/dist/culling.d.ts +84 -0
- package/dist/culling.js +498 -0
- package/dist/debug-shaders.d.ts +3 -0
- package/dist/debug-shaders.js +207 -0
- package/dist/debug-views.d.ts +60 -0
- package/dist/debug-views.js +197 -0
- package/dist/deferred-plugin.d.ts +5 -0
- package/dist/deferred-plugin.js +20 -0
- package/dist/deferred-shaders.d.ts +2 -0
- package/dist/deferred-shaders.js +151 -0
- package/dist/deferred.d.ts +22 -0
- package/dist/deferred.js +296 -0
- package/dist/deform.d.ts +80 -0
- package/dist/deform.js +213 -0
- package/dist/describe-plugin.d.ts +5 -0
- package/dist/describe-plugin.js +19 -0
- package/dist/display-nodes.d.ts +5 -0
- package/dist/display-nodes.js +189 -0
- package/dist/dynamic-resolution.d.ts +47 -0
- package/dist/dynamic-resolution.js +191 -0
- package/dist/environment-plugin.d.ts +6 -0
- package/dist/environment-plugin.js +44 -0
- package/dist/environment-shaders.d.ts +2 -0
- package/dist/environment-shaders.js +260 -0
- package/dist/environment-state.d.ts +71 -0
- package/dist/environment-state.js +144 -0
- package/dist/environment.d.ts +70 -0
- package/dist/environment.js +529 -0
- package/dist/forward.d.ts +123 -0
- package/dist/forward.js +1216 -0
- package/dist/fxaa.d.ts +2 -0
- package/dist/fxaa.js +65 -0
- package/dist/gizmo-font.d.ts +21 -0
- package/dist/gizmo-font.js +132 -0
- package/dist/gizmos-plugin.d.ts +5 -0
- package/dist/gizmos-plugin.js +34 -0
- package/dist/gizmos.d.ts +156 -0
- package/dist/gizmos.js +772 -0
- package/dist/gpu-assets.d.ts +114 -0
- package/dist/gpu-assets.js +494 -0
- package/dist/graph.d.ts +204 -0
- package/dist/graph.js +417 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +540 -0
- package/dist/instances.d.ts +349 -0
- package/dist/instances.js +1335 -0
- package/dist/large-world.d.ts +13 -0
- package/dist/large-world.js +20 -0
- package/dist/lights.d.ts +160 -0
- package/dist/lights.js +468 -0
- package/dist/material-noise.d.ts +19 -0
- package/dist/material-noise.js +7 -0
- package/dist/material-pipelines.d.ts +46 -0
- package/dist/material-pipelines.js +166 -0
- package/dist/materials.d.ts +108 -0
- package/dist/materials.js +219 -0
- package/dist/noise.d.ts +76 -0
- package/dist/noise.js +286 -0
- package/dist/overlay-registry.d.ts +18 -0
- package/dist/overlay-registry.js +30 -0
- package/dist/overlays.d.ts +35 -0
- package/dist/overlays.js +344 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.js +46 -0
- package/dist/picking-plugin.d.ts +2 -0
- package/dist/picking-plugin.js +20 -0
- package/dist/picking.d.ts +94 -0
- package/dist/picking.js +456 -0
- package/dist/pixel-perfect-plugin.d.ts +9 -0
- package/dist/pixel-perfect-plugin.js +135 -0
- package/dist/pixel-perfect.d.ts +27 -0
- package/dist/pixel-perfect.js +70 -0
- package/dist/plugin.d.ts +111 -0
- package/dist/plugin.js +182 -0
- package/dist/pool.d.ts +19 -0
- package/dist/pool.js +43 -0
- package/dist/post-common.d.ts +51 -0
- package/dist/post-common.js +148 -0
- package/dist/post-nodes.d.ts +39 -0
- package/dist/post-nodes.js +849 -0
- package/dist/post-plugin.d.ts +5 -0
- package/dist/post-plugin.js +26 -0
- package/dist/post-shaders.d.ts +8 -0
- package/dist/post-shaders.js +698 -0
- package/dist/post.d.ts +175 -0
- package/dist/post.js +453 -0
- package/dist/readback.d.ts +4 -0
- package/dist/readback.js +95 -0
- package/dist/render-scale.d.ts +53 -0
- package/dist/render-scale.js +63 -0
- package/dist/shaders.d.ts +122 -0
- package/dist/shaders.js +1168 -0
- package/dist/shadows.d.ts +119 -0
- package/dist/shadows.js +562 -0
- package/dist/skin-asset.d.ts +26 -0
- package/dist/skin-asset.js +25 -0
- package/dist/skinning-plugin.d.ts +5 -0
- package/dist/skinning-plugin.js +16 -0
- package/dist/skinning.d.ts +19 -0
- package/dist/skinning.js +343 -0
- package/dist/standard-fields.d.ts +67 -0
- package/dist/standard-fields.js +73 -0
- package/dist/standard.d.ts +7 -0
- package/dist/standard.js +37 -0
- package/dist/stats.d.ts +25 -0
- package/dist/stats.js +18 -0
- package/dist/target.d.ts +55 -0
- package/dist/target.js +70 -0
- package/dist/testing.d.ts +34 -0
- package/dist/testing.js +126 -0
- package/dist/timer.d.ts +28 -0
- package/dist/timer.js +136 -0
- package/dist/view.d.ts +117 -0
- package/dist/view.js +389 -0
- package/dist/visibility.d.ts +10 -0
- package/dist/visibility.js +84 -0
- package/package.json +49 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AssetStore } from '@aethervtt/shard-assets';
|
|
2
|
+
/** Joints one skin can have: the vertex stage indexes joints with 16 bits, budgets assume 256. */
|
|
3
|
+
export declare const MAX_JOINTS = 256;
|
|
4
|
+
export interface SkinAsset {
|
|
5
|
+
name: string;
|
|
6
|
+
/** Joint entity paths, relative to the model root (e.g. "Armature/Hips/Spine"). */
|
|
7
|
+
joints: string[];
|
|
8
|
+
skeleton?: string;
|
|
9
|
+
/** Each joint's rest pose (local TRS), for retargeting. */
|
|
10
|
+
restPose: {
|
|
11
|
+
translation: number[];
|
|
12
|
+
rotation: number[];
|
|
13
|
+
scale: number[];
|
|
14
|
+
}[];
|
|
15
|
+
/** 16 floats per joint, column-major. */
|
|
16
|
+
inverseBindMatrices: Float32Array;
|
|
17
|
+
}
|
|
18
|
+
export declare const Skins: import("@aethervtt/shard-core").ResourceDef<AssetStore<SkinAsset, "Skin">>;
|
|
19
|
+
interface SkinHeader extends Omit<SkinAsset, 'inverseBindMatrices'> {
|
|
20
|
+
matrices: number;
|
|
21
|
+
}
|
|
22
|
+
/** Skin artifacts: the header as JSON, the inverse bind matrices as bytes. */
|
|
23
|
+
export declare const SkinAssetType: import("@aethervtt/shard-assets").AssetTypeDef<SkinAsset>;
|
|
24
|
+
/** A skin's artifact JSON (with `bytes` holding the inverse bind matrices), for importers. */
|
|
25
|
+
export declare function skinArtifact(skin: Omit<SkinAsset, 'inverseBindMatrices'>): SkinHeader;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AssetStore, defineAssetType } from "@aethervtt/shard-assets";
|
|
2
|
+
import { defineResource } from "@aethervtt/shard-core";
|
|
3
|
+
const MAX_JOINTS = 256;
|
|
4
|
+
const Skins = defineResource("render/Skins", {
|
|
5
|
+
description: "Skins (joint paths, inverse bind matrices, rest pose) by guid.",
|
|
6
|
+
init: () => new AssetStore("Skin")
|
|
7
|
+
});
|
|
8
|
+
const SkinAssetType = defineAssetType("Skin", {
|
|
9
|
+
store: Skins,
|
|
10
|
+
load: (artifact) => {
|
|
11
|
+
const header = artifact.json;
|
|
12
|
+
const bytes = artifact.bytes.slice();
|
|
13
|
+
const { matrices, ...rest } = header;
|
|
14
|
+
return { ...rest, inverseBindMatrices: new Float32Array(bytes.buffer, 0, matrices * 16) };
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
function skinArtifact(skin) {
|
|
18
|
+
return { ...skin, matrices: skin.joints.length };
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
MAX_JOINTS,
|
|
22
|
+
SkinAssetType,
|
|
23
|
+
Skins,
|
|
24
|
+
skinArtifact
|
|
25
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skinning and morph targets (spec 0034): SkinnedMesh and MorphWeights deform the meshes they're on,
|
|
3
|
+
* and the skeleton overlay draws joints. Without it those meshes draw in their rest pose.
|
|
4
|
+
*/
|
|
5
|
+
export declare const skinningPlugin: import("@aethervtt/shard-runtime").Plugin;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Last } from "@aethervtt/shard-core";
|
|
2
|
+
import { definePlugin } from "@aethervtt/shard-runtime";
|
|
3
|
+
import { DeformPath, prepareInstances } from "./instances.js";
|
|
4
|
+
import { RenderSet } from "./plugin.js";
|
|
5
|
+
import { prepareDeforms, skeletonOverlay } from "./skinning.js";
|
|
6
|
+
const skinningPlugin = definePlugin({
|
|
7
|
+
name: "render/skinning",
|
|
8
|
+
provides: [skeletonOverlay],
|
|
9
|
+
dependencies: ["render/forward"],
|
|
10
|
+
build(app) {
|
|
11
|
+
app.insertResource(DeformPath, { installed: true }).addSystems(Last, prepareDeforms.inSet(RenderSet.Prepare).after(prepareInstances));
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
export {
|
|
15
|
+
skinningPlugin
|
|
16
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Entity } from '@aethervtt/shard-core';
|
|
2
|
+
interface DeformState {
|
|
3
|
+
/** Frame at which to retry resolving an entity's joints. */
|
|
4
|
+
retry: Map<Entity, number>;
|
|
5
|
+
warned: Set<string>;
|
|
6
|
+
frame: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Writes joint matrices (`inverse(mesh) × joint × inverseBind`, so the instance transform still
|
|
10
|
+
* applies), a pose-following cull sphere, and the active morph weights of every visible skinned or
|
|
11
|
+
* morphed instance. Runs after instance slots are assigned, before culling.
|
|
12
|
+
*/
|
|
13
|
+
export declare const prepareDeforms: import("@aethervtt/shard-core").SystemDef<{
|
|
14
|
+
skinned: import("@aethervtt/shard-core").Query;
|
|
15
|
+
morphed: import("@aethervtt/shard-core").Query;
|
|
16
|
+
state: DeformState;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const skeletonOverlay: import("./overlay-registry.js").OverlayDef;
|
|
19
|
+
export {};
|
package/dist/skinning.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import {
|
|
2
|
+
affine,
|
|
3
|
+
ChildOf,
|
|
4
|
+
defineSystem,
|
|
5
|
+
ShardError
|
|
6
|
+
} from "@aethervtt/shard-core";
|
|
7
|
+
import { LogResource } from "@aethervtt/shard-runtime";
|
|
8
|
+
import { GlobalTransform } from "@aethervtt/shard-transform";
|
|
9
|
+
import { DEFORM_WORDS } from "./deform.js";
|
|
10
|
+
import { InstanceSlot, Instances, MorphWeights, SkinnedMesh } from "./instances.js";
|
|
11
|
+
import { defineOverlay } from "./overlay-registry.js";
|
|
12
|
+
import { findModelRoot } from "./paths.js";
|
|
13
|
+
import { MAX_JOINTS, Skins } from "./skin-asset.js";
|
|
14
|
+
import { ComputedVisibility } from "./visibility.js";
|
|
15
|
+
function meshOfSlot(store, slot) {
|
|
16
|
+
const a2 = store.batchOf[slot];
|
|
17
|
+
if (a2 >= 0) return store.batches[a2].mesh;
|
|
18
|
+
if (a2 <= -2) return store.batches[store.lodSets[-2 - a2].batches[0]].mesh;
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
function jointRadii(mesh, skin) {
|
|
22
|
+
const count = skin.joints.length;
|
|
23
|
+
const radii = new Float32Array(count).fill(-1);
|
|
24
|
+
const joints = mesh.joints;
|
|
25
|
+
const weights = mesh.weights;
|
|
26
|
+
const p = mesh.positions;
|
|
27
|
+
const ibm = skin.inverseBindMatrices;
|
|
28
|
+
for (let v = 0; v < mesh.vertexCount; v++) {
|
|
29
|
+
let main = 0;
|
|
30
|
+
for (let k = 1; k < 4; k++) if (weights[v * 4 + k] > weights[v * 4 + main]) main = k;
|
|
31
|
+
const j = joints[v * 4 + main];
|
|
32
|
+
if (j >= count) continue;
|
|
33
|
+
const m = j * 16;
|
|
34
|
+
const x = p[v * 3];
|
|
35
|
+
const y = p[v * 3 + 1];
|
|
36
|
+
const z = p[v * 3 + 2];
|
|
37
|
+
const lx = ibm[m] * x + ibm[m + 4] * y + ibm[m + 8] * z + ibm[m + 12];
|
|
38
|
+
const ly = ibm[m + 1] * x + ibm[m + 5] * y + ibm[m + 9] * z + ibm[m + 13];
|
|
39
|
+
const lz = ibm[m + 2] * x + ibm[m + 6] * y + ibm[m + 10] * z + ibm[m + 14];
|
|
40
|
+
const r = Math.sqrt(lx * lx + ly * ly + lz * lz);
|
|
41
|
+
if (r > radii[j]) radii[j] = r;
|
|
42
|
+
}
|
|
43
|
+
let pad = 0;
|
|
44
|
+
for (const target of mesh.targets ?? []) {
|
|
45
|
+
let reach = 0;
|
|
46
|
+
const d = target.positions;
|
|
47
|
+
for (let i = 0; i < d.length; i += 3) {
|
|
48
|
+
const r = d[i] * d[i] + d[i + 1] * d[i + 1] + d[i + 2] * d[i + 2];
|
|
49
|
+
if (r > reach) reach = r;
|
|
50
|
+
}
|
|
51
|
+
pad += Math.sqrt(reach);
|
|
52
|
+
}
|
|
53
|
+
if (pad > 0) {
|
|
54
|
+
for (let j = 0; j < count; j++) if (radii[j] >= 0) radii[j] = radii[j] + pad;
|
|
55
|
+
}
|
|
56
|
+
return radii;
|
|
57
|
+
}
|
|
58
|
+
function resolveJoints(world, entity, skin) {
|
|
59
|
+
if (skin.joints.length === 0) return void 0;
|
|
60
|
+
const found = findModelRoot(world, entity, skin.joints[0]);
|
|
61
|
+
if (!found) return void 0;
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const path of skin.joints) {
|
|
64
|
+
const e = found.paths.get(path);
|
|
65
|
+
if (e === void 0) return void 0;
|
|
66
|
+
out.push(e);
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
const a = new Float32Array(12);
|
|
71
|
+
const meshWorld = new Float32Array(12);
|
|
72
|
+
const meshInverse = new Float32Array(12);
|
|
73
|
+
const bounds = new Float32Array(6);
|
|
74
|
+
const topIndex = new Int32Array(8);
|
|
75
|
+
const topWeight = new Float32Array(8);
|
|
76
|
+
const prepareDeforms = defineSystem({
|
|
77
|
+
name: "render/prepare-deforms",
|
|
78
|
+
description: "Joint matrices, pose bounds, and morph weights of skinned and morphed meshes, uploaded for the vertex stage.",
|
|
79
|
+
setup: (world) => ({
|
|
80
|
+
skinned: world.query({
|
|
81
|
+
with: [SkinnedMesh, InstanceSlot, GlobalTransform, ComputedVisibility]
|
|
82
|
+
}),
|
|
83
|
+
morphed: world.query({
|
|
84
|
+
with: [MorphWeights, InstanceSlot, ComputedVisibility],
|
|
85
|
+
without: [SkinnedMesh]
|
|
86
|
+
}),
|
|
87
|
+
state: { retry: /* @__PURE__ */ new Map(), warned: /* @__PURE__ */ new Set(), frame: 0 }
|
|
88
|
+
}),
|
|
89
|
+
run: ({ skinned, morphed, state }, world) => {
|
|
90
|
+
const store = world.resource(Instances);
|
|
91
|
+
const d = store.deform;
|
|
92
|
+
d.beginFrame();
|
|
93
|
+
state.frame++;
|
|
94
|
+
const skins = world.tryResource(Skins);
|
|
95
|
+
const records = d.records;
|
|
96
|
+
for (let ti = 0; ti < skinned.tables.length; ti++) {
|
|
97
|
+
const table = skinned.tables[ti];
|
|
98
|
+
const n = table.count;
|
|
99
|
+
if (n === 0) continue;
|
|
100
|
+
const slots = table.column(InstanceSlot, "slot");
|
|
101
|
+
const vis = table.column(ComputedVisibility, "visible");
|
|
102
|
+
const g = table.column(GlobalTransform, "matrix");
|
|
103
|
+
const skinRefs = table.column(SkinnedMesh, "skin");
|
|
104
|
+
const jointLists = table.column(SkinnedMesh, "joints");
|
|
105
|
+
const weights = table.has(MorphWeights) ? table.column(MorphWeights, "weights") : void 0;
|
|
106
|
+
for (let i = 0; i < n; i++) {
|
|
107
|
+
const slot = slots[i] - 1;
|
|
108
|
+
if (slot < 0 || vis[i] === 0) continue;
|
|
109
|
+
const mesh = meshOfSlot(store, slot);
|
|
110
|
+
if (!mesh) {
|
|
111
|
+
d.clear(slot);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const md = d.meshData(mesh);
|
|
115
|
+
const o = slot * DEFORM_WORDS;
|
|
116
|
+
writeMeshFields(records, o, md);
|
|
117
|
+
const skin = skins?.get(skinRefs[i]);
|
|
118
|
+
let jointCount = 0;
|
|
119
|
+
if (skin && md.skinBase >= 0) {
|
|
120
|
+
let joints = jointLists[i];
|
|
121
|
+
if (joints.length !== skin.joints.length) {
|
|
122
|
+
const entity = table.entities[i];
|
|
123
|
+
const at = state.retry.get(entity) ?? 0;
|
|
124
|
+
if (at <= state.frame) {
|
|
125
|
+
const resolved = resolveJoints(world, entity, skin);
|
|
126
|
+
if (resolved) {
|
|
127
|
+
jointLists[i] = resolved;
|
|
128
|
+
joints = resolved;
|
|
129
|
+
state.retry.delete(entity);
|
|
130
|
+
} else {
|
|
131
|
+
state.retry.set(entity, state.frame + 60);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (skin.joints.length > MAX_JOINTS) warnTooMany(world, state, skin);
|
|
136
|
+
else if (joints.length === skin.joints.length) {
|
|
137
|
+
jointCount = writeJoints(world, d, o, g, i * 12, skin, joints, md, mesh);
|
|
138
|
+
if (jointCount < 0) {
|
|
139
|
+
jointLists[i] = [];
|
|
140
|
+
jointCount = 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
records[o + 5] = jointCount;
|
|
145
|
+
if (jointCount === 0) d.recordF32[o + 3] = 0;
|
|
146
|
+
writeMorph(d, o, weights?.[i], md);
|
|
147
|
+
d.touch(slot);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (let ti = 0; ti < morphed.tables.length; ti++) {
|
|
151
|
+
const table = morphed.tables[ti];
|
|
152
|
+
const n = table.count;
|
|
153
|
+
if (n === 0) continue;
|
|
154
|
+
const slots = table.column(InstanceSlot, "slot");
|
|
155
|
+
const vis = table.column(ComputedVisibility, "visible");
|
|
156
|
+
const weights = table.column(MorphWeights, "weights");
|
|
157
|
+
for (let i = 0; i < n; i++) {
|
|
158
|
+
const slot = slots[i] - 1;
|
|
159
|
+
if (slot < 0 || vis[i] === 0) continue;
|
|
160
|
+
const mesh = meshOfSlot(store, slot);
|
|
161
|
+
if (!mesh) {
|
|
162
|
+
d.clear(slot);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const md = d.meshData(mesh);
|
|
166
|
+
const o = slot * DEFORM_WORDS;
|
|
167
|
+
writeMeshFields(records, o, md);
|
|
168
|
+
records[o + 5] = 0;
|
|
169
|
+
d.recordF32[o + 3] = 0;
|
|
170
|
+
writeMorph(d, o, weights[i], md);
|
|
171
|
+
d.touch(slot);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
function writeMeshFields(records, o, md) {
|
|
177
|
+
records[o + 6] = md.skinBase >= 0 ? md.skinBase : 0;
|
|
178
|
+
records[o + 7] = md.morphBase >= 0 ? md.morphBase : 0;
|
|
179
|
+
records[o + 8] = md.vertexCount;
|
|
180
|
+
}
|
|
181
|
+
function warnTooMany(world, state, skin) {
|
|
182
|
+
if (state.warned.has(skin.name)) return;
|
|
183
|
+
state.warned.add(skin.name);
|
|
184
|
+
world.tryResource(LogResource)?.error(
|
|
185
|
+
new ShardError(
|
|
186
|
+
"render/too-many-joints",
|
|
187
|
+
`Skin "${skin.name}" has ${skin.joints.length} joints; skinning supports ${MAX_JOINTS}`,
|
|
188
|
+
{ hint: "Split the mesh, or remove helper bones before exporting. It draws in bind pose." }
|
|
189
|
+
)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
function writeJoints(world, d, o, g, go, skin, joints, md, mesh) {
|
|
193
|
+
const count = joints.length;
|
|
194
|
+
for (let k = 0; k < 12; k++) meshWorld[k] = g[go + k];
|
|
195
|
+
if (!affine.invert(meshInverse, meshWorld)) return 0;
|
|
196
|
+
if (md.radiiFor !== skin) {
|
|
197
|
+
md.radii = jointRadii(mesh, skin);
|
|
198
|
+
md.radiiFor = skin;
|
|
199
|
+
}
|
|
200
|
+
const radii = md.radii;
|
|
201
|
+
const base = d.allocPoses(count * 3);
|
|
202
|
+
const poses = d.poses;
|
|
203
|
+
const ibm = skin.inverseBindMatrices;
|
|
204
|
+
const inv = meshInverse;
|
|
205
|
+
bounds[0] = bounds[1] = bounds[2] = Number.POSITIVE_INFINITY;
|
|
206
|
+
bounds[3] = bounds[4] = bounds[5] = Number.NEGATIVE_INFINITY;
|
|
207
|
+
for (let j = 0; j < count; j++) {
|
|
208
|
+
const entity = joints[j];
|
|
209
|
+
if (entity === null || entity === void 0 || !world.isAlive(entity)) return -1;
|
|
210
|
+
const table = world.entityTableUnchecked(entity);
|
|
211
|
+
if (!table.has(GlobalTransform)) return -1;
|
|
212
|
+
const jw = table.column(GlobalTransform, "matrix");
|
|
213
|
+
const jo = world.entityRowUnchecked(entity) * 12;
|
|
214
|
+
const m = j * 16;
|
|
215
|
+
const out = (base + j * 3) * 4;
|
|
216
|
+
for (let r = 0; r < 3; r++) {
|
|
217
|
+
const j0 = jw[jo + r * 4];
|
|
218
|
+
const j1 = jw[jo + r * 4 + 1];
|
|
219
|
+
const j2 = jw[jo + r * 4 + 2];
|
|
220
|
+
const j3 = jw[jo + r * 4 + 3];
|
|
221
|
+
for (let c = 0; c < 4; c++) {
|
|
222
|
+
a[r * 4 + c] = j0 * ibm[m + c * 4] + j1 * ibm[m + c * 4 + 1] + j2 * ibm[m + c * 4 + 2] + j3 * ibm[m + c * 4 + 3];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for (let r = 0; r < 3; r++) {
|
|
226
|
+
const i0 = inv[r * 4];
|
|
227
|
+
const i1 = inv[r * 4 + 1];
|
|
228
|
+
const i2 = inv[r * 4 + 2];
|
|
229
|
+
for (let c = 0; c < 4; c++) {
|
|
230
|
+
poses[out + r * 4 + c] = i0 * a[c] + i1 * a[4 + c] + i2 * a[8 + c] + (c === 3 ? inv[r * 4 + 3] : 0);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const radius = radii[j];
|
|
234
|
+
if (radius < 0) continue;
|
|
235
|
+
const sx = jw[jo] * jw[jo] + jw[jo + 4] * jw[jo + 4] + jw[jo + 8] * jw[jo + 8];
|
|
236
|
+
const sy = jw[jo + 1] * jw[jo + 1] + jw[jo + 5] * jw[jo + 5] + jw[jo + 9] * jw[jo + 9];
|
|
237
|
+
const sz = jw[jo + 2] * jw[jo + 2] + jw[jo + 6] * jw[jo + 6] + jw[jo + 10] * jw[jo + 10];
|
|
238
|
+
const wr = radius * Math.sqrt(Math.max(sx, sy, sz));
|
|
239
|
+
const px = jw[jo + 3];
|
|
240
|
+
const py = jw[jo + 7];
|
|
241
|
+
const pz = jw[jo + 11];
|
|
242
|
+
if (px - wr < bounds[0]) bounds[0] = px - wr;
|
|
243
|
+
if (py - wr < bounds[1]) bounds[1] = py - wr;
|
|
244
|
+
if (pz - wr < bounds[2]) bounds[2] = pz - wr;
|
|
245
|
+
if (px + wr > bounds[3]) bounds[3] = px + wr;
|
|
246
|
+
if (py + wr > bounds[4]) bounds[4] = py + wr;
|
|
247
|
+
if (pz + wr > bounds[5]) bounds[5] = pz + wr;
|
|
248
|
+
}
|
|
249
|
+
const f = d.recordF32;
|
|
250
|
+
d.records[o + 4] = base;
|
|
251
|
+
if (bounds[0] <= bounds[3]) {
|
|
252
|
+
const ex = (bounds[3] - bounds[0]) * 0.5;
|
|
253
|
+
const ey = (bounds[4] - bounds[1]) * 0.5;
|
|
254
|
+
const ez = (bounds[5] - bounds[2]) * 0.5;
|
|
255
|
+
f[o] = bounds[0] + ex;
|
|
256
|
+
f[o + 1] = bounds[1] + ey;
|
|
257
|
+
f[o + 2] = bounds[2] + ez;
|
|
258
|
+
f[o + 3] = Math.sqrt(ex * ex + ey * ey + ez * ez);
|
|
259
|
+
} else {
|
|
260
|
+
f[o + 3] = 0;
|
|
261
|
+
}
|
|
262
|
+
return count;
|
|
263
|
+
}
|
|
264
|
+
function writeMorph(d, o, weights, md) {
|
|
265
|
+
let active = 0;
|
|
266
|
+
const n = weights ? Math.min(weights.length, md.targetCount) : 0;
|
|
267
|
+
for (let t = 0; t < n; t++) {
|
|
268
|
+
const w = weights[t];
|
|
269
|
+
const mag = Math.abs(w);
|
|
270
|
+
if (mag < 1e-5) continue;
|
|
271
|
+
let k;
|
|
272
|
+
if (active < 8) k = active++;
|
|
273
|
+
else if (mag <= Math.abs(topWeight[7])) continue;
|
|
274
|
+
else k = 7;
|
|
275
|
+
while (k > 0 && Math.abs(topWeight[k - 1]) < mag) {
|
|
276
|
+
topWeight[k] = topWeight[k - 1];
|
|
277
|
+
topIndex[k] = topIndex[k - 1];
|
|
278
|
+
k--;
|
|
279
|
+
}
|
|
280
|
+
topWeight[k] = w;
|
|
281
|
+
topIndex[k] = t;
|
|
282
|
+
}
|
|
283
|
+
d.records[o + 9] = active;
|
|
284
|
+
if (active === 0) return;
|
|
285
|
+
const base = d.allocPoses(4);
|
|
286
|
+
const poses = d.poses;
|
|
287
|
+
for (let k = 0; k < 8; k++) {
|
|
288
|
+
poses[base * 4 + k * 2] = k < active ? topIndex[k] : 0;
|
|
289
|
+
poses[base * 4 + k * 2 + 1] = k < active ? topWeight[k] : 0;
|
|
290
|
+
}
|
|
291
|
+
d.records[o + 10] = base;
|
|
292
|
+
}
|
|
293
|
+
const JOINT = [1, 0.85, 0.2, 1];
|
|
294
|
+
const BONE = [0.3, 0.9, 1, 1];
|
|
295
|
+
const pa = new Float32Array(3);
|
|
296
|
+
const pb = new Float32Array(3);
|
|
297
|
+
const pc = new Float32Array(3);
|
|
298
|
+
function positionOf(world, entity, out) {
|
|
299
|
+
if (!world.isAlive(entity) || !world.has(entity, GlobalTransform)) return false;
|
|
300
|
+
const m = world.entityTable(entity).column(GlobalTransform, "matrix");
|
|
301
|
+
const o = world.entityRow(entity) * 12;
|
|
302
|
+
out[0] = m[o + 3];
|
|
303
|
+
out[1] = m[o + 7];
|
|
304
|
+
out[2] = m[o + 11];
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
const skeletonOverlay = defineOverlay({
|
|
308
|
+
name: "skeleton",
|
|
309
|
+
description: "Skinned meshes\u2019 joints (yellow) and bones to their parent joints (cyan), in the current pose.",
|
|
310
|
+
draw(world, g, passes) {
|
|
311
|
+
const q = world.query({ with: [SkinnedMesh] });
|
|
312
|
+
for (const table of q.tables) {
|
|
313
|
+
const lists = table.column(SkinnedMesh, "joints");
|
|
314
|
+
for (let i = 0; i < table.count; i++) {
|
|
315
|
+
if (!passes(table.entities[i])) continue;
|
|
316
|
+
const joints = lists[i];
|
|
317
|
+
const set = new Set(joints);
|
|
318
|
+
for (const joint of joints) {
|
|
319
|
+
if (joint === null || !positionOf(world, joint, pa)) continue;
|
|
320
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
321
|
+
pb[0] = pa[0];
|
|
322
|
+
pb[1] = pa[1];
|
|
323
|
+
pb[2] = pa[2];
|
|
324
|
+
pc[0] = pa[0];
|
|
325
|
+
pc[1] = pa[1];
|
|
326
|
+
pc[2] = pa[2];
|
|
327
|
+
pb[axis] = pb[axis] - 0.015;
|
|
328
|
+
pc[axis] = pc[axis] + 0.015;
|
|
329
|
+
g.line(pb, pc, JOINT);
|
|
330
|
+
}
|
|
331
|
+
const parent = world.has(joint, ChildOf) ? world.get(joint, ChildOf).parent : null;
|
|
332
|
+
if (parent !== null && set.has(parent) && positionOf(world, parent, pb)) {
|
|
333
|
+
g.line(pb, pa, BONE);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
export {
|
|
341
|
+
prepareDeforms,
|
|
342
|
+
skeletonOverlay
|
|
343
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export declare const TextureSlot: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
2
|
+
texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
3
|
+
uv: import("@aethervtt/shard-core").FieldType<number, "u8">;
|
|
4
|
+
offset: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
5
|
+
scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
6
|
+
rotation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
7
|
+
wrap: import("@aethervtt/shard-core").FieldType<"clamp" | "mirror" | "repeat", "u8">;
|
|
8
|
+
filter: import("@aethervtt/shard-core").FieldType<"linear" | "nearest", "u8">;
|
|
9
|
+
}>, "object">;
|
|
10
|
+
export declare const TEXTURE_SLOTS: readonly ['baseColorTexture', 'metallicRoughnessTexture', 'normalTexture', 'occlusionTexture', 'emissiveTexture'];
|
|
11
|
+
export declare const STANDARD_FIELDS: {
|
|
12
|
+
baseColor: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
13
|
+
metallic: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
14
|
+
roughness: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
15
|
+
emissive: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
16
|
+
emissiveLuminance: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
17
|
+
doubleSided: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
|
|
18
|
+
alphaMode: import("@aethervtt/shard-core").FieldType<"additive" | "alpha" | "mask" | "opaque" | "premultiplied", "u8">;
|
|
19
|
+
alphaCutoff: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
20
|
+
normalScale: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
21
|
+
occlusionStrength: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
22
|
+
baseColorTexture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
23
|
+
texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
24
|
+
uv: import("@aethervtt/shard-core").FieldType<number, "u8">;
|
|
25
|
+
offset: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
26
|
+
scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
27
|
+
rotation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
28
|
+
wrap: import("@aethervtt/shard-core").FieldType<"clamp" | "mirror" | "repeat", "u8">;
|
|
29
|
+
filter: import("@aethervtt/shard-core").FieldType<"linear" | "nearest", "u8">;
|
|
30
|
+
}>, "object">;
|
|
31
|
+
metallicRoughnessTexture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
32
|
+
texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
33
|
+
uv: import("@aethervtt/shard-core").FieldType<number, "u8">;
|
|
34
|
+
offset: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
35
|
+
scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
36
|
+
rotation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
37
|
+
wrap: import("@aethervtt/shard-core").FieldType<"clamp" | "mirror" | "repeat", "u8">;
|
|
38
|
+
filter: import("@aethervtt/shard-core").FieldType<"linear" | "nearest", "u8">;
|
|
39
|
+
}>, "object">;
|
|
40
|
+
normalTexture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
41
|
+
texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
42
|
+
uv: import("@aethervtt/shard-core").FieldType<number, "u8">;
|
|
43
|
+
offset: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
44
|
+
scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
45
|
+
rotation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
46
|
+
wrap: import("@aethervtt/shard-core").FieldType<"clamp" | "mirror" | "repeat", "u8">;
|
|
47
|
+
filter: import("@aethervtt/shard-core").FieldType<"linear" | "nearest", "u8">;
|
|
48
|
+
}>, "object">;
|
|
49
|
+
occlusionTexture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
50
|
+
texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
51
|
+
uv: import("@aethervtt/shard-core").FieldType<number, "u8">;
|
|
52
|
+
offset: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
53
|
+
scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
54
|
+
rotation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
55
|
+
wrap: import("@aethervtt/shard-core").FieldType<"clamp" | "mirror" | "repeat", "u8">;
|
|
56
|
+
filter: import("@aethervtt/shard-core").FieldType<"linear" | "nearest", "u8">;
|
|
57
|
+
}>, "object">;
|
|
58
|
+
emissiveTexture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
59
|
+
texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
60
|
+
uv: import("@aethervtt/shard-core").FieldType<number, "u8">;
|
|
61
|
+
offset: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
62
|
+
scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
|
|
63
|
+
rotation: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
64
|
+
wrap: import("@aethervtt/shard-core").FieldType<"clamp" | "mirror" | "repeat", "u8">;
|
|
65
|
+
filter: import("@aethervtt/shard-core").FieldType<"linear" | "nearest", "u8">;
|
|
66
|
+
}>, "object">;
|
|
67
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { t } from "@aethervtt/shard-core";
|
|
2
|
+
function textureSlot(description) {
|
|
3
|
+
return t.struct(
|
|
4
|
+
{
|
|
5
|
+
texture: t.handle("Texture", {
|
|
6
|
+
description: 'The texture, e.g. { "path": "assets/rock.png" }.'
|
|
7
|
+
}),
|
|
8
|
+
uv: t.u8({ max: 1, description: "UV set: 0 or 1." }),
|
|
9
|
+
offset: t.vec2({ description: "UV offset." }),
|
|
10
|
+
scale: t.vec2({ default: [1, 1], description: "UV scale." }),
|
|
11
|
+
rotation: t.f32({ unit: "rad", description: "UV rotation." }),
|
|
12
|
+
wrap: t.enum(["repeat", "clamp", "mirror"], { description: "Addressing outside [0, 1]." }),
|
|
13
|
+
filter: t.enum(["linear", "nearest"], { description: "Texel filtering." })
|
|
14
|
+
},
|
|
15
|
+
{ description }
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
const TextureSlot = textureSlot(
|
|
19
|
+
"A texture slot: which texture, which UVs, and how to sample it."
|
|
20
|
+
);
|
|
21
|
+
const TEXTURE_SLOTS = [
|
|
22
|
+
"baseColorTexture",
|
|
23
|
+
"metallicRoughnessTexture",
|
|
24
|
+
"normalTexture",
|
|
25
|
+
"occlusionTexture",
|
|
26
|
+
"emissiveTexture"
|
|
27
|
+
];
|
|
28
|
+
const STANDARD_FIELDS = {
|
|
29
|
+
baseColor: t.color({
|
|
30
|
+
default: [0.8, 0.8, 0.8, 1],
|
|
31
|
+
description: "Albedo (linear), alpha in w."
|
|
32
|
+
}),
|
|
33
|
+
metallic: t.f32({ min: 0, max: 1, description: "0 for dielectrics, 1 for metals." }),
|
|
34
|
+
roughness: t.f32({ default: 0.5, min: 0, max: 1, description: "Microsurface roughness." }),
|
|
35
|
+
emissive: t.color({
|
|
36
|
+
default: [1, 1, 1, 1],
|
|
37
|
+
description: "Emitted color (linear), scaled by emissiveLuminance."
|
|
38
|
+
}),
|
|
39
|
+
emissiveLuminance: t.f32({
|
|
40
|
+
min: 0,
|
|
41
|
+
unit: "cd/m\xB2",
|
|
42
|
+
description: "Emitted luminance. 0 = not emissive."
|
|
43
|
+
}),
|
|
44
|
+
doubleSided: t.bool({ description: "Draw back faces too (no culling)." }),
|
|
45
|
+
alphaMode: t.enum(["opaque", "mask", "alpha", "additive", "premultiplied"], {
|
|
46
|
+
description: "opaque ignores alpha; mask discards pixels below alphaCutoff; alpha blends (transparent, drawn after opaque, sorted back to front); additive adds light (glows); premultiplied expects color already multiplied by alpha."
|
|
47
|
+
}),
|
|
48
|
+
alphaCutoff: t.f32({
|
|
49
|
+
default: 0.5,
|
|
50
|
+
min: 0,
|
|
51
|
+
max: 1,
|
|
52
|
+
description: 'Alpha threshold for alphaMode "mask".'
|
|
53
|
+
}),
|
|
54
|
+
normalScale: t.f32({ default: 1, description: "Strength of the normal map." }),
|
|
55
|
+
occlusionStrength: t.f32({
|
|
56
|
+
default: 1,
|
|
57
|
+
min: 0,
|
|
58
|
+
max: 1,
|
|
59
|
+
description: "Strength of the occlusion map."
|
|
60
|
+
}),
|
|
61
|
+
baseColorTexture: textureSlot("Albedo (sRGB), multiplied with baseColor."),
|
|
62
|
+
metallicRoughnessTexture: textureSlot(
|
|
63
|
+
"G = roughness, B = metallic (linear), multiplied with the factors."
|
|
64
|
+
),
|
|
65
|
+
normalTexture: textureSlot("Tangent-space normal map."),
|
|
66
|
+
occlusionTexture: textureSlot("Ambient occlusion in R (linear)."),
|
|
67
|
+
emissiveTexture: textureSlot("Emission color (sRGB), multiplied with emissive.")
|
|
68
|
+
};
|
|
69
|
+
export {
|
|
70
|
+
STANDARD_FIELDS,
|
|
71
|
+
TEXTURE_SLOTS,
|
|
72
|
+
TextureSlot
|
|
73
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Plugin } from '@aethervtt/shard-runtime';
|
|
2
|
+
import { type ForwardPluginOptions } from './forward.js';
|
|
3
|
+
/**
|
|
4
|
+
* The whole 3D renderer: `forwardCorePlugin` and every render feature. Apps that want a smaller
|
|
5
|
+
* build install `forwardCorePlugin` and only the features they use (spec 0056).
|
|
6
|
+
*/
|
|
7
|
+
export declare function forwardPlugin(options?: ForwardPluginOptions): Plugin;
|
package/dist/standard.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { definePlugin } from "@aethervtt/shard-runtime";
|
|
2
|
+
import { atmospherePlugin } from "./atmosphere-plugin.js";
|
|
3
|
+
import { deferredPlugin } from "./deferred-plugin.js";
|
|
4
|
+
import { renderDescribePlugin } from "./describe-plugin.js";
|
|
5
|
+
import { dynamicResolutionPlugin } from "./dynamic-resolution.js";
|
|
6
|
+
import { environmentPlugin } from "./environment-plugin.js";
|
|
7
|
+
import { forwardCorePlugin } from "./forward.js";
|
|
8
|
+
import { fxaaPlugin } from "./fxaa.js";
|
|
9
|
+
import { gizmosPlugin } from "./gizmos-plugin.js";
|
|
10
|
+
import { pickingPlugin } from "./picking-plugin.js";
|
|
11
|
+
import { pixelPerfectPlugin } from "./pixel-perfect-plugin.js";
|
|
12
|
+
import { postPlugin } from "./post-plugin.js";
|
|
13
|
+
import { skinningPlugin } from "./skinning-plugin.js";
|
|
14
|
+
function forwardPlugin(options = {}) {
|
|
15
|
+
return definePlugin({
|
|
16
|
+
name: "render/standard",
|
|
17
|
+
build(app) {
|
|
18
|
+
app.addPlugin(
|
|
19
|
+
forwardCorePlugin(options),
|
|
20
|
+
environmentPlugin,
|
|
21
|
+
atmospherePlugin,
|
|
22
|
+
postPlugin,
|
|
23
|
+
fxaaPlugin,
|
|
24
|
+
gizmosPlugin,
|
|
25
|
+
pickingPlugin,
|
|
26
|
+
deferredPlugin,
|
|
27
|
+
skinningPlugin,
|
|
28
|
+
pixelPerfectPlugin,
|
|
29
|
+
dynamicResolutionPlugin,
|
|
30
|
+
renderDescribePlugin
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
forwardPlugin
|
|
37
|
+
};
|
package/dist/stats.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ViewStats {
|
|
2
|
+
visible: number;
|
|
3
|
+
culled: number;
|
|
4
|
+
hidden: number;
|
|
5
|
+
/** Meshes skipped because their mesh or material isn't loaded yet (or failed). */
|
|
6
|
+
pending: number;
|
|
7
|
+
drawCalls: number;
|
|
8
|
+
/** Pipeline changes in the opaque pass (sorting keeps these near the number of material types). */
|
|
9
|
+
pipelineSwitches?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const RenderStats: import("@aethervtt/shard-core").ResourceDef<Map<string, ViewStats>>;
|
|
12
|
+
export interface GpuMemoryData {
|
|
13
|
+
/** Textures uploaded by the forward renderer. */
|
|
14
|
+
textures: number;
|
|
15
|
+
/** Bytes of GPU memory those textures use (all mip levels). */
|
|
16
|
+
textureBytes: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const GpuMemory: import("@aethervtt/shard-core").ResourceDef<GpuMemoryData>;
|
|
19
|
+
export interface RenderCountersData {
|
|
20
|
+
/** Times a TAA history started over (new view, resize, device loss). Never on origin shifts. */
|
|
21
|
+
taaResets: number;
|
|
22
|
+
/** Floating-origin shifts the renderer followed (spec 0040): history moved, not reset. */
|
|
23
|
+
originShifts: number;
|
|
24
|
+
}
|
|
25
|
+
export declare const RenderCounters: import("@aethervtt/shard-core").ResourceDef<RenderCountersData>;
|