diff --git a/src/ts/butterfly/butterflyMaterial.ts b/src/ts/butterfly/butterflyMaterial.ts index 35232b6..16b7380 100644 --- a/src/ts/butterfly/butterflyMaterial.ts +++ b/src/ts/butterfly/butterflyMaterial.ts @@ -8,8 +8,9 @@ import butterflyTexture from "../../assets/butterfly.png"; import { Texture } from "@babylonjs/core/Materials/Textures/texture"; import { TransformNode } from "@babylonjs/core/Meshes/transformNode"; import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; -export function createButterflyMaterial(player: TransformNode, light: DirectionalLight, scene: Scene) { +export function createButterflyMaterial(light: DirectionalLight, scene: Scene, player?: TransformNode) { const shaderName = "butterflyMaterial"; Effect.ShadersStore[`${shaderName}FragmentShader`] = butterflyFragment; Effect.ShadersStore[`${shaderName}VertexShader`] = butterflyVertex; @@ -28,7 +29,8 @@ export function createButterflyMaterial(player: TransformNode, light: Directiona let elapsedSeconds = 0; scene.onBeforeRenderObservable.add(() => { elapsedSeconds += scene.getEngine().getDeltaTime() / 1000; - butterflyMaterial.setVector3("playerPosition", player.position); + const playerPosition = player?.position ?? new Vector3(0, 500, 0); + butterflyMaterial.setVector3("playerPosition", playerPosition); butterflyMaterial.setFloat("time", elapsedSeconds); }); diff --git a/src/ts/flatfield.ts b/src/ts/flatfield.ts index 0148e1c..8718cb0 100644 --- a/src/ts/flatfield.ts +++ b/src/ts/flatfield.ts @@ -103,7 +103,7 @@ const butterfly = createButterfly(scene); butterfly.position.y = 1; butterfly.isVisible = false; -const butterflyMaterial = createButterflyMaterial(character, light, scene); +const butterflyMaterial = createButterflyMaterial(light, scene, character); butterfly.material = butterflyMaterial; const butterflyPatch = ThinInstancePatch.CreateSquare(Vector3.Zero(), patchSize * fieldRadius * 2, 100); diff --git a/src/ts/index.ts b/src/ts/index.ts index ee0f1f1..ef6a80c 100644 --- a/src/ts/index.ts +++ b/src/ts/index.ts @@ -23,19 +23,20 @@ import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera"; import windSound from "../assets/wind.mp3"; -import "@babylonjs/core/Collisions/collisionCoordinator"; import "@babylonjs/core/Audio/audioSceneComponent"; import "@babylonjs/core/Audio/audioEngine"; import { Sound } from "@babylonjs/core/Audio/sound"; import { Engine } from "@babylonjs/core/Engines/engine"; -import "@babylonjs/core/Physics/physicsEngineComponent"; import { PatchManager } from "./instancing/patchManager"; import { downSample, randomDownSample } from "./utils/matrixBuffer"; import { Terrain } from "./terrain/terrain"; +import "@babylonjs/core/Collisions/collisionCoordinator"; +import "@babylonjs/core/Physics/physicsEngineComponent"; import HavokPhysics from "@babylonjs/havok"; import { HavokPlugin } from "@babylonjs/core/Physics/v2/Plugins/havokPlugin"; + import { IPatch } from "./instancing/iPatch"; import { InstancePatch } from "./instancing/instancePatch"; import { TerrainChunk } from "./terrain/terrainChunk"; @@ -92,7 +93,7 @@ const butterfly = createButterfly(scene); butterfly.position.y = 1; butterfly.isVisible = false; -const butterflyMaterial = createButterflyMaterial(character, light, scene); +const butterflyMaterial = createButterflyMaterial(light, scene, character); butterfly.material = butterflyMaterial; const tree = await createTree(scene); diff --git a/src/ts/planet.ts b/src/ts/planet.ts index d74716e..6f9d3fe 100644 --- a/src/ts/planet.ts +++ b/src/ts/planet.ts @@ -16,6 +16,13 @@ import "@babylonjs/core/Misc/screenshotTools"; import { Tools } from "@babylonjs/core/Misc/tools"; import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight"; +import "@babylonjs/core/Collisions/collisionCoordinator"; +import "@babylonjs/core/Physics/physicsEngineComponent"; +import HavokPhysics from "@babylonjs/havok"; +import { HavokPlugin } from "@babylonjs/core/Physics/v2/Plugins/havokPlugin"; +import { createCharacterController } from "./utils/character"; +import { setUpVector } from "./utils/algebra"; + // Init babylonjs const canvas = document.getElementById("renderer") as HTMLCanvasElement; canvas.width = window.innerWidth; @@ -24,9 +31,15 @@ canvas.height = window.innerHeight; const engine = new Engine(canvas, true); engine.displayLoadingUI(); +const havokInstance = await HavokPhysics(); +const havokPlugin = new HavokPlugin(true, havokInstance); + const scene = new Scene(engine); +scene.enablePhysics(Vector3.Zero(), havokPlugin); -const camera = new ArcRotateCamera("camera", 0, 1.4, 10, Vector3.Zero(), scene); +const planetRadius = 10; + +const camera = new ArcRotateCamera("camera", 0, 1.4, 6, Vector3.Zero(), scene); camera.attachControl(); const light = new DirectionalLight("light", new Vector3(1, -2, -2).normalize(), scene); @@ -35,8 +48,18 @@ ambient.intensity = 0.2; createSkybox(scene, light.direction.scale(-1)); +const character = await createCharacterController(scene, camera); +scene.onAfterPhysicsObservable.add(() => { + setUpVector(character, character.position.subtract(planet.node.position).normalize()); + + camera.upVector = character.up; + + character.computeWorldMatrix(true); + camera.getViewMatrix(true); +}); + // Interesting part starts here -const planet = new Planet(4, scene); +const planet = new Planet(planetRadius, scene); document.addEventListener("keypress", (e) => { if (e.key === "p") { diff --git a/src/ts/planet/planetChunk.ts b/src/ts/planet/planetChunk.ts index 6fa5ecd..28ce9c0 100644 --- a/src/ts/planet/planetChunk.ts +++ b/src/ts/planet/planetChunk.ts @@ -9,8 +9,12 @@ import { createGrassMaterial } from "../grass/grassMaterial"; import { DirectionalLight } from "@babylonjs/core/Lights/directionalLight"; import { ThinInstancePatch } from "../instancing/thinInstancePatch"; import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder"; -import { downSample, randomDownSample } from "../utils/matrixBuffer"; +import { downSample } from "../utils/matrixBuffer"; import { createTree } from "../utils/tree"; +import { createButterfly } from "../butterfly/butterfly"; +import { createButterflyMaterial } from "../butterfly/butterflyMaterial"; +import { PhysicsAggregate } from "@babylonjs/core/Physics/v2/physicsAggregate"; +import { PhysicsShapeType } from "@babylonjs/core/Physics/v2/IPhysicsEnginePlugin"; import { InstancePatch } from "../instancing/instancePatch"; export enum Direction { @@ -163,39 +167,48 @@ export class PlanetChunk { vertexData.applyToMesh(this.mesh); + new PhysicsAggregate(this.mesh, PhysicsShapeType.MESH, { mass: 0 }, scene); + this.scatterAssets(scene); } scatterAssets(scene: Scene) { const grassBlade = createGrassBlade(scene, 2); grassBlade.isVisible = false; - const grassMaterial = createGrassMaterial(scene.lights[0] as DirectionalLight, scene); - grassBlade.material = grassMaterial; + grassBlade.material = createGrassMaterial(scene.lights[0] as DirectionalLight, scene); const patch = new ThinInstancePatch(this.mesh.position, this.alignedInstancesMatrixBuffer); patch.createInstances(grassBlade); - createTree(scene).then((tree) => { + /*createTree(scene).then((tree) => { tree.scaling.scaleInPlace(3); tree.position.y = -1; tree.bakeCurrentTransformIntoVertices(); tree.isVisible = false; - const treePatch = new ThinInstancePatch(this.mesh.position, downSample(this.instancesMatrixBuffer, 10000)); + const treePatch = new ThinInstancePatch(this.mesh.position, downSample(this.instancesMatrixBuffer, 20000)); treePatch.createInstances(tree); - }); + });*/ const cube = MeshBuilder.CreateBox("cube", { size: 1 }, scene); cube.position.y = 0.5; cube.bakeCurrentTransformIntoVertices(); cube.isVisible = false; - const cubePatch = new ThinInstancePatch(this.mesh.position, downSample(this.instancesMatrixBuffer, 3000)); + cube.checkCollisions = true; + const cubePatch = new InstancePatch(this.mesh.position, downSample(this.instancesMatrixBuffer, 5000)); cubePatch.createInstances(cube); + /*const butterfly = createButterfly(scene); + //butterfly.position.y = 1; + //butterfly.bakeCurrentTransformIntoVertices(); + butterfly.material = createButterflyMaterial(scene.lights[0] as DirectionalLight, scene); + butterfly.isVisible = false; + const butterflyPatch = new ThinInstancePatch(this.mesh.position, downSample(this.instancesMatrixBuffer, 1000)); + butterflyPatch.createInstances(butterfly);*/ } } function terrainFunction(position: Vector3): [height: number, grad: Vector3] { - const heightMultiplier = 0.2; + const heightMultiplier = 0.2 * 0; const frequency = 3; const height = Math.cos(position.x * frequency) * Math.sin(position.y * frequency) * Math.cos(position.z * frequency) * heightMultiplier; const gradX = -Math.sin(position.x * frequency) * Math.sin(position.y * frequency) * Math.cos(position.z * frequency) * frequency * heightMultiplier; diff --git a/src/ts/utils/algebra.ts b/src/ts/utils/algebra.ts index eff1e4b..bfee105 100644 --- a/src/ts/utils/algebra.ts +++ b/src/ts/utils/algebra.ts @@ -1,7 +1,15 @@ import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { TransformNode } from "@babylonjs/core/Meshes/transformNode"; export function getTransformationQuaternion(from: Vector3, to: Vector3): Quaternion { const rotationAxis = Vector3.Cross(from, to); const angle = Math.acos(Vector3.Dot(from, to)); return Quaternion.RotationAxis(rotationAxis, angle); +} + +export function setUpVector(transformNode: TransformNode, newUpVector: Vector3) { + const currentUpVector = transformNode.up; + const rotationQuaternion = getTransformationQuaternion(currentUpVector, newUpVector); + if(transformNode.rotationQuaternion === null) transformNode.rotationQuaternion = rotationQuaternion; + else transformNode.rotationQuaternion = rotationQuaternion.multiply(transformNode.rotationQuaternion); } \ No newline at end of file diff --git a/src/ts/utils/character.ts b/src/ts/utils/character.ts index 34deba8..014b480 100644 --- a/src/ts/utils/character.ts +++ b/src/ts/utils/character.ts @@ -13,6 +13,7 @@ import { TransformNode } from "@babylonjs/core/Meshes/transformNode"; import { PhysicsRaycastResult } from "@babylonjs/core/Physics/physicsRaycastResult"; import { PhysicsEngineV2 } from "@babylonjs/core/Physics/v2"; import { ActionManager, ExecuteCodeAction } from "@babylonjs/core/Actions"; +import { setUpVector } from "./algebra"; export async function createCharacterController(scene: Scene, camera: ArcRotateCamera): Promise { const result = await SceneLoader.ImportMeshAsync("", "", character, scene); @@ -115,20 +116,12 @@ export async function createCharacterController(scene: Scene, camera: ArcRotateC } // downward raycast - const start = hero.position.add(Vector3.Up().scale(50)); - const end = hero.position.add(Vector3.Down().scale(50)); + const start = hero.position.add(hero.up.scale(50)); + const end = hero.position.add(hero.up.scale(-50)); (scene.getPhysicsEngine() as PhysicsEngineV2).raycastToRef(start, end, raycastResult); if (raycastResult.hasHit) { hero.position.y = raycastResult.hitPointWorld.y + 0.01; } - - // camera down raycast - const cameraStart = camera.position.add(Vector3.Up().scale(50)); - const cameraEnd = camera.position.add(Vector3.Down().scale(50)); - (scene.getPhysicsEngine() as PhysicsEngineV2).raycastToRef(cameraStart, cameraEnd, raycastResult); - if (raycastResult.hasHit) { - camera.position.y = raycastResult.hitPointWorld.y + 0.01; - } }); return hero;