@babylonjs-toolkit/next 9.0.1 → 9.5.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 +0 -5
- package/lib/index.d.ts +0 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +0 -2
- package/lib/scenemanager.d.ts +102 -66
- package/lib/scenemanager.d.ts.map +1 -1
- package/lib/scenemanager.js +1159 -524
- package/package.json +11 -11
- package/lib/treebranchmaterial.d.ts +0 -31
- package/lib/treebranchmaterial.d.ts.map +0 -1
- package/lib/treebranchmaterial.js +0 -369
- package/lib/universalterrainmaterial.d.ts +0 -48
- package/lib/universalterrainmaterial.d.ts.map +0 -1
- package/lib/universalterrainmaterial.js +0 -639
package/lib/scenemanager.js
CHANGED
|
@@ -40,7 +40,7 @@ import { Skeleton } from '@babylonjs/core/Bones/skeleton';
|
|
|
40
40
|
import { Control } from '@babylonjs/gui/2D/controls/control';
|
|
41
41
|
import { TextBlock } from '@babylonjs/gui/2D/controls/textBlock';
|
|
42
42
|
import { Image } from '@babylonjs/gui/2D/controls/image';
|
|
43
|
-
import { PhysicsBody, PhysicsShape, PhysicsShapeCapsule, PhysicsRaycastResult, PhysicsMotionType, PhysicsShapeContainer, ShapeCastResult,
|
|
43
|
+
import { PhysicsBody, PhysicsShape, PhysicsShapeCapsule, PhysicsRaycastResult, PhysicsMotionType, PhysicsShapeContainer, ShapeCastResult, PhysicsShapeMesh, PhysicsShapeConvexHull, PhysicsShapeBox, PhysicsShapeSphere, PhysicsShapeCylinder, PhysicsMaterialCombineMode, PhysicsShapeType } from '@babylonjs/core/Physics';
|
|
44
44
|
import { HavokPlugin } from '@babylonjs/core/Physics/v2/Plugins/havokPlugin';
|
|
45
45
|
import { GLTFLoader, ArrayItem } from '@babylonjs/loaders/glTF/2.0/glTFLoader';
|
|
46
46
|
import { Texture } from '@babylonjs/core/Materials/Textures/texture';
|
|
@@ -75,13 +75,11 @@ import { GPUParticleSystem } from '@babylonjs/core/Particles/gpuParticleSystem';
|
|
|
75
75
|
import { AdvancedDynamicTexture } from '@babylonjs/gui/2D/advancedDynamicTexture';
|
|
76
76
|
import { InputText } from '@babylonjs/gui/2D/controls/inputText';
|
|
77
77
|
import { VideoTexture } from '@babylonjs/core/Materials/Textures/videoTexture';
|
|
78
|
-
import { UniversalTerrainMaterial, UniversalTerrainMaterialPlugin } from './universalterrainmaterial';
|
|
79
|
-
import { TreeBranchMaterial, TreeBranchMaterialPlugin } from './treebranchmaterial';
|
|
80
78
|
import { UnitySlider } from './unityslider';
|
|
81
79
|
import { UnityScrollBar } from './unityscrollbar';
|
|
82
80
|
import { UnityDropdownMenu } from './unitydropdownmenu';
|
|
83
81
|
export class SceneManager {
|
|
84
|
-
static get Version() { return "9.0
|
|
82
|
+
static get Version() { return "9.5.0 - R1"; }
|
|
85
83
|
static get Copyright() { return "All rights reserved (c) 2024 Mackey Kinard"; }
|
|
86
84
|
static GetEngine(scene) {
|
|
87
85
|
let result = null;
|
|
@@ -117,16 +115,17 @@ export class SceneManager {
|
|
|
117
115
|
}
|
|
118
116
|
static get PlaygroundCdn() { return "https://cdn.jsdelivr.net/gh/BabylonJS/BabylonToolkit@master/Runtime/"; }
|
|
119
117
|
static get PlaygroundRepo() { return "https://www.babylontoolkit.com/playground/"; }
|
|
120
|
-
static async InitializePlayground(engine, options = null) {
|
|
121
|
-
return SceneManager.InitializeRuntime(engine, options);
|
|
118
|
+
static async InitializePlayground(engine, options = null, scene = null, inputOptions = null) {
|
|
119
|
+
return SceneManager.InitializeRuntime(engine, options, scene, inputOptions);
|
|
122
120
|
}
|
|
123
|
-
static async InitializeRuntime(engine, options = null) {
|
|
121
|
+
static async InitializeRuntime(engine, options = null, scene = null, inputOptions = null) {
|
|
124
122
|
Tools.Log("Babylon.js Toolkit v" + SceneManager.Version);
|
|
125
123
|
if (AudioSource.IsLegacyEngine())
|
|
126
124
|
Tools.Log("Legacy audio engine detected");
|
|
127
125
|
const hardwareScalingLevel = (options != null && options.hardwareScalingLevel != null) ? options.hardwareScalingLevel : WindowManager.GetHardwareScalingLevel();
|
|
128
126
|
const initSceneFileLoaders = (options != null && options.initSceneFileLoaders != null) ? options.initSceneFileLoaders : true;
|
|
129
127
|
const loadAsyncRuntimeLibs = (options != null && options.loadAsyncRuntimeLibs != null) ? options.loadAsyncRuntimeLibs : true;
|
|
128
|
+
const enableUserInput = (options != null && options.enableUserInput != null) ? options.enableUserInput : false;
|
|
130
129
|
const defaultProjectScriptBundle = (SceneManager.PlaygroundCdn + "default.playground.js");
|
|
131
130
|
const loadProjectScriptBundle = (options != null && options.loadProjectScriptBundle != null) ? options.loadProjectScriptBundle : false;
|
|
132
131
|
const projectScriptBundleUrl = (options != null && options.projectScriptBundleUrl != null) ? options.projectScriptBundleUrl : defaultProjectScriptBundle;
|
|
@@ -137,6 +136,8 @@ export class SceneManager {
|
|
|
137
136
|
SceneManager.ShowLoadingScreen(engine, hideLoadingUIWithEngine, defaultLoadingUIMarginTop);
|
|
138
137
|
if (hardwareScalingLevel != null && hardwareScalingLevel > 0)
|
|
139
138
|
engine.setHardwareScalingLevel(hardwareScalingLevel);
|
|
139
|
+
if (enableUserInput === true)
|
|
140
|
+
InputController.EnableUserInput(engine, scene, inputOptions);
|
|
140
141
|
SceneManager.UniversalModuleDefinition = false;
|
|
141
142
|
try {
|
|
142
143
|
SceneManager.InitializeSceneLoaderPlugin();
|
|
@@ -369,38 +370,6 @@ export class SceneManager {
|
|
|
369
370
|
}
|
|
370
371
|
});
|
|
371
372
|
}
|
|
372
|
-
static NavigateTo(scene, route, options = null, useWindowLocation = false) {
|
|
373
|
-
if (useWindowLocation === true) {
|
|
374
|
-
if (options?.replace === true) {
|
|
375
|
-
window.location.replace(route);
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
window.location.assign(route);
|
|
379
|
-
}
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
if (scene.reactNavigationFunction != null) {
|
|
383
|
-
const navOptions = { ...options, state: { ...(options?.state || {}), fromApp: true } };
|
|
384
|
-
scene.reactNavigationFunction(route, navOptions);
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
console.warn("React navigation hook is not set on the scene.");
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
static SetReactNavigationHook(scene, navigateToFunction) {
|
|
391
|
-
scene.reactNavigationFunction = navigateToFunction;
|
|
392
|
-
}
|
|
393
|
-
static DeleteReactNavigationHook(scene) {
|
|
394
|
-
if (scene.reactNavigationFunction != null) {
|
|
395
|
-
scene.reactNavigationFunction = null;
|
|
396
|
-
try {
|
|
397
|
-
delete scene.reactNavigationFunction;
|
|
398
|
-
}
|
|
399
|
-
catch (e) {
|
|
400
|
-
console.warn(e);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
373
|
static EnableSceneParsing(enabled) {
|
|
405
374
|
SceneManager.SceneParsingEnabled = enabled;
|
|
406
375
|
}
|
|
@@ -3213,6 +3182,11 @@ export class ScriptComponent {
|
|
|
3213
3182
|
return result;
|
|
3214
3183
|
}
|
|
3215
3184
|
}
|
|
3185
|
+
export class GameModeController extends ScriptComponent {
|
|
3186
|
+
constructor(transform, scene, properties = {}, alias = null) {
|
|
3187
|
+
super(transform, scene, properties, alias);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3216
3190
|
export var System;
|
|
3217
3191
|
(function (System) {
|
|
3218
3192
|
System[System["Deg2Rad"] = (Math.PI * 2 / 360)] = "Deg2Rad";
|
|
@@ -9263,6 +9237,643 @@ Utilities.LoadingState = -1;
|
|
|
9263
9237
|
Utilities.OnPreloaderProgress = null;
|
|
9264
9238
|
Utilities.OnPreloaderComplete = null;
|
|
9265
9239
|
Utilities.LoaderItemsMarkedForDisposal = [];
|
|
9240
|
+
export class UniversalTerrainMaterial extends CustomShaderMaterial {
|
|
9241
|
+
constructor(name, scene) {
|
|
9242
|
+
super(name, scene);
|
|
9243
|
+
this.terrainInfo = null;
|
|
9244
|
+
this.shader = this.getShaderName();
|
|
9245
|
+
this.plugin = new UniversalTerrainMaterialPlugin(this, this.shader);
|
|
9246
|
+
}
|
|
9247
|
+
awake() {
|
|
9248
|
+
}
|
|
9249
|
+
update() {
|
|
9250
|
+
}
|
|
9251
|
+
getShaderName() {
|
|
9252
|
+
return "UniversalTerrainMaterial";
|
|
9253
|
+
}
|
|
9254
|
+
getTerrainInfo() {
|
|
9255
|
+
return this.terrainInfo;
|
|
9256
|
+
}
|
|
9257
|
+
}
|
|
9258
|
+
export class UniversalTerrainMaterialPlugin extends CustomShaderMaterialPlugin {
|
|
9259
|
+
constructor(customMaterial, shaderName) {
|
|
9260
|
+
super(customMaterial, shaderName, 100, { UNIVERSALTERRAINMATERIAL: false }, true, true, false);
|
|
9261
|
+
this.colorName = "surfaceAlbedo";
|
|
9262
|
+
this.splatmapSampler = "splatmapSampler";
|
|
9263
|
+
this.detailsSampler = "detailsSampler";
|
|
9264
|
+
this.normalsSampler = "normalsSampler";
|
|
9265
|
+
this.GLSL_CustomFragment = null;
|
|
9266
|
+
this.GLSL_CustomVertex = null;
|
|
9267
|
+
this.GLSL_VertexMainEnd = null;
|
|
9268
|
+
this.GLSL_FragmentUpdateColor = null;
|
|
9269
|
+
this.WGSL_CustomFragment = null;
|
|
9270
|
+
this.WGSL_CustomVertex = null;
|
|
9271
|
+
this.WGSL_VertexMainEnd = null;
|
|
9272
|
+
this.WGSL_FragmentUpdateColor = null;
|
|
9273
|
+
}
|
|
9274
|
+
isCompatible(shaderLanguage) {
|
|
9275
|
+
return (shaderLanguage === ShaderLanguage.WGSL || shaderLanguage === ShaderLanguage.GLSL);
|
|
9276
|
+
}
|
|
9277
|
+
getClassName() {
|
|
9278
|
+
return "UniversalTerrainMaterialPlugin";
|
|
9279
|
+
}
|
|
9280
|
+
getCustomCode(shaderType, shaderLanguage) {
|
|
9281
|
+
const terrainInfo = this.getCustomShaderMaterial().getTerrainInfo();
|
|
9282
|
+
if (shaderType === "vertex") {
|
|
9283
|
+
if (shaderLanguage === ShaderLanguage.WGSL) {
|
|
9284
|
+
if (this.WGSL_CustomVertex == null)
|
|
9285
|
+
this.WGSL_CustomVertex = this.WGSL_FormatTerrainVertexDefintions(terrainInfo);
|
|
9286
|
+
if (this.WGSL_VertexMainEnd == null)
|
|
9287
|
+
this.WGSL_VertexMainEnd = this.WGSL_FormatTerrainVertexMainEnd(terrainInfo);
|
|
9288
|
+
return {
|
|
9289
|
+
CUSTOM_VERTEX_DEFINITIONS: this.WGSL_CustomVertex,
|
|
9290
|
+
CUSTOM_VERTEX_MAIN_END: this.WGSL_VertexMainEnd,
|
|
9291
|
+
};
|
|
9292
|
+
}
|
|
9293
|
+
else if (shaderLanguage === ShaderLanguage.GLSL) {
|
|
9294
|
+
if (this.GLSL_CustomVertex == null)
|
|
9295
|
+
this.GLSL_CustomVertex = this.GLSL_FormatTerrainVertexDefintions(terrainInfo);
|
|
9296
|
+
if (this.GLSL_VertexMainEnd == null)
|
|
9297
|
+
this.GLSL_VertexMainEnd = this.GLSL_FormatTerrainVertexMainEnd(terrainInfo);
|
|
9298
|
+
return {
|
|
9299
|
+
CUSTOM_VERTEX_DEFINITIONS: this.GLSL_CustomVertex,
|
|
9300
|
+
CUSTOM_VERTEX_MAIN_END: this.GLSL_VertexMainEnd,
|
|
9301
|
+
};
|
|
9302
|
+
}
|
|
9303
|
+
}
|
|
9304
|
+
else if (shaderType === "fragment") {
|
|
9305
|
+
if (shaderLanguage === ShaderLanguage.WGSL) {
|
|
9306
|
+
if (this.WGSL_CustomFragment == null)
|
|
9307
|
+
this.WGSL_CustomFragment = this.WGSL_FormatTerrainFragmentDefintions(terrainInfo, this.splatmapSampler, this.detailsSampler, this.normalsSampler);
|
|
9308
|
+
if (this.WGSL_FragmentUpdateColor == null)
|
|
9309
|
+
this.WGSL_FragmentUpdateColor = this.WGSL_FormatTerrainFragmentUpdateColor(terrainInfo, this.colorName, this.splatmapSampler, this.detailsSampler, this.normalsSampler, SceneManager.TerrainColorCorrection);
|
|
9310
|
+
return {
|
|
9311
|
+
CUSTOM_FRAGMENT_DEFINITIONS: this.WGSL_CustomFragment,
|
|
9312
|
+
CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.WGSL_FragmentUpdateColor,
|
|
9313
|
+
};
|
|
9314
|
+
}
|
|
9315
|
+
else if (shaderLanguage === ShaderLanguage.GLSL) {
|
|
9316
|
+
if (this.GLSL_CustomFragment == null)
|
|
9317
|
+
this.GLSL_CustomFragment = this.GLSL_FormatTerrainFragmentDefintions(terrainInfo, this.splatmapSampler, this.detailsSampler, this.normalsSampler);
|
|
9318
|
+
if (this.GLSL_FragmentUpdateColor == null)
|
|
9319
|
+
this.GLSL_FragmentUpdateColor = this.GLSL_FormatTerrainFragmentUpdateColor(terrainInfo, this.colorName, this.splatmapSampler, this.detailsSampler, this.normalsSampler, SceneManager.TerrainColorCorrection);
|
|
9320
|
+
return {
|
|
9321
|
+
CUSTOM_FRAGMENT_DEFINITIONS: this.GLSL_CustomFragment,
|
|
9322
|
+
CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.GLSL_FragmentUpdateColor,
|
|
9323
|
+
};
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
return null;
|
|
9327
|
+
}
|
|
9328
|
+
getUniforms(shaderLanguage) {
|
|
9329
|
+
const wgsl = (shaderLanguage === ShaderLanguage.WGSL);
|
|
9330
|
+
this.vertexDefinitions = this.getCustomShaderMaterial().getCustomVertexCode(wgsl);
|
|
9331
|
+
this.fragmentDefinitions = (wgsl === true) ? this.getCustomShaderMaterial().getCustomFragmentCode(wgsl) : null;
|
|
9332
|
+
return this.getCustomShaderMaterial().getCustomUniforms(wgsl);
|
|
9333
|
+
}
|
|
9334
|
+
getSamplers(samplers) {
|
|
9335
|
+
const customSamplers = this.getCustomShaderMaterial().getCustomSamplers();
|
|
9336
|
+
if (customSamplers != null && customSamplers.length > 0)
|
|
9337
|
+
samplers.push(...customSamplers);
|
|
9338
|
+
}
|
|
9339
|
+
getAttributes(attributes, scene, mesh) {
|
|
9340
|
+
const customAttributes = this.getCustomShaderMaterial().getCustomAttributes();
|
|
9341
|
+
if (customAttributes != null && customAttributes.length > 0)
|
|
9342
|
+
attributes.push(...customAttributes);
|
|
9343
|
+
}
|
|
9344
|
+
prepareDefines(defines, scene, mesh) {
|
|
9345
|
+
if (!this.getIsEnabled())
|
|
9346
|
+
return;
|
|
9347
|
+
this.getCustomShaderMaterial().prepareCustomDefines(defines);
|
|
9348
|
+
}
|
|
9349
|
+
bindForSubMesh(uniformBuffer, scene, engine, subMesh) {
|
|
9350
|
+
if (!this.getIsEnabled())
|
|
9351
|
+
return;
|
|
9352
|
+
this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
|
|
9353
|
+
}
|
|
9354
|
+
WGSL_FormatTerrainVertexDefintions(terrainInfo) {
|
|
9355
|
+
let result = "";
|
|
9356
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9357
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
9358
|
+
+ "#define TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
9359
|
+
+ "varying vSplatmapUV: vec2<f32>;\r\n"
|
|
9360
|
+
+ "\r\n"
|
|
9361
|
+
+ "#endif\r\n\r\n");
|
|
9362
|
+
}
|
|
9363
|
+
return result;
|
|
9364
|
+
}
|
|
9365
|
+
WGSL_FormatTerrainVertexMainEnd(terrainInfo) {
|
|
9366
|
+
let result = "";
|
|
9367
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9368
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
9369
|
+
+ "#define TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
9370
|
+
+ "#ifdef UV1\r\n"
|
|
9371
|
+
+ "vertexOutputs.vSplatmapUV = uvUpdated;\r\n"
|
|
9372
|
+
+ "#endif\r\n"
|
|
9373
|
+
+ "\r\n"
|
|
9374
|
+
+ "#endif\r\n\r\n");
|
|
9375
|
+
}
|
|
9376
|
+
return result;
|
|
9377
|
+
}
|
|
9378
|
+
WGSL_FormatTerrainFragmentDefintions(terrainInfo, splatmapSampler, detailsSampler, normalsSampler) {
|
|
9379
|
+
let result = "";
|
|
9380
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9381
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
9382
|
+
+ "#define TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
9383
|
+
+ "varying vSplatmapUV: vec2<f32>;\r\n"
|
|
9384
|
+
+ "\r\n"
|
|
9385
|
+
+ "fn srgb_to_linear(c: vec3<f32>) -> vec3<f32>\r\n"
|
|
9386
|
+
+ "{\r\n"
|
|
9387
|
+
+ " return pow(c, vec3<f32>(2.2));\r\n"
|
|
9388
|
+
+ "}\r\n"
|
|
9389
|
+
+ "\r\n"
|
|
9390
|
+
+ "fn linear_to_srgb(c: vec3<f32>) -> vec3<f32>\r\n"
|
|
9391
|
+
+ "{\r\n"
|
|
9392
|
+
+ " return pow(c, vec3<f32>(1.0 / 2.2));\r\n"
|
|
9393
|
+
+ "}\r\n"
|
|
9394
|
+
+ "\r\n"
|
|
9395
|
+
+ "fn calculateMipmapLevel(uvs: vec2<f32>, size: vec2<f32>) -> f32\r\n"
|
|
9396
|
+
+ "{\r\n"
|
|
9397
|
+
+ " let dx: vec2<f32> = dpdx(uvs * size.x);\r\n"
|
|
9398
|
+
+ " let dy: vec2<f32> = dpdy(uvs * size.y);\r\n"
|
|
9399
|
+
+ " let d: f32 = max(dot(dx, dx), dot(dy, dy));\r\n"
|
|
9400
|
+
+ " return (0.4 * log2(d));\r\n"
|
|
9401
|
+
+ "}\r\n"
|
|
9402
|
+
+ "\r\n"
|
|
9403
|
+
+ "fn sampleTextureAtlas2D(atlas: texture_2d<f32>, sampler: sampler, gamma: f32, tile: vec2<f32>, rect: vec4<f32>, uvs: vec2<f32>, lod: f32) -> vec4<f32>\r\n"
|
|
9404
|
+
+ "{\r\n"
|
|
9405
|
+
+ " var level: f32 = lod;\r\n"
|
|
9406
|
+
+ " if (level < 0.0) {\r\n"
|
|
9407
|
+
+ " level = clamp(calculateMipmapLevel(uvs, vec2(tile.x, tile.x)), 0.0, tile.y);\r\n"
|
|
9408
|
+
+ " }\r\n"
|
|
9409
|
+
+ " let size: f32 = pow(2.0, tile.y - level);\r\n"
|
|
9410
|
+
+ " let sizex: f32 = size / rect.z;\r\n"
|
|
9411
|
+
+ " let sizey: f32 = size / rect.w;\r\n"
|
|
9412
|
+
+ " var uv: vec2<f32> = fract(uvs);\r\n"
|
|
9413
|
+
+ " uv.x = uv.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x;\r\n"
|
|
9414
|
+
+ " uv.y = uv.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y;\r\n"
|
|
9415
|
+
+ " var color: vec4<f32> = textureSampleLevel(atlas, sampler, uv, level);\r\n"
|
|
9416
|
+
+ " if (gamma != 1.0) {\r\n"
|
|
9417
|
+
+ " color.r = pow(color.r, gamma);\r\n"
|
|
9418
|
+
+ " color.g = pow(color.g, gamma);\r\n"
|
|
9419
|
+
+ " color.b = pow(color.b, gamma);\r\n"
|
|
9420
|
+
+ " }\r\n"
|
|
9421
|
+
+ " return color;\r\n"
|
|
9422
|
+
+ "}\r\n"
|
|
9423
|
+
+ "\r\n"
|
|
9424
|
+
+ "fn sampleSplatmapAtlas2D(atlas: texture_2d<f32>, sampler: sampler, tile: vec2<f32>, rect: vec4<f32>, uvs: vec2<f32>) -> vec4<f32>\r\n"
|
|
9425
|
+
+ "{\r\n"
|
|
9426
|
+
+ " let size: f32 = pow(2.0, tile.y);\r\n"
|
|
9427
|
+
+ " let sizex: f32 = size / rect.z;\r\n"
|
|
9428
|
+
+ " let sizey: f32 = size / rect.w;\r\n"
|
|
9429
|
+
+ " var uv: vec2<f32> = uvs;\r\n"
|
|
9430
|
+
+ " uv.x = uv.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x;\r\n"
|
|
9431
|
+
+ " uv.y = uv.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y;\r\n"
|
|
9432
|
+
+ " return textureSample(atlas, sampler, uv);\r\n"
|
|
9433
|
+
+ "}\r\n"
|
|
9434
|
+
+ "\r\n"
|
|
9435
|
+
+ "fn blendSplatmapAtlasColors(splatmap: vec4<f32>, color1: vec4<f32>, color2: vec4<f32>, color3: vec4<f32>, color4: vec4<f32>, mixbuffer: vec3<f32>) -> vec3<f32>\r\n"
|
|
9436
|
+
+ "{\r\n"
|
|
9437
|
+
+ " let buffer1: vec3<f32> = mix(mixbuffer, color1.rgb, splatmap.r);\r\n"
|
|
9438
|
+
+ " let buffer2: vec3<f32> = mix(buffer1, color2.rgb, splatmap.g);\r\n"
|
|
9439
|
+
+ " let buffer3: vec3<f32> = mix(buffer2, color3.rgb, splatmap.b);\r\n"
|
|
9440
|
+
+ " return mix(buffer3, color4.rgb, splatmap.a);\r\n"
|
|
9441
|
+
+ "}\r\n"
|
|
9442
|
+
+ "\r\n"
|
|
9443
|
+
+ "fn perturbNormalSamplerColor(cotangentFrame: mat3x3<f32>, samplerColor: vec3<f32>, scale: f32) -> vec3<f32>\r\n"
|
|
9444
|
+
+ "{\r\n"
|
|
9445
|
+
+ " var map: vec3<f32> = samplerColor.xyz;\r\n"
|
|
9446
|
+
+ " map = map * 2.00787402 - 1.00787402;\r\n"
|
|
9447
|
+
+ " #ifdef NORMALXYSCALE\r\n"
|
|
9448
|
+
+ " map = normalize(map * vec3<f32>(scale, scale, 1.0));\r\n"
|
|
9449
|
+
+ " #endif\r\n"
|
|
9450
|
+
+ " return normalize(cotangentFrame * map);\r\n"
|
|
9451
|
+
+ "}\r\n"
|
|
9452
|
+
+ "#endif\r\n\r\n");
|
|
9453
|
+
}
|
|
9454
|
+
return result;
|
|
9455
|
+
}
|
|
9456
|
+
WGSL_FormatTerrainFragmentUpdateColor(terrainInfo, colorName, splatmapSampler, detailsSampler, normalsSampler, colorCorrection = 1.0) {
|
|
9457
|
+
let result = "";
|
|
9458
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9459
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
9460
|
+
+ "#define TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
9461
|
+
+ "var normalsColor: vec3<f32> = vec3<f32>(0.5, 0.5, 1.0);\r\n"
|
|
9462
|
+
+ "var normalsBuffer: vec3<f32> = normalW.rgb;\r\n"
|
|
9463
|
+
+ "var splatmapBuffer: vec3<f32> = " + colorName + ".rgb;\r\n"
|
|
9464
|
+
+ "var autoMipMapLevel: f32 = -1.0;\r\n"
|
|
9465
|
+
+ "var normalCorrection: f32 = 1.0;\r\n"
|
|
9466
|
+
+ "var detailCorrection: f32 = " + colorCorrection.toFixed(4) + ";\r\n"
|
|
9467
|
+
+ "\r\n"
|
|
9468
|
+
+ "#if defined(ALBEDO) && defined(" + splatmapSampler.toUpperCase() + ") && defined(" + detailsSampler.toUpperCase() + ")\r\n"
|
|
9469
|
+
+ "\r\n"
|
|
9470
|
+
+ "// Reset Normal Values\r\n"
|
|
9471
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
9472
|
+
+ " uvOffset = vec2<f32>(0.0, 0.0);\r\n"
|
|
9473
|
+
+ " #ifdef NORMAL\r\n"
|
|
9474
|
+
+ " normalW = normalize(input.vNormalW);\r\n"
|
|
9475
|
+
+ " #else\r\n"
|
|
9476
|
+
+ " normalW = normalize(cross(dpdx(input.vPositionW), dpdy(input.vPositionW))) * scene.vEyePosition.w;\r\n"
|
|
9477
|
+
+ " #endif\r\n"
|
|
9478
|
+
+ " #ifdef CLEARCOAT\r\n"
|
|
9479
|
+
+ " clearCoatNormalW = normalW;\r\n"
|
|
9480
|
+
+ " #endif\r\n"
|
|
9481
|
+
+ " #if defined(BUMP) || defined(PARALLAX)\r\n"
|
|
9482
|
+
+ " #if defined(CLEARCOAT_BUMP) && defined(TANGENT) && defined(NORMAL)\r\n"
|
|
9483
|
+
+ " TBN = vTBN;\r\n"
|
|
9484
|
+
+ " #else\r\n"
|
|
9485
|
+
+ " TBN = cotangent_frame(normalW, input.vPositionW, fragmentInputs.vSplatmapUV, vec2<f32>(1.0, 1.0));\r\n"
|
|
9486
|
+
+ " #endif\r\n"
|
|
9487
|
+
+ " #elif defined(ANISOTROPIC)\r\n"
|
|
9488
|
+
+ " #if defined(CLEARCOAT_BUMP) && defined(TANGENT) && defined(NORMAL)\r\n"
|
|
9489
|
+
+ " TBN = vTBN;\r\n"
|
|
9490
|
+
+ " #else\r\n"
|
|
9491
|
+
+ " TBN = cotangent_frame(normalW, input.vPositionW, fragmentInputs.vSplatmapUV, vec2<f32>(1.0, 1.0));\r\n"
|
|
9492
|
+
+ " #endif\r\n"
|
|
9493
|
+
+ " #endif\r\n"
|
|
9494
|
+
+ " #ifdef PARALLAX\r\n"
|
|
9495
|
+
+ " invTBN = transposeMat3(TBN);\r\n"
|
|
9496
|
+
+ " #endif\r\n"
|
|
9497
|
+
+ " normalW = perturbNormalSamplerColor(TBN, normalsColor, 1.0);\r\n"
|
|
9498
|
+
+ "#endif\r\n"
|
|
9499
|
+
+ "\r\n"
|
|
9500
|
+
+ "// Global Atlas Values\r\n"
|
|
9501
|
+
+ "let splatTileSize: f32 = " + terrainInfo.splatmapAtlas[2].toFixed(4) + ";\r\n"
|
|
9502
|
+
+ "let splatTileBits: f32 = " + terrainInfo.splatmapAtlas[3].toFixed(4) + ";\r\n"
|
|
9503
|
+
+ "let detailTileSize: f32 = " + terrainInfo.textureAtlas[2].toFixed(4) + ";\r\n"
|
|
9504
|
+
+ "let detailTileBits: f32 = " + terrainInfo.textureAtlas[3].toFixed(4) + ";\r\n"
|
|
9505
|
+
+ "\r\n"
|
|
9506
|
+
+ "// Sample splatmap textures\r\n");
|
|
9507
|
+
if (terrainInfo.splatmapCount > 0) {
|
|
9508
|
+
let counter = 0;
|
|
9509
|
+
result += "normalsBuffer = vec3<f32>(0.0,0.0,0.0);\r\n";
|
|
9510
|
+
for (let index = 0; index < terrainInfo.splatmapCount; index++) {
|
|
9511
|
+
counter = (index * 4);
|
|
9512
|
+
const splatmapRect = terrainInfo["splatmapRect" + index];
|
|
9513
|
+
result += "var splatmapRect" + index + ": vec4<f32> = vec4<f32>(" + splatmapRect[0].toFixed(4) + ", " + splatmapRect[1].toFixed(4) + ", " + splatmapRect[2].toFixed(4) + ", " + splatmapRect[3].toFixed(4) + ");\r\n";
|
|
9514
|
+
result += "var splatmapAlbedo" + index + ": vec4<f32> = sampleSplatmapAtlas2D(" + splatmapSampler + ", " + splatmapSampler + "Sampler, vec2<f32>(splatTileSize, splatTileBits), splatmapRect" + index + ", (fragmentInputs.vSplatmapUV + uvOffset));\r\n";
|
|
9515
|
+
result += "var textureAlbedo" + (counter + 0) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9516
|
+
result += "var textureAlbedo" + (counter + 1) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9517
|
+
result += "var textureAlbedo" + (counter + 2) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9518
|
+
result += "var textureAlbedo" + (counter + 3) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9519
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
9520
|
+
const textureRect = terrainInfo["textureRect" + (counter + 0)];
|
|
9521
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 0)];
|
|
9522
|
+
result += "var textureRect" + (counter + 0) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9523
|
+
result += "var textureScale" + (counter + 0) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9524
|
+
result += "var textureOffset" + (counter + 0) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9525
|
+
result += "var textureTileUV" + (counter + 0) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 0) + ") * textureScale" + (counter + 0) + ");\r\n";
|
|
9526
|
+
result += "textureAlbedo" + (counter + 0) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
9527
|
+
}
|
|
9528
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
9529
|
+
const textureRect = terrainInfo["textureRect" + (counter + 1)];
|
|
9530
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 1)];
|
|
9531
|
+
result += "var textureRect" + (counter + 1) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9532
|
+
result += "var textureScale" + (counter + 1) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9533
|
+
result += "var textureOffset" + (counter + 1) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9534
|
+
result += "var textureTileUV" + (counter + 1) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 1) + ") * textureScale" + (counter + 1) + ");\r\n";
|
|
9535
|
+
result += "textureAlbedo" + (counter + 1) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
9536
|
+
}
|
|
9537
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
9538
|
+
const textureRect = terrainInfo["textureRect" + (counter + 2)];
|
|
9539
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 2)];
|
|
9540
|
+
result += "var textureRect" + (counter + 2) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9541
|
+
result += "var textureScale" + (counter + 2) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9542
|
+
result += "var textureOffset" + (counter + 2) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9543
|
+
result += "var textureTileUV" + (counter + 2) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 2) + ") * textureScale" + (counter + 2) + ");\r\n";
|
|
9544
|
+
result += "textureAlbedo" + (counter + 2) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
9545
|
+
}
|
|
9546
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
9547
|
+
const textureRect = terrainInfo["textureRect" + (counter + 3)];
|
|
9548
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 3)];
|
|
9549
|
+
result += "var textureRect" + (counter + 3) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9550
|
+
result += "var textureScale" + (counter + 3) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9551
|
+
result += "var textureOffset" + (counter + 3) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9552
|
+
result += "var textureTileUV" + (counter + 3) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 3) + ") * textureScale" + (counter + 3) + ");\r\n";
|
|
9553
|
+
result += "textureAlbedo" + (counter + 3) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
9554
|
+
}
|
|
9555
|
+
result += "splatmapBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", textureAlbedo" + (counter + 0) + ", textureAlbedo" + (counter + 1) + ", textureAlbedo" + (counter + 2) + ", textureAlbedo" + (counter + 3) + ", splatmapBuffer);\r\n";
|
|
9556
|
+
result += "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n";
|
|
9557
|
+
result += " #if defined(" + normalsSampler.toUpperCase() + ")\r\n";
|
|
9558
|
+
result += " var normalColor" + (counter + 0) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9559
|
+
result += " var normalColor" + (counter + 1) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9560
|
+
result += " var normalColor" + (counter + 2) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9561
|
+
result += " var normalColor" + (counter + 3) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9562
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
9563
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 0)];
|
|
9564
|
+
result += " var normalScale" + (counter + 0) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
9565
|
+
result += " normalColor" + (counter + 0) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
9566
|
+
result += " normalColor" + (counter + 0) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 0) + ".rgb, normalScale" + (counter + 0) + "), 1.0);\r\n";
|
|
9567
|
+
}
|
|
9568
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
9569
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 1)];
|
|
9570
|
+
result += " var normalScale" + (counter + 1) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
9571
|
+
result += " normalColor" + (counter + 1) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
9572
|
+
result += " normalColor" + (counter + 1) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 1) + ".rgb, normalScale" + (counter + 1) + "), 1.0);\r\n";
|
|
9573
|
+
}
|
|
9574
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
9575
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 2)];
|
|
9576
|
+
result += " var normalScale" + (counter + 2) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
9577
|
+
result += " normalColor" + (counter + 2) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
9578
|
+
result += " normalColor" + (counter + 2) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 2) + ".rgb, normalScale" + (counter + 2) + "), 1.0);\r\n";
|
|
9579
|
+
}
|
|
9580
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
9581
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 3)];
|
|
9582
|
+
result += " var normalScale" + (counter + 3) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
|
|
9583
|
+
result += " normalColor" + (counter + 3) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
9584
|
+
result += " normalColor" + (counter + 3) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 3) + ".rgb, normalScale" + (counter + 3) + "), 1.0);\r\n";
|
|
9585
|
+
}
|
|
9586
|
+
result += " normalsBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", normalColor" + (counter + 0) + ", normalColor" + (counter + 1) + ", normalColor" + (counter + 2) + ", normalColor" + (counter + 3) + ", normalsBuffer);\r\n";
|
|
9587
|
+
result += " #endif\r\n";
|
|
9588
|
+
result += "#endif\r\n";
|
|
9589
|
+
result += "\r\n";
|
|
9590
|
+
}
|
|
9591
|
+
}
|
|
9592
|
+
result += ("// Update Color Values\r\n"
|
|
9593
|
+
+ colorName + " = splatmapBuffer.rgb;\r\n"
|
|
9594
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
9595
|
+
+ " #if defined(" + normalsSampler.toUpperCase() + ")\r\n"
|
|
9596
|
+
+ " normalW = normalsBuffer.rgb;\r\n"
|
|
9597
|
+
+ " #endif\r\n"
|
|
9598
|
+
+ " #if defined(FORCENORMALFORWARD) && defined(NORMAL)\r\n"
|
|
9599
|
+
+ " var faceNormal: vec3<f32> = normalize(cross(dpdx(input.vPositionW), dpdy(input.vPositionW))) * scene.vEyePosition.w;\r\n"
|
|
9600
|
+
+ " #if defined(TWOSIDEDLIGHTING)\r\n"
|
|
9601
|
+
+ " faceNormal = select(-faceNormal, faceNormal, fragmentInputs.frontFacing)\r\n"
|
|
9602
|
+
+ " #endif\r\n"
|
|
9603
|
+
+ " normalW *= sign(dot(normalW, faceNormal));\r\n"
|
|
9604
|
+
+ " #endif\r\n"
|
|
9605
|
+
+ " #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)\r\n"
|
|
9606
|
+
+ " normalW = select(-normalW, normalW, fragmentInputs.frontFacing);\r\n"
|
|
9607
|
+
+ " #endif\r\n"
|
|
9608
|
+
+ "#endif\r\n"
|
|
9609
|
+
+ "\r\n"
|
|
9610
|
+
+ "#endif\r\n"
|
|
9611
|
+
+ "\r\n"
|
|
9612
|
+
+ "#endif\r\n\r\n");
|
|
9613
|
+
}
|
|
9614
|
+
return result;
|
|
9615
|
+
}
|
|
9616
|
+
GLSL_FormatTerrainVertexDefintions(terrainInfo) {
|
|
9617
|
+
let result = "";
|
|
9618
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9619
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
9620
|
+
+ "#define TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
|
|
9621
|
+
+ "varying vec2 vSplatmapUV;\r\n"
|
|
9622
|
+
+ "\r\n"
|
|
9623
|
+
+ "#endif\r\n\r\n");
|
|
9624
|
+
}
|
|
9625
|
+
return result;
|
|
9626
|
+
}
|
|
9627
|
+
GLSL_FormatTerrainVertexMainEnd(terrainInfo) {
|
|
9628
|
+
let result = "";
|
|
9629
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9630
|
+
result = ("\r\n#ifndef TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
9631
|
+
+ "#define TERRAIN_VERTEX_MAIN_END\r\n\r\n"
|
|
9632
|
+
+ "#ifdef UV1\r\n"
|
|
9633
|
+
+ "vSplatmapUV = uv;\r\n"
|
|
9634
|
+
+ "#endif\r\n"
|
|
9635
|
+
+ "\r\n"
|
|
9636
|
+
+ "#endif\r\n\r\n");
|
|
9637
|
+
}
|
|
9638
|
+
return result;
|
|
9639
|
+
}
|
|
9640
|
+
GLSL_FormatTerrainFragmentDefintions(terrainInfo, splatmapSampler, detailsSampler, normalsSampler) {
|
|
9641
|
+
let result = "";
|
|
9642
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9643
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
9644
|
+
+ "#define TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
|
|
9645
|
+
+ "varying vec2 vSplatmapUV;\r\n"
|
|
9646
|
+
+ "\r\n"
|
|
9647
|
+
+ "vec3 srgb_to_linear(const in vec3 c)\r\n"
|
|
9648
|
+
+ "{\r\n"
|
|
9649
|
+
+ " return pow(c, vec3(2.2));\r\n"
|
|
9650
|
+
+ "}\r\n"
|
|
9651
|
+
+ "\r\n"
|
|
9652
|
+
+ "vec3 linear_to_srgb(const in vec3 c)\r\n"
|
|
9653
|
+
+ "{\r\n"
|
|
9654
|
+
+ " return pow(c, vec3(1.0 / 2.2));\r\n"
|
|
9655
|
+
+ "}\r\n"
|
|
9656
|
+
+ "\r\n"
|
|
9657
|
+
+ "float calculateMipmapLevel(const in vec2 uvs, const in vec2 size)\r\n"
|
|
9658
|
+
+ "{\r\n"
|
|
9659
|
+
+ " vec2 dx = dFdx(uvs * size.x);\r\n"
|
|
9660
|
+
+ " vec2 dy = dFdy(uvs * size.y);\r\n"
|
|
9661
|
+
+ " float d = max(dot(dx, dx), dot(dy, dy));\r\n"
|
|
9662
|
+
+ " return 0.4 * log2(d);\r\n"
|
|
9663
|
+
+ "}\r\n"
|
|
9664
|
+
+ "\r\n"
|
|
9665
|
+
+ "vec4 sampleTextureAtlas2D(const in sampler2D atlas, const in float gamma, const in vec2 tile, const in vec4 rect, in vec2 uvs, in float lod)\r\n"
|
|
9666
|
+
+ "{\r\n"
|
|
9667
|
+
+ " if (lod < 0.0) lod = clamp(calculateMipmapLevel(uvs, vec2(tile.x, tile.x)), 0.0, tile.y); // Tile Info (tile.xy)\r\n"
|
|
9668
|
+
+ " float size = pow(2.0, tile.y - lod); // Tile Bits (tile.y)\r\n"
|
|
9669
|
+
+ " float sizex = size / rect.z; // Tile Width (rect.z)\r\n"
|
|
9670
|
+
+ " float sizey = size / rect.w; // Tile Height (rect.w)\r\n"
|
|
9671
|
+
+ " uvs = fract(uvs); // Perfrom Tiling (fract)\r\n"
|
|
9672
|
+
+ " uvs.x = uvs.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x; // Tile Position X (rect.x)\r\n"
|
|
9673
|
+
+ " uvs.y = uvs.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y; // Tile Position Y (rect.y)\r\n"
|
|
9674
|
+
+ " vec4 color = texture2DLodEXT(atlas, uvs, lod);\r\n"
|
|
9675
|
+
+ " if (gamma != 1.0) {\r\n"
|
|
9676
|
+
+ " color.r = pow(color.r, gamma);\r\n"
|
|
9677
|
+
+ " color.g = pow(color.g, gamma);\r\n"
|
|
9678
|
+
+ " color.b = pow(color.b, gamma);\r\n"
|
|
9679
|
+
+ " }\r\n"
|
|
9680
|
+
+ " return color;\r\n"
|
|
9681
|
+
+ "}\r\n"
|
|
9682
|
+
+ "\r\n"
|
|
9683
|
+
+ "vec4 sampleSplatmapAtlas2D(const in sampler2D atlas, const in vec2 tile, const in vec4 rect, in vec2 uvs)\r\n"
|
|
9684
|
+
+ "{\r\n"
|
|
9685
|
+
+ " float size = pow(2.0, tile.y); // Tile Bits (tile.y)\r\n"
|
|
9686
|
+
+ " float sizex = size / rect.z; // Tile Width (rect.z)\r\n"
|
|
9687
|
+
+ " float sizey = size / rect.w; // Tile Height (rect.w)\r\n"
|
|
9688
|
+
+ " uvs.x = uvs.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x; // Tile Position X (rect.x)\r\n"
|
|
9689
|
+
+ " uvs.y = uvs.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y; // Tile Position Y (rect.y)\r\n"
|
|
9690
|
+
+ " return texture2D(atlas, uvs);\r\n"
|
|
9691
|
+
+ "}\r\n"
|
|
9692
|
+
+ "\r\n"
|
|
9693
|
+
+ "vec3 blendSplatmapAtlasColors(const in vec4 splatmap, in vec4 color1, in vec4 color2, in vec4 color3, in vec4 color4, in vec3 mixbuffer)\r\n"
|
|
9694
|
+
+ "{\r\n"
|
|
9695
|
+
+ " vec3 buffer1 = mix(mixbuffer, color1.rgb, splatmap.r);\r\n"
|
|
9696
|
+
+ " vec3 buffer2 = mix(buffer1, color2.rgb, splatmap.g);\r\n"
|
|
9697
|
+
+ " vec3 buffer3 = mix(buffer2, color3.rgb, splatmap.b);\r\n"
|
|
9698
|
+
+ " return mix(buffer3, color4.rgb, splatmap.a);\r\n"
|
|
9699
|
+
+ "}\r\n"
|
|
9700
|
+
+ "\r\n"
|
|
9701
|
+
+ "vec3 perturbNormalSamplerColor(mat3 cotangentFrame, vec3 samplerColor, float scale)\r\n"
|
|
9702
|
+
+ "{\r\n"
|
|
9703
|
+
+ " vec3 map = samplerColor.xyz;\r\n"
|
|
9704
|
+
+ " map = map * 2.00787402 - 1.00787402;\r\n"
|
|
9705
|
+
+ " #ifdef NORMALXYSCALE\r\n"
|
|
9706
|
+
+ " map = normalize(map * vec3(scale, scale, 1.0));\r\n"
|
|
9707
|
+
+ " #endif\r\n"
|
|
9708
|
+
+ " return normalize(cotangentFrame * map);\r\n"
|
|
9709
|
+
+ "}\r\n"
|
|
9710
|
+
+ "\r\n"
|
|
9711
|
+
+ "\r\n"
|
|
9712
|
+
+ "#endif\r\n\r\n");
|
|
9713
|
+
}
|
|
9714
|
+
return result;
|
|
9715
|
+
}
|
|
9716
|
+
GLSL_FormatTerrainFragmentUpdateColor(terrainInfo, colorName, splatmapSampler, detailsSampler, normalsSampler, colorCorrection = 1.0) {
|
|
9717
|
+
let result = "";
|
|
9718
|
+
if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
|
|
9719
|
+
result = ("\r\n#ifndef TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
9720
|
+
+ "#define TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
|
|
9721
|
+
+ "vec3 normalsColor = vec3(0.5, 0.5, 1.0);\r\n"
|
|
9722
|
+
+ "vec3 normalsBuffer = normalW.rgb;\r\n"
|
|
9723
|
+
+ "vec3 splatmapBuffer = " + colorName + ".rgb;\r\n"
|
|
9724
|
+
+ "float autoMipMapLevel = -1.0;\r\n"
|
|
9725
|
+
+ "float normalCorrection = 1.0;\r\n"
|
|
9726
|
+
+ "float detailCorrection = " + colorCorrection.toFixed(4) + ";\r\n"
|
|
9727
|
+
+ "\r\n"
|
|
9728
|
+
+ "#if defined(ALBEDO) && defined(" + splatmapSampler.toUpperCase() + ") && defined(" + detailsSampler.toUpperCase() + ")\r\n"
|
|
9729
|
+
+ "\r\n"
|
|
9730
|
+
+ "// Reset Normal Values\r\n"
|
|
9731
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
9732
|
+
+ " uvOffset = vec2(0.0, 0.0);\r\n"
|
|
9733
|
+
+ " #ifdef NORMAL\r\n"
|
|
9734
|
+
+ " normalW = normalize(vNormalW);\r\n"
|
|
9735
|
+
+ " #else\r\n"
|
|
9736
|
+
+ " normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;\r\n"
|
|
9737
|
+
+ " #endif\r\n"
|
|
9738
|
+
+ " #ifdef CLEARCOAT\r\n"
|
|
9739
|
+
+ " clearCoatNormalW = normalW;\r\n"
|
|
9740
|
+
+ " #endif\r\n"
|
|
9741
|
+
+ " #if defined(BUMP) || defined(PARALLAX)\r\n"
|
|
9742
|
+
+ " #if defined(TANGENT) && defined(NORMAL)\r\n"
|
|
9743
|
+
+ " TBN = vTBN;\r\n"
|
|
9744
|
+
+ " #else\r\n"
|
|
9745
|
+
+ " TBN = cotangent_frame(normalW, vPositionW, vSplatmapUV);\r\n"
|
|
9746
|
+
+ " #endif\r\n"
|
|
9747
|
+
+ " #elif defined(ANISOTROPIC)\r\n"
|
|
9748
|
+
+ " #if defined(TANGENT) && defined(NORMAL)\r\n"
|
|
9749
|
+
+ " TBN = vTBN;\r\n"
|
|
9750
|
+
+ " #else\r\n"
|
|
9751
|
+
+ " TBN = cotangent_frame(normalW, vPositionW, vSplatmapUV, vec2(1.0, 1.0));\r\n"
|
|
9752
|
+
+ " #endif\r\n"
|
|
9753
|
+
+ " #endif\r\n"
|
|
9754
|
+
+ " #ifdef PARALLAX\r\n"
|
|
9755
|
+
+ " invTBN = transposeMat3(TBN);\r\n"
|
|
9756
|
+
+ " #endif\r\n"
|
|
9757
|
+
+ " normalW = perturbNormalSamplerColor(TBN, normalsColor, 1.0);\r\n"
|
|
9758
|
+
+ "#endif\r\n"
|
|
9759
|
+
+ "\r\n"
|
|
9760
|
+
+ "// Global Atlas Values\r\n"
|
|
9761
|
+
+ "float splatTileSize = " + terrainInfo.splatmapAtlas[2].toFixed(4) + ";\r\n"
|
|
9762
|
+
+ "float splatTileBits = " + terrainInfo.splatmapAtlas[3].toFixed(4) + ";\r\n"
|
|
9763
|
+
+ "float detailTileSize = " + terrainInfo.textureAtlas[2].toFixed(4) + ";\r\n"
|
|
9764
|
+
+ "float detailTileBits = " + terrainInfo.textureAtlas[3].toFixed(4) + ";\r\n"
|
|
9765
|
+
+ "\r\n"
|
|
9766
|
+
+ "// Sample splatmap textures\r\n");
|
|
9767
|
+
if (terrainInfo.splatmapCount > 0) {
|
|
9768
|
+
let counter = 0;
|
|
9769
|
+
result += "normalsBuffer = vec3(0.0,0.0,0.0);\r\n";
|
|
9770
|
+
for (let index = 0; index < terrainInfo.splatmapCount; index++) {
|
|
9771
|
+
counter = (index * 4);
|
|
9772
|
+
const splatmapRect = terrainInfo["splatmapRect" + index];
|
|
9773
|
+
result += "vec4 splatmapRect" + index + " = vec4(" + splatmapRect[0].toFixed(4) + ", " + splatmapRect[1].toFixed(4) + ", " + splatmapRect[2].toFixed(4) + ", " + splatmapRect[3].toFixed(4) + ");\r\n";
|
|
9774
|
+
result += "vec4 splatmapAlbedo" + index + " = sampleSplatmapAtlas2D(" + splatmapSampler + ", vec2(splatTileSize, splatTileBits), splatmapRect" + index + ", (vSplatmapUV + uvOffset));\r\n";
|
|
9775
|
+
result += "vec4 textureAlbedo" + (counter + 0) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9776
|
+
result += "vec4 textureAlbedo" + (counter + 1) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9777
|
+
result += "vec4 textureAlbedo" + (counter + 2) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9778
|
+
result += "vec4 textureAlbedo" + (counter + 3) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9779
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
9780
|
+
const textureRect = terrainInfo["textureRect" + (counter + 0)];
|
|
9781
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 0)];
|
|
9782
|
+
result += "vec4 textureRect" + (counter + 0) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9783
|
+
result += "vec2 textureScale" + (counter + 0) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9784
|
+
result += "vec2 textureOffset" + (counter + 0) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9785
|
+
result += "vec2 textureTileUV" + (counter + 0) + " = ((vSplatmapUV + textureOffset" + (counter + 0) + ") * textureScale" + (counter + 0) + ");\r\n";
|
|
9786
|
+
result += "textureAlbedo" + (counter + 0) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
9787
|
+
}
|
|
9788
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
9789
|
+
const textureRect = terrainInfo["textureRect" + (counter + 1)];
|
|
9790
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 1)];
|
|
9791
|
+
result += "vec4 textureRect" + (counter + 1) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9792
|
+
result += "vec2 textureScale" + (counter + 1) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9793
|
+
result += "vec2 textureOffset" + (counter + 1) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9794
|
+
result += "vec2 textureTileUV" + (counter + 1) + " = ((vSplatmapUV + textureOffset" + (counter + 1) + ") * textureScale" + (counter + 1) + ");\r\n";
|
|
9795
|
+
result += "textureAlbedo" + (counter + 1) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
9796
|
+
}
|
|
9797
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
9798
|
+
const textureRect = terrainInfo["textureRect" + (counter + 2)];
|
|
9799
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 2)];
|
|
9800
|
+
result += "vec4 textureRect" + (counter + 2) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9801
|
+
result += "vec2 textureScale" + (counter + 2) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9802
|
+
result += "vec2 textureOffset" + (counter + 2) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9803
|
+
result += "vec2 textureTileUV" + (counter + 2) + " = ((vSplatmapUV + textureOffset" + (counter + 2) + ") * textureScale" + (counter + 2) + ");\r\n";
|
|
9804
|
+
result += "textureAlbedo" + (counter + 2) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
9805
|
+
}
|
|
9806
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
9807
|
+
const textureRect = terrainInfo["textureRect" + (counter + 3)];
|
|
9808
|
+
const textureInfo = terrainInfo["textureInfo" + (counter + 3)];
|
|
9809
|
+
result += "vec4 textureRect" + (counter + 3) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
|
|
9810
|
+
result += "vec2 textureScale" + (counter + 3) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
|
|
9811
|
+
result += "vec2 textureOffset" + (counter + 3) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
|
|
9812
|
+
result += "vec2 textureTileUV" + (counter + 3) + " = ((vSplatmapUV + textureOffset" + (counter + 3) + ") * textureScale" + (counter + 3) + ");\r\n";
|
|
9813
|
+
result += "textureAlbedo" + (counter + 3) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
9814
|
+
}
|
|
9815
|
+
result += "splatmapBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", textureAlbedo" + (counter + 0) + ", textureAlbedo" + (counter + 1) + ", textureAlbedo" + (counter + 2) + ", textureAlbedo" + (counter + 3) + ", splatmapBuffer);\r\n";
|
|
9816
|
+
result += "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n";
|
|
9817
|
+
result += " #if defined(" + normalsSampler.toUpperCase() + ")\r\n";
|
|
9818
|
+
result += " vec4 normalColor" + (counter + 0) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9819
|
+
result += " vec4 normalColor" + (counter + 1) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9820
|
+
result += " vec4 normalColor" + (counter + 2) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9821
|
+
result += " vec4 normalColor" + (counter + 3) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
|
|
9822
|
+
if (terrainInfo["textureRect" + (counter + 0)]) {
|
|
9823
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 0)];
|
|
9824
|
+
result += " float normalScale" + (counter + 0) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
9825
|
+
result += " normalColor" + (counter + 0) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
|
|
9826
|
+
result += " normalColor" + (counter + 0) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 0) + ".rgb, normalScale" + (counter + 0) + ");\r\n";
|
|
9827
|
+
}
|
|
9828
|
+
if (terrainInfo["textureRect" + (counter + 1)]) {
|
|
9829
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 1)];
|
|
9830
|
+
result += " float normalScale" + (counter + 1) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
9831
|
+
result += " normalColor" + (counter + 1) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
|
|
9832
|
+
result += " normalColor" + (counter + 1) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 1) + ".rgb, normalScale" + (counter + 1) + ");\r\n";
|
|
9833
|
+
}
|
|
9834
|
+
if (terrainInfo["textureRect" + (counter + 2)]) {
|
|
9835
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 2)];
|
|
9836
|
+
result += " float normalScale" + (counter + 2) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
9837
|
+
result += " normalColor" + (counter + 2) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
|
|
9838
|
+
result += " normalColor" + (counter + 2) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 2) + ".rgb, normalScale" + (counter + 2) + ");\r\n";
|
|
9839
|
+
}
|
|
9840
|
+
if (terrainInfo["textureRect" + (counter + 3)]) {
|
|
9841
|
+
const normalScale = terrainInfo["normalsScale" + (counter + 3)];
|
|
9842
|
+
result += " float normalScale" + (counter + 3) + " = " + normalScale.toFixed(4) + ";\r\n";
|
|
9843
|
+
result += " normalColor" + (counter + 3) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
|
|
9844
|
+
result += " normalColor" + (counter + 3) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 3) + ".rgb, normalScale" + (counter + 3) + ");\r\n";
|
|
9845
|
+
}
|
|
9846
|
+
result += " normalsBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", normalColor" + (counter + 0) + ", normalColor" + (counter + 1) + ", normalColor" + (counter + 2) + ", normalColor" + (counter + 3) + ", normalsBuffer);\r\n";
|
|
9847
|
+
result += " #endif\r\n";
|
|
9848
|
+
result += "#endif\r\n";
|
|
9849
|
+
result += "\r\n";
|
|
9850
|
+
}
|
|
9851
|
+
}
|
|
9852
|
+
result += ("// Update Color Values\r\n"
|
|
9853
|
+
+ colorName + " = splatmapBuffer.rgb;\r\n"
|
|
9854
|
+
+ "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
|
|
9855
|
+
+ " #if defined(" + normalsSampler.toUpperCase() + ")\r\n"
|
|
9856
|
+
+ " normalW = normalsBuffer.rgb;\r\n"
|
|
9857
|
+
+ " #endif\r\n"
|
|
9858
|
+
+ " #if defined(FORCENORMALFORWARD) && defined(NORMAL)\r\n"
|
|
9859
|
+
+ " vec3 faceNormal = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;\r\n"
|
|
9860
|
+
+ " #if defined(TWOSIDEDLIGHTING)\r\n"
|
|
9861
|
+
+ " faceNormal = gl_FrontFacing ? faceNormal : -faceNormal;\r\n"
|
|
9862
|
+
+ " #endif\r\n"
|
|
9863
|
+
+ " normalW *= sign(dot(normalW, faceNormal));\r\n"
|
|
9864
|
+
+ " #endif\r\n"
|
|
9865
|
+
+ " #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)\r\n"
|
|
9866
|
+
+ " normalW = gl_FrontFacing ? normalW : -normalW;\r\n"
|
|
9867
|
+
+ " #endif\r\n"
|
|
9868
|
+
+ "#endif\r\n"
|
|
9869
|
+
+ "\r\n"
|
|
9870
|
+
+ "#endif\r\n"
|
|
9871
|
+
+ "\r\n"
|
|
9872
|
+
+ "#endif\r\n\r\n");
|
|
9873
|
+
}
|
|
9874
|
+
return result;
|
|
9875
|
+
}
|
|
9876
|
+
}
|
|
9266
9877
|
export class GrassStandardMaterial extends StandardShaderMaterial {
|
|
9267
9878
|
constructor(name, scene) {
|
|
9268
9879
|
super(name, scene);
|
|
@@ -10365,6 +10976,372 @@ export class GrassBillboardMaterialPlugin extends StandardShaderMaterialPlugin {
|
|
|
10365
10976
|
this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
|
|
10366
10977
|
}
|
|
10367
10978
|
}
|
|
10979
|
+
export class TreeBranchMaterial extends CustomShaderMaterial {
|
|
10980
|
+
constructor(name, scene) {
|
|
10981
|
+
super(name, scene);
|
|
10982
|
+
this._windTimeAccum = 0.0;
|
|
10983
|
+
this.shader = this.getShaderName();
|
|
10984
|
+
this.plugin = new TreeBranchMaterialPlugin(this, this.shader);
|
|
10985
|
+
this.addFloatUniform("g_windTime", 0.0);
|
|
10986
|
+
this.addFloatUniform("g_windAmount", 0.5);
|
|
10987
|
+
this.addFloatUniform("g_windSpeed", 1.0);
|
|
10988
|
+
this.addFloatUniform("g_windStrength", 0.5);
|
|
10989
|
+
this.addVector4Uniform("g_windDirection", new Vector4(1.0, 0.0, 0.0, 0.0));
|
|
10990
|
+
this.addFloatUniform("g_branchRootY", 0.0);
|
|
10991
|
+
this.addFloatUniform("g_branchMaxAngle", 0.35);
|
|
10992
|
+
this.addFloatUniform("g_branchMaxHeight", 2.0);
|
|
10993
|
+
this.addFloatUniform("g_useVertexColorMask", 1.0);
|
|
10994
|
+
this.addFloatUniform("g_spatialFrequency", 0.15);
|
|
10995
|
+
this._windTimeAccum = 0.0;
|
|
10996
|
+
}
|
|
10997
|
+
awake() {
|
|
10998
|
+
}
|
|
10999
|
+
update() {
|
|
11000
|
+
try {
|
|
11001
|
+
const rawDt = SceneManager.GetDeltaSeconds(this.getScene());
|
|
11002
|
+
const dt = Math.min(rawDt, 1 / 30);
|
|
11003
|
+
const speed = this.getFloatValue("g_windSpeed") ?? 0;
|
|
11004
|
+
if (speed > 0) {
|
|
11005
|
+
this._windTimeAccum += dt;
|
|
11006
|
+
this.setFloatValue("g_windTime", this._windTimeAccum);
|
|
11007
|
+
}
|
|
11008
|
+
}
|
|
11009
|
+
catch (e) {
|
|
11010
|
+
}
|
|
11011
|
+
}
|
|
11012
|
+
getShaderName() {
|
|
11013
|
+
return "TreeBranchMaterial";
|
|
11014
|
+
}
|
|
11015
|
+
setWindDirection(x, y, z) {
|
|
11016
|
+
const v = new Vector3(x, y, z);
|
|
11017
|
+
if (v.lengthSquared() < 1e-8) {
|
|
11018
|
+
this.setVector4Value("g_windDirection", new Vector4(1, 0, 0, 0));
|
|
11019
|
+
return;
|
|
11020
|
+
}
|
|
11021
|
+
v.normalize();
|
|
11022
|
+
this.setVector4Value("g_windDirection", new Vector4(v.x, v.y, v.z, 0.0));
|
|
11023
|
+
}
|
|
11024
|
+
getWindDirection() {
|
|
11025
|
+
return this.getVector4Value("g_windDirection");
|
|
11026
|
+
}
|
|
11027
|
+
}
|
|
11028
|
+
export class TreeBranchMaterialPlugin extends CustomShaderMaterialPlugin {
|
|
11029
|
+
constructor(customMaterial, shaderName) {
|
|
11030
|
+
super(customMaterial, shaderName, 110, { TREEBRANCHMATERIAL: false });
|
|
11031
|
+
}
|
|
11032
|
+
isCompatible(shaderLanguage) {
|
|
11033
|
+
return (shaderLanguage === ShaderLanguage.WGSL || shaderLanguage === ShaderLanguage.GLSL);
|
|
11034
|
+
}
|
|
11035
|
+
getCustomCode(shaderType, shaderLanguage) {
|
|
11036
|
+
if (shaderType === "vertex") {
|
|
11037
|
+
if (shaderLanguage === ShaderLanguage.WGSL) {
|
|
11038
|
+
return {
|
|
11039
|
+
CUSTOM_VERTEX_MAIN_END: `
|
|
11040
|
+
// -------------------------
|
|
11041
|
+
// TreeBranch wind bending (local-space)
|
|
11042
|
+
// Authoring (recommended):
|
|
11043
|
+
// vertexColor.r = bend weight (0 trunk .. 1 tips)
|
|
11044
|
+
// vertexColor.g = phase offset noise (0..1)
|
|
11045
|
+
// Falls back to height-based weight if vertex color missing/disabled.
|
|
11046
|
+
// -------------------------
|
|
11047
|
+
|
|
11048
|
+
fn safeNormalize(v: vec3f) -> vec3f {
|
|
11049
|
+
let lsq = dot(v, v);
|
|
11050
|
+
if (lsq < 1e-8) { return vec3f(1.0, 0.0, 0.0); }
|
|
11051
|
+
return v * inverseSqrt(lsq);
|
|
11052
|
+
}
|
|
11053
|
+
|
|
11054
|
+
fn rotateRodrigues(p: vec3f, axis: vec3f, angle: f32) -> vec3f {
|
|
11055
|
+
let a = safeNormalize(axis);
|
|
11056
|
+
let c = cos(angle);
|
|
11057
|
+
let s = sin(angle);
|
|
11058
|
+
return p * c + cross(a, p) * s + a * dot(a, p) * (1.0 - c);
|
|
11059
|
+
}
|
|
11060
|
+
|
|
11061
|
+
// Local position & normal (preferred: use normalUpdated if present in shader)
|
|
11062
|
+
var pL: vec3f = positionUpdated;
|
|
11063
|
+
|
|
11064
|
+
// If COLOR attribute exists, Babylon usually provides vertexInputs.color.
|
|
11065
|
+
// If not available, these will be 1,1,1,1 (depending on pipeline) — we guard with g_useVertexColorMask.
|
|
11066
|
+
var bendMask: f32 = 1.0;
|
|
11067
|
+
var phaseNoise: f32 = 0.0;
|
|
11068
|
+
if (uniforms.g_useVertexColorMask > 0.5) {
|
|
11069
|
+
bendMask = clamp(vertexInputs.color.r, 0.0, 1.0);
|
|
11070
|
+
phaseNoise = vertexInputs.color.g; // 0..1
|
|
11071
|
+
}
|
|
11072
|
+
|
|
11073
|
+
// Height-based fallback weight (normalized between rootY..maxHeight)
|
|
11074
|
+
let denom = max(uniforms.g_branchMaxHeight - uniforms.g_branchRootY, 1e-3);
|
|
11075
|
+
let h01 = clamp((pL.y - uniforms.g_branchRootY) / denom, 0.0, 1.0);
|
|
11076
|
+
|
|
11077
|
+
// Final per-vertex weight:
|
|
11078
|
+
// - If using vertex colors: bendMask * smooth tip emphasis
|
|
11079
|
+
// - Else: height-based
|
|
11080
|
+
var w: f32 = h01;
|
|
11081
|
+
if (uniforms.g_useVertexColorMask > 0.5) {
|
|
11082
|
+
w = bendMask * (h01 * h01 * (3.0 - 2.0 * h01)); // smoothstep(h01)
|
|
11083
|
+
} else {
|
|
11084
|
+
w = h01 * h01; // a bit stiffer near base
|
|
11085
|
+
}
|
|
11086
|
+
|
|
11087
|
+
// Wind direction in LOCAL space:
|
|
11088
|
+
// We transform windDirWorld by inverse world matrix rotation (approx using finalWorld columns).
|
|
11089
|
+
// finalWorld transforms local->world. For direction, inverse rotation is transpose of 3x3 if no shear.
|
|
11090
|
+
let windW = safeNormalize(uniforms.g_windDirection.xyz);
|
|
11091
|
+
let rightW = safeNormalize(finalWorld[0].xyz);
|
|
11092
|
+
let upW = safeNormalize(finalWorld[1].xyz);
|
|
11093
|
+
let fwdW = safeNormalize(finalWorld[2].xyz);
|
|
11094
|
+
|
|
11095
|
+
// Convert world dir to local by dot with basis
|
|
11096
|
+
let windL = safeNormalize(vec3f(dot(windW, rightW), dot(windW, upW), dot(windW, fwdW)));
|
|
11097
|
+
|
|
11098
|
+
// Bend axis: perpendicular to up and wind, in LOCAL space
|
|
11099
|
+
var axisL = safeNormalize(cross(vec3f(0.0, 1.0, 0.0), windL));
|
|
11100
|
+
if (dot(axisL, axisL) < 1e-6) { axisL = vec3f(1.0, 0.0, 0.0); }
|
|
11101
|
+
|
|
11102
|
+
// Spatially varying phase so whole tree doesn't move as a rigid sheet
|
|
11103
|
+
// Use local xz for coherence; optionally mod by vertexColor.g
|
|
11104
|
+
let spatial = (pL.x + pL.z) * uniforms.g_spatialFrequency;
|
|
11105
|
+
let phase = uniforms.g_windTime * uniforms.g_windSpeed
|
|
11106
|
+
+ spatial * uniforms.g_windStrength
|
|
11107
|
+
+ phaseNoise * 6.28318;
|
|
11108
|
+
|
|
11109
|
+
// Two-wave composite like Unity-ish wind
|
|
11110
|
+
let base = sin(phase);
|
|
11111
|
+
let gust = 0.5 * sin(phase * 1.7 + 1.2);
|
|
11112
|
+
let sway = (base + gust) * uniforms.g_windAmount;
|
|
11113
|
+
|
|
11114
|
+
// Convert sway to angle (clamped)
|
|
11115
|
+
var angle = sway * w * uniforms.g_branchMaxAngle;
|
|
11116
|
+
angle = clamp(angle, -uniforms.g_branchMaxAngle, uniforms.g_branchMaxAngle);
|
|
11117
|
+
|
|
11118
|
+
// Pivot in local space
|
|
11119
|
+
let pivotL = vec3f(0.0, uniforms.g_branchRootY, 0.0);
|
|
11120
|
+
let relL = pL - pivotL;
|
|
11121
|
+
let bentRelL = rotateRodrigues(relL, axisL, angle);
|
|
11122
|
+
let bentPL = pivotL + bentRelL;
|
|
11123
|
+
|
|
11124
|
+
// Transform to world using finalWorld (keeps proper anchoring)
|
|
11125
|
+
let branchWorld = (finalWorld * vec4f(bentPL, 1.0)).xyz;
|
|
11126
|
+
|
|
11127
|
+
// Rotate normal similarly (only if NORMAL is active)
|
|
11128
|
+
#ifdef NORMAL
|
|
11129
|
+
// Babylon WGSL PBR usually provides normalUpdated in local/object space
|
|
11130
|
+
var nL: vec3f = normalize(normalUpdated);
|
|
11131
|
+
let bentNL = rotateRodrigues(nL, axisL, angle);
|
|
11132
|
+
// Convert to world by world 3x3
|
|
11133
|
+
let nW = normalize(vec3f(
|
|
11134
|
+
dot(bentNL, vec3f(finalWorld[0].x, finalWorld[0].y, finalWorld[0].z)),
|
|
11135
|
+
dot(bentNL, vec3f(finalWorld[1].x, finalWorld[1].y, finalWorld[1].z)),
|
|
11136
|
+
dot(bentNL, vec3f(finalWorld[2].x, finalWorld[2].y, finalWorld[2].z))
|
|
11137
|
+
));
|
|
11138
|
+
vertexOutputs.vNormalW = nW;
|
|
11139
|
+
#endif
|
|
11140
|
+
|
|
11141
|
+
#if defined(POSITION) || defined(BUMP)
|
|
11142
|
+
vertexOutputs.vPositionW = branchWorld;
|
|
11143
|
+
#endif
|
|
11144
|
+
|
|
11145
|
+
vertexOutputs.position = scene.viewProjection * vec4f(branchWorld, 1.0);
|
|
11146
|
+
`
|
|
11147
|
+
};
|
|
11148
|
+
}
|
|
11149
|
+
else {
|
|
11150
|
+
return {
|
|
11151
|
+
CUSTOM_VERTEX_DEFINITIONS: `
|
|
11152
|
+
uniform float g_windTime;
|
|
11153
|
+
uniform float g_windAmount;
|
|
11154
|
+
uniform float g_windSpeed;
|
|
11155
|
+
uniform float g_windStrength;
|
|
11156
|
+
uniform vec4 g_windDirection;
|
|
11157
|
+
|
|
11158
|
+
uniform float g_branchRootY;
|
|
11159
|
+
uniform float g_branchMaxAngle;
|
|
11160
|
+
uniform float g_branchMaxHeight;
|
|
11161
|
+
uniform float g_useVertexColorMask;
|
|
11162
|
+
uniform float g_spatialFrequency;
|
|
11163
|
+
|
|
11164
|
+
vec3 safeNormalize(vec3 v) {
|
|
11165
|
+
float lsq = dot(v, v);
|
|
11166
|
+
if (lsq < 1e-8) return vec3(1.0, 0.0, 0.0);
|
|
11167
|
+
return v * inversesqrt(lsq);
|
|
11168
|
+
}
|
|
11169
|
+
|
|
11170
|
+
vec3 rotateRodrigues(vec3 p, vec3 axis, float angle) {
|
|
11171
|
+
vec3 a = safeNormalize(axis);
|
|
11172
|
+
float c = cos(angle);
|
|
11173
|
+
float s = sin(angle);
|
|
11174
|
+
return p * c + cross(a, p) * s + a * dot(a, p) * (1.0 - c);
|
|
11175
|
+
}
|
|
11176
|
+
`,
|
|
11177
|
+
CUSTOM_VERTEX_MAIN_END: `
|
|
11178
|
+
// Local/object position
|
|
11179
|
+
vec3 pL = positionUpdated;
|
|
11180
|
+
|
|
11181
|
+
// Vertex color mask (recommended authoring)
|
|
11182
|
+
float bendMask = 1.0;
|
|
11183
|
+
float phaseNoise = 0.0;
|
|
11184
|
+
#ifdef VERTEXCOLOR
|
|
11185
|
+
if (g_useVertexColorMask > 0.5) {
|
|
11186
|
+
bendMask = clamp(color.r, 0.0, 1.0);
|
|
11187
|
+
phaseNoise = color.g;
|
|
11188
|
+
}
|
|
11189
|
+
#endif
|
|
11190
|
+
|
|
11191
|
+
float denom = max(g_branchMaxHeight - g_branchRootY, 1e-3);
|
|
11192
|
+
float h01 = clamp((pL.y - g_branchRootY) / denom, 0.0, 1.0);
|
|
11193
|
+
|
|
11194
|
+
float w = h01;
|
|
11195
|
+
if (g_useVertexColorMask > 0.5) {
|
|
11196
|
+
// smoothstep(h01)
|
|
11197
|
+
w = bendMask * (h01 * h01 * (3.0 - 2.0 * h01));
|
|
11198
|
+
} else {
|
|
11199
|
+
w = h01 * h01;
|
|
11200
|
+
}
|
|
11201
|
+
|
|
11202
|
+
// Convert wind world dir to local using world basis (transpose approx)
|
|
11203
|
+
vec3 windW = safeNormalize(g_windDirection.xyz);
|
|
11204
|
+
vec3 rightW = safeNormalize(finalWorld[0].xyz);
|
|
11205
|
+
vec3 upW = safeNormalize(finalWorld[1].xyz);
|
|
11206
|
+
vec3 fwdW = safeNormalize(finalWorld[2].xyz);
|
|
11207
|
+
vec3 windL = safeNormalize(vec3(dot(windW, rightW), dot(windW, upW), dot(windW, fwdW)));
|
|
11208
|
+
|
|
11209
|
+
vec3 axisL = safeNormalize(cross(vec3(0.0, 1.0, 0.0), windL));
|
|
11210
|
+
if (dot(axisL, axisL) < 1e-6) axisL = vec3(1.0, 0.0, 0.0);
|
|
11211
|
+
|
|
11212
|
+
float spatial = (pL.x + pL.z) * g_spatialFrequency;
|
|
11213
|
+
float phase = g_windTime * g_windSpeed
|
|
11214
|
+
+ spatial * g_windStrength
|
|
11215
|
+
+ phaseNoise * 6.2831853;
|
|
11216
|
+
|
|
11217
|
+
float base = sin(phase);
|
|
11218
|
+
float gust = 0.5 * sin(phase * 1.7 + 1.2);
|
|
11219
|
+
float sway = (base + gust) * g_windAmount;
|
|
11220
|
+
|
|
11221
|
+
float angle = clamp(sway * w * g_branchMaxAngle, -g_branchMaxAngle, g_branchMaxAngle);
|
|
11222
|
+
|
|
11223
|
+
vec3 pivotL = vec3(0.0, g_branchRootY, 0.0);
|
|
11224
|
+
vec3 relL = pL - pivotL;
|
|
11225
|
+
vec3 bentPL = pivotL + rotateRodrigues(relL, axisL, angle);
|
|
11226
|
+
|
|
11227
|
+
vec3 branchWorld = (finalWorld * vec4(bentPL, 1.0)).xyz;
|
|
11228
|
+
|
|
11229
|
+
#if defined(POSITION) || defined(BUMP)
|
|
11230
|
+
vPositionW = branchWorld;
|
|
11231
|
+
#endif
|
|
11232
|
+
|
|
11233
|
+
#ifdef NORMAL
|
|
11234
|
+
// normalUpdated is local/object normal
|
|
11235
|
+
vec3 nL = normalize(normalUpdated);
|
|
11236
|
+
vec3 bentNL = rotateRodrigues(nL, axisL, angle);
|
|
11237
|
+
|
|
11238
|
+
// world normal from world basis (approx; assumes no non-uniform scale/shear)
|
|
11239
|
+
vec3 nW = normalize(bentNL.x * rightW + bentNL.y * upW + bentNL.z * fwdW);
|
|
11240
|
+
vNormalW = nW;
|
|
11241
|
+
#endif
|
|
11242
|
+
|
|
11243
|
+
gl_Position = viewProjection * vec4(branchWorld, 1.0);
|
|
11244
|
+
`
|
|
11245
|
+
};
|
|
11246
|
+
}
|
|
11247
|
+
}
|
|
11248
|
+
if (shaderType === "fragment") {
|
|
11249
|
+
if (shaderLanguage === ShaderLanguage.WGSL) {
|
|
11250
|
+
return {
|
|
11251
|
+
CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: `
|
|
11252
|
+
// no-op
|
|
11253
|
+
`
|
|
11254
|
+
};
|
|
11255
|
+
}
|
|
11256
|
+
else {
|
|
11257
|
+
return {
|
|
11258
|
+
CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: `
|
|
11259
|
+
// no-op
|
|
11260
|
+
`
|
|
11261
|
+
};
|
|
11262
|
+
}
|
|
11263
|
+
}
|
|
11264
|
+
return null;
|
|
11265
|
+
}
|
|
11266
|
+
getUniforms(shaderLanguage) {
|
|
11267
|
+
const wgsl = (shaderLanguage === ShaderLanguage.WGSL);
|
|
11268
|
+
this.vertexDefinitions = this.getCustomShaderMaterial().getCustomVertexCode(wgsl);
|
|
11269
|
+
this.fragmentDefinitions = (wgsl === true) ? this.getCustomShaderMaterial().getCustomFragmentCode(wgsl) : null;
|
|
11270
|
+
return this.getCustomShaderMaterial().getCustomUniforms(wgsl);
|
|
11271
|
+
}
|
|
11272
|
+
getSamplers(samplers) {
|
|
11273
|
+
const customSamplers = this.getCustomShaderMaterial().getCustomSamplers();
|
|
11274
|
+
if (customSamplers && customSamplers.length > 0)
|
|
11275
|
+
samplers.push(...customSamplers);
|
|
11276
|
+
}
|
|
11277
|
+
getAttributes(attributes, scene, mesh) {
|
|
11278
|
+
const customAttributes = this.getCustomShaderMaterial().getCustomAttributes();
|
|
11279
|
+
if (customAttributes && customAttributes.length > 0)
|
|
11280
|
+
attributes.push(...customAttributes);
|
|
11281
|
+
if (attributes.indexOf("color") === -1)
|
|
11282
|
+
attributes.push("color");
|
|
11283
|
+
}
|
|
11284
|
+
prepareDefines(defines, scene, mesh) {
|
|
11285
|
+
if (!this.getIsEnabled())
|
|
11286
|
+
return;
|
|
11287
|
+
this.getCustomShaderMaterial().prepareCustomDefines(defines);
|
|
11288
|
+
}
|
|
11289
|
+
bindForSubMesh(uniformBuffer, scene, engine, subMesh) {
|
|
11290
|
+
if (!this.getIsEnabled())
|
|
11291
|
+
return;
|
|
11292
|
+
this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
|
|
11293
|
+
}
|
|
11294
|
+
static ExtractWindZoneOverride(properties, terrainTransform, builderInstance) {
|
|
11295
|
+
const candidates = [];
|
|
11296
|
+
if (properties)
|
|
11297
|
+
candidates.push(properties);
|
|
11298
|
+
if (properties && properties.properties)
|
|
11299
|
+
candidates.push(properties.properties);
|
|
11300
|
+
const meta = terrainTransform?.metadata;
|
|
11301
|
+
if (meta)
|
|
11302
|
+
candidates.push(meta);
|
|
11303
|
+
if (meta && meta.toolkit)
|
|
11304
|
+
candidates.push(meta.toolkit);
|
|
11305
|
+
if (meta && meta.properties)
|
|
11306
|
+
candidates.push(meta.properties);
|
|
11307
|
+
const bmeta = builderInstance?.metadata;
|
|
11308
|
+
if (bmeta)
|
|
11309
|
+
candidates.push(bmeta);
|
|
11310
|
+
if (bmeta && bmeta.toolkit)
|
|
11311
|
+
candidates.push(bmeta.toolkit);
|
|
11312
|
+
const tmeta = builderInstance?.transform?.metadata;
|
|
11313
|
+
if (tmeta)
|
|
11314
|
+
candidates.push(tmeta);
|
|
11315
|
+
if (tmeta && tmeta.toolkit)
|
|
11316
|
+
candidates.push(tmeta.toolkit);
|
|
11317
|
+
let zones = null;
|
|
11318
|
+
for (const c of candidates) {
|
|
11319
|
+
if (!c)
|
|
11320
|
+
continue;
|
|
11321
|
+
if (Array.isArray(c.windzones)) {
|
|
11322
|
+
zones = c.windzones;
|
|
11323
|
+
break;
|
|
11324
|
+
}
|
|
11325
|
+
}
|
|
11326
|
+
if (!zones || zones.length === 0)
|
|
11327
|
+
return null;
|
|
11328
|
+
let best = null;
|
|
11329
|
+
let bestScore = -1e9;
|
|
11330
|
+
for (const z of zones) {
|
|
11331
|
+
if (!z)
|
|
11332
|
+
continue;
|
|
11333
|
+
const t = ((z.type != null) ? z.type : (z.mode != null ? z.mode : "")).toString().toLowerCase();
|
|
11334
|
+
const isDirectional = (t.indexOf("direction") >= 0);
|
|
11335
|
+
const main = (z.windMain != null) ? z.windMain : 0.0;
|
|
11336
|
+
const score = (isDirectional ? 1000.0 : 0.0) + main;
|
|
11337
|
+
if (score > bestScore) {
|
|
11338
|
+
bestScore = score;
|
|
11339
|
+
best = z;
|
|
11340
|
+
}
|
|
11341
|
+
}
|
|
11342
|
+
return best;
|
|
11343
|
+
}
|
|
11344
|
+
}
|
|
10368
11345
|
export class VertexAnimationMaterial extends CustomShaderMaterial {
|
|
10369
11346
|
constructor(name, scene) {
|
|
10370
11347
|
super(name, scene);
|
|
@@ -15070,7 +16047,7 @@ export class WindowManager {
|
|
|
15070
16047
|
printer.style.left = "6px";
|
|
15071
16048
|
printer.style.bottom = "3px";
|
|
15072
16049
|
printer.style.fontSize = "12px";
|
|
15073
|
-
printer.style.zIndex = "
|
|
16050
|
+
printer.style.zIndex = "9001";
|
|
15074
16051
|
printer.style.color = "#0c0";
|
|
15075
16052
|
printer.style.pointerEvents = "none";
|
|
15076
16053
|
document.body.appendChild(printer);
|
|
@@ -15845,9 +16822,6 @@ export class AnimationState extends ScriptComponent {
|
|
|
15845
16822
|
if (this.onAnimationAwakeObservable && this.onAnimationAwakeObservable.hasObservers()) {
|
|
15846
16823
|
this.onAnimationAwakeObservable.notifyObservers(this.transform);
|
|
15847
16824
|
}
|
|
15848
|
-
console.warn("Animation State Mahine: " + this.transform.name);
|
|
15849
|
-
console.log(this);
|
|
15850
|
-
SceneManager.SetWindowState(this.transform.name, this);
|
|
15851
16825
|
}
|
|
15852
16826
|
updateStateMachine(deltaTime = null) {
|
|
15853
16827
|
if (this.delayUpdateUntilReady === false || (this.delayUpdateUntilReady === true && this.isReady())) {
|
|
@@ -18797,44 +19771,6 @@ export class btRaycastVehicle {
|
|
|
18797
19771
|
this.isArcadeHandBrakeActive = false;
|
|
18798
19772
|
this.isArcadeWheelSkidActive = false;
|
|
18799
19773
|
this.isArcadeYawAssistActive = false;
|
|
18800
|
-
this.arcadeYawAssistDebugLogEnabled = false;
|
|
18801
|
-
this.arcadeYawAssistDebugLogIntervalFrames = 30;
|
|
18802
|
-
this.arcadeYawAssistDebugLogEdgeEvents = true;
|
|
18803
|
-
this.arcadeHandbrakeKickStrengthDegPerSec = 120.0;
|
|
18804
|
-
this.arcadeHandbrakeKickFrames = 6;
|
|
18805
|
-
this.arcadeHandbrakeYawAuthority = 1.8;
|
|
18806
|
-
this.arcadeHandbrakeMaxYawRateDegPerSec = 180.0;
|
|
18807
|
-
this.arcadeHandbrakeReferenceSpeedKmh = 90.0;
|
|
18808
|
-
this.arcadeHandbrakeSpeedGateEnabled = true;
|
|
18809
|
-
this.arcadeHandbrakeLowSpeedShape = 2.0;
|
|
18810
|
-
this.arcadeHandbrakeDirectYawEnabled = true;
|
|
18811
|
-
this.arcadeHandbrakeDirectYawDegPerSec = 60.0;
|
|
18812
|
-
this.arcadeHandbrakeDirectYawDurationMs = 0;
|
|
18813
|
-
this.arcadeHandbrakeDirectYawFadeMs = 200;
|
|
18814
|
-
this.arcadeDonutDirectYawEnabled = true;
|
|
18815
|
-
this.arcadeDonutDirectYawDegPerSec = 5.0;
|
|
18816
|
-
this.arcadeDonutDirectYawDurationMs = 0;
|
|
18817
|
-
this.arcadeDonutDirectYawFadeMs = 200;
|
|
18818
|
-
this.arcadeHandbrakeMaxSlideAngleDeg = 220.0;
|
|
18819
|
-
this.arcadeHandbrakeCounterSteerClampEnabled = true;
|
|
18820
|
-
this.arcadeHandbrakeCounterSteerYawThresholdDegPerSec = 10.0;
|
|
18821
|
-
this.arcadeHandbrakeClampReleaseFadeMs = 250;
|
|
18822
|
-
this.arcadeHandbrakeSteerSlewLimitEnabled = false;
|
|
18823
|
-
this.arcadeHandbrakeSteerSlewLimitDegPerSec = 360.0;
|
|
18824
|
-
this._wasArcadeHandBrakeActive = false;
|
|
18825
|
-
this._wasArcadeYawAssistApplyingForce = false;
|
|
18826
|
-
this._handbrakeKickJzRemaining = 0;
|
|
18827
|
-
this._handbrakeKickFramesRemaining = 0;
|
|
18828
|
-
this._arcadeHandbrakeLatchedDriveSign = 0;
|
|
18829
|
-
this._arcadeHandbrakeHoldElapsedSec = 0;
|
|
18830
|
-
this._arcadeHandbrakeSlewedSteerRad = 0;
|
|
18831
|
-
this._arcadeHandbrakeClampReleaseFadeSec = 999;
|
|
18832
|
-
this._arcadeDonutHoldElapsedSec = 0;
|
|
18833
|
-
this._arcadeYawAssistDebugFrameCounter = 0;
|
|
18834
|
-
this._arcadeYawAssistLastKickRad = 0;
|
|
18835
|
-
this._arcadeYawAssistLastIaddPerWheel = 0;
|
|
18836
|
-
this._arcadeYawAssistLastClampScalar = 1.0;
|
|
18837
|
-
this._arcadeYawAssistLastLeverSum = 0;
|
|
18838
19774
|
this.burnoutFrictionFloor = 1.0;
|
|
18839
19775
|
this.frictionRestoreSpeed = 8.0;
|
|
18840
19776
|
this.arcadeBurnoutWheelSpinGain = 1.0;
|
|
@@ -18847,16 +19783,20 @@ export class btRaycastVehicle {
|
|
|
18847
19783
|
this.arcadeWheelSpinMaxAngularVelocity = 160.0;
|
|
18848
19784
|
this.arcadeStationaryBurnoutWheelSpinGain = 3.25;
|
|
18849
19785
|
this.arcadeStationaryBurnoutMinAngularVelocity = 65.0;
|
|
19786
|
+
this.arcadeHandbrakeYawCapMultiplier = 1.85;
|
|
19787
|
+
this.arcadeBurnoutYawCapMultiplier = 1.25;
|
|
19788
|
+
this.arcadeDonutYawCapMultiplier = 1.15;
|
|
18850
19789
|
this.arcadeSkidFadeInSpeed = 18.0;
|
|
18851
19790
|
this.arcadeSkidFadeOutSpeed = 6.0;
|
|
18852
|
-
this.arcadeYawCapMultiplier = 1.5;
|
|
18853
19791
|
this.wheelAtRestSpeedThresholdKmh = 1.0;
|
|
18854
|
-
this.
|
|
18855
|
-
this.
|
|
18856
|
-
this.
|
|
18857
|
-
this.
|
|
18858
|
-
this.
|
|
18859
|
-
this.
|
|
19792
|
+
this.arcadeHandbrakeAssistEnabled = true;
|
|
19793
|
+
this.currentSteeringInput = 0;
|
|
19794
|
+
this.arcadeDonutDirectYawEnabled = true;
|
|
19795
|
+
this.arcadeDonutDirectYawDegPerSec = 60.0;
|
|
19796
|
+
this.arcadeDonutDirectYawDurationMs = 0;
|
|
19797
|
+
this.arcadeDonutDirectYawFadeMs = 120;
|
|
19798
|
+
this._arcadeDonutHoldElapsedSec = 0;
|
|
19799
|
+
this._arcadeDonutDirectionSign = 0;
|
|
18860
19800
|
this._forwardWS = [];
|
|
18861
19801
|
this._axle = [];
|
|
18862
19802
|
this._forwardImpulse = [];
|
|
@@ -18864,6 +19804,11 @@ export class btRaycastVehicle {
|
|
|
18864
19804
|
this._arcadeSkidInfo = [];
|
|
18865
19805
|
this._arcadePreviousWheelSpin = [];
|
|
18866
19806
|
this.sideFrictionStiffness = 1.0;
|
|
19807
|
+
this.arcadeSideSlipSaturationEnabled = true;
|
|
19808
|
+
this.arcadeSideSlipPeakDeg = 8.0;
|
|
19809
|
+
this.arcadeSideSlipFalloffDeg = 45.0;
|
|
19810
|
+
this.arcadeSideSlipFalloffFactor = 0.4;
|
|
19811
|
+
this.arcadeSideSlipMinSpeedMps = 1.5;
|
|
18867
19812
|
this._chassisMass = 0;
|
|
18868
19813
|
this._chassisInvMass = 0;
|
|
18869
19814
|
this._chassisTransform = Matrix.Identity();
|
|
@@ -18988,6 +19933,9 @@ export class btRaycastVehicle {
|
|
|
18988
19933
|
return this.isArcadeBurnoutModeActive;
|
|
18989
19934
|
}
|
|
18990
19935
|
setIsArcadeDonutActive(active) {
|
|
19936
|
+
if (active !== true) {
|
|
19937
|
+
this._arcadeDonutDirectionSign = 0;
|
|
19938
|
+
}
|
|
18991
19939
|
this.isArcadeDonutModeActive = active;
|
|
18992
19940
|
}
|
|
18993
19941
|
getIsArcadeDonutActive() {
|
|
@@ -19608,29 +20556,6 @@ export class btRaycastVehicle {
|
|
|
19608
20556
|
const chassisAtRest = (this.wheelAtRestSpeedThresholdKmh > 0
|
|
19609
20557
|
&& Math.abs(this._currentVehicleSpeedKmHour) < this.wheelAtRestSpeedThresholdKmh
|
|
19610
20558
|
&& (!arcadeModeActiveAny || !anyWheelEngineForceActive));
|
|
19611
|
-
let wheelSpinLogThisTick = false;
|
|
19612
|
-
if (this.wheelSpinDebugLogEnabled === true) {
|
|
19613
|
-
this._wheelSpinDebugLogCounter++;
|
|
19614
|
-
const intv = (this.wheelSpinDebugLogIntervalFrames > 0)
|
|
19615
|
-
? this.wheelSpinDebugLogIntervalFrames : 1;
|
|
19616
|
-
if ((this._wheelSpinDebugLogCounter % intv) === 0) {
|
|
19617
|
-
wheelSpinLogThisTick = true;
|
|
19618
|
-
}
|
|
19619
|
-
}
|
|
19620
|
-
let chassisAngVelY = 0;
|
|
19621
|
-
if (wheelSpinLogThisTick === true) {
|
|
19622
|
-
this._chassisBody.getAngularVelocityToRef(this._sv8);
|
|
19623
|
-
chassisAngVelY = this._sv8.y;
|
|
19624
|
-
console.log("[HavokVehicle.wheelSpin] tick=" + this._wheelSpinDebugLogCounter
|
|
19625
|
-
+ " speedKmh=" + this._currentVehicleSpeedKmHour.toFixed(3)
|
|
19626
|
-
+ " yawRadPerSec=" + chassisAngVelY.toFixed(4)
|
|
19627
|
-
+ " atRest=" + chassisAtRest
|
|
19628
|
-
+ " arcadeAny=" + arcadeModeActiveAny
|
|
19629
|
-
+ " burn=" + this.isArcadeBurnoutModeActive
|
|
19630
|
-
+ " donut=" + this.isArcadeDonutModeActive
|
|
19631
|
-
+ " hand=" + this.isArcadeHandBrakeActive
|
|
19632
|
-
+ " skid=" + this.isArcadeWheelSkidActive);
|
|
19633
|
-
}
|
|
19634
20559
|
for (var i = 0; i < numWheels; i++) {
|
|
19635
20560
|
var wheel = this._wheelInfo[i];
|
|
19636
20561
|
var groundAngularVelocity = 0.0;
|
|
@@ -19646,29 +20571,12 @@ export class btRaycastVehicle {
|
|
|
19646
20571
|
if (chassisAtRest === true) {
|
|
19647
20572
|
wheel.rotationBoost = 0;
|
|
19648
20573
|
wheel.deltaRotation = 0;
|
|
19649
|
-
if (wheelSpinLogThisTick === true) {
|
|
19650
|
-
console.log(" wheel[" + i + "] SUPPRESSED"
|
|
19651
|
-
+ " contact=" + wheel.raycastInfo.isInContact
|
|
19652
|
-
+ " groundAngVel=" + groundAngularVelocity.toFixed(4)
|
|
19653
|
-
+ " rotation=" + wheel.rotation.toFixed(4));
|
|
19654
|
-
}
|
|
19655
20574
|
continue;
|
|
19656
20575
|
}
|
|
19657
20576
|
var wheelAngularVelocity = this.getWheelAngularVelocity(i, groundAngularVelocity, step, wheel.raycastInfo.isInContact);
|
|
19658
20577
|
wheel.deltaRotation = wheelAngularVelocity * step;
|
|
19659
20578
|
wheel.rotation += wheel.deltaRotation;
|
|
19660
20579
|
wheel.deltaRotation *= 0.99;
|
|
19661
|
-
if (wheelSpinLogThisTick === true) {
|
|
19662
|
-
console.log(" wheel[" + i + "]"
|
|
19663
|
-
+ " contact=" + wheel.raycastInfo.isInContact
|
|
19664
|
-
+ " groundAngVel=" + groundAngularVelocity.toFixed(4)
|
|
19665
|
-
+ " rotBoost=" + wheel.rotationBoost.toFixed(4)
|
|
19666
|
-
+ " wheelAngVel=" + wheelAngularVelocity.toFixed(4)
|
|
19667
|
-
+ " deltaRot=" + wheel.deltaRotation.toFixed(5)
|
|
19668
|
-
+ " rotation=" + wheel.rotation.toFixed(4)
|
|
19669
|
-
+ " engineForce=" + wheel.engineForce.toFixed(2)
|
|
19670
|
-
+ " brake=" + wheel.brake.toFixed(2));
|
|
19671
|
-
}
|
|
19672
20580
|
}
|
|
19673
20581
|
this.clampChassisYawRate(step);
|
|
19674
20582
|
}
|
|
@@ -19719,9 +20627,20 @@ export class btRaycastVehicle {
|
|
|
19719
20627
|
}
|
|
19720
20628
|
}
|
|
19721
20629
|
}
|
|
20630
|
+
getSteeringAuthorityInput() {
|
|
20631
|
+
var steeringInput = Scalar.Clamp(this.currentSteeringInput, -1.0, 1.0);
|
|
20632
|
+
if (Math.abs(steeringInput) > 0.001)
|
|
20633
|
+
return steeringInput;
|
|
20634
|
+
var signedFrontSteer = this.getSignedFrontSteeringAngleRad();
|
|
20635
|
+
return Scalar.Clamp(signedFrontSteer / 0.6, -1.0, 1.0);
|
|
20636
|
+
}
|
|
20637
|
+
getSteeringMagnitude() {
|
|
20638
|
+
return Math.abs(this.getSteeringAuthorityInput());
|
|
20639
|
+
}
|
|
19722
20640
|
applyEasyDonutYawAssist(timeStep, absSpeedKmh, signedSpeedKmh) {
|
|
19723
20641
|
if (this.isArcadeDonutModeActive !== true) {
|
|
19724
20642
|
this._arcadeDonutHoldElapsedSec = 0;
|
|
20643
|
+
this._arcadeDonutDirectionSign = 0;
|
|
19725
20644
|
return;
|
|
19726
20645
|
}
|
|
19727
20646
|
if (this.arcadeDonutDirectYawEnabled !== true) {
|
|
@@ -19734,14 +20653,6 @@ export class btRaycastVehicle {
|
|
|
19734
20653
|
this._arcadeDonutHoldElapsedSec += timeStep;
|
|
19735
20654
|
return;
|
|
19736
20655
|
}
|
|
19737
|
-
var L = this.getApproxWheelbaseMeters();
|
|
19738
|
-
if (L < 0.5)
|
|
19739
|
-
L = 2.5;
|
|
19740
|
-
var Iz = 0.22 * this._chassisMass * L * L;
|
|
19741
|
-
if (Iz <= 0 || this._chassisMass <= 0) {
|
|
19742
|
-
this._arcadeDonutHoldElapsedSec += timeStep;
|
|
19743
|
-
return;
|
|
19744
|
-
}
|
|
19745
20656
|
var durSec = this.arcadeDonutDirectYawDurationMs > 0 ? this.arcadeDonutDirectYawDurationMs / 1000.0 : 0;
|
|
19746
20657
|
var fadeSec = this.arcadeDonutDirectYawFadeMs > 0 ? this.arcadeDonutDirectYawFadeMs / 1000.0 : 0;
|
|
19747
20658
|
var elapsed = this._arcadeDonutHoldElapsedSec;
|
|
@@ -19770,337 +20681,76 @@ export class btRaycastVehicle {
|
|
|
19770
20681
|
chassisUp.set(cm[ui * 4], cm[ui * 4 + 1], cm[ui * 4 + 2]);
|
|
19771
20682
|
this._chassisBody.getLinearVelocityToRef(this._sv8);
|
|
19772
20683
|
var signedVxMps = Vector3.Dot(this._sv8, chassisForward);
|
|
19773
|
-
var driveSign = Math.abs(signedVxMps) > 0.25 ? (signedVxMps >= 0 ? 1 : -1) : 1;
|
|
19774
20684
|
var steerSign = signedFrontSteerRad >= 0 ? 1 : -1;
|
|
19775
|
-
|
|
19776
|
-
|
|
19777
|
-
* fade * this.arcadeSteeringAssist;
|
|
19778
|
-
var donutYawJz = Iz * extraRad * timeStep;
|
|
19779
|
-
if (Math.abs(donutYawJz) <= 1e-6) {
|
|
19780
|
-
this._arcadeDonutHoldElapsedSec += timeStep;
|
|
19781
|
-
return;
|
|
20685
|
+
if (this._arcadeDonutDirectionSign === 0) {
|
|
20686
|
+
this._arcadeDonutDirectionSign = Math.abs(signedVxMps) > 0.25 ? (signedVxMps >= 0 ? 1 : -1) : steerSign;
|
|
19782
20687
|
}
|
|
19783
|
-
|
|
19784
|
-
|
|
19785
|
-
|
|
19786
|
-
|
|
19787
|
-
for (var w = 0; w < this._wheelInfo.length; w++) {
|
|
19788
|
-
var wi = this._wheelInfo[w];
|
|
19789
|
-
if (!wi.isFrontWheel && wi.raycastInfo.groundObject) {
|
|
19790
|
-
wi.raycastInfo.contactPointWS.subtractToRef(this._sv1, this._sv2);
|
|
19791
|
-
Vector3.CrossToRef(this._sv2, this._axle[w], this._sv3);
|
|
19792
|
-
var lever_w = Vector3.Dot(this._sv3, chassisUp);
|
|
19793
|
-
rearIdxList.push(w);
|
|
19794
|
-
rearLeverList.push(lever_w);
|
|
19795
|
-
leverSum += lever_w;
|
|
19796
|
-
}
|
|
19797
|
-
}
|
|
19798
|
-
if (rearIdxList.length === 0 || Math.abs(leverSum) <= MIN_LEVER_EPS) {
|
|
20688
|
+
var targetYawRad = Tools.ToRadians(this.arcadeDonutDirectYawDegPerSec)
|
|
20689
|
+
* this._arcadeDonutDirectionSign * steerSign
|
|
20690
|
+
* fade * this.arcadeSteeringAssist;
|
|
20691
|
+
if (Math.abs(targetYawRad) <= 1e-5) {
|
|
19799
20692
|
this._arcadeDonutHoldElapsedSec += timeStep;
|
|
19800
20693
|
return;
|
|
19801
20694
|
}
|
|
19802
|
-
|
|
19803
|
-
|
|
19804
|
-
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
20695
|
+
this._chassisBody.getAngularVelocityToRef(this._sv9);
|
|
20696
|
+
var currentYawRad = Vector3.Dot(this._sv9, chassisUp);
|
|
20697
|
+
var lockRate = 14.0;
|
|
20698
|
+
var lockBlend = Scalar.Clamp(1.0 - Math.exp(-lockRate * timeStep), 0.0, 1.0);
|
|
20699
|
+
var nextYawRad = currentYawRad + (targetYawRad - currentYawRad) * lockBlend;
|
|
20700
|
+
var minHoldYawRad = Math.abs(targetYawRad) * 0.55;
|
|
20701
|
+
if (Math.sign(nextYawRad) !== Math.sign(targetYawRad) || Math.abs(nextYawRad) < minHoldYawRad) {
|
|
20702
|
+
nextYawRad = Math.sign(targetYawRad) * minHoldYawRad;
|
|
20703
|
+
}
|
|
20704
|
+
chassisUp.scaleToRef(currentYawRad, this._sv1);
|
|
20705
|
+
this._sv9.subtractToRef(this._sv1, this._sv2);
|
|
20706
|
+
chassisUp.scaleToRef(nextYawRad, this._sv3);
|
|
20707
|
+
this._sv2.addToRef(this._sv3, this._sv9);
|
|
20708
|
+
this._chassisBody.setAngularVelocity(this._sv9);
|
|
19809
20709
|
this._arcadeDonutHoldElapsedSec += timeStep;
|
|
19810
20710
|
}
|
|
19811
20711
|
applyHandbrakeYawAssist(timeStep, absSpeedKmh, signedSpeedKmh) {
|
|
19812
20712
|
this.isArcadeYawAssistActive = false;
|
|
19813
|
-
if (this.
|
|
19814
|
-
this._wasArcadeHandBrakeActive = false;
|
|
19815
|
-
this._wasArcadeYawAssistApplyingForce = false;
|
|
19816
|
-
this._handbrakeKickJzRemaining = 0;
|
|
19817
|
-
this._handbrakeKickFramesRemaining = 0;
|
|
19818
|
-
this._arcadeHandbrakeLatchedDriveSign = 0;
|
|
19819
|
-
this._arcadeHandbrakeHoldElapsedSec = 0;
|
|
19820
|
-
this._arcadeHandbrakeSlewedSteerRad = 0;
|
|
19821
|
-
this._arcadeHandbrakeClampReleaseFadeSec = 999;
|
|
20713
|
+
if (!this.arcadeHandbrakeAssistEnabled)
|
|
19822
20714
|
return;
|
|
19823
|
-
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
19827
|
-
|
|
19828
|
-
|
|
19829
|
-
|
|
19830
|
-
|
|
19831
|
-
const
|
|
19832
|
-
const
|
|
19833
|
-
const K_ASSIST = this.arcadeHandbrakeYawAuthority;
|
|
19834
|
-
const R_MAX_DEG_PER_SEC = this.arcadeHandbrakeMaxYawRateDegPerSec;
|
|
19835
|
-
const K_KICK_DEG_PER_SEC = this.arcadeHandbrakeKickStrengthDegPerSec;
|
|
19836
|
-
const V_REF_MPS = this.arcadeHandbrakeReferenceSpeedKmh / 3.6;
|
|
19837
|
-
const MAX_SLIDE_ANGLE_RAD = Tools.ToRadians(this.arcadeHandbrakeMaxSlideAngleDeg);
|
|
19838
|
-
const KICK_FRAMES = Math.max(1, this.arcadeHandbrakeKickFrames | 0);
|
|
19839
|
-
this._arcadeYawAssistDebugFrameCounter++;
|
|
19840
|
-
var debugEnabled = this.arcadeYawAssistDebugLogEnabled === true;
|
|
19841
|
-
var debugIntervalHit = debugEnabled && this.arcadeYawAssistDebugLogIntervalFrames > 0 && (this._arcadeYawAssistDebugFrameCounter % this.arcadeYawAssistDebugLogIntervalFrames) === 0;
|
|
19842
|
-
var signedFrontSteerRad = this.getSignedFrontSteeringAngleRad();
|
|
19843
|
-
var steeringMagnitude = Math.abs(signedFrontSteerRad);
|
|
19844
|
-
var eligible = this.isArcadeHandBrakeActive === true && this.arcadeSteeringAssist > 0 && steeringMagnitude > MIN_STEER_RAD && absSpeedKmh > V_ENGAGE_MIN_KMH;
|
|
19845
|
-
var risingEdge = this.isArcadeHandBrakeActive === true && this._wasArcadeHandBrakeActive === false;
|
|
19846
|
-
if (this.arcadeHandbrakeSteerSlewLimitEnabled === true && !risingEdge) {
|
|
19847
|
-
var slewMaxRad = Tools.ToRadians(this.arcadeHandbrakeSteerSlewLimitDegPerSec) * timeStep;
|
|
19848
|
-
var steerDelta = signedFrontSteerRad - this._arcadeHandbrakeSlewedSteerRad;
|
|
19849
|
-
if (steerDelta > slewMaxRad)
|
|
19850
|
-
steerDelta = slewMaxRad;
|
|
19851
|
-
else if (steerDelta < -slewMaxRad)
|
|
19852
|
-
steerDelta = -slewMaxRad;
|
|
19853
|
-
this._arcadeHandbrakeSlewedSteerRad += steerDelta;
|
|
19854
|
-
}
|
|
19855
|
-
else {
|
|
19856
|
-
this._arcadeHandbrakeSlewedSteerRad = signedFrontSteerRad;
|
|
19857
|
-
}
|
|
19858
|
-
var effectiveSteerRad = this._arcadeHandbrakeSlewedSteerRad;
|
|
19859
|
-
const cm = this._chassisTransform.m;
|
|
20715
|
+
var steeringMagnitude = this.getSteeringMagnitude();
|
|
20716
|
+
var steeringAuthorityInput = this.getSteeringAuthorityInput();
|
|
20717
|
+
if (!this.isArcadeHandBrakeActive || steeringMagnitude < 0.02)
|
|
20718
|
+
return;
|
|
20719
|
+
var speedMps = Math.max(0.0, absSpeedKmh / 3.6);
|
|
20720
|
+
var speedScale = Scalar.Clamp((speedMps - 0.5) / 3.0, 0.0, 1.0);
|
|
20721
|
+
if (speedScale <= 0.0)
|
|
20722
|
+
return;
|
|
20723
|
+
const m = this._chassisTransform.m;
|
|
20724
|
+
const ri = this._indexRightAxis;
|
|
19860
20725
|
const ui = this._indexUpAxis;
|
|
19861
20726
|
const fi = this._indexForwardAxis;
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
this.
|
|
19870
|
-
var
|
|
19871
|
-
var
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
var
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
}
|
|
19881
|
-
else {
|
|
19882
|
-
relSlipRad = slipAngleRad >= 0 ? slipAngleRad - Math.PI : slipAngleRad + Math.PI;
|
|
19883
|
-
}
|
|
19884
|
-
var relSlipAbs = Math.abs(relSlipRad);
|
|
19885
|
-
var relSlipSign = relSlipAbs > 1e-3 ? (relSlipRad > 0 ? 1 : -1) : 0;
|
|
19886
|
-
var L = this.getApproxWheelbaseMeters();
|
|
19887
|
-
if (L < 0.5)
|
|
19888
|
-
L = 2.5;
|
|
19889
|
-
var Iz = I_Z_FACTOR * this._chassisMass * L * L;
|
|
19890
|
-
var rTarget = 0;
|
|
19891
|
-
var currentYawRate = 0;
|
|
19892
|
-
var gate = 0;
|
|
19893
|
-
var slideTaper = 1.0;
|
|
19894
|
-
var desiredMz = 0;
|
|
19895
|
-
this._chassisBody.getAngularVelocityToRef(this._sv4);
|
|
19896
|
-
currentYawRate = Vector3.Dot(this._sv4, chassisUp);
|
|
19897
|
-
if (eligible) {
|
|
19898
|
-
var gateRange = V_ENGAGE_FULL_KMH - V_ENGAGE_MIN_KMH;
|
|
19899
|
-
var gateRaw = gateRange > 0 ? (absSpeedKmh - V_ENGAGE_MIN_KMH) / gateRange : 1.0;
|
|
19900
|
-
gate = Scalar.Clamp(gateRaw, 0, 1);
|
|
19901
|
-
gate = gate * gate * (3.0 - 2.0 * gate);
|
|
19902
|
-
var vForTarget = driveSign * Math.min(speedMagMps, V_REF_MPS);
|
|
19903
|
-
var rTargetRaw = K_ASSIST * vForTarget * effectiveSteerRad / L;
|
|
19904
|
-
var rMaxLinear = Scalar.Clamp(speedMagMps / V_REF_MPS, 0, 1);
|
|
19905
|
-
var lowSpeedShape = this.arcadeHandbrakeLowSpeedShape > 0 ? this.arcadeHandbrakeLowSpeedShape : 1.0;
|
|
19906
|
-
var rMaxSpeedFactor = this.arcadeHandbrakeSpeedGateEnabled ? Math.pow(rMaxLinear, lowSpeedShape) : 1.0;
|
|
19907
|
-
var rMax = Tools.ToRadians(R_MAX_DEG_PER_SEC) * rMaxSpeedFactor;
|
|
19908
|
-
if (rTargetRaw > rMax)
|
|
19909
|
-
rTargetRaw = rMax;
|
|
19910
|
-
else if (rTargetRaw < -rMax)
|
|
19911
|
-
rTargetRaw = -rMax;
|
|
19912
|
-
rTarget = rTargetRaw * gate * this.arcadeSteeringAssist;
|
|
19913
|
-
var counterSteerClampActive = false;
|
|
19914
|
-
if (this.arcadeHandbrakeCounterSteerClampEnabled === true) {
|
|
19915
|
-
var yawThresholdRadPerSec = Tools.ToRadians(this.arcadeHandbrakeCounterSteerYawThresholdDegPerSec);
|
|
19916
|
-
if (Math.abs(currentYawRate) > yawThresholdRadPerSec && rTarget * currentYawRate < 0) {
|
|
19917
|
-
rTarget = 0;
|
|
19918
|
-
counterSteerClampActive = true;
|
|
19919
|
-
}
|
|
19920
|
-
}
|
|
19921
|
-
var releaseFadeMs = this.arcadeHandbrakeClampReleaseFadeMs;
|
|
19922
|
-
var clampFadeActive = false;
|
|
19923
|
-
var clampFadeMul = 1.0;
|
|
19924
|
-
if (counterSteerClampActive) {
|
|
19925
|
-
this._arcadeHandbrakeClampReleaseFadeSec = 0;
|
|
19926
|
-
}
|
|
19927
|
-
else {
|
|
19928
|
-
this._arcadeHandbrakeClampReleaseFadeSec += timeStep;
|
|
19929
|
-
if (releaseFadeMs > 0) {
|
|
19930
|
-
var fadeSec = releaseFadeMs / 1000.0;
|
|
19931
|
-
if (this._arcadeHandbrakeClampReleaseFadeSec < fadeSec) {
|
|
19932
|
-
var fadeT = this._arcadeHandbrakeClampReleaseFadeSec / fadeSec;
|
|
19933
|
-
clampFadeMul = fadeT * fadeT * (3.0 - 2.0 * fadeT);
|
|
19934
|
-
rTarget *= clampFadeMul;
|
|
19935
|
-
clampFadeActive = true;
|
|
19936
|
-
}
|
|
19937
|
-
}
|
|
19938
|
-
}
|
|
19939
|
-
if (relSlipAbs > MAX_SLIDE_ANGLE_RAD) {
|
|
19940
|
-
var pastMax = (relSlipAbs - MAX_SLIDE_ANGLE_RAD) / SLIDE_TAPER_RANGE_RAD;
|
|
19941
|
-
if (pastMax > 1.0)
|
|
19942
|
-
pastMax = 1.0;
|
|
19943
|
-
slideTaper = 1.0 - pastMax;
|
|
19944
|
-
rTarget *= slideTaper;
|
|
19945
|
-
}
|
|
19946
|
-
if (rTarget > rMax)
|
|
19947
|
-
rTarget = rMax;
|
|
19948
|
-
else if (rTarget < -rMax)
|
|
19949
|
-
rTarget = -rMax;
|
|
19950
|
-
desiredMz = Iz * (rTarget - currentYawRate) / TAU_RESPONSE;
|
|
19951
|
-
}
|
|
19952
|
-
var newMz;
|
|
19953
|
-
if (eligible) {
|
|
19954
|
-
newMz = desiredMz;
|
|
20727
|
+
this._stb1.set(m[ri * 4], m[ri * 4 + 1], m[ri * 4 + 2]);
|
|
20728
|
+
this._stb2.set(m[ui * 4], m[ui * 4 + 1], m[ui * 4 + 2]);
|
|
20729
|
+
this._stb3.set(m[fi * 4], m[fi * 4 + 1], m[fi * 4 + 2]);
|
|
20730
|
+
this._chassisBody.getAngularVelocityToRef(this._sv9);
|
|
20731
|
+
var localRoll = Vector3.Dot(this._sv9, this._stb1);
|
|
20732
|
+
var localYaw = Vector3.Dot(this._sv9, this._stb2);
|
|
20733
|
+
var localPitch = Vector3.Dot(this._sv9, this._stb3);
|
|
20734
|
+
var signedFrontSteerRad = this.getSignedFrontSteeringAngleRad();
|
|
20735
|
+
var wheelbaseMeters = this.getApproxWheelbaseMeters();
|
|
20736
|
+
var bicycleYawRate = 0.0;
|
|
20737
|
+
if (wheelbaseMeters > 0.5 && speedMps > 0.2 && Math.abs(signedFrontSteerRad) > 1e-4) {
|
|
20738
|
+
}
|
|
20739
|
+
var minimumYawRate = steeringAuthorityInput * this.arcadeSteeringAssist * (0.25 + steeringMagnitude * 1.0) * speedScale;
|
|
20740
|
+
var desiredYawBoost = Math.abs(bicycleYawRate) > Math.abs(minimumYawRate) ? bicycleYawRate : minimumYawRate;
|
|
20741
|
+
var sameDir = (desiredYawBoost * localYaw) >= 0.0;
|
|
20742
|
+
if (sameDir && Math.abs(localYaw) >= Math.abs(desiredYawBoost)) {
|
|
20743
|
+
this.isArcadeYawAssistActive = true;
|
|
20744
|
+
return;
|
|
19955
20745
|
}
|
|
19956
|
-
|
|
19957
|
-
|
|
19958
|
-
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
19964
|
-
var kickRange = V_KICK_FULL_KMH - V_ENGAGE_MIN_KMH;
|
|
19965
|
-
var kickRaw = kickRange > 0 ? (absSpeedKmh - V_ENGAGE_MIN_KMH) / kickRange : 1.0;
|
|
19966
|
-
var kickGate = Scalar.Clamp(kickRaw, 0, 1);
|
|
19967
|
-
kickGate = kickGate * kickGate * (3.0 - 2.0 * kickGate);
|
|
19968
|
-
var steerSign = signedFrontSteerRad >= 0 ? 1 : -1;
|
|
19969
|
-
var velSign = signedVxMps >= 0 ? 1 : -1;
|
|
19970
|
-
var kickLinear = Scalar.Clamp(speedMagMps / V_REF_MPS, 0, 1);
|
|
19971
|
-
var kickShape = this.arcadeHandbrakeLowSpeedShape > 0 ? this.arcadeHandbrakeLowSpeedShape : 1.0;
|
|
19972
|
-
var kickSpeedFactor = this.arcadeHandbrakeSpeedGateEnabled ? Math.pow(kickLinear, kickShape) : 1.0;
|
|
19973
|
-
var kickRadTotal = Tools.ToRadians(K_KICK_DEG_PER_SEC) * steerSign * velSign * kickGate * kickSpeedFactor * this.arcadeSteeringAssist;
|
|
19974
|
-
var kickSuppressed = false;
|
|
19975
|
-
if (this.arcadeHandbrakeCounterSteerClampEnabled === true) {
|
|
19976
|
-
var kickYawThresholdRadPerSec = Tools.ToRadians(this.arcadeHandbrakeCounterSteerYawThresholdDegPerSec);
|
|
19977
|
-
if (Math.abs(currentYawRate) > kickYawThresholdRadPerSec && kickRadTotal * currentYawRate < 0) {
|
|
19978
|
-
kickRadTotal = 0;
|
|
19979
|
-
kickSuppressed = true;
|
|
19980
|
-
}
|
|
19981
|
-
}
|
|
19982
|
-
this._handbrakeKickJzRemaining = Iz * kickRadTotal;
|
|
19983
|
-
this._handbrakeKickFramesRemaining = kickSuppressed ? 0 : KICK_FRAMES;
|
|
19984
|
-
}
|
|
19985
|
-
var kickAngularImpulseJz = 0;
|
|
19986
|
-
var kickRad = 0;
|
|
19987
|
-
if (eligible && this._handbrakeKickFramesRemaining > 0) {
|
|
19988
|
-
var thisFrameKick = this._handbrakeKickJzRemaining / this._handbrakeKickFramesRemaining;
|
|
19989
|
-
kickAngularImpulseJz += thisFrameKick;
|
|
19990
|
-
this._handbrakeKickJzRemaining -= thisFrameKick;
|
|
19991
|
-
this._handbrakeKickFramesRemaining--;
|
|
19992
|
-
}
|
|
19993
|
-
else if (!eligible) {
|
|
19994
|
-
this._handbrakeKickJzRemaining = 0;
|
|
19995
|
-
this._handbrakeKickFramesRemaining = 0;
|
|
19996
|
-
}
|
|
19997
|
-
if (Iz > 0)
|
|
19998
|
-
kickRad = kickAngularImpulseJz / Iz;
|
|
19999
|
-
var directYawJz = 0;
|
|
20000
|
-
if (eligible && this.arcadeHandbrakeDirectYawEnabled === true && Iz > 0) {
|
|
20001
|
-
var directDurSec = this.arcadeHandbrakeDirectYawDurationMs > 0 ? this.arcadeHandbrakeDirectYawDurationMs / 1000.0 : 0;
|
|
20002
|
-
var directFadeSec = this.arcadeHandbrakeDirectYawFadeMs > 0 ? this.arcadeHandbrakeDirectYawFadeMs / 1000.0 : 0;
|
|
20003
|
-
var elapsed = this._arcadeHandbrakeHoldElapsedSec;
|
|
20004
|
-
var fadeIn = directFadeSec > 0 ? Scalar.Clamp(elapsed / directFadeSec, 0, 1) : 1.0;
|
|
20005
|
-
var fadeOut = 1.0;
|
|
20006
|
-
if (directDurSec > 0) {
|
|
20007
|
-
var timeLeft = directDurSec - elapsed;
|
|
20008
|
-
if (timeLeft <= 0) {
|
|
20009
|
-
fadeOut = 0;
|
|
20010
|
-
}
|
|
20011
|
-
else if (directFadeSec > 0 && timeLeft < directFadeSec) {
|
|
20012
|
-
fadeOut = timeLeft / directFadeSec;
|
|
20013
|
-
}
|
|
20014
|
-
}
|
|
20015
|
-
var directFade = Math.min(fadeIn, fadeOut);
|
|
20016
|
-
if (directFade > 0) {
|
|
20017
|
-
var dyLinear = Scalar.Clamp(speedMagMps / V_REF_MPS, 0, 1);
|
|
20018
|
-
var dyShape = this.arcadeHandbrakeLowSpeedShape > 0 ? this.arcadeHandbrakeLowSpeedShape : 1.0;
|
|
20019
|
-
var dySpeedFactor = this.arcadeHandbrakeSpeedGateEnabled ? Math.pow(dyLinear, dyShape) : 1.0;
|
|
20020
|
-
var dySteerSign = signedFrontSteerRad >= 0 ? 1 : -1;
|
|
20021
|
-
var dyExtraRad = Tools.ToRadians(this.arcadeHandbrakeDirectYawDegPerSec)
|
|
20022
|
-
* driveSign * dySteerSign
|
|
20023
|
-
* gate * dySpeedFactor
|
|
20024
|
-
* directFade * this.arcadeSteeringAssist;
|
|
20025
|
-
directYawJz = Iz * dyExtraRad * timeStep;
|
|
20026
|
-
}
|
|
20027
|
-
}
|
|
20028
|
-
if (eligible) {
|
|
20029
|
-
this._arcadeHandbrakeHoldElapsedSec += timeStep;
|
|
20030
|
-
}
|
|
20031
|
-
var leverSum = 0;
|
|
20032
|
-
var rearGroundedCount = 0;
|
|
20033
|
-
var IaddCtrlDesired = 0;
|
|
20034
|
-
var IaddCtrlAppliedSum = 0;
|
|
20035
|
-
var IaddKickPerWheel = 0;
|
|
20036
|
-
var IaddKickAppliedSum = 0;
|
|
20037
|
-
var IaddDirectPerWheel = 0;
|
|
20038
|
-
var IaddDirectAppliedSum = 0;
|
|
20039
|
-
var Jz_through_tires = 0;
|
|
20040
|
-
var minCtrlClamp = 1.0;
|
|
20041
|
-
var perWheelLog = "";
|
|
20042
|
-
var rearIdxList = [];
|
|
20043
|
-
var rearLeverList = [];
|
|
20044
|
-
var hasController = Math.abs(newMz) > 0;
|
|
20045
|
-
var hasKick = Math.abs(kickAngularImpulseJz) > 0;
|
|
20046
|
-
var hasDirect = Math.abs(directYawJz) > 0;
|
|
20047
|
-
if ((hasController || hasKick || hasDirect) && this._chassisMass > 0) {
|
|
20048
|
-
this._chassisBody.getObjectCenterWorldToRef(this._sv1);
|
|
20049
|
-
for (var w = 0; w < this._wheelInfo.length; w++) {
|
|
20050
|
-
var wi = this._wheelInfo[w];
|
|
20051
|
-
if (!wi.isFrontWheel && wi.raycastInfo.groundObject) {
|
|
20052
|
-
wi.raycastInfo.contactPointWS.subtractToRef(this._sv1, this._sv2);
|
|
20053
|
-
Vector3.CrossToRef(this._sv2, this._axle[w], this._sv3);
|
|
20054
|
-
var lever_w = Vector3.Dot(this._sv3, chassisUp);
|
|
20055
|
-
rearIdxList.push(w);
|
|
20056
|
-
rearLeverList.push(lever_w);
|
|
20057
|
-
leverSum += lever_w;
|
|
20058
|
-
rearGroundedCount++;
|
|
20059
|
-
}
|
|
20060
|
-
}
|
|
20061
|
-
if (rearGroundedCount > 0 && Math.abs(leverSum) > MIN_LEVER_EPS) {
|
|
20062
|
-
if (hasController)
|
|
20063
|
-
IaddCtrlDesired = (newMz * timeStep) / leverSum;
|
|
20064
|
-
if (hasKick)
|
|
20065
|
-
IaddKickPerWheel = kickAngularImpulseJz / leverSum;
|
|
20066
|
-
if (hasDirect)
|
|
20067
|
-
IaddDirectPerWheel = directYawJz / leverSum;
|
|
20068
|
-
for (var ii = 0; ii < rearIdxList.length; ii++) {
|
|
20069
|
-
var w2 = rearIdxList[ii];
|
|
20070
|
-
var lever_w2 = rearLeverList[ii];
|
|
20071
|
-
var wi2 = this._wheelInfo[w2];
|
|
20072
|
-
var IaddCtrlW = hasController ? IaddCtrlDesired : 0;
|
|
20073
|
-
var IaddTotalW = IaddCtrlW + IaddKickPerWheel + IaddDirectPerWheel;
|
|
20074
|
-
if (IaddTotalW !== 0) {
|
|
20075
|
-
this._axle[w2].scaleToRef(IaddTotalW, this._sv2);
|
|
20076
|
-
this._chassisBody.applyImpulse(this._sv2, wi2.raycastInfo.contactPointWS);
|
|
20077
|
-
IaddCtrlAppliedSum += IaddCtrlW;
|
|
20078
|
-
IaddKickAppliedSum += IaddKickPerWheel;
|
|
20079
|
-
IaddDirectAppliedSum += IaddDirectPerWheel;
|
|
20080
|
-
Jz_through_tires += lever_w2 * IaddTotalW;
|
|
20081
|
-
}
|
|
20082
|
-
if (debugEnabled) {
|
|
20083
|
-
perWheelLog += " w" + w2
|
|
20084
|
-
+ "{maximp=" + (wi2.suspensionForce * timeStep * wi2.frictionSlip).toFixed(1)
|
|
20085
|
-
+ " side=" + this._sideImpulse[w2].toFixed(1)
|
|
20086
|
-
+ " fwd=" + (this._forwardImpulse[w2] * 0.5).toFixed(1)
|
|
20087
|
-
+ " ctrlDes=" + IaddCtrlDesired.toFixed(1)
|
|
20088
|
-
+ " ctrl=" + IaddCtrlW.toFixed(1)
|
|
20089
|
-
+ " kick=" + IaddKickPerWheel.toFixed(1)
|
|
20090
|
-
+ " direct=" + IaddDirectPerWheel.toFixed(1)
|
|
20091
|
-
+ " tot=" + IaddTotalW.toFixed(1) + "}";
|
|
20092
|
-
}
|
|
20093
|
-
}
|
|
20094
|
-
}
|
|
20095
|
-
}
|
|
20096
|
-
this._arcadeYawAssistLastKickRad = kickRad;
|
|
20097
|
-
this._arcadeYawAssistLastIaddPerWheel = IaddCtrlDesired + IaddKickPerWheel + IaddDirectPerWheel;
|
|
20098
|
-
this._arcadeYawAssistLastClampScalar = minCtrlClamp;
|
|
20099
|
-
this._arcadeYawAssistLastLeverSum = leverSum;
|
|
20100
|
-
var assistActiveThisFrame = eligible || assistApplyingForce;
|
|
20101
|
-
this.isArcadeYawAssistActive = assistActiveThisFrame;
|
|
20102
|
-
this._wasArcadeHandBrakeActive = this.isArcadeHandBrakeActive;
|
|
20103
|
-
this._wasArcadeYawAssistApplyingForce = assistApplyingForce;
|
|
20746
|
+
var yawDeltaMax = (0.15 + steeringMagnitude * 0.35) * speedScale;
|
|
20747
|
+
var yawDelta = Scalar.Clamp(desiredYawBoost * (2.0 + steeringMagnitude * 4.0) * timeStep, -yawDeltaMax, yawDeltaMax);
|
|
20748
|
+
if (Math.abs(yawDelta) < 1e-6)
|
|
20749
|
+
return;
|
|
20750
|
+
localYaw += yawDelta;
|
|
20751
|
+
this._sv9.set(this._stb1.x * localRoll + this._stb2.x * localYaw + this._stb3.x * localPitch, this._stb1.y * localRoll + this._stb2.y * localYaw + this._stb3.y * localPitch, this._stb1.z * localRoll + this._stb2.z * localYaw + this._stb3.z * localPitch);
|
|
20752
|
+
this._chassisBody.setAngularVelocity(this._sv9);
|
|
20753
|
+
this.isArcadeYawAssistActive = true;
|
|
20104
20754
|
}
|
|
20105
20755
|
resolveWheelSpinDirection(wheelIndex, requestedDriveImpulse, appliedDriveImpulse) {
|
|
20106
20756
|
if (Math.abs(requestedDriveImpulse) > 0.0001) {
|
|
@@ -20298,6 +20948,24 @@ export class btRaycastVehicle {
|
|
|
20298
20948
|
this._forwardWS[i].normalize();
|
|
20299
20949
|
this._sideImpulse[i] = this.resolveSingleBilateral(this._chassisBody, wheel.raycastInfo.contactPointWS, this._axle[i]);
|
|
20300
20950
|
this._sideImpulse[i] *= this.sideFrictionStiffness;
|
|
20951
|
+
if (this.arcadeSideSlipSaturationEnabled && this._sideImpulse[i] !== 0) {
|
|
20952
|
+
this.velocityAtWorldPoint(this._chassisBody, wheel.raycastInfo.contactPointWS, this._sv8);
|
|
20953
|
+
var vLat = Vector3.Dot(this._axle[i], this._sv8);
|
|
20954
|
+
var vFwd = Vector3.Dot(this._forwardWS[i], this._sv8);
|
|
20955
|
+
var wheelSpeedSqr = vLat * vLat + vFwd * vFwd;
|
|
20956
|
+
var minSpeed = this.arcadeSideSlipMinSpeedMps;
|
|
20957
|
+
if (wheelSpeedSqr > minSpeed * minSpeed) {
|
|
20958
|
+
var slipRad = Math.atan2(Math.abs(vLat), Math.max(Math.abs(vFwd), 1e-3));
|
|
20959
|
+
var peakRad = Tools.ToRadians(Math.max(this.arcadeSideSlipPeakDeg, 0.1));
|
|
20960
|
+
if (slipRad > peakRad) {
|
|
20961
|
+
var holdFactor = Math.sin(peakRad) / Math.max(Math.sin(slipRad), 1e-3);
|
|
20962
|
+
var falloffSpan = Math.max(this.arcadeSideSlipFalloffDeg - this.arcadeSideSlipPeakDeg, 0.1);
|
|
20963
|
+
var t = Math.min(Math.max((Tools.ToDegrees(slipRad) - this.arcadeSideSlipPeakDeg) / falloffSpan, 0.0), 1.0);
|
|
20964
|
+
var rolloff = Scalar.Lerp(1.0, this.arcadeSideSlipFalloffFactor, t);
|
|
20965
|
+
this._sideImpulse[i] *= holdFactor * rolloff;
|
|
20966
|
+
}
|
|
20967
|
+
}
|
|
20968
|
+
}
|
|
20301
20969
|
}
|
|
20302
20970
|
}
|
|
20303
20971
|
var sideFactor = 1.0;
|
|
@@ -20423,12 +21091,6 @@ export class btRaycastVehicle {
|
|
|
20423
21091
|
this._wheelInfo[wheel_i].skidInfo = Math.min(this._wheelInfo[wheel_i].skidInfo, arcadeSkidInfo);
|
|
20424
21092
|
}
|
|
20425
21093
|
}
|
|
20426
|
-
var isAnyArcadeModeActiveNow = this.isArcadeHandBrakeActive || this.isArcadeWheelSkidActive || this.isArcadeBurnoutModeActive || this.isArcadeDonutModeActive;
|
|
20427
|
-
if (this._wasAnyArcadeModeActive && !isAnyArcadeModeActiveNow) {
|
|
20428
|
-
this._postHandbrakeLogFrames = 180;
|
|
20429
|
-
this._postHandbrakeLogCounter = 0;
|
|
20430
|
-
}
|
|
20431
|
-
this._wasAnyArcadeModeActive = isAnyArcadeModeActiveNow;
|
|
20432
21094
|
if (sliding) {
|
|
20433
21095
|
for (var wheel_i = 0; wheel_i < numWheels; wheel_i++) {
|
|
20434
21096
|
if (this._sideImpulse[wheel_i] !== 0) {
|
|
@@ -20452,7 +21114,8 @@ export class btRaycastVehicle {
|
|
|
20452
21114
|
avgSideImpulse /= groundedSideCount;
|
|
20453
21115
|
var sideBlendToCoM = 0;
|
|
20454
21116
|
var absSpeedKmh = Math.abs(this._currentVehicleSpeedKmHour);
|
|
20455
|
-
|
|
21117
|
+
var arcadeSlideActive = this.isArcadeHandBrakeActive || this.isArcadeWheelSkidActive || this.isArcadeBurnoutModeActive || this.isArcadeDonutModeActive;
|
|
21118
|
+
if (!arcadeSlideActive && this.sideToSideStabilityEnabled && absSpeedKmh > this.sideToSideStabilityStartKmh) {
|
|
20456
21119
|
var sideBlendRange = this.sideToSideStabilityFullKmh - this.sideToSideStabilityStartKmh;
|
|
20457
21120
|
if (sideBlendRange > 0) {
|
|
20458
21121
|
sideBlendToCoM = 0.7 * Math.min((absSpeedKmh - this.sideToSideStabilityStartKmh) / sideBlendRange, 1.0);
|
|
@@ -20521,12 +21184,22 @@ export class btRaycastVehicle {
|
|
|
20521
21184
|
clampChassisYawRate(deltaTime) {
|
|
20522
21185
|
if (this.maximumYawRateLow <= 0 && this.maximumYawRateHigh <= 0)
|
|
20523
21186
|
return;
|
|
20524
|
-
const arcadeActive = (this.isArcadeHandBrakeActive === true || this.isArcadeBurnoutModeActive === true || this.isArcadeDonutModeActive === true
|
|
20525
|
-
if (arcadeActive === true && this.
|
|
21187
|
+
const arcadeActive = (this.isArcadeHandBrakeActive === true || this.isArcadeBurnoutModeActive === true || this.isArcadeDonutModeActive === true);
|
|
21188
|
+
if (arcadeActive === true && this.isArcadeHandBrakeActive === true && this.arcadeHandbrakeYawCapMultiplier <= 0)
|
|
21189
|
+
return;
|
|
21190
|
+
if (arcadeActive === true && this.isArcadeBurnoutModeActive === true && this.arcadeBurnoutYawCapMultiplier <= 0)
|
|
21191
|
+
return;
|
|
21192
|
+
if (arcadeActive === true && this.isArcadeDonutModeActive === true && this.arcadeDonutYawCapMultiplier <= 0)
|
|
20526
21193
|
return;
|
|
20527
21194
|
let capDeg = Scalar.Lerp(this.maximumYawRateLow, this.maximumYawRateHigh, this.smoothedGradientSpeed);
|
|
20528
|
-
if (arcadeActive === true)
|
|
20529
|
-
|
|
21195
|
+
if (arcadeActive === true) {
|
|
21196
|
+
if (this.isArcadeHandBrakeActive === true)
|
|
21197
|
+
capDeg *= this.arcadeHandbrakeYawCapMultiplier;
|
|
21198
|
+
if (this.isArcadeBurnoutModeActive === true)
|
|
21199
|
+
capDeg *= this.arcadeBurnoutYawCapMultiplier;
|
|
21200
|
+
if (this.isArcadeDonutModeActive === true)
|
|
21201
|
+
capDeg *= this.arcadeDonutYawCapMultiplier;
|
|
21202
|
+
}
|
|
20530
21203
|
if (capDeg <= 0)
|
|
20531
21204
|
return;
|
|
20532
21205
|
const capRad = Tools.ToRadians(capDeg);
|
|
@@ -22189,81 +22862,59 @@ export class RaycastVehicle {
|
|
|
22189
22862
|
this.m_vehicle.arcadeSteeringAssist = value;
|
|
22190
22863
|
}
|
|
22191
22864
|
}
|
|
22192
|
-
|
|
22193
|
-
if (this.m_vehicle != null) {
|
|
22194
|
-
return this.m_vehicle.arcadeDonutDirectYawDegPerSec;
|
|
22195
|
-
}
|
|
22196
|
-
return 1.0;
|
|
22197
|
-
}
|
|
22198
|
-
setArcadeDonutDirectYawDegPerSec(value) {
|
|
22199
|
-
if (this.m_vehicle != null) {
|
|
22200
|
-
this.m_vehicle.arcadeDonutDirectYawDegPerSec = value;
|
|
22201
|
-
}
|
|
22202
|
-
}
|
|
22203
|
-
getArcadeHandbrakeDirectYawDegPerSec() {
|
|
22204
|
-
if (this.m_vehicle != null) {
|
|
22205
|
-
return this.m_vehicle.arcadeHandbrakeDirectYawDegPerSec;
|
|
22206
|
-
}
|
|
22207
|
-
return 1.0;
|
|
22208
|
-
}
|
|
22209
|
-
setArcadeHandbrakeDirectYawDegPerSec(value) {
|
|
22210
|
-
if (this.m_vehicle != null) {
|
|
22211
|
-
this.m_vehicle.arcadeHandbrakeDirectYawDegPerSec = value;
|
|
22212
|
-
}
|
|
22213
|
-
}
|
|
22214
|
-
getArcadeHandbrakeMaxSlideAngleDeg() {
|
|
22865
|
+
getArcadeDonutDirectYawEnabled() {
|
|
22215
22866
|
if (this.m_vehicle != null) {
|
|
22216
|
-
return this.m_vehicle.
|
|
22867
|
+
return this.m_vehicle.arcadeDonutDirectYawEnabled;
|
|
22217
22868
|
}
|
|
22218
|
-
return
|
|
22869
|
+
return false;
|
|
22219
22870
|
}
|
|
22220
|
-
|
|
22871
|
+
setArcadeDonutDirectYawEnabled(value) {
|
|
22221
22872
|
if (this.m_vehicle != null) {
|
|
22222
|
-
this.m_vehicle.
|
|
22873
|
+
this.m_vehicle.arcadeDonutDirectYawEnabled = value;
|
|
22223
22874
|
}
|
|
22224
22875
|
}
|
|
22225
|
-
|
|
22876
|
+
getArcadeDonutDirectYawDegPerSec() {
|
|
22226
22877
|
if (this.m_vehicle != null) {
|
|
22227
|
-
return this.m_vehicle.
|
|
22878
|
+
return this.m_vehicle.arcadeDonutDirectYawDegPerSec;
|
|
22228
22879
|
}
|
|
22229
22880
|
return 1.0;
|
|
22230
22881
|
}
|
|
22231
|
-
|
|
22882
|
+
setArcadeDonutDirectYawDegPerSec(value) {
|
|
22232
22883
|
if (this.m_vehicle != null) {
|
|
22233
|
-
this.m_vehicle.
|
|
22884
|
+
this.m_vehicle.arcadeDonutDirectYawDegPerSec = value;
|
|
22234
22885
|
}
|
|
22235
22886
|
}
|
|
22236
|
-
|
|
22887
|
+
getArcadeHandbrakeYawCapMultiplier() {
|
|
22237
22888
|
if (this.m_vehicle != null) {
|
|
22238
|
-
return this.m_vehicle.
|
|
22889
|
+
return this.m_vehicle.arcadeHandbrakeYawCapMultiplier;
|
|
22239
22890
|
}
|
|
22240
22891
|
return 1.0;
|
|
22241
22892
|
}
|
|
22242
|
-
|
|
22893
|
+
setArcadeHandbrakeYawCapMultiplier(value) {
|
|
22243
22894
|
if (this.m_vehicle != null) {
|
|
22244
|
-
this.m_vehicle.
|
|
22895
|
+
this.m_vehicle.arcadeHandbrakeYawCapMultiplier = value;
|
|
22245
22896
|
}
|
|
22246
22897
|
}
|
|
22247
|
-
|
|
22898
|
+
getArcadeBurnoutYawCapMultiplier() {
|
|
22248
22899
|
if (this.m_vehicle != null) {
|
|
22249
|
-
return this.m_vehicle.
|
|
22900
|
+
return this.m_vehicle.arcadeBurnoutYawCapMultiplier;
|
|
22250
22901
|
}
|
|
22251
22902
|
return 1.0;
|
|
22252
22903
|
}
|
|
22253
|
-
|
|
22904
|
+
setArcadeBurnoutYawCapMultiplier(value) {
|
|
22254
22905
|
if (this.m_vehicle != null) {
|
|
22255
|
-
this.m_vehicle.
|
|
22906
|
+
this.m_vehicle.arcadeBurnoutYawCapMultiplier = value;
|
|
22256
22907
|
}
|
|
22257
22908
|
}
|
|
22258
|
-
|
|
22909
|
+
getArcadeDonutYawCapMultiplier() {
|
|
22259
22910
|
if (this.m_vehicle != null) {
|
|
22260
|
-
return this.m_vehicle.
|
|
22911
|
+
return this.m_vehicle.arcadeDonutYawCapMultiplier;
|
|
22261
22912
|
}
|
|
22262
22913
|
return 1.0;
|
|
22263
22914
|
}
|
|
22264
|
-
|
|
22915
|
+
setArcadeDonutYawCapMultiplier(value) {
|
|
22265
22916
|
if (this.m_vehicle != null) {
|
|
22266
|
-
this.m_vehicle.
|
|
22917
|
+
this.m_vehicle.arcadeDonutYawCapMultiplier = value;
|
|
22267
22918
|
}
|
|
22268
22919
|
}
|
|
22269
22920
|
getArcadeBurnoutActive() {
|
|
@@ -22525,22 +23176,6 @@ export class RaycastVehicle {
|
|
|
22525
23176
|
this.m_vehicle.wheelAtRestSpeedThresholdKmh = value;
|
|
22526
23177
|
}
|
|
22527
23178
|
}
|
|
22528
|
-
setWheelSpinDebugLogEnabled(enabled) {
|
|
22529
|
-
if (this.m_vehicle != null) {
|
|
22530
|
-
this.m_vehicle.wheelSpinDebugLogEnabled = enabled;
|
|
22531
|
-
}
|
|
22532
|
-
}
|
|
22533
|
-
getWheelSpinDebugLogEnabled() {
|
|
22534
|
-
if (this.m_vehicle != null) {
|
|
22535
|
-
return this.m_vehicle.wheelSpinDebugLogEnabled;
|
|
22536
|
-
}
|
|
22537
|
-
return false;
|
|
22538
|
-
}
|
|
22539
|
-
setWheelSpinDebugLogIntervalFrames(frames) {
|
|
22540
|
-
if (this.m_vehicle != null) {
|
|
22541
|
-
this.m_vehicle.wheelSpinDebugLogIntervalFrames = frames;
|
|
22542
|
-
}
|
|
22543
|
-
}
|
|
22544
23179
|
updateWheelInformation() {
|
|
22545
23180
|
const wheels = this.getNumWheels();
|
|
22546
23181
|
if (wheels > 0) {
|