reze-engine 0.19.0 → 0.21.0
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/README.md +300 -252
- package/dist/engine.d.ts +12 -25
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +97 -312
- package/dist/graph/compile.d.ts +3 -3
- package/dist/graph/compile.d.ts.map +1 -1
- package/dist/graph/presets/body.d.ts +2 -2
- package/dist/graph/presets/body.d.ts.map +1 -1
- package/dist/graph/presets/body.js +1 -1
- package/dist/graph/presets/cloth_rough.d.ts +2 -2
- package/dist/graph/presets/cloth_rough.d.ts.map +1 -1
- package/dist/graph/presets/cloth_rough.js +1 -1
- package/dist/graph/presets/cloth_smooth.d.ts +2 -2
- package/dist/graph/presets/cloth_smooth.d.ts.map +1 -1
- package/dist/graph/presets/cloth_smooth.js +1 -1
- package/dist/graph/presets/default.d.ts +2 -2
- package/dist/graph/presets/default.d.ts.map +1 -1
- package/dist/graph/presets/default.js +13 -6
- package/dist/graph/presets/eye.d.ts +2 -2
- package/dist/graph/presets/eye.d.ts.map +1 -1
- package/dist/graph/presets/eye.js +1 -1
- package/dist/graph/presets/face.d.ts +2 -2
- package/dist/graph/presets/face.d.ts.map +1 -1
- package/dist/graph/presets/face.js +1 -1
- package/dist/graph/presets/hair.d.ts +2 -2
- package/dist/graph/presets/hair.d.ts.map +1 -1
- package/dist/graph/presets/hair.js +1 -1
- package/dist/graph/presets/metal.d.ts +2 -2
- package/dist/graph/presets/metal.d.ts.map +1 -1
- package/dist/graph/presets/metal.js +1 -1
- package/dist/graph/presets/stockings.d.ts +2 -2
- package/dist/graph/presets/stockings.d.ts.map +1 -1
- package/dist/graph/presets/stockings.js +1 -1
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +8 -0
- package/dist/graph/render-class.js +1 -1
- package/dist/graph/schema.d.ts +1 -1
- package/dist/graph/schema.d.ts.map +1 -1
- package/dist/graph/schema.js +1 -1
- package/dist/graph/style-group.d.ts +3 -3
- package/dist/graph/style-group.d.ts.map +1 -1
- package/dist/graph/style-group.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/tga-loader.d.ts +7 -0
- package/dist/tga-loader.d.ts.map +1 -0
- package/dist/tga-loader.js +154 -0
- package/package.json +2 -2
- package/src/engine.ts +4488 -4718
- package/src/graph/compile.ts +3 -3
- package/src/graph/presets/body.ts +3 -3
- package/src/graph/presets/cloth_rough.ts +3 -3
- package/src/graph/presets/cloth_smooth.ts +3 -3
- package/src/graph/presets/default.ts +15 -8
- package/src/graph/presets/eye.ts +3 -3
- package/src/graph/presets/face.ts +3 -3
- package/src/graph/presets/hair.ts +3 -3
- package/src/graph/presets/metal.ts +3 -3
- package/src/graph/presets/stockings.ts +3 -3
- package/src/graph/registry.ts +8 -0
- package/src/graph/render-class.ts +1 -1
- package/src/graph/schema.ts +2 -2
- package/src/graph/style-group.ts +4 -4
- package/src/index.ts +62 -62
- package/src/tga-loader.ts +154 -0
package/src/graph/compile.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// See docs/graph-compiler-spec.md.
|
|
5
5
|
|
|
6
6
|
import { MAX_NODES, MAX_PARAMS } from "./schema"
|
|
7
|
-
import type { Diagnostic, ExposedParam, GraphNode, SocketValue,
|
|
7
|
+
import type { Diagnostic, ExposedParam, GraphNode, SocketValue, ShaderGraph } from "./schema"
|
|
8
8
|
import { NODE_REGISTRY, canConvert, convert, fmtValue, literalFits } from "./registry"
|
|
9
9
|
import type { NodeSpec, SockT } from "./registry"
|
|
10
10
|
import { assembleModule } from "./slots"
|
|
@@ -83,7 +83,7 @@ export function assignStyleSlots(params: ExposedParam[]): StyleSlot[] {
|
|
|
83
83
|
|
|
84
84
|
// ─── Validation ─────────────────────────────────────────────────────
|
|
85
85
|
|
|
86
|
-
export function validateGraph(graph:
|
|
86
|
+
export function validateGraph(graph: ShaderGraph, opts: CompileOptions = {}): Diagnostic[] {
|
|
87
87
|
const d: Diagnostic[] = []
|
|
88
88
|
const nodes = new Map<string, GraphNode>()
|
|
89
89
|
|
|
@@ -188,7 +188,7 @@ export function validateGraph(graph: StyleGraph, opts: CompileOptions = {}): Dia
|
|
|
188
188
|
|
|
189
189
|
// ─── Compile ────────────────────────────────────────────────────────
|
|
190
190
|
|
|
191
|
-
export function compileGraph(graph:
|
|
191
|
+
export function compileGraph(graph: ShaderGraph, opts: CompileOptions = {}): CompileResult {
|
|
192
192
|
const fail = (diagnostics: Diagnostic[]): CompileResult => ({
|
|
193
193
|
ok: false,
|
|
194
194
|
wgsl: "",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// M_Body as a
|
|
1
|
+
// M_Body as a ShaderGraph — port of shaders/materials/body.ts (仿深空之眼 "M_Body").
|
|
2
2
|
// Toon + warm rim + rim1/rim2 stack mixed 50/50 against a Principled BSDF with
|
|
3
3
|
// noise-bumped normal. The hand port's local ramp_ease is ramp_cardinal (identical
|
|
4
4
|
// smoothstep form); the Mapping node's zero loc/rot folds to a scale multiply.
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { ShaderGraph } from "../schema"
|
|
7
7
|
|
|
8
|
-
export const BODY_GRAPH:
|
|
8
|
+
export const BODY_GRAPH: ShaderGraph = {
|
|
9
9
|
version: 1,
|
|
10
10
|
name: "Body",
|
|
11
11
|
tags: ["body"],
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// M_Rough_Cloth as a
|
|
1
|
+
// M_Rough_Cloth as a ShaderGraph — port of shaders/materials/cloth_rough.ts.
|
|
2
2
|
// NPR graph identical to M_Smooth_Cloth, but the noise bump subtree IS live on
|
|
3
3
|
// Principled.Normal (weave bump in rest space) and Roughness is raised to 0.8187.
|
|
4
4
|
// The tex_noise node hits the detail=2 peephole → tex_noise_d2.
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { ShaderGraph } from "../schema"
|
|
7
7
|
|
|
8
|
-
export const CLOTH_ROUGH_GRAPH:
|
|
8
|
+
export const CLOTH_ROUGH_GRAPH: ShaderGraph = {
|
|
9
9
|
version: 1,
|
|
10
10
|
name: "Rough Cloth",
|
|
11
11
|
tags: ["cloth_rough"],
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// M_Smooth_Cloth as a
|
|
1
|
+
// M_Smooth_Cloth as a ShaderGraph — port of shaders/materials/cloth_smooth.ts.
|
|
2
2
|
// NPR toon + bevel + overlay-boosted emission (18.2×) mixed 10/90 against a plain
|
|
3
3
|
// Principled BSDF. The Blender graph's dead bump subtree is omitted (as in the hand
|
|
4
4
|
// port). hue_sat nodes with hue=0.5 compile to the hue_sat_id specialization.
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { ShaderGraph } from "../schema"
|
|
7
7
|
|
|
8
|
-
export const CLOTH_SMOOTH_GRAPH:
|
|
8
|
+
export const CLOTH_SMOOTH_GRAPH: ShaderGraph = {
|
|
9
9
|
version: 1,
|
|
10
10
|
name: "Smooth Cloth",
|
|
11
11
|
tags: ["cloth_smooth"],
|
|
@@ -1,23 +1,30 @@
|
|
|
1
|
-
// Default material
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// Default material — the neutral base used two ways: the ungrouped fallback (a material
|
|
2
|
+
// in no style group renders this) and the blank-canvas starter the editor's "New graph"
|
|
3
|
+
// begins from, so the two always agree. MMD-correct PBSDF base: diffuse texture × the
|
|
4
|
+
// PMX material diffuse color → Principled BSDF (Metallic 0, Specular 0.5, Roughness 0.5).
|
|
5
|
+
// The material-color multiply is what keeps untextured/solid-color materials from
|
|
6
|
+
// rendering white (they carry their color in material.diffuse, not a texture).
|
|
6
7
|
|
|
7
|
-
import type {
|
|
8
|
+
import type { ShaderGraph } from "../schema"
|
|
8
9
|
|
|
9
|
-
export const DEFAULT_GRAPH:
|
|
10
|
+
export const DEFAULT_GRAPH: ShaderGraph = {
|
|
10
11
|
version: 1,
|
|
11
12
|
name: "Principled BSDF",
|
|
12
13
|
tags: ["default"],
|
|
13
14
|
nodes: [
|
|
14
15
|
{ id: "tex", type: "texture" },
|
|
16
|
+
{ id: "mat", type: "material_diffuse" },
|
|
17
|
+
{ id: "base", type: "mix/multiply", inputs: { fac: 1.0 } }, // texture × material diffuse
|
|
15
18
|
{
|
|
16
19
|
id: "principled",
|
|
17
20
|
type: "principled",
|
|
18
21
|
inputs: { metallic: 0.0, specular: 0.5, roughness: 0.5, spec_clamp: 10.0, sheen: 0.0, sheen_tint: 0.0 },
|
|
19
22
|
},
|
|
20
23
|
],
|
|
21
|
-
links: [
|
|
24
|
+
links: [
|
|
25
|
+
{ from: { node: "tex", socket: "color" }, to: { node: "base", socket: "a" } },
|
|
26
|
+
{ from: { node: "mat", socket: "color" }, to: { node: "base", socket: "b" } },
|
|
27
|
+
{ from: { node: "base", socket: "color" }, to: { node: "principled", socket: "base" } },
|
|
28
|
+
],
|
|
22
29
|
output: { node: "principled", socket: "color" },
|
|
23
30
|
}
|
package/src/graph/presets/eye.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Eye as a
|
|
1
|
+
// Eye as a ShaderGraph — port of shaders/materials/eye.ts. The published preset
|
|
2
2
|
// author's instruction: "keep eyes in the default nodegraph, add emission 1.5".
|
|
3
3
|
// So it's the default Principled BSDF plus an Emission of the diffuse texture at
|
|
4
4
|
// 1.5× (Blender's Principled Emission socket, decomposed as a separate Emission +
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
// The rear-view gate and the see-through stencil stamp are slot-owned (built-in eye
|
|
8
8
|
// behavior, see EYE_TEMPLATE in slots.ts + createSlotPipeline) — not in this graph.
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type { ShaderGraph } from "../schema"
|
|
11
11
|
|
|
12
|
-
export const EYE_GRAPH:
|
|
12
|
+
export const EYE_GRAPH: ShaderGraph = {
|
|
13
13
|
version: 1,
|
|
14
14
|
name: "Eye",
|
|
15
15
|
tags: ["eye"],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// M_Face as a
|
|
1
|
+
// M_Face as a ShaderGraph — port of shaders/materials/face.ts (仿深空之眼 "M_Face").
|
|
2
2
|
// Toon + warm rim + dual fresnel rim + BT.601 bright-tex gate, mixed 50/50 against a
|
|
3
3
|
// Principled BSDF with noise bump. Structurally close to body, but: the toon uses the
|
|
4
4
|
// edge-AA ramp, hue tints are 0.46 (full hue_sat, not the 0.5 _id specialization),
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// near-white texture gate that emits (freckle/highlight paint). Plain material — no
|
|
7
7
|
// built-in slot effect, so it uses the default prelude/epilogue.
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { ShaderGraph } from "../schema"
|
|
10
10
|
|
|
11
|
-
export const FACE_GRAPH:
|
|
11
|
+
export const FACE_GRAPH: ShaderGraph = {
|
|
12
12
|
version: 1,
|
|
13
13
|
name: "Face",
|
|
14
14
|
tags: ["face"],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// M_Hair — 仿深空之眼渲染预设v1.0_by_小绿毛猫, as a
|
|
1
|
+
// M_Hair — 仿深空之眼渲染预设v1.0_by_小绿毛猫, as a ShaderGraph. Node ids mirror the
|
|
2
2
|
// Blender node names referenced in shaders/materials/hair.ts comments; compiled with
|
|
3
3
|
// { inlineParams: true } this graph must reproduce HAIR_SHADER_WGSL's fragment body
|
|
4
4
|
// (golden test #1 — see tests/graph.test.mjs).
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
// The object is pure JSON (no functions/undefined) — JSON.stringify round-trips it,
|
|
11
11
|
// which is how reze.design will ship additional presets.
|
|
12
12
|
|
|
13
|
-
import type {
|
|
13
|
+
import type { ShaderGraph } from "../schema"
|
|
14
14
|
|
|
15
|
-
export const HAIR_GRAPH:
|
|
15
|
+
export const HAIR_GRAPH: ShaderGraph = {
|
|
16
16
|
version: 1,
|
|
17
17
|
name: "Hair",
|
|
18
18
|
tags: ["hair"],
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// M_Metal as a
|
|
1
|
+
// M_Metal as a ShaderGraph — port of shaders/materials/metal.ts.
|
|
2
2
|
// Metallic Principled (Metallic=1, Specular=1, Roughness=0.3) with a
|
|
3
3
|
// reflection-coord voronoi driving base color (metallic sparkle), plus an NPR
|
|
4
4
|
// toon/overlay emission stack mixed at Fac=0.6967. The voronoi Color→ramp Fac
|
|
5
5
|
// link goes through Blender's implicit BT.601 conversion (color_to_value).
|
|
6
6
|
|
|
7
|
-
import type {
|
|
7
|
+
import type { ShaderGraph } from "../schema"
|
|
8
8
|
|
|
9
|
-
export const METAL_GRAPH:
|
|
9
|
+
export const METAL_GRAPH: ShaderGraph = {
|
|
10
10
|
version: 1,
|
|
11
11
|
name: "Metal",
|
|
12
12
|
tags: ["metal"],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// M_Stockings as a
|
|
1
|
+
// M_Stockings as a ShaderGraph — port of shaders/materials/stockings.ts.
|
|
2
2
|
// A bbox-gradient × facing-rim mask drives a Mix Shader between an HSV-boosted
|
|
3
3
|
// emission (val 5×) and a sheen Principled. The hashed-alpha discard and the
|
|
4
4
|
// alpha=1 output are slot-owned (see STOCKINGS_TEMPLATE in slots.ts) — the graph
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// as in the hand port. The grayscale mask feeds Mix Shader Fac through Blender's
|
|
7
7
|
// implicit BT.601 color→float conversion (equal-component vector → same scalar).
|
|
8
8
|
|
|
9
|
-
import type {
|
|
9
|
+
import type { ShaderGraph } from "../schema"
|
|
10
10
|
|
|
11
|
-
export const STOCKINGS_GRAPH:
|
|
11
|
+
export const STOCKINGS_GRAPH: ShaderGraph = {
|
|
12
12
|
version: 1,
|
|
13
13
|
name: "Stockings",
|
|
14
14
|
tags: ["stockings"],
|
package/src/graph/registry.ts
CHANGED
|
@@ -137,6 +137,14 @@ export const NODE_REGISTRY: Record<string, NodeSpec> = {
|
|
|
137
137
|
reflection: "reflect(-v, n)",
|
|
138
138
|
},
|
|
139
139
|
},
|
|
140
|
+
// PMX material's diffuse color (the authored base tint). Multiply the diffuse texture
|
|
141
|
+
// by this for the MMD-correct base — untextured materials carry their color here, so a
|
|
142
|
+
// texture-only base would render them white.
|
|
143
|
+
material_diffuse: {
|
|
144
|
+
inputs: {},
|
|
145
|
+
outputs: { color: "color" },
|
|
146
|
+
contextOutputs: { color: "material.diffuseColor" },
|
|
147
|
+
},
|
|
140
148
|
|
|
141
149
|
// ── Literals as nodes (for editor ergonomics; inlined literals work too) ──
|
|
142
150
|
value: { inputs: { value: F(0) }, outputs: { value: "float" }, emit: (a) => a.value },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Render-class + alpha-mode: the small, closed, engine-owned vocabularies that carry
|
|
2
|
-
// pass integration for a style group. A group's
|
|
2
|
+
// pass integration for a style group. A group's shader graph is pure shading; these select
|
|
3
3
|
// how the engine wires that shading into the passes. Not user-extensible — this is where
|
|
4
4
|
// rendering correctness lives (stencil, cull, draw order, alpha test). See
|
|
5
5
|
// docs/style-groups-spec.md §5.
|
package/src/graph/schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ShaderGraph — a JSON-serializable graph of shading nodes, compiled to WGSL by compile.ts.
|
|
2
2
|
// Engine-space (LH, Y-up): porting a Blender graph converts coordinates at authoring
|
|
3
3
|
// time (Blender Normal Z → engine Y), the compiler never sees Blender conventions.
|
|
4
4
|
// See docs/graph-compiler-spec.md.
|
|
@@ -34,7 +34,7 @@ export type ExposedParam = {
|
|
|
34
34
|
default: SocketValue
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export type
|
|
37
|
+
export type ShaderGraph = {
|
|
38
38
|
version: 1
|
|
39
39
|
name: string
|
|
40
40
|
nodes: GraphNode[]
|
package/src/graph/style-group.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Style groups — the user-facing unit of material styling. A group binds a set of
|
|
2
|
-
// materials to a
|
|
2
|
+
// materials to a shader graph (pure shading) plus the engine-owned pass-integration axes
|
|
3
3
|
// (renderClass + alphaMode). Each material belongs to at most one group; grouped
|
|
4
4
|
// materials render via the group's compiled-graph pipeline, ungrouped ones fall back to
|
|
5
5
|
// the hand-written preset path. See docs/style-groups-spec.md.
|
|
6
6
|
|
|
7
|
-
import type { Diagnostic,
|
|
7
|
+
import type { Diagnostic, ShaderGraph } from "./schema"
|
|
8
8
|
import type { AlphaMode, RenderClass } from "./render-class"
|
|
9
9
|
import type { StyleSlot } from "./compile"
|
|
10
10
|
|
|
@@ -16,8 +16,8 @@ export type StyleGroup = {
|
|
|
16
16
|
label?: string
|
|
17
17
|
/** Material names in this group; each material is in AT MOST one group. */
|
|
18
18
|
materials: string[]
|
|
19
|
-
/** Pure shading (
|
|
20
|
-
graph:
|
|
19
|
+
/** Pure shading (ShaderGraph has no `slot` — integration lives here on the group). */
|
|
20
|
+
graph: ShaderGraph
|
|
21
21
|
/** Pass-integration class: stencil/cull/draw-order. Default "auto". */
|
|
22
22
|
renderClass?: RenderClass
|
|
23
23
|
/** Alpha-handling axis, orthogonal to renderClass. Default "opaque". */
|
package/src/index.ts
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
export {
|
|
2
|
-
Engine,
|
|
3
|
-
DEFAULT_BLOOM_OPTIONS,
|
|
4
|
-
DEFAULT_VIEW_TRANSFORM,
|
|
5
|
-
type EngineStats,
|
|
6
|
-
type EngineOptions,
|
|
7
|
-
type BloomOptions,
|
|
8
|
-
type ViewTransformOptions,
|
|
9
|
-
type LoadModelFromFilesOptions,
|
|
10
|
-
type MaterialPreset,
|
|
11
|
-
type ResolvedMaterialPreset,
|
|
12
|
-
type MaterialPresetMap,
|
|
13
|
-
type GizmoDragEvent,
|
|
14
|
-
type GizmoDragCallback,
|
|
15
|
-
type GizmoDragKind,
|
|
16
|
-
} from "./engine"
|
|
17
|
-
export { parsePmxFolderInput, pmxFileAtRelativePath, type PmxFolderInputResult } from "./folder-upload"
|
|
18
|
-
export {
|
|
19
|
-
compileGraph,
|
|
20
|
-
validateGraph,
|
|
21
|
-
assignStyleSlots,
|
|
22
|
-
type CompileOptions,
|
|
23
|
-
type CompileResult,
|
|
24
|
-
type StyleSlot,
|
|
25
|
-
} from "./graph/compile"
|
|
26
|
-
export type {
|
|
27
|
-
|
|
28
|
-
GraphNode,
|
|
29
|
-
GraphLink,
|
|
30
|
-
ExposedParam,
|
|
31
|
-
SocketValue,
|
|
32
|
-
Diagnostic,
|
|
33
|
-
} from "./graph/schema"
|
|
34
|
-
export { NODE_REGISTRY, type NodeSpec, type SockT } from "./graph/registry"
|
|
35
|
-
export { RENDER_CLASSES, type RenderClass, type AlphaMode, type RenderClassInfo } from "./graph/render-class"
|
|
36
|
-
export type {
|
|
37
|
-
StyleGroup,
|
|
38
|
-
GroupDiagnostic,
|
|
39
|
-
ApplyStyleGroupsResult,
|
|
40
|
-
ApplyStyleGroupResult,
|
|
41
|
-
} from "./graph/style-group"
|
|
42
|
-
export { HAIR_GRAPH } from "./graph/presets/hair"
|
|
43
|
-
export { DEFAULT_GRAPH } from "./graph/presets/default"
|
|
44
|
-
export { CLOTH_SMOOTH_GRAPH } from "./graph/presets/cloth_smooth"
|
|
45
|
-
export { CLOTH_ROUGH_GRAPH } from "./graph/presets/cloth_rough"
|
|
46
|
-
export { METAL_GRAPH } from "./graph/presets/metal"
|
|
47
|
-
export { BODY_GRAPH } from "./graph/presets/body"
|
|
48
|
-
export { STOCKINGS_GRAPH } from "./graph/presets/stockings"
|
|
49
|
-
export { EYE_GRAPH } from "./graph/presets/eye"
|
|
50
|
-
export { FACE_GRAPH } from "./graph/presets/face"
|
|
51
|
-
export { Model } from "./model"
|
|
52
|
-
export { Vec3, Quat, Mat4 } from "./math"
|
|
53
|
-
export type {
|
|
54
|
-
AnimationClip,
|
|
55
|
-
AnimationPlayOptions,
|
|
56
|
-
AnimationProgress,
|
|
57
|
-
BoneKeyframe,
|
|
58
|
-
MorphKeyframe,
|
|
59
|
-
BoneInterpolation,
|
|
60
|
-
ControlPoint,
|
|
61
|
-
} from "./animation"
|
|
62
|
-
export { FPS } from "./animation"
|
|
1
|
+
export {
|
|
2
|
+
Engine,
|
|
3
|
+
DEFAULT_BLOOM_OPTIONS,
|
|
4
|
+
DEFAULT_VIEW_TRANSFORM,
|
|
5
|
+
type EngineStats,
|
|
6
|
+
type EngineOptions,
|
|
7
|
+
type BloomOptions,
|
|
8
|
+
type ViewTransformOptions,
|
|
9
|
+
type LoadModelFromFilesOptions,
|
|
10
|
+
type MaterialPreset,
|
|
11
|
+
type ResolvedMaterialPreset,
|
|
12
|
+
type MaterialPresetMap,
|
|
13
|
+
type GizmoDragEvent,
|
|
14
|
+
type GizmoDragCallback,
|
|
15
|
+
type GizmoDragKind,
|
|
16
|
+
} from "./engine"
|
|
17
|
+
export { parsePmxFolderInput, pmxFileAtRelativePath, type PmxFolderInputResult } from "./folder-upload"
|
|
18
|
+
export {
|
|
19
|
+
compileGraph,
|
|
20
|
+
validateGraph,
|
|
21
|
+
assignStyleSlots,
|
|
22
|
+
type CompileOptions,
|
|
23
|
+
type CompileResult,
|
|
24
|
+
type StyleSlot,
|
|
25
|
+
} from "./graph/compile"
|
|
26
|
+
export type {
|
|
27
|
+
ShaderGraph,
|
|
28
|
+
GraphNode,
|
|
29
|
+
GraphLink,
|
|
30
|
+
ExposedParam,
|
|
31
|
+
SocketValue,
|
|
32
|
+
Diagnostic,
|
|
33
|
+
} from "./graph/schema"
|
|
34
|
+
export { NODE_REGISTRY, type NodeSpec, type SockT } from "./graph/registry"
|
|
35
|
+
export { RENDER_CLASSES, type RenderClass, type AlphaMode, type RenderClassInfo } from "./graph/render-class"
|
|
36
|
+
export type {
|
|
37
|
+
StyleGroup,
|
|
38
|
+
GroupDiagnostic,
|
|
39
|
+
ApplyStyleGroupsResult,
|
|
40
|
+
ApplyStyleGroupResult,
|
|
41
|
+
} from "./graph/style-group"
|
|
42
|
+
export { HAIR_GRAPH } from "./graph/presets/hair"
|
|
43
|
+
export { DEFAULT_GRAPH } from "./graph/presets/default"
|
|
44
|
+
export { CLOTH_SMOOTH_GRAPH } from "./graph/presets/cloth_smooth"
|
|
45
|
+
export { CLOTH_ROUGH_GRAPH } from "./graph/presets/cloth_rough"
|
|
46
|
+
export { METAL_GRAPH } from "./graph/presets/metal"
|
|
47
|
+
export { BODY_GRAPH } from "./graph/presets/body"
|
|
48
|
+
export { STOCKINGS_GRAPH } from "./graph/presets/stockings"
|
|
49
|
+
export { EYE_GRAPH } from "./graph/presets/eye"
|
|
50
|
+
export { FACE_GRAPH } from "./graph/presets/face"
|
|
51
|
+
export { Model } from "./model"
|
|
52
|
+
export { Vec3, Quat, Mat4 } from "./math"
|
|
53
|
+
export type {
|
|
54
|
+
AnimationClip,
|
|
55
|
+
AnimationPlayOptions,
|
|
56
|
+
AnimationProgress,
|
|
57
|
+
BoneKeyframe,
|
|
58
|
+
MorphKeyframe,
|
|
59
|
+
BoneInterpolation,
|
|
60
|
+
ControlPoint,
|
|
61
|
+
} from "./animation"
|
|
62
|
+
export { FPS } from "./animation"
|
|
63
63
|
export { RezePhysics } from "./physics"
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Minimal TGA decoder. TGA is not a web-native format (createImageBitmap can't decode
|
|
2
|
+
// it), yet it's common in PMX assets — especially sphere maps (.spa/.sph) and eye/detail
|
|
3
|
+
// textures — so without this those materials render untextured. Produces top-left-origin
|
|
4
|
+
// RGBA8, ready for queue.writeTexture into an rgba8unorm(-srgb) texture.
|
|
5
|
+
//
|
|
6
|
+
// Handles the variants that actually appear in the wild: true-color (16/24/32), grayscale
|
|
7
|
+
// (8), and color-mapped (8-bit index), each raw or RLE-compressed, with the image
|
|
8
|
+
// descriptor's origin bits honored. Throws on malformed/unsupported input — the caller
|
|
9
|
+
// catches, logs, and falls back to the white texture (never panics the render loop).
|
|
10
|
+
|
|
11
|
+
export type DecodedImage = { width: number; height: number; rgba: Uint8Array }
|
|
12
|
+
|
|
13
|
+
// Image type byte (header offset 2).
|
|
14
|
+
const enum TgaType {
|
|
15
|
+
ColorMapped = 1,
|
|
16
|
+
TrueColor = 2,
|
|
17
|
+
Grayscale = 3,
|
|
18
|
+
RleColorMapped = 9,
|
|
19
|
+
RleTrueColor = 10,
|
|
20
|
+
RleGrayscale = 11,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function decodeTga(buffer: ArrayBuffer): DecodedImage {
|
|
24
|
+
const bytes = new Uint8Array(buffer)
|
|
25
|
+
const view = new DataView(buffer)
|
|
26
|
+
if (bytes.length < 18) throw new Error("TGA too small for header")
|
|
27
|
+
|
|
28
|
+
const idLength = view.getUint8(0)
|
|
29
|
+
const colorMapType = view.getUint8(1)
|
|
30
|
+
const imageType = view.getUint8(2) as TgaType
|
|
31
|
+
const cmapLength = view.getUint16(5, true)
|
|
32
|
+
const cmapEntryBits = view.getUint8(7)
|
|
33
|
+
const width = view.getUint16(12, true)
|
|
34
|
+
const height = view.getUint16(14, true)
|
|
35
|
+
const pixelDepth = view.getUint8(16)
|
|
36
|
+
const descriptor = view.getUint8(17)
|
|
37
|
+
|
|
38
|
+
if (width <= 0 || height <= 0) throw new Error(`TGA bad dimensions ${width}x${height}`)
|
|
39
|
+
|
|
40
|
+
const rle =
|
|
41
|
+
imageType === TgaType.RleColorMapped ||
|
|
42
|
+
imageType === TgaType.RleTrueColor ||
|
|
43
|
+
imageType === TgaType.RleGrayscale
|
|
44
|
+
const colorMapped = imageType === TgaType.ColorMapped || imageType === TgaType.RleColorMapped
|
|
45
|
+
const grayscale = imageType === TgaType.Grayscale || imageType === TgaType.RleGrayscale
|
|
46
|
+
const trueColor = imageType === TgaType.TrueColor || imageType === TgaType.RleTrueColor
|
|
47
|
+
if (!colorMapped && !grayscale && !trueColor) throw new Error(`TGA unsupported image type ${imageType}`)
|
|
48
|
+
|
|
49
|
+
let offset = 18 + idLength
|
|
50
|
+
|
|
51
|
+
// Color map (BGR/BGRA entries) → precomputed RGBA lookup.
|
|
52
|
+
let colorMap: Uint8Array | null = null
|
|
53
|
+
if (colorMapType === 1) {
|
|
54
|
+
const entryBytes = Math.ceil(cmapEntryBits / 8)
|
|
55
|
+
colorMap = new Uint8Array(cmapLength * 4)
|
|
56
|
+
for (let i = 0; i < cmapLength; i++) {
|
|
57
|
+
const [r, g, b, a] = unpackColor(bytes, offset + i * entryBytes, cmapEntryBits)
|
|
58
|
+
colorMap.set([r, g, b, a], i * 4)
|
|
59
|
+
}
|
|
60
|
+
offset += cmapLength * entryBytes
|
|
61
|
+
} else if (colorMapped) {
|
|
62
|
+
throw new Error("TGA color-mapped image without a color map")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Bytes per stored element (index byte for color-mapped, else pixel bytes).
|
|
66
|
+
const elemBits = colorMapped ? pixelDepth : pixelDepth
|
|
67
|
+
const elemBytes = Math.ceil(elemBits / 8)
|
|
68
|
+
const pixelCount = width * height
|
|
69
|
+
|
|
70
|
+
// Decode the (possibly RLE) element stream into one element per pixel, file order.
|
|
71
|
+
const elements = new Uint8Array(pixelCount * elemBytes)
|
|
72
|
+
if (rle) {
|
|
73
|
+
let out = 0
|
|
74
|
+
let src = offset
|
|
75
|
+
while (out < elements.length) {
|
|
76
|
+
if (src >= bytes.length) throw new Error("TGA RLE stream truncated")
|
|
77
|
+
const packet = bytes[src++]
|
|
78
|
+
const count = (packet & 0x7f) + 1
|
|
79
|
+
if (packet & 0x80) {
|
|
80
|
+
// RLE packet: one element repeated `count` times.
|
|
81
|
+
if (src + elemBytes > bytes.length) throw new Error("TGA RLE packet truncated")
|
|
82
|
+
for (let i = 0; i < count && out < elements.length; i++) {
|
|
83
|
+
elements.set(bytes.subarray(src, src + elemBytes), out)
|
|
84
|
+
out += elemBytes
|
|
85
|
+
}
|
|
86
|
+
src += elemBytes
|
|
87
|
+
} else {
|
|
88
|
+
// Raw packet: `count` elements follow.
|
|
89
|
+
const span = count * elemBytes
|
|
90
|
+
if (src + span > bytes.length) throw new Error("TGA raw packet truncated")
|
|
91
|
+
elements.set(bytes.subarray(src, src + span), out)
|
|
92
|
+
out += span
|
|
93
|
+
src += span
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
const span = pixelCount * elemBytes
|
|
98
|
+
if (offset + span > bytes.length) throw new Error("TGA pixel data truncated")
|
|
99
|
+
elements.set(bytes.subarray(offset, offset + span), 0)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Convert elements → RGBA in file order.
|
|
103
|
+
const linear = new Uint8Array(pixelCount * 4)
|
|
104
|
+
for (let p = 0; p < pixelCount; p++) {
|
|
105
|
+
let rgba: [number, number, number, number]
|
|
106
|
+
if (colorMapped) {
|
|
107
|
+
const index = elements[p] // 8-bit index (pixelDepth 8)
|
|
108
|
+
const c = index * 4
|
|
109
|
+
rgba = colorMap ? [colorMap[c], colorMap[c + 1], colorMap[c + 2], colorMap[c + 3]] : [0, 0, 0, 255]
|
|
110
|
+
} else if (grayscale) {
|
|
111
|
+
const g = elements[p * elemBytes]
|
|
112
|
+
rgba = [g, g, g, 255]
|
|
113
|
+
} else {
|
|
114
|
+
rgba = unpackColor(elements, p * elemBytes, pixelDepth)
|
|
115
|
+
}
|
|
116
|
+
linear.set(rgba, p * 4)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Honor origin bits: bit5 set → top-to-bottom (no flip); clear → bottom-to-top.
|
|
120
|
+
// bit4 set → right-to-left. Output is always top-left origin for GPU upload.
|
|
121
|
+
const flipY = (descriptor & 0x20) === 0
|
|
122
|
+
const flipX = (descriptor & 0x10) !== 0
|
|
123
|
+
if (!flipX && !flipY) return { width, height, rgba: linear }
|
|
124
|
+
|
|
125
|
+
const rgba = new Uint8Array(pixelCount * 4)
|
|
126
|
+
for (let y = 0; y < height; y++) {
|
|
127
|
+
const sy = flipY ? height - 1 - y : y
|
|
128
|
+
for (let x = 0; x < width; x++) {
|
|
129
|
+
const sx = flipX ? width - 1 - x : x
|
|
130
|
+
const src = (sy * width + sx) * 4
|
|
131
|
+
const dst = (y * width + x) * 4
|
|
132
|
+
rgba[dst] = linear[src]
|
|
133
|
+
rgba[dst + 1] = linear[src + 1]
|
|
134
|
+
rgba[dst + 2] = linear[src + 2]
|
|
135
|
+
rgba[dst + 3] = linear[src + 3]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { width, height, rgba }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Unpack one true-color element (16/24/32-bit, stored BGR[A]) to RGBA8.
|
|
142
|
+
function unpackColor(src: Uint8Array, o: number, bits: number): [number, number, number, number] {
|
|
143
|
+
if (bits === 32) return [src[o + 2], src[o + 1], src[o], src[o + 3]]
|
|
144
|
+
if (bits === 24) return [src[o + 2], src[o + 1], src[o], 255]
|
|
145
|
+
if (bits === 16 || bits === 15) {
|
|
146
|
+
// 1-5-5-5: bit15 = attribute, then 5 red, 5 green, 5 blue (little-endian).
|
|
147
|
+
const val = src[o] | (src[o + 1] << 8)
|
|
148
|
+
const r = ((val >> 10) & 0x1f) * 255
|
|
149
|
+
const g = ((val >> 5) & 0x1f) * 255
|
|
150
|
+
const b = (val & 0x1f) * 255
|
|
151
|
+
return [Math.round(r / 31), Math.round(g / 31), Math.round(b / 31), 255]
|
|
152
|
+
}
|
|
153
|
+
throw new Error(`TGA unsupported pixel depth ${bits}`)
|
|
154
|
+
}
|