babylonjs-toolkit 9.16.1 → 9.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -7,7 +7,7 @@ declare namespace TOOLKIT {
7
7
  * @class SceneManager - All rights reserved (c) 2024 Mackey Kinard
8
8
  */
9
9
  class SceneManager {
10
- /** Gets the toolkit framework version string (9.16.1 - R1) */
10
+ /** Gets the toolkit framework version string (9.20.1 - R1) */
11
11
  static get Version(): string;
12
12
  /** Gets the toolkit framework copyright notice */
13
13
  static get Copyright(): string;
@@ -57,7 +57,18 @@ declare namespace TOOLKIT {
57
57
  static FogExp2DensityScale: number;
58
58
  /** Set the linear fog density scale factor */
59
59
  static FogLinearDensityScale: number;
60
- /** Set the terrain shader color correction value */
60
+ /**
61
+ * Set the terrain shader color correction value - the exponent the terrain shader applies to the
62
+ * detail (layers) atlas via `pow(color.rgb, gamma)` in sampleTextureAtlas2D. A value of 1.0 disables it.
63
+ *
64
+ * This MUST stay 1.0 while the atlas is loaded as an sRGB texture. Babylon's glTF loader derives the
65
+ * color space from the texture info - `useSRGBBuffer = !textureInfo.nonColorData` - and our detail
66
+ * atlas is color, so the GPU already performs the sRGB -> linear decode when sampling. The old 2.2
67
+ * did that conversion by hand back when the atlas arrived as a plain linear buffer; leaving it on now
68
+ * applies gamma a SECOND time (0.5 -> 0.218), which is what made terrain render far too dark while
69
+ * ordinary meshes looked correct. Hardware decode is also the better of the two, because filtering
70
+ * and mipmapping then happen in linear space.
71
+ */
61
72
  static TerrainColorCorrection: number;
62
73
  /** Set the allow camera movement flag */
63
74
  static AllowCameraMovement: boolean;
@@ -1982,6 +1993,24 @@ declare namespace TOOLKIT {
1982
1993
  */
1983
1994
  static ApplyGlobalReflectionProbeDiffuse(texture: BABYLON.BaseTexture, scene: BABYLON.Scene, createPolynomialsFromFaces?: boolean): void;
1984
1995
  /** Tracks whether the global box-projection reflection shader fix has been installed (install-once). */
1996
+ /**
1997
+ * Custom sampler names that carry DATA, not color, and must be loaded into a LINEAR buffer.
1998
+ *
1999
+ * Babylon's glTF loader decides the color space from the texture info: internally it calls
2000
+ * `_createTextureAsync(..., useSRGBBuffer: !textureInfo.nonColorData)`. Every standard glTF slot
2001
+ * (normalTexture, occlusionTexture, metallicRoughnessTexture, ...) sets `nonColorData = true` on
2002
+ * itself before loading, which is why stock materials are correct. Textures referenced from our
2003
+ * CVTOOLS `customTextures` extras have no such marking, so they all defaulted to an sRGB hardware
2004
+ * buffer - the GPU then applies an sRGB->linear decode to values that were never color.
2005
+ *
2006
+ * For the terrain that is destructive: a normal atlas texel of 0.5 decodes to 0.214, so every
2007
+ * terrain fragment gets a normal tilted ~39 degrees off, and the splat weights are gamma-crushed
2008
+ * on top of it. The result is terrain that renders far too dark while ordinary meshes look right.
2009
+ * The details/layers atlas IS color and must stay sRGB, so only mark the true data maps.
2010
+ */
2011
+ private static readonly NonColorDataSamplers;
2012
+ /** True when a custom sampler holds linear data and must not be loaded as an sRGB color texture. */
2013
+ static IsNonColorDataSampler(samplerName: string): boolean;
1985
2014
  private static _boxProjectionShaderPatched;
1986
2015
  /**
1987
2016
  * Installs a Unity-faithful per-pixel containment test into Babylon's shared `parallaxCorrectNormal`
@@ -4724,6 +4753,303 @@ declare namespace PROJECT {
4724
4753
  getRearRightWheelContactNormal(): BABYLON.Vector3;
4725
4754
  }
4726
4755
  }
4756
+ declare namespace PROJECT {
4757
+ /**
4758
+ * Babylon standard kart controller class — Mario Kart style drifting on a raycast vehicle.
4759
+ * @class StandardKartController
4760
+ *
4761
+ * =========================================================================================
4762
+ * WHAT THIS IS, AND HOW IT RELATES TO StandardCarController
4763
+ * =========================================================================================
4764
+ *
4765
+ * The same stack, with exactly ONE layer swapped. Rig it in Unity the way you rig a car:
4766
+ * a rigidbody chassis, four wheel colliders, four wheel meshes, exhaust points. Export the
4767
+ * scene as interactive glTF and the parser attaches this component.
4768
+ *
4769
+ * Havok rigidbody gravity, RAMPS, AIR, walls, kart-vs-kart << unchanged
4770
+ * TOOLKIT.RaycastVehicle suspension, wheel raycasts, ground normals << unchanged
4771
+ * wheel meshes/spinners visuals, skid, smoke << unchanged
4772
+ * ---------------------------------------------------------------------------------
4773
+ * ground-plane direction Bullet tyre friction + Ackermann steering << REPLACED
4774
+ *
4775
+ * That last line is the whole component. Jumps, ramps and flight come from Havok exactly as
4776
+ * they do for the car, because the chassis is still a Havok body being thrown off a lip.
4777
+ *
4778
+ * =========================================================================================
4779
+ * WHY THE TYRE MODEL HAS TO GO, AND NOT JUST BE RETUNED
4780
+ * =========================================================================================
4781
+ *
4782
+ * A Mario Kart drift is NOT a friction simulation, and no amount of tuning `frictionSlip`
4783
+ * produces one. It is kinematic — a decreed offset between where the kart POINTS and where it
4784
+ * GOES. Mario Kart Wii's shipped code, reimplemented function-for-function by vabold/Kinoko
4785
+ * and validated frame-perfect against world-record ghost replays, is three lines:
4786
+ *
4787
+ * // KartMove::calcDirs()
4788
+ * mat.setAxisRotation(DEG2RAD * m_outsideDriftAngle, m_smoothedUp); // travel = R(angle) * nose
4789
+ * // KartMove::calcAcceleration()
4790
+ * EGG::Vector3f nextSpeed = m_speed * m_vel1Dir; // scalar x unit vector
4791
+ * // KartMove::controlOutsideDriftAngle()
4792
+ * m_outsideDriftAngle += 150.0f * driftManualTightness; // ramped to a flat 45
4793
+ *
4794
+ * Velocity is a SCALAR along a UNIT VECTOR. There is no lateral velocity term anywhere in the
4795
+ * kart module, and `grep -rni "friction|grip|slipangle|cornering"` over Kinoko's kart source
4796
+ * returns nothing. The sideways look is produced by decree, not emerged from forces.
4797
+ *
4798
+ * So while a drift is live this component takes the ground plane: it drops `frictionSlip` to
4799
+ * `driftFrictionSlip` (the same field the car's handbrake already uses, so nothing new is
4800
+ * being invented) and writes the chassis' planar velocity direction itself. The VERTICAL
4801
+ * component is never touched, which is what leaves gravity, the suspension and every ramp
4802
+ * launch entirely to Havok.
4803
+ *
4804
+ * =========================================================================================
4805
+ * WHAT IT DOES NOT TOUCH
4806
+ * =========================================================================================
4807
+ *
4808
+ * The velocity write is planar only, decomposed against the wheels' averaged contact normal
4809
+ * rather than world up — so a drift on a banked corner stays in the road's plane instead of
4810
+ * trying to climb out of it. Airborne, it writes nothing at all: `RaycastVehicle`'s own
4811
+ * flying impulse, rise damping and auto-level own the air, and the drift angle simply holds
4812
+ * until the wheels touch down. You land still sideways and still drifting.
4813
+ */
4814
+ class StandardKartController extends TOOLKIT.ScriptComponent {
4815
+ /** Extra yaw authority during the hop. MKW `calcRotation`: a bare 1.4 literal. */
4816
+ static readonly HOP_TURN_BOOST: number;
4817
+ /** Airtime past which the drift stops controlling its angle. MKW: 5 frames. */
4818
+ static readonly AIRBORNE_ANGLE_HOLD: number;
4819
+ /** Planar speed below which the travel direction is left alone. Normalisation guard. */
4820
+ static readonly RESTING_SPEED: number;
4821
+ chassisTransform: BABYLON.TransformNode;
4822
+ exhaustLeftTransform: BABYLON.TransformNode;
4823
+ exhaustRightTransform: BABYLON.TransformNode;
4824
+ private frontLeftWheelTrans;
4825
+ private frontRightWheelTrans;
4826
+ private backLeftWheelTrans;
4827
+ private backRightWheelTrans;
4828
+ private frontLeftWheelCollider;
4829
+ private frontRightWheelCollider;
4830
+ private backLeftWheelCollider;
4831
+ private backRightWheelCollider;
4832
+ private frontLeftWheelMesh;
4833
+ private frontRightWheelMesh;
4834
+ private backLeftWheelMesh;
4835
+ private backRightWheelMesh;
4836
+ /** Top speed, metres per second. */
4837
+ topSpeed: number;
4838
+ /** Seconds from rest to top speed. */
4839
+ accelerationTime: number;
4840
+ /** Deceleration off throttle, m/s^2. */
4841
+ coastDeceleration: number;
4842
+ /** Braking deceleration, m/s^2. */
4843
+ brakeDeceleration: number;
4844
+ /** Gravity multiplier on the chassis body. Arcade karts want more than Earth. */
4845
+ gravitationalForce: number;
4846
+ /** Yaw rate at full lock while NOT drifting, radians per second. */
4847
+ steeringYawRate: number;
4848
+ /** Seconds for keyboard steering to ramp centre to full lock. */
4849
+ steeringRamp: number;
4850
+ /** Visual steering angle of the front wheels, degrees. Cosmetic. */
4851
+ maxSteeringAngle: number;
4852
+ driftEnabled: boolean;
4853
+ /**
4854
+ * Yaw rate with the stick held FULLY INTO the drift, radians per second.
4855
+ *
4856
+ * 1.658 = 95 deg/s, Mario Kart 8's measured full-lock stop. THE size of the drift: the
4857
+ * traced radius is `speed / (driftYawRate * realTurn)` and nothing else in the model moves
4858
+ * it — not the angle, not the slide, not the surface.
4859
+ */
4860
+ driftYawRate: number;
4861
+ /**
4862
+ * Stick and direction shares. `realTurn = stickShare * stick + baseShare * driftDirection`.
4863
+ *
4864
+ * MKW ships 0.4 / 0.6, which floors counter-steer at 0.2 of full lock so a held drift can
4865
+ * NEVER be straightened. Accurate, and it makes a held drift a donut — measured at 1.97
4866
+ * complete circles in eight seconds with five metres of net displacement. 0.5 / 0.5 puts
4867
+ * the floor at zero: full counter-steer drives straight while still drifting. Worse model
4868
+ * of Mario Kart Wii, better kart to drive. Set 0.4 / 0.6 for strict MKW.
4869
+ */
4870
+ driftStickShare: number;
4871
+ driftBaseShare: number;
4872
+ /** Body-vs-travel angle a drift settles at, degrees. MKW: 45, all 36 vehicles. */
4873
+ driftTargetAngle: number;
4874
+ /** Hard clamp on that angle, degrees. MKW's `startManualDrift` clamp. */
4875
+ driftMaxAngle: number;
4876
+ /** Rate the angle ramps on at, degrees per second. */
4877
+ driftAngleRampRate: number;
4878
+ /** Rate it bleeds off at after release. MKW: 0.8 deg/frame = 48 deg/s, four times slower. */
4879
+ driftAngleUnwindRate: number;
4880
+ /** Seconds the entry hop lasts. The line is frozen for all of it. */
4881
+ hopDuration: number;
4882
+ /** Upward impulse of the entry hop. */
4883
+ hopImpulse: number;
4884
+ /** Speed below which a drift cannot start or survive. MKW: 0.55 x top speed. */
4885
+ minimumDriftSpeed: number;
4886
+ /** Steering magnitude needed at touchdown to latch a direction. */
4887
+ driftLatchThreshold: number;
4888
+ /** Fraction of speed kept per second while drifting. 1 makes drifting free. */
4889
+ driftSpeedRetention: number;
4890
+ /**
4891
+ * Wheel friction while the drift owns the ground plane.
4892
+ *
4893
+ * Near-zero, and it must be: the tyres and the decree cannot both be right about the same
4894
+ * axis in the same step. Left at its normal value the friction hauls the velocity back
4895
+ * toward the nose that the decree keeps pushing it away from, at over 100 m/s^2 — most of
4896
+ * it pointing down the path, so it reads as the kart braking hard in every corner.
4897
+ */
4898
+ driftFrictionSlip: number;
4899
+ miniTurboEnabled: boolean;
4900
+ /** Seconds of drift for each of the three spark stages. */
4901
+ miniTurboTime: number;
4902
+ superMiniTurboTime: number;
4903
+ ultraMiniTurboTime: number;
4904
+ /** Boost speed multiplier and duration per stage. */
4905
+ boostSpeedFactor: number;
4906
+ boostDurationMini: number;
4907
+ boostDurationSuper: number;
4908
+ boostDurationUltra: number;
4909
+ /** Planar pace, m/s. */
4910
+ get currentSpeed(): number;
4911
+ /** Nose minus travel, radians. THE number: how sideways the kart actually is. */
4912
+ get slideAngle(): number;
4913
+ /** 0 none, 1 hop, 2 drifting. */
4914
+ get driftState(): number;
4915
+ /** -1 left, +1 right, 0 unlatched. */
4916
+ get driftDirection(): number;
4917
+ /** 0 none, 1 mini, 2 super, 3 ultra. */
4918
+ get driftStage(): number;
4919
+ /** True while no wheel is in contact. */
4920
+ get isAirborne(): boolean;
4921
+ private _rigidbody;
4922
+ private _raycastVehicle;
4923
+ private _engineAudioSource;
4924
+ private FRONT_LEFT;
4925
+ private FRONT_RIGHT;
4926
+ private BACK_LEFT;
4927
+ private BACK_RIGHT;
4928
+ private _defaultFriction;
4929
+ private _steer;
4930
+ private _throttle;
4931
+ private _brake;
4932
+ private _driftHeld;
4933
+ private _driftWasHeld;
4934
+ /** Last values handed in by `drive()`. Consumed by `readInput` each frame. */
4935
+ private _steerRequest;
4936
+ private _driftRequest;
4937
+ private _externalBoost;
4938
+ private _driftState;
4939
+ private _driftDirection;
4940
+ private _driftStage;
4941
+ private _driftTime;
4942
+ private _hopTime;
4943
+ private _hopSteer;
4944
+ private _hopPending;
4945
+ /**
4946
+ * Body-vs-travel angle, radians, signed in the world frame.
4947
+ *
4948
+ * NEGATIVE means travel lies to the LEFT of the nose, which is a RIGHT-hand drift: nose
4949
+ * turned into the corner, tail hung out. Signed in the world rather than outward-positive
4950
+ * because it is used to ROTATE a vector, and because it has to survive `_driftDirection`
4951
+ * going to zero — the unwind runs for the best part of a second after the drift ends.
4952
+ */
4953
+ private _driftAngle;
4954
+ private _yawRate;
4955
+ private _planarSpeed;
4956
+ private _groundedWheels;
4957
+ private _airborneTime;
4958
+ private _boostTimer;
4959
+ private _boostFactor;
4960
+ private readonly _groundNormal;
4961
+ private readonly _groundForward;
4962
+ private readonly _velocity;
4963
+ private readonly _angular;
4964
+ private readonly _travel;
4965
+ private readonly _scratchA;
4966
+ private readonly _scratchB;
4967
+ private readonly _worldMatrix;
4968
+ protected awake(): void;
4969
+ protected start(): void;
4970
+ protected update(): void;
4971
+ protected step(): void;
4972
+ protected destroy(): void;
4973
+ /** Reads every inspector property. Mirrors StandardCarController's own awake. */
4974
+ protected awakeKartState(): void;
4975
+ /** Resolves the rig and takes hold of the raycast vehicle. */
4976
+ protected initKartState(): void;
4977
+ /** Maps the Unity wheel colliders onto raycast vehicle wheel indexes, and binds the meshes. */
4978
+ private resolveWheelIndexes;
4979
+ /** Remembers the rig's authored friction so the drift can hand it back exactly. */
4980
+ private captureDefaultFriction;
4981
+ protected destroyKartState(): void;
4982
+ protected updateKartState(): void;
4983
+ /**
4984
+ * THE INPUT ENTRY POINT. Same contract as `StandardCarController.drive`.
4985
+ *
4986
+ * This component reads NO input of its own, deliberately: in this library the vehicle
4987
+ * controller is a driver, and `VehicleInputController` is the thing that decides what the
4988
+ * driver is being told to do — keyboard, gamepad, pedals, a network peer or an AI. One
4989
+ * surface, any number of writers, and the AI needs no second code path.
4990
+ *
4991
+ * @param throttle -1..1. Positive drives, negative brakes and then reverses.
4992
+ * @param steering -1..1. Ramped internally by `steeringRamp`; pass it raw.
4993
+ * @param drifting the drift control, HELD not tapped. Press opens the hop, release ends
4994
+ * the drift and pays out whatever mini-turbo was charged.
4995
+ * @param booster 0..1 external boost, from an item or a pad. Scales top speed.
4996
+ */
4997
+ drive(throttle: number, steering: number, drifting: boolean, booster?: number): void;
4998
+ /** Consumes the last `drive()` call. Ramping happens here so it is frame-rate correct. */
4999
+ private readInput;
5000
+ /**
5001
+ * Averages the wheels' contact normals and builds the ground-plane basis.
5002
+ *
5003
+ * From `RaycastVehicle`'s own raycasts, not a second cast of our own — the suspension has
5004
+ * already paid for those and a separate probe could disagree with the wheels the kart is
5005
+ * visibly standing on.
5006
+ */
5007
+ private readGroundFrame;
5008
+ private advanceMachine;
5009
+ private updateDriftStage;
5010
+ private releaseDrift;
5011
+ /**
5012
+ * Integrates the drift angle. MKW `controlOutsideDriftAngle`, and it really is this short:
5013
+ * a constant-rate ramp toward a constant, with no stick, no speed and no surface in it.
5014
+ *
5015
+ * That is the fact most imitations get wrong. Making the angle a function of steering means
5016
+ * steering out of a drift flattens it, so the flattest drift is the one a player driving
5017
+ * the racing line holds all the way round the corner.
5018
+ */
5019
+ private integrateDriftAngle;
5020
+ /** True while this component, rather than the tyres, decides where the kart is going. */
5021
+ private ownsGroundPlane;
5022
+ /**
5023
+ * Hands the tyres' lateral grip out of the way for exactly as long as the decree lasts.
5024
+ *
5025
+ * Both halves must agree or the model breaks in a way that is very hard to see: grip
5026
+ * pulling the velocity back toward a nose the decree keeps pushing it away from, at over
5027
+ * 100 m/s^2, most of it down the path — so it reads as mysterious braking, not as a fight.
5028
+ */
5029
+ private applyFrictionOwnership;
5030
+ private updateBoost;
5031
+ protected stepKartState(): void;
5032
+ /** Pace is a scalar, start to finish. There is no lateral velocity term anywhere. */
5033
+ private integratePace;
5034
+ /**
5035
+ * THE DECREE. Sets the ground-plane velocity to `speed * R(driftAngle) * nose`.
5036
+ *
5037
+ * Rodrigues about the ground normal for a vector already in that plane: `R(v) = v cos +
5038
+ * (n x v) sin`. Decomposed against the CONTACT normal rather than world up, so a drift on
5039
+ * a banked corner stays in the road's plane. The component along the normal is preserved
5040
+ * untouched, which is what leaves gravity, the suspension and the hop entirely alone.
5041
+ */
5042
+ private applyTravelDirection;
5043
+ /** Replaces only the yaw about the ground normal. Pitch and roll stay with the suspension. */
5044
+ private applyYawRate;
5045
+ /** Front wheels turn with the stick. Purely cosmetic — the yaw is decreed, not steered. */
5046
+ private updateWheelVisuals;
5047
+ private restoreFriction;
5048
+ /** Hard reset. MKW's `clearDrift` — respawn and control locks, where the pose must vanish. */
5049
+ clearDrift(): void;
5050
+ private static MoveTowards;
5051
+ }
5052
+ }
4727
5053
  declare namespace PROJECT {
4728
5054
  /**
4729
5055
  * Babylon Script Component
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babylonjs-toolkit",
3
- "version": "9.16.1",
3
+ "version": "9.20.1",
4
4
  "description": "Babylon Toolkit Runtime Library (UMD)",
5
5
  "author": "MackeyK24 <mackeyk24@gmail.com>",
6
6
  "main": "lib/babylon.toolkit.js",
@@ -29,13 +29,13 @@
29
29
  "lib/**/*"
30
30
  ],
31
31
  "devDependencies": {
32
- "babylonjs": "9.16.0",
33
- "babylonjs-gltf2interface": "9.16.0",
34
- "babylonjs-gui": "9.16.0",
35
- "babylonjs-addons": "9.16.0",
36
- "babylonjs-loaders": "9.16.0",
37
- "babylonjs-materials": "9.16.0",
38
- "babylonjs-inspector": "9.16.0",
32
+ "babylonjs": "9.20.0",
33
+ "babylonjs-gltf2interface": "9.20.0",
34
+ "babylonjs-gui": "9.20.0",
35
+ "babylonjs-addons": "9.20.0",
36
+ "babylonjs-loaders": "9.20.0",
37
+ "babylonjs-materials": "9.20.0",
38
+ "babylonjs-inspector": "9.20.0",
39
39
  "abitbol": "^1.0.3",
40
40
  "lodash": "^4.17.21",
41
41
  "rimraf": "^6.0.1",
@@ -45,12 +45,12 @@
45
45
  "webpack-cli": "^5.1.4"
46
46
  },
47
47
  "peerDependencies": {
48
- "babylonjs": "9.16.0",
49
- "babylonjs-gltf2interface": "9.16.0",
50
- "babylonjs-gui": "9.16.0",
51
- "babylonjs-addons": "9.16.0",
52
- "babylonjs-loaders": "9.16.0",
53
- "babylonjs-materials": "9.16.0",
54
- "babylonjs-inspector": "9.16.0"
48
+ "babylonjs": "9.20.0",
49
+ "babylonjs-gltf2interface": "9.20.0",
50
+ "babylonjs-gui": "9.20.0",
51
+ "babylonjs-addons": "9.20.0",
52
+ "babylonjs-loaders": "9.20.0",
53
+ "babylonjs-materials": "9.20.0",
54
+ "babylonjs-inspector": "9.20.0"
55
55
  }
56
56
  }