reze-engine 0.9.4 → 0.10.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 +28 -5
- package/dist/animation.d.ts +26 -13
- package/dist/animation.d.ts.map +1 -1
- package/dist/animation.js +95 -35
- package/dist/engine.d.ts +5 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +78 -23
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +8 -11
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +55 -38
- package/package.json +1 -1
- package/src/animation.ts +124 -40
- package/src/engine.ts +121 -69
- package/src/ik-solver.ts +7 -7
- package/src/index.ts +9 -5
- package/src/model.ts +64 -42
package/src/engine.ts
CHANGED
|
@@ -81,7 +81,7 @@ interface ModelInstance {
|
|
|
81
81
|
export class Engine {
|
|
82
82
|
private static instance: Engine | null = null
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
static getInstance(): Engine {
|
|
85
85
|
if (!Engine.instance) {
|
|
86
86
|
throw new Error("Engine not ready: create Engine, await init(), then load models via engine.loadModel().")
|
|
87
87
|
}
|
|
@@ -199,7 +199,7 @@ export class Engine {
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
// Step 1: Get WebGPU device and context
|
|
202
|
-
|
|
202
|
+
async init() {
|
|
203
203
|
const adapter = await navigator.gpu?.requestAdapter()
|
|
204
204
|
const device = await adapter?.requestDevice()
|
|
205
205
|
if (!device) {
|
|
@@ -566,7 +566,12 @@ export class Engine {
|
|
|
566
566
|
struct CameraUniforms { view: mat4x4f, projection: mat4x4f, viewPos: vec3f, _p: f32, };
|
|
567
567
|
struct Light { direction: vec4f, color: vec4f, };
|
|
568
568
|
struct LightUniforms { ambientColor: vec4f, lights: array<Light, 4>, };
|
|
569
|
-
struct GroundShadowMat {
|
|
569
|
+
struct GroundShadowMat {
|
|
570
|
+
diffuseColor: vec3f, fadeStart: f32,
|
|
571
|
+
fadeEnd: f32, shadowStrength: f32, pcfTexel: f32, gridSpacing: f32,
|
|
572
|
+
gridLineWidth: f32, gridLineOpacity: f32, noiseStrength: f32, _pad: f32,
|
|
573
|
+
gridLineColor: vec3f, _pad2: f32,
|
|
574
|
+
};
|
|
570
575
|
struct LightVP { viewProj: mat4x4f, };
|
|
571
576
|
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
572
577
|
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
@@ -574,6 +579,32 @@ export class Engine {
|
|
|
574
579
|
@group(0) @binding(3) var shadowSampler: sampler_comparison;
|
|
575
580
|
@group(0) @binding(4) var<uniform> material: GroundShadowMat;
|
|
576
581
|
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
582
|
+
|
|
583
|
+
// Hash-based noise for frosted/matte surface
|
|
584
|
+
fn hash2(p: vec2f) -> f32 {
|
|
585
|
+
var p3 = fract(vec3f(p.x, p.y, p.x) * 0.1031);
|
|
586
|
+
p3 += dot(p3, vec3f(p3.y + 33.33, p3.z + 33.33, p3.x + 33.33));
|
|
587
|
+
return fract((p3.x + p3.y) * p3.z);
|
|
588
|
+
}
|
|
589
|
+
fn valueNoise(p: vec2f) -> f32 {
|
|
590
|
+
let i = floor(p);
|
|
591
|
+
let f = fract(p);
|
|
592
|
+
let u = f * f * (3.0 - 2.0 * f);
|
|
593
|
+
return mix(mix(hash2(i), hash2(i + vec2f(1.0, 0.0)), u.x),
|
|
594
|
+
mix(hash2(i + vec2f(0.0, 1.0)), hash2(i + vec2f(1.0, 1.0)), u.x), u.y);
|
|
595
|
+
}
|
|
596
|
+
fn fbmNoise(p: vec2f) -> f32 {
|
|
597
|
+
var v = 0.0;
|
|
598
|
+
var a = 0.5;
|
|
599
|
+
var pp = p;
|
|
600
|
+
for (var i = 0; i < 4; i++) {
|
|
601
|
+
v += a * valueNoise(pp);
|
|
602
|
+
pp *= 2.0;
|
|
603
|
+
a *= 0.5;
|
|
604
|
+
}
|
|
605
|
+
return v;
|
|
606
|
+
}
|
|
607
|
+
|
|
577
608
|
struct VO { @builtin(position) position: vec4f, @location(0) worldPos: vec3f, @location(1) normal: vec3f, };
|
|
578
609
|
@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f) -> VO {
|
|
579
610
|
var o: VO; o.worldPos = position; o.normal = normal;
|
|
@@ -583,6 +614,8 @@ export class Engine {
|
|
|
583
614
|
let n = normalize(i.normal);
|
|
584
615
|
let centerDist = length(i.worldPos.xz);
|
|
585
616
|
let edgeFade = 1.0 - smoothstep(0.0, 1.0, clamp((centerDist - material.fadeStart) / max(material.fadeEnd - material.fadeStart, 0.001), 0.0, 1.0));
|
|
617
|
+
|
|
618
|
+
// Shadow sampling
|
|
586
619
|
let lclip = lightVP.viewProj * vec4f(i.worldPos, 1.0);
|
|
587
620
|
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
588
621
|
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
@@ -596,10 +629,26 @@ export class Engine {
|
|
|
596
629
|
}
|
|
597
630
|
}
|
|
598
631
|
vis *= 0.04;
|
|
632
|
+
|
|
633
|
+
// Frosted/matte micro-texture (磨砂)
|
|
634
|
+
let noiseVal = fbmNoise(i.worldPos.xz * 3.0);
|
|
635
|
+
let noiseTint = 1.0 + (noiseVal - 0.5) * material.noiseStrength;
|
|
636
|
+
|
|
637
|
+
// Grid lines — anti-aliased via screen-space derivatives
|
|
638
|
+
let gp = i.worldPos.xz / material.gridSpacing;
|
|
639
|
+
let gridFrac = abs(fract(gp - 0.5) - 0.5);
|
|
640
|
+
let gridDeriv = fwidth(gp);
|
|
641
|
+
let halfLine = material.gridLineWidth * 0.5;
|
|
642
|
+
let gridLine = 1.0 - min(
|
|
643
|
+
smoothstep(halfLine - gridDeriv.x, halfLine + gridDeriv.x, gridFrac.x),
|
|
644
|
+
smoothstep(halfLine - gridDeriv.y, halfLine + gridDeriv.y, gridFrac.y)
|
|
645
|
+
);
|
|
599
646
|
let sun = light.ambientColor.xyz + light.lights[0].color.xyz * light.lights[0].color.w * max(dot(n, -light.lights[0].direction.xyz), 0.0);
|
|
600
647
|
let dark = (1.0 - vis) * material.shadowStrength;
|
|
601
|
-
|
|
602
|
-
|
|
648
|
+
var baseColor = material.diffuseColor * sun * (1.0 - dark * 0.65);
|
|
649
|
+
baseColor *= noiseTint;
|
|
650
|
+
let finalColor = mix(baseColor, material.gridLineColor, gridLine * material.gridLineOpacity * edgeFade);
|
|
651
|
+
return vec4f(finalColor * edgeFade, edgeFade);
|
|
603
652
|
}
|
|
604
653
|
`,
|
|
605
654
|
})
|
|
@@ -698,7 +747,7 @@ export class Engine {
|
|
|
698
747
|
// Screen-stable edgeline: extrusion ∝ camera distance (same idea as MMD viewers / babylon-mmd-style scaling)
|
|
699
748
|
let camDist = max(length(camera.viewPos - worldPos), 0.25);
|
|
700
749
|
let refDist = 30.0;
|
|
701
|
-
let edgeScale = 0.
|
|
750
|
+
let edgeScale = 0.025;
|
|
702
751
|
let expandedPos = worldPos + worldNormal * material.edgeSize * edgeScale * (camDist / refDist);
|
|
703
752
|
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
704
753
|
return output;
|
|
@@ -923,10 +972,10 @@ export class Engine {
|
|
|
923
972
|
}
|
|
924
973
|
|
|
925
974
|
/** Set static camera look-at / orbit center. Clears any model follow binding. */
|
|
926
|
-
|
|
975
|
+
setCameraTarget(v: Vec3): void
|
|
927
976
|
/** Bind camera orbit center to a model's bone (Souls-style follow cam). Pass null to unbind. */
|
|
928
|
-
|
|
929
|
-
|
|
977
|
+
setCameraTarget(model: Model | null, boneName: string, offset?: Vec3): void
|
|
978
|
+
setCameraTarget(modelOrVec: Model | Vec3 | null, boneName?: string, offset?: Vec3): void {
|
|
930
979
|
if (modelOrVec === null) {
|
|
931
980
|
this.cameraTargetModel = null
|
|
932
981
|
return
|
|
@@ -946,7 +995,7 @@ export class Engine {
|
|
|
946
995
|
}
|
|
947
996
|
|
|
948
997
|
/** Souls-style follow cam: orbit center tracks a model bone each frame. Shorthand for setCameraTarget(model, boneName, offset). */
|
|
949
|
-
|
|
998
|
+
setCameraFollow(model: Model | null, boneName?: string, offset?: Vec3): void {
|
|
950
999
|
if (model === null) {
|
|
951
1000
|
this.cameraTargetModel = null
|
|
952
1001
|
return
|
|
@@ -958,12 +1007,12 @@ export class Engine {
|
|
|
958
1007
|
this.cameraTargetOffset.z = offset?.z ?? 0
|
|
959
1008
|
}
|
|
960
1009
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1010
|
+
getCameraDistance(): number { return this.camera.radius }
|
|
1011
|
+
setCameraDistance(d: number): void { this.camera.radius = d }
|
|
1012
|
+
getCameraAlpha(): number { return this.camera.alpha }
|
|
1013
|
+
setCameraAlpha(a: number): void { this.camera.alpha = a }
|
|
1014
|
+
getCameraBeta(): number { return this.camera.beta }
|
|
1015
|
+
setCameraBeta(b: number): void { this.camera.beta = b }
|
|
967
1016
|
|
|
968
1017
|
// Step 5: Create lighting buffers
|
|
969
1018
|
private setupLighting() {
|
|
@@ -990,16 +1039,6 @@ export class Engine {
|
|
|
990
1039
|
this.updateLightBuffer()
|
|
991
1040
|
}
|
|
992
1041
|
|
|
993
|
-
public clearLights() {
|
|
994
|
-
this.lightCount = 0
|
|
995
|
-
// Clear all light data by setting intensity to 0
|
|
996
|
-
for (let i = 0; i < 4; i++) {
|
|
997
|
-
const baseIndex = 4 + i * 8
|
|
998
|
-
this.lightData[baseIndex + 7] = 0 // color.w / intensity
|
|
999
|
-
}
|
|
1000
|
-
this.updateLightBuffer()
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
1042
|
private addLight(direction: Vec3, color: Vec3, intensity: number = 1.0): boolean {
|
|
1004
1043
|
if (this.lightCount >= 4) return false
|
|
1005
1044
|
|
|
@@ -1019,7 +1058,7 @@ export class Engine {
|
|
|
1019
1058
|
return true
|
|
1020
1059
|
}
|
|
1021
1060
|
|
|
1022
|
-
|
|
1061
|
+
addGround(options?: {
|
|
1023
1062
|
width?: number
|
|
1024
1063
|
height?: number
|
|
1025
1064
|
diffuseColor?: Vec3
|
|
@@ -1027,19 +1066,29 @@ export class Engine {
|
|
|
1027
1066
|
fadeEnd?: number
|
|
1028
1067
|
shadowMapSize?: number
|
|
1029
1068
|
shadowStrength?: number
|
|
1069
|
+
gridSpacing?: number
|
|
1070
|
+
gridLineWidth?: number
|
|
1071
|
+
gridLineOpacity?: number
|
|
1072
|
+
gridLineColor?: Vec3
|
|
1073
|
+
noiseStrength?: number
|
|
1030
1074
|
}): void {
|
|
1031
1075
|
const opts = {
|
|
1032
|
-
width:
|
|
1033
|
-
height:
|
|
1034
|
-
diffuseColor: new Vec3(
|
|
1035
|
-
fadeStart:
|
|
1036
|
-
fadeEnd:
|
|
1076
|
+
width: 160,
|
|
1077
|
+
height: 160,
|
|
1078
|
+
diffuseColor: new Vec3(0.8, 0.1, 1.0),
|
|
1079
|
+
fadeStart: 10.0,
|
|
1080
|
+
fadeEnd: 80.0,
|
|
1037
1081
|
shadowMapSize: 4096,
|
|
1038
1082
|
shadowStrength: 1.0,
|
|
1083
|
+
gridSpacing: 4.2,
|
|
1084
|
+
gridLineWidth: 0.012,
|
|
1085
|
+
gridLineOpacity: 0.4,
|
|
1086
|
+
gridLineColor: new Vec3(0.85, 0.85, 0.85),
|
|
1087
|
+
noiseStrength: 0.05,
|
|
1039
1088
|
...options,
|
|
1040
1089
|
}
|
|
1041
1090
|
this.createGroundGeometry(opts.width, opts.height)
|
|
1042
|
-
this.createShadowGroundResources(opts
|
|
1091
|
+
this.createShadowGroundResources(opts)
|
|
1043
1092
|
this.hasGround = true
|
|
1044
1093
|
this.groundDrawCall = {
|
|
1045
1094
|
type: "ground",
|
|
@@ -1054,11 +1103,11 @@ export class Engine {
|
|
|
1054
1103
|
this.device.queue.writeBuffer(this.lightUniformBuffer, 0, this.lightData)
|
|
1055
1104
|
}
|
|
1056
1105
|
|
|
1057
|
-
|
|
1106
|
+
getStats(): EngineStats {
|
|
1058
1107
|
return { ...this.stats }
|
|
1059
1108
|
}
|
|
1060
1109
|
|
|
1061
|
-
|
|
1110
|
+
runRenderLoop(callback?: () => void) {
|
|
1062
1111
|
this.renderLoopCallback = callback || null
|
|
1063
1112
|
|
|
1064
1113
|
const loop = () => {
|
|
@@ -1074,7 +1123,7 @@ export class Engine {
|
|
|
1074
1123
|
this.animationFrameId = requestAnimationFrame(loop)
|
|
1075
1124
|
}
|
|
1076
1125
|
|
|
1077
|
-
|
|
1126
|
+
stopRenderLoop() {
|
|
1078
1127
|
if (this.animationFrameId !== null) {
|
|
1079
1128
|
cancelAnimationFrame(this.animationFrameId)
|
|
1080
1129
|
this.animationFrameId = null
|
|
@@ -1082,7 +1131,7 @@ export class Engine {
|
|
|
1082
1131
|
this.renderLoopCallback = null
|
|
1083
1132
|
}
|
|
1084
1133
|
|
|
1085
|
-
|
|
1134
|
+
dispose() {
|
|
1086
1135
|
this.stopRenderLoop()
|
|
1087
1136
|
this.forEachInstance((inst) => inst.model.stopAnimation())
|
|
1088
1137
|
if (Engine.instance === this) Engine.instance = null
|
|
@@ -1100,9 +1149,9 @@ export class Engine {
|
|
|
1100
1149
|
}
|
|
1101
1150
|
}
|
|
1102
1151
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1152
|
+
async loadModel(path: string): Promise<Model>
|
|
1153
|
+
async loadModel(name: string, path: string): Promise<Model>
|
|
1154
|
+
async loadModel(nameOrPath: string, path?: string): Promise<Model> {
|
|
1106
1155
|
const pmxPath = path === undefined ? nameOrPath : path
|
|
1107
1156
|
const name = path === undefined ? "model_" + (this._nextDefaultModelId++) : nameOrPath
|
|
1108
1157
|
const model = await PmxLoader.load(pmxPath)
|
|
@@ -1111,7 +1160,7 @@ export class Engine {
|
|
|
1111
1160
|
return model
|
|
1112
1161
|
}
|
|
1113
1162
|
|
|
1114
|
-
|
|
1163
|
+
async addModel(model: Model, pmxPath: string, name?: string): Promise<string> {
|
|
1115
1164
|
const requested = name ?? model.name
|
|
1116
1165
|
let key = requested
|
|
1117
1166
|
let n = 1
|
|
@@ -1125,19 +1174,19 @@ export class Engine {
|
|
|
1125
1174
|
return key
|
|
1126
1175
|
}
|
|
1127
1176
|
|
|
1128
|
-
|
|
1177
|
+
removeModel(name: string): void {
|
|
1129
1178
|
this.modelInstances.delete(name)
|
|
1130
1179
|
}
|
|
1131
1180
|
|
|
1132
|
-
|
|
1181
|
+
getModelNames(): string[] {
|
|
1133
1182
|
return Array.from(this.modelInstances.keys())
|
|
1134
1183
|
}
|
|
1135
1184
|
|
|
1136
|
-
|
|
1185
|
+
getModel(name: string): Model | null {
|
|
1137
1186
|
return this.modelInstances.get(name)?.model ?? null
|
|
1138
1187
|
}
|
|
1139
1188
|
|
|
1140
|
-
|
|
1189
|
+
markVertexBufferDirty(modelNameOrModel?: string | Model): void {
|
|
1141
1190
|
if (modelNameOrModel === undefined) return
|
|
1142
1191
|
if (typeof modelNameOrModel === "string") {
|
|
1143
1192
|
const inst = this.modelInstances.get(modelNameOrModel)
|
|
@@ -1152,38 +1201,38 @@ export class Engine {
|
|
|
1152
1201
|
}
|
|
1153
1202
|
}
|
|
1154
1203
|
|
|
1155
|
-
|
|
1204
|
+
setMaterialVisible(modelName: string, materialName: string, visible: boolean): void {
|
|
1156
1205
|
const inst = this.modelInstances.get(modelName)
|
|
1157
1206
|
if (!inst) return
|
|
1158
1207
|
if (visible) inst.hiddenMaterials.delete(materialName)
|
|
1159
1208
|
else inst.hiddenMaterials.add(materialName)
|
|
1160
1209
|
}
|
|
1161
1210
|
|
|
1162
|
-
|
|
1211
|
+
toggleMaterialVisible(modelName: string, materialName: string): void {
|
|
1163
1212
|
const inst = this.modelInstances.get(modelName)
|
|
1164
1213
|
if (!inst) return
|
|
1165
1214
|
if (inst.hiddenMaterials.has(materialName)) inst.hiddenMaterials.delete(materialName)
|
|
1166
1215
|
else inst.hiddenMaterials.add(materialName)
|
|
1167
1216
|
}
|
|
1168
1217
|
|
|
1169
|
-
|
|
1218
|
+
isMaterialVisible(modelName: string, materialName: string): boolean {
|
|
1170
1219
|
const inst = this.modelInstances.get(modelName)
|
|
1171
1220
|
return inst ? !inst.hiddenMaterials.has(materialName) : false
|
|
1172
1221
|
}
|
|
1173
1222
|
|
|
1174
|
-
|
|
1223
|
+
setIKEnabled(enabled: boolean): void {
|
|
1175
1224
|
this.ikEnabled = enabled
|
|
1176
1225
|
}
|
|
1177
1226
|
|
|
1178
|
-
|
|
1227
|
+
getIKEnabled(): boolean {
|
|
1179
1228
|
return this.ikEnabled
|
|
1180
1229
|
}
|
|
1181
1230
|
|
|
1182
|
-
|
|
1231
|
+
setPhysicsEnabled(enabled: boolean): void {
|
|
1183
1232
|
this.physicsEnabled = enabled
|
|
1184
1233
|
}
|
|
1185
1234
|
|
|
1186
|
-
|
|
1235
|
+
getPhysicsEnabled(): boolean {
|
|
1187
1236
|
return this.physicsEnabled
|
|
1188
1237
|
}
|
|
1189
1238
|
|
|
@@ -1391,13 +1440,19 @@ export class Engine {
|
|
|
1391
1440
|
this.device.queue.writeBuffer(this.groundIndexBuffer, 0, indices)
|
|
1392
1441
|
}
|
|
1393
1442
|
|
|
1394
|
-
private createShadowGroundResources(
|
|
1395
|
-
shadowMapSize: number
|
|
1396
|
-
diffuseColor: Vec3
|
|
1397
|
-
fadeStart: number
|
|
1398
|
-
fadeEnd: number
|
|
1443
|
+
private createShadowGroundResources(opts: {
|
|
1444
|
+
shadowMapSize: number
|
|
1445
|
+
diffuseColor: Vec3
|
|
1446
|
+
fadeStart: number
|
|
1447
|
+
fadeEnd: number
|
|
1399
1448
|
shadowStrength: number
|
|
1400
|
-
|
|
1449
|
+
gridSpacing: number
|
|
1450
|
+
gridLineWidth: number
|
|
1451
|
+
gridLineOpacity: number
|
|
1452
|
+
gridLineColor: Vec3
|
|
1453
|
+
noiseStrength: number
|
|
1454
|
+
}) {
|
|
1455
|
+
const { shadowMapSize, diffuseColor, fadeStart, fadeEnd, shadowStrength, gridSpacing, gridLineWidth, gridLineOpacity, gridLineColor, noiseStrength } = opts
|
|
1401
1456
|
this.shadowMapTexture = this.device.createTexture({
|
|
1402
1457
|
label: "shadow map",
|
|
1403
1458
|
size: [shadowMapSize, shadowMapSize],
|
|
@@ -1405,16 +1460,13 @@ export class Engine {
|
|
|
1405
1460
|
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
1406
1461
|
})
|
|
1407
1462
|
this.shadowMapDepthView = this.shadowMapTexture.createView()
|
|
1408
|
-
|
|
1409
|
-
gb
|
|
1410
|
-
gb[1] = diffuseColor.y
|
|
1411
|
-
gb[
|
|
1412
|
-
gb[
|
|
1413
|
-
gb[
|
|
1414
|
-
|
|
1415
|
-
gb[6] = 1 / shadowMapSize
|
|
1416
|
-
gb[7] = 0
|
|
1417
|
-
this.groundShadowMaterialBuffer = this.device.createBuffer({ size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST })
|
|
1463
|
+
// Layout: diffuseColor(3f) fadeStart(1f) | fadeEnd(1f) shadowStrength(1f) pcfTexel(1f) gridSpacing(1f) | gridLineWidth(1f) gridOpacity(1f) noiseStrength(1f) _pad(1f) | gridColor(3f) _pad2(1f)
|
|
1464
|
+
const gb = new Float32Array(16)
|
|
1465
|
+
gb[0] = diffuseColor.x; gb[1] = diffuseColor.y; gb[2] = diffuseColor.z; gb[3] = fadeStart
|
|
1466
|
+
gb[4] = fadeEnd; gb[5] = shadowStrength; gb[6] = 1 / shadowMapSize; gb[7] = gridSpacing
|
|
1467
|
+
gb[8] = gridLineWidth; gb[9] = gridLineOpacity; gb[10] = noiseStrength; gb[11] = 0
|
|
1468
|
+
gb[12] = gridLineColor.x; gb[13] = gridLineColor.y; gb[14] = gridLineColor.z; gb[15] = 0
|
|
1469
|
+
this.groundShadowMaterialBuffer = this.device.createBuffer({ size: gb.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST })
|
|
1418
1470
|
this.device.queue.writeBuffer(this.groundShadowMaterialBuffer, 0, gb)
|
|
1419
1471
|
this.groundShadowBindGroup = this.device.createBindGroup({
|
|
1420
1472
|
label: "ground shadow bind",
|
|
@@ -1741,7 +1793,7 @@ export class Engine {
|
|
|
1741
1793
|
this.onRaycast(hitModel, hitMaterial, screenX, screenY)
|
|
1742
1794
|
}
|
|
1743
1795
|
|
|
1744
|
-
|
|
1796
|
+
render() {
|
|
1745
1797
|
if (!this.multisampleTexture || !this.camera || !this.device) return
|
|
1746
1798
|
|
|
1747
1799
|
const currentTime = performance.now()
|
package/src/ik-solver.ts
CHANGED
|
@@ -21,13 +21,13 @@ const enum InternalSolveAxis {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
class IKChain {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
readonly boneIndex: number
|
|
25
|
+
readonly minimumAngle: Vec3 | null
|
|
26
|
+
readonly maximumAngle: Vec3 | null
|
|
27
|
+
readonly rotationOrder: InternalEulerRotationOrder
|
|
28
|
+
readonly solveAxis: InternalSolveAxis
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
constructor(boneIndex: number, link: IKLink) {
|
|
31
31
|
this.boneIndex = boneIndex
|
|
32
32
|
|
|
33
33
|
if (link.hasLimit && link.minAngle && link.maxAngle) {
|
|
@@ -76,7 +76,7 @@ export class IKSolverSystem {
|
|
|
76
76
|
private static readonly EPSILON = 1.0e-8
|
|
77
77
|
private static readonly THRESHOLD = (88 * Math.PI) / 180
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
static solve(
|
|
80
80
|
ikSolvers: IKSolver[],
|
|
81
81
|
bones: Bone[],
|
|
82
82
|
localRotations: Quat[],
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
export { Engine, type EngineStats } from "./engine"
|
|
2
2
|
export { Model } from "./model"
|
|
3
3
|
export { Vec3, Quat, Mat4 } from "./math"
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
export type {
|
|
5
|
+
AnimationClip,
|
|
6
|
+
AnimationPlayOptions,
|
|
7
|
+
AnimationProgress,
|
|
8
|
+
BoneKeyframe,
|
|
9
|
+
MorphKeyframe,
|
|
10
|
+
BoneInterpolation,
|
|
11
|
+
ControlPoint,
|
|
9
12
|
} from "./animation"
|
|
13
|
+
export { FPS } from "./animation"
|
|
10
14
|
export { Physics, type PhysicsOptions } from "./physics"
|