How to remove Camera Movement Jitter

I think that is not convention, and LERPs are the correct way to achieve a camera animation system fo what you are trying to do.

turnSpeed = Math.max((1 - smoothing) * turnSpeed + smoothing * -_accel, -1) is the LERP btw.

And you say boom arm? can you explain? If you just need it to rotate I can help you with that. Sorry its 3AM I just woke up for a second, Ill look at this in the morning after coffee and make sure I read correct.

I am trying to make a playground with a sample race track and car setup. Then you can really see how things are getting used. You should be able to the test car transform from the scene. Then do whatever you gotta do to create some kinda Smooth Car Follow Camera and use the test car transform from the gltf scene at the lockedTarget

But there seems to be an issue with Ammo.JS on the Playground… As soon as @Deltakosh can figure out whats going there… i will make a Starter Racing Template Playground… Any and all parties are welcome to try and make smooth follow camera for test car…

Note the Race Track Scene and Test Car All complete with Physics And User Input to drive the car was all prepared using the Unity Exporter to gltf files which the Scene Manager then parses the scene metadata… In short… You dont have to do anything except load the GLTF… Any script components attached to entities will automatically get instantiated.

All you gotta do is make the FreeCamera follow the car transform :slight_smile:

Well i can seem to LERP but then cant figure out to get decent camera lag for turning left or right…

Just check out Need For Speed or any other driving game… The follow camera stays within its desired follow distance… and also lags a bit when turning right or left… When lerping the position… If you make the lerp ratio large enough to stay like 5 units behind car the lag for turn is too fast and it looks stiff… If i lower the lerp enough to not look stiff when turning… it lags too far behind the car and the car can get away from

Unreal Engine uses a USpringArmComponent… That is what i meant by camera boom… basically its was going to be a Spring Arm or Camera Boom with the camera attached at the end of the arm… The Spring Arm is a child of the transform and the camera is a child of Spring Arm… The spring can be rotated left and right while being dragged around track by being a child of car transform instead of trying to catch up using its own separate position.

I am trying whatever i can to create a Need For Speed style racing game for BabylonJS. I got ALOT working already for the actual driving mechanics including Gearbox, Engine Pitching, RPMS, Skidding/Drifting… Even the most koolest BabylonJS implementation of the native UnitySkidmarks system. Where it one big mesh and all the skid marks for all the cars are dynamically rendered whith virtually NO OVERHEAD.

Things i need to work on still:

1… Smooth High Speed Follow Camera
2… GPU Based Tire Smoke Particles
3… Projectors to project a fake shadow on the ground
4… Physics Logic for burning rubber, peel outs and doughnuts

I will show you when we get the playground fix for Ammo.JS

If you get me into your scene here, I bet I could script up a follow cam that you would like in a couple hours.

I could prolly help you with a custom Shader to show rubber on the ground after burnouts too.

https://playground.babylonjs.com/#TTSC4C#3

Hopefully you have a controler, If you look here though as you turn the camera swings to catch up. You make that more dramatic with a smaller LERP value.

I’m trying to get Ammo working on da playground… once that is working I can put Babylon Toolkit generated content including script components … on da playground too

Yo @Pryme8 … You probobly dont know about the SceneManager … Input Manager functionality…

All that Gamepad User input from your last Playground can be simplified like so:

var player = 1;
var horz = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.Horizontal, player);
var vert = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.Vertical, player);
var mousex = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.MouseX, player);
var mousey = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.MouseY, player);

Example updateVehicleController function that supports all the required Keyboard and or Gamepad usage… Buttons, triggers etc…

    protected updateVehicleController(): void {
        if (this.m_standardCarController != null) {
            if (this.enableInput === true) {
                this.playerDeltaX = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.Horizontal, this.playerNumber);
                this.playerDeltaY = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.Vertical, this.playerNumber);
                this.playerMouseX = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.MouseX, this.playerNumber);
                this.playerMouseY = BABYLON.SceneManager.GetUserInput(BABYLON.UserInputAxis.MouseY, this.playerNumber);

                if (Math.abs(this.playerDeltaX) <= BABYLON.UserInputOptions.GamepadDeadStickValue) this.playerDeltaX = 0;
                if (Math.abs(this.playerDeltaY) <= BABYLON.UserInputOptions.GamepadDeadStickValue) this.playerDeltaY = 0;
                if (Math.abs(this.playerMouseX) <= BABYLON.UserInputOptions.GamepadDeadStickValue) this.playerMouseX = 0;
                if (Math.abs(this.playerMouseY) <= BABYLON.UserInputOptions.GamepadDeadStickValue) this.playerMouseY = 0;

                const auxForwardKeyboard:boolean = BABYLON.SceneManager.GetKeyboardInput(this.auxKeyboardForawrd); // Player One Only
                const forwardKeyboard:boolean = BABYLON.SceneManager.GetKeyboardInput(this.keyboardForawrd); // Player One Only
                const forwardTrigger:number = BABYLON.SceneManager.GetGamepadTriggerInput(this.triggerForward, this.playerNumber);
                const auxBackwardKeyboard:boolean = BABYLON.SceneManager.GetKeyboardInput(this.auxKeyboardBackwards); // Player One Only
                const backwardKeyboard:boolean = BABYLON.SceneManager.GetKeyboardInput(this.keyboardBackwards); // Player One Only
                const backwardTrigger:number = BABYLON.SceneManager.GetGamepadTriggerInput(this.triggerBackwards, this.playerNumber);
                const handbrakeKeyboard:boolean = BABYLON.SceneManager.GetKeyboardInput(this.keyboardHandbrake); // Player One Only
                const handbrakeButton:boolean = BABYLON.SceneManager.GetGamepadButtonInput(this.buttonHandbrake, this.playerNumber);

                let playerMovement:number = 0;
                let playerSteering:number = 0;
                let playerHandbrake:boolean = false;

                // Primary Accelerattion
                if (forwardTrigger > 0.2) playerMovement = forwardTrigger;
                else if (this.playerNumber === BABYLON.PlayerNumber.One && (forwardKeyboard === true || auxForwardKeyboard === true)) playerMovement = 1;
                // Braking Takes Precedence
                if (backwardTrigger > 0.2) playerMovement = -backwardTrigger;
                else if (this.playerNumber === BABYLON.PlayerNumber.One && (backwardKeyboard === true || auxBackwardKeyboard === true)) playerMovement = -1;
                // Includes AD And Arrow Keys
                playerSteering = this.playerDeltaX; 
                // Pull Hard Hand Brake Lever
                playerHandbrake = (handbrakeKeyboard === true || handbrakeButton === true);
                // Drive Standard Car Controller
                this.m_standardCarController.drive(playerMovement, playerSteering, playerHandbrake);
            }
        }
    }

The Babylon Scene Manager User Input Functions gives you more a Unity Like game mechanics…

I can call BABYLON.SceneManager.GetUserInput in the update loop directly to get that User Input weather from keyboard and mouse, Virtual Joysticks or GamePads… Also support up to 4 Local player User Input… Its the heart of all my Local Multi Player Support… All built in… and all on the playground now :slight_smile:

Yo @Pryme8 … I wrote the most awesome Skidmark Manager Class (Again… Using Unity Style Game Mechanics… Actually ported from the UnitySkidmarks.cs)

My Babylon Version:
1…Set up a large EMPTY mesh to hold the skidmarks for all the cars
2… Each car’s wheel call AddSkidmarkSegment when the wheill it actually skidding
3… I update the actual underlying Float32Array directly adding new skid segments
4… A global update runs and applies all the Float32Array buffers Directly to mesh vertex buffer

And we end up with a High Performance skidmarks with virtually no addition overhead… Unless you make REALLY LARGE skidmark mesh… But anything under 65000 verts… About 4096 max Skidmark segments, take no additional MS

Check out my SkidmarkManager.ts


/**
 * Babylon skidmark section class (Native Bullet Physics 2.82)
 * @class SkidmarkSection
 */
class SkidmarkSection {
    public Pos:BABYLON.Vector3 = BABYLON.Vector3.Zero();
    public Normal:BABYLON.Vector3 = BABYLON.Vector3.Zero();
    public Tangent:BABYLON.Vector4 = BABYLON.Vector4.Zero();
    public Posl:BABYLON.Vector3 = BABYLON.Vector3.Zero();
    public Posr:BABYLON.Vector3 = BABYLON.Vector3.Zero();
    public Intensity:number = 0.0;
    public LastIndex:number = -1;
}
/**
 * Babylon Script Component
 * @class SkidmarkManager
 */
class SkidmarkManager extends BABYLON.ScriptComponent {
	private static MAX_MARKS:number = 4096;                                 // Max number of marks total for everyone together
    private static GROUND_OFFSET:number = 0.01;                             // Distance above surface in metres
	private static VELOCITY_SCALE:number = 0.02;                            // Velocity offset scale factor
    private static GPU_TRIANGLES:boolean = true;                            // GPU quad triangle indices only
    private static MARK_COLOR:BABYLON.Color3 = BABYLON.Color3.Black();      // Skidmark Color
	private static MARK_WIDTH:number = 0.3;                                 // Width of the Skidmarks. Should match the width of the wheels
	private static MIN_DISTANCE:number = 0.1;                               // Distance between skid texture sections in metres. Bigger = better performance, less smooth
    private static MIN_SQR_DISTANCE = (SkidmarkManager.MIN_DISTANCE * SkidmarkManager.MIN_DISTANCE);
    private static TEXTURE_MARKS:any = null;

    private static SkidBufferPositions:Float32Array = null;
    private static SkidBufferNormals:Float32Array = null;
    private static SkidBufferTangents:Float32Array = null;
    private static SkidBufferColors:Float32Array = null;
    private static SkidBufferUvs:Float32Array = null;
    private static SkidBufferIndices:Int32Array = null;
	private static SkidmarkSections:SkidmarkSection[] = null;
	private static SkidmarkIndex:number = 0;
	private static SkidmarkMesh:BABYLON.Mesh = null;
    private static SkidmarkUpdated:boolean = false;

    private static TempVector3_POS:BABYLON.Vector3 = new BABYLON.Vector3(0,0,0);
    private static TempVector3_DIR:BABYLON.Vector3 = new BABYLON.Vector3(0,0,0);
    private static TempVector3_XDIR:BABYLON.Vector3 = new BABYLON.Vector3(0,0,0);
    private static TempVector3_SDIR:BABYLON.Vector3 = new BABYLON.Vector3(0,0,0);
    public constructor(transform: BABYLON.TransformNode, scene: BABYLON.Scene, properties: any = {}) {
        super(transform, scene, properties);
        SkidmarkManager.MAX_MARKS = this.getEditorProperty("maxSections", SkidmarkManager.MAX_MARKS);
        SkidmarkManager.MARK_COLOR = BABYLON.Utilities.ParseColor3(this.getEditorProperty("textureColor", SkidmarkManager.MARK_COLOR));
        SkidmarkManager.MARK_WIDTH = this.getEditorProperty("textureWidth", SkidmarkManager.MARK_WIDTH);
        SkidmarkManager.GROUND_OFFSET = this.getEditorProperty("groundOffset", SkidmarkManager.GROUND_OFFSET);
        SkidmarkManager.VELOCITY_SCALE = this.getEditorProperty("velocityScale", SkidmarkManager.VELOCITY_SCALE);
        SkidmarkManager.GPU_TRIANGLES = this.getEditorProperty("gpuQuadIndices", SkidmarkManager.GPU_TRIANGLES);
        SkidmarkManager.TEXTURE_MARKS = this.getEditorProperty("textureMarks", SkidmarkManager.TEXTURE_MARKS);
        SkidmarkManager.MIN_DISTANCE = this.getEditorProperty("textureDistance", SkidmarkManager.MIN_DISTANCE);
        SkidmarkManager.MIN_SQR_DISTANCE = (SkidmarkManager.MIN_DISTANCE * SkidmarkManager.MIN_DISTANCE);
    }

    protected start(): void { SkidmarkManager.CreateSkidmarkManager(this.scene); }
    protected update(): void { SkidmarkManager.UpdateSkidmarkManager(); }

    public static GetVelocityScale():number { return SkidmarkManager.VELOCITY_SCALE; }

    public static AddSkidmarkSegment(pos:BABYLON.Vector3, normal:BABYLON.Vector3, intensity:number, lastIndex:number):BABYLON.Nullable<number> {
        if (SkidmarkManager.SkidmarkMesh == null) return null;
        SkidmarkManager.TempVector3_POS.set(0,0,0);
        SkidmarkManager.TempVector3_DIR.set(0,0,0);
        SkidmarkManager.TempVector3_XDIR.set(0,0,0);
        SkidmarkManager.TempVector3_SDIR.set(0,0,0);
        // ..
        if (intensity > 1.0) intensity = 1.0;
        else if (intensity < 0.0) return -1.0;
        // ..
		if (lastIndex > 0) {
            pos.subtractToRef(SkidmarkManager.SkidmarkSections[lastIndex].Pos, SkidmarkManager.TempVector3_POS);
			const sqrDistance:number = SkidmarkManager.TempVector3_POS.lengthSquared();
            if (sqrDistance < SkidmarkManager.MIN_SQR_DISTANCE) return lastIndex;
        }
        // ..
		const curSection:SkidmarkSection = SkidmarkManager.SkidmarkSections[SkidmarkManager.SkidmarkIndex];
        normal.scaleToRef(SkidmarkManager.GROUND_OFFSET, SkidmarkManager.TempVector3_POS);
        pos.addToRef(SkidmarkManager.TempVector3_POS, curSection.Pos);
		curSection.Normal.copyFrom(normal);
		curSection.Intensity = intensity;
        curSection.LastIndex = lastIndex;
        // ..
        if (lastIndex != -1) {
            const lastSection:SkidmarkSection = SkidmarkManager.SkidmarkSections[lastIndex];
            curSection.Pos.subtractToRef(lastSection.Pos, SkidmarkManager.TempVector3_DIR);
            BABYLON.Vector3.CrossToRef(SkidmarkManager.TempVector3_DIR, normal, SkidmarkManager.TempVector3_XDIR);
            SkidmarkManager.TempVector3_XDIR.normalizeToRef(SkidmarkManager.TempVector3_XDIR);
            // ..
            SkidmarkManager.TempVector3_XDIR.scaleToRef(SkidmarkManager.MARK_WIDTH * 0.5, SkidmarkManager.TempVector3_SDIR)
            curSection.Pos.addToRef(SkidmarkManager.TempVector3_SDIR, curSection.Posl);
            curSection.Pos.subtractToRef(SkidmarkManager.TempVector3_SDIR, curSection.Posr);
            curSection.Tangent.set(SkidmarkManager.TempVector3_XDIR.x, SkidmarkManager.TempVector3_XDIR.y, SkidmarkManager.TempVector3_XDIR.z, 1);
            // ..
			if (lastSection.LastIndex === -1) {
                curSection.Pos.addToRef(SkidmarkManager.TempVector3_SDIR, lastSection.Posl);
                curSection.Pos.subtractToRef(SkidmarkManager.TempVector3_SDIR, lastSection.Posr);
                lastSection.Tangent.copyFrom(curSection.Tangent);
			}
        }
        // ..
        SkidmarkManager.AddSkidmarkVertexData();
		const curIndex:number = SkidmarkManager.SkidmarkIndex;
		SkidmarkManager.SkidmarkIndex = ++SkidmarkManager.SkidmarkIndex % SkidmarkManager.MAX_MARKS;
		return curIndex;
    }
    private static CreateSkidmarkManager(scene:BABYLON.Scene):void {
        if (SkidmarkManager.SkidmarkMesh == null) {
            const skidmarkMaterial:BABYLON.StandardMaterial = new BABYLON.StandardMaterial("SkidmarkMaterial", scene);
            skidmarkMaterial.backFaceCulling = false;
            skidmarkMaterial.disableLighting = true;
            skidmarkMaterial.emissiveColor = SkidmarkManager.MARK_COLOR;
            skidmarkMaterial.diffuseColor = SkidmarkManager.MARK_COLOR;
            skidmarkMaterial.diffuseTexture = BABYLON.Utilities.ParseTexture(SkidmarkManager.TEXTURE_MARKS, scene);
            if (skidmarkMaterial.diffuseTexture != null) {
                skidmarkMaterial.diffuseTexture.hasAlpha = true;
                skidmarkMaterial.useAlphaFromDiffuseTexture = true;
            }
            skidmarkMaterial.freeze();
            SkidmarkManager.SkidmarkMesh = new BABYLON.Mesh("SkidmarkMesh", scene);
            SkidmarkManager.SkidmarkMesh.material = skidmarkMaterial;
            SkidmarkManager.SkidmarkMesh.alwaysSelectAsActiveMesh = true;
            SkidmarkManager.SkidmarkMesh.doNotSyncBoundingInfo = true;
            SkidmarkManager.SkidmarkMesh.receiveShadows = false;
            SkidmarkManager.SkidmarkMesh.checkCollisions = false;
            SkidmarkManager.SkidmarkMesh.useVertexColors = true;
            SkidmarkManager.SkidmarkMesh.hasVertexAlpha = true;
            SkidmarkManager.SkidmarkMesh.isPickable = false;
            // ..
            // Setup Skidmark Section Properties
            // ..
            SkidmarkManager.SkidmarkSections = new Array<SkidmarkSection>(SkidmarkManager.MAX_MARKS);
            for (let i = 0; i < SkidmarkManager.SkidmarkSections.length; i++) {
                SkidmarkManager.SkidmarkSections[i] = new SkidmarkSection();
            }
            // ..
            // Setup Raw Mesh Vertex Buffer Data
            // ..
            SkidmarkManager.SkidBufferPositions = new Float32Array(SkidmarkManager.MAX_MARKS * 4 * 3);
            SkidmarkManager.SkidBufferNormals = new Float32Array(SkidmarkManager.MAX_MARKS * 4 * 3);
            SkidmarkManager.SkidBufferTangents = new Float32Array(SkidmarkManager.MAX_MARKS * 4 * 4);
            SkidmarkManager.SkidBufferColors = new Float32Array(SkidmarkManager.MAX_MARKS * 4 * 4);
            SkidmarkManager.SkidBufferUvs = new Float32Array(SkidmarkManager.MAX_MARKS * 4 * 2);
            SkidmarkManager.SkidBufferIndices = new Int32Array(SkidmarkManager.MAX_MARKS * 6);
            // ..
            // Apply Raw Vertex Buffer Data To Mesh
            // ..
            const vertexData = new BABYLON.VertexData();
            vertexData.positions = SkidmarkManager.SkidBufferPositions;
            vertexData.normals = SkidmarkManager.SkidBufferNormals;
            vertexData.tangents = SkidmarkManager.SkidBufferTangents;
            vertexData.colors = SkidmarkManager.SkidBufferColors;
            vertexData.uvs = SkidmarkManager.SkidBufferUvs;            
            vertexData.indices = SkidmarkManager.SkidBufferIndices;
            vertexData.applyToMesh(SkidmarkManager.SkidmarkMesh, true);
            SkidmarkManager.SkidmarkMesh.freezeWorldMatrix();
        }
    }
    private static AddSkidmarkVertexData():void {
		const curr:SkidmarkSection = SkidmarkManager.SkidmarkSections[SkidmarkManager.SkidmarkIndex];
        if (curr.LastIndex === -1) return;
        const last:SkidmarkSection = SkidmarkManager.SkidmarkSections[curr.LastIndex];
		SkidmarkManager.SkidmarkUpdated = true;
        // ..
        // Update Position Buffers Directly
        // ..
        let index:number = SkidmarkManager.SkidmarkIndex * 4 + 0;
        SkidmarkManager.SkidBufferPositions[index * 3] = last.Posl.x;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 1] = last.Posl.y;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 2] = last.Posl.z;
        index = SkidmarkManager.SkidmarkIndex * 4 + 1;
        SkidmarkManager.SkidBufferPositions[index * 3] = last.Posr.x;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 1] = last.Posr.y;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 2] = last.Posr.z;
        index = SkidmarkManager.SkidmarkIndex * 4 + 2;
        SkidmarkManager.SkidBufferPositions[index * 3] = curr.Posl.x;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 1] = curr.Posl.y;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 2] = curr.Posl.z;
        index = SkidmarkManager.SkidmarkIndex * 4 + 3;
        SkidmarkManager.SkidBufferPositions[index * 3] = curr.Posr.x;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 1] = curr.Posr.y;
        SkidmarkManager.SkidBufferPositions[(index * 3) + 2] = curr.Posr.z;
        // ..
        // Update Normal Buffers Directly
        // ..
        index = SkidmarkManager.SkidmarkIndex * 4 + 0;
        SkidmarkManager.SkidBufferNormals[index * 3] = last.Normal.x;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 1] = last.Normal.y;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 2] = last.Normal.z;
        index = SkidmarkManager.SkidmarkIndex * 4 + 1;
        SkidmarkManager.SkidBufferNormals[index * 3] = last.Normal.x;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 1] = last.Normal.y;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 2] = last.Normal.z;
        index = SkidmarkManager.SkidmarkIndex * 4 + 2;
        SkidmarkManager.SkidBufferNormals[index * 3] = curr.Normal.x;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 1] = curr.Normal.y;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 2] = curr.Normal.z;
        index = SkidmarkManager.SkidmarkIndex * 4 + 3;
        SkidmarkManager.SkidBufferNormals[index * 3] = curr.Normal.x;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 1] = curr.Normal.y;
        SkidmarkManager.SkidBufferNormals[(index * 3) + 2] = curr.Normal.z;
        // ..
        // Update Tangent Buffers Directly
        // ..
        index = SkidmarkManager.SkidmarkIndex * 4 + 0;
        SkidmarkManager.SkidBufferTangents[index * 4] = last.Tangent.x;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 1] = last.Tangent.y;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 2] = last.Tangent.z;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 3] = last.Tangent.w;
        index = SkidmarkManager.SkidmarkIndex * 4 + 1;
        SkidmarkManager.SkidBufferTangents[index * 4] = last.Tangent.x;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 1] = last.Tangent.y;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 2] = last.Tangent.z;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 3] = last.Tangent.w;
        index = SkidmarkManager.SkidmarkIndex * 4 + 2;
        SkidmarkManager.SkidBufferTangents[index * 4] = curr.Tangent.x;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 1] = curr.Tangent.y;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 2] = curr.Tangent.z;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 3] = curr.Tangent.w;
        index = SkidmarkManager.SkidmarkIndex * 4 + 3;
        SkidmarkManager.SkidBufferTangents[index * 4] = curr.Tangent.x;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 1] = curr.Tangent.y;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 2] = curr.Tangent.z;
        SkidmarkManager.SkidBufferTangents[(index * 4) + 3] = curr.Tangent.w;
        // ..
        // Update Color Buffers Directly
        // ..
        index = SkidmarkManager.SkidmarkIndex * 4 + 0;
        SkidmarkManager.SkidBufferColors[index * 4] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 1] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 2] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 3] = last.Intensity;
        index = SkidmarkManager.SkidmarkIndex * 4 + 1;
        SkidmarkManager.SkidBufferColors[index * 4] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 1] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 2] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 3] = last.Intensity;
        index = SkidmarkManager.SkidmarkIndex * 4 + 2;
        SkidmarkManager.SkidBufferColors[index * 4] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 1] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 2] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 3] = curr.Intensity;
        index = SkidmarkManager.SkidmarkIndex * 4 + 3;
        SkidmarkManager.SkidBufferColors[index * 4] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 1] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 2] = 1.0;
        SkidmarkManager.SkidBufferColors[(index * 4) + 3] = curr.Intensity;
        // ..
        // Update Coord Buffers Directly
        // ..
        index = SkidmarkManager.SkidmarkIndex * 4 + 0;
        SkidmarkManager.SkidBufferUvs[index * 2] = 0;
        SkidmarkManager.SkidBufferUvs[(index * 2) + 1] = 0;
        index = SkidmarkManager.SkidmarkIndex * 4 + 1;
        SkidmarkManager.SkidBufferUvs[index * 2] = 1;
        SkidmarkManager.SkidBufferUvs[(index * 2) + 1] = 0;
        index = SkidmarkManager.SkidmarkIndex * 4 + 2;
        SkidmarkManager.SkidBufferUvs[index * 2] = 0;
        SkidmarkManager.SkidBufferUvs[(index * 2) + 1] = 1;
        index = SkidmarkManager.SkidmarkIndex * 4 + 3;
        SkidmarkManager.SkidBufferUvs[index * 2] = 1;
        SkidmarkManager.SkidBufferUvs[(index * 2) + 1] = 1;
        // ..
        // Update Triangle 1 Buffers Directly (QUAD)
        //..
        SkidmarkManager.SkidBufferIndices[SkidmarkManager.SkidmarkIndex * 6 + 0] = SkidmarkManager.SkidmarkIndex * 4 + 0;
        SkidmarkManager.SkidBufferIndices[SkidmarkManager.SkidmarkIndex * 6 + 2] = SkidmarkManager.SkidmarkIndex * 4 + 1;
        SkidmarkManager.SkidBufferIndices[SkidmarkManager.SkidmarkIndex * 6 + 1] = SkidmarkManager.SkidmarkIndex * 4 + 2;
        // ..
        // Update Triangle 2 Buffers Directly (QUAD)
        // ..
        SkidmarkManager.SkidBufferIndices[SkidmarkManager.SkidmarkIndex * 6 + 3] = SkidmarkManager.SkidmarkIndex * 4 + 2;
        SkidmarkManager.SkidBufferIndices[SkidmarkManager.SkidmarkIndex * 6 + 5] = SkidmarkManager.SkidmarkIndex * 4 + 1;
        SkidmarkManager.SkidBufferIndices[SkidmarkManager.SkidmarkIndex * 6 + 4] = SkidmarkManager.SkidmarkIndex * 4 + 3;
    }
    private static UpdateSkidmarkManager():void {
        if (SkidmarkManager.SkidmarkMesh != null && SkidmarkManager.SkidmarkUpdated === true) {
            SkidmarkManager.SkidmarkUpdated = false;
            if (SkidmarkManager.SkidmarkMesh.geometry != null) {
                SkidmarkManager.SkidmarkMesh.geometry.updateVerticesDataDirectly(BABYLON.VertexBuffer.PositionKind, SkidmarkManager.SkidBufferPositions, 0, false);
                SkidmarkManager.SkidmarkMesh.geometry.updateVerticesDataDirectly(BABYLON.VertexBuffer.NormalKind, SkidmarkManager.SkidBufferNormals, 0, false);
                SkidmarkManager.SkidmarkMesh.geometry.updateVerticesDataDirectly(BABYLON.VertexBuffer.TangentKind, SkidmarkManager.SkidBufferTangents, 0, false);
                SkidmarkManager.SkidmarkMesh.geometry.updateVerticesDataDirectly(BABYLON.VertexBuffer.ColorKind, SkidmarkManager.SkidBufferColors, 0, false);
                SkidmarkManager.SkidmarkMesh.geometry.updateVerticesDataDirectly(BABYLON.VertexBuffer.UVKind, SkidmarkManager.SkidBufferUvs, 0, false);
                SkidmarkManager.SkidmarkMesh.geometry.updateIndices(SkidmarkManager.SkidBufferIndices, 0, SkidmarkManager.GPU_TRIANGLES);
            }
        }
    }
}

And it works beautifully well :slight_smile:

Ill show when we get playground ammo fixed :slight_smile:

Yo @Pryme8 … I made a little video so you can get a look at it.

I dont know why my screen recorder is so shitty all of the sudden. I gotta get better screen recording software for the mac.

Anyways… Take a look to see my progress on creating native Ammo.JS Driving Physics.

All the track peices are meshes with mesh colliders (mesh impostors) using native Bullet Raycast Vehicle… Hundreds of Scene Enviroments meshes … Very detailed Mustang model… All rocking 60 FPS…

My high speed follow camera is fucked up… Thats why car so far ahead of camera… so i can at least get that turn lag… At it still jitters every now and then… But that is what you and i working on fixing… I think you be able to better make the follow camera once i get you setup in da playground with all this Physics Stuff :slight_smile:

Check out: http://mackey.cloud/files/videos/TestLap1.mp4

Yo @Pryme8 … Here is a playground to test (Once the Painting issue gets fixed)

Test Race Track Playground: Babylon.js Playground

use the WASD or ARROW Keys or Gamepad to drive the car.

The Space Bar and Gamepad X Button is For Handbrake for Skidding/Drifting

The Gamepad Triggers Accelerate and Brake As Well :slight_smile:

Try it out

1 Like

image

Something is happening… lol

Yo @MarianG found the problem. I fixed the Scene Manager… Try Again Please :slight_smile:

FYI… Here is Another Playground Test with the native Babylon Follow Camera… To show what i going for… has Jitter problems and cant have different lagging for the BEHIND position and the LEFT/RIGHT Turn .

But you can take a lap around the track… Should have Audience Audio when racing by the crowds :slight_smile:

Note: When driving try go Full Screen … Way better since the Playground Editor takes up browser window space

Also… I did in typescript and included the Babylon Scene Manager declaration file at bottom so you can get code completion and get a since of What the Scene Manager Can Do :slight_smile:

1 Like

https://www.babylonjs-playground.com/ts.html#3BIQ4K#3

It will need some fixing, but here is the basic steps. I ran into the wall and got the car on it side, and everything still works great.

I tested your scene with the default camera, I’m not seeing the jitter?

Try and get up to high speeds… Every now and then the car will jitter/skip a little forward or backwards…

I tried on both my Mac and Dell… And i always get that little jitter/skip… Not a constant jitter… but take a few laps and watch the car and the background… I can see it

But if i attach the camera as a child and drive its really smooth… But you loose all the LAGGING.

Thats why i suggested that camera boom or camera spring arm approach…

I am open to Whatever… And whoever want to give a crack… I just need to end up with SMOOTHEST high speed camera movement i can… When its choppy, its makes the game look like shit… I spent alot of time writing logic (Especially Physics Code) for Pro Vehicle SDK… And i eventually want to use my Race Game Project to create a full blown Babylon Game Development Training Course… So i want all the mechanics as perfect as possible.

And i REALLY appreciate all your help… Everyone :slight_smile:

Im just really stoked about running Pure Babylon Scene Manager content on the Playground… That just proves my point of what i been saying about the Toolkit…

Its All the Babylon Scene Manager Extension that does all the magic. The Unity Exporter is really just a fancy GLTF exporter that serializes a bit of extra metadata and it also bundles all your project script and has an OPTIONAL Preview Engine to view GLTF content… That it… Now its very detailed in its export… But it is just an Exporter (With a ton of Tooling to help create the best GLTF i can possibly make)

The Babylon Scene Manager is the real star of the show :slight_smile:

1 Like

Technically it is a camera boom setup, the height is angle the distance is the arm length.

If you want it to be more swingy then we activate the swivel that I was showing you in the first scene (the param you asked about) and we interpolate some sort of value from what the cars speed is and turn ratio.

Ok I see the jitter now:
https://www.babylonjs-playground.com/ts.html#3BIQ4K#4

hmmmm I feel like that is how the car is being updated for some reason.

https://www.babylonjs-playground.com/ts.html#3BIQ4K#5

I know this sounds like it might be dumb… but I LERPED the LERP (derpa dooo)… and that seems to of fixed the jumps so what. Which means… I dont know… I figured doing a single one with half the value would effectively do the same, but I’m not sure… I honestly think the culprit is the jumpy updates to position from the physics engine.

Btw the double LERP thing was a joke… I don’t think that would ever really fix something…

I dunno either… but try attaching camera as child of Mustang transform node with an offset to be be behind… take a few laps like that… I don’t get any skips then… So that says to me it’s not the physics moving the car that’s the problem… I would think :thinking:

I tell you though, out of the complex issues I encountered while making the toolkit… A smooth camera has been one of the more difficult things to get right.

But if we can get that going… that’s is the making of a web based racing game

What is the latest update we can do to the camera? Timing wise? I bet that’s our problem.

Give me a second

Give this a shot:
https://www.babylonjs-playground.com/ts.html#3BIQ4K#8

Another option is if you are saying that when we parent it to the car there is no chop, lets go with that, and then just manipulate its relative position and target in accordance to the cars stats, like speed and turn amount.