Skip to content

Commit

Permalink
1.0
Browse files Browse the repository at this point in the history
- added autodeploy
- added tree shaking
- added wind background sound
  • Loading branch information
BarthPaleologue committed Nov 8, 2023
1 parent 1d7eb5f commit 7cc2940
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 20 deletions.
70 changes: 70 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Sample workflow for building and deploying a website to GitHub Pages
name: Build & Deploy site to Pages

on:
# Runs on pushes targeting the default branch
push:
branches: ["main", "master"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: "16"
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Restore cache
uses: actions/cache@v3
with:
path: |
.cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Build with Webpack
run: pnpm run build
- name: debug
run: ls -la
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: ./dist

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# BabylonJS Template
# Asset Scattering

[![NodeJS with Webpack](https://github.com/BarthPaleologue/babylonjs-template/actions/workflows/webpack.yml/badge.svg)](https://github.com/BarthPaleologue/babylonjs-template/actions/workflows/webpack.yml)

A simple template to quick start a BabylonJS project with a working webpack config out of the box and prettier and eslint.
![Screenshot](./cover.png)

This project has been created using **webpack-cli**, you can now run
An asset scattering system built with BabylonJS.

The grass rendering is based on [this video]() from Simon Dev. It is itself based on [this gdc conference]().

The wind sound effect is from [this video](https://www.youtube.com/watch?v=a3aFMAalCpk).

```
npm run build
Expand Down
Binary file added cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/wind.mp3
Binary file not shown.
5 changes: 4 additions & 1 deletion src/ts/character.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import {ArcRotateCamera} from "@babylonjs/core/Cameras/arcRotateCamera";
import character from "../assets/character.glb";
import {Vector3} from "@babylonjs/core/Maths/math.vector";

import "@babylonjs/core/Animations/animatable";

import "@babylonjs/loaders/glTF/2.0/glTFLoader";
import {AbstractMesh, TransformNode} from "@babylonjs/core";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";

export async function createCharacterController(scene: Scene, camera: ArcRotateCamera, inputMap: Map<string, boolean>): Promise<AbstractMesh> {
const result = await SceneLoader.ImportMeshAsync("", "", character, scene);
Expand Down
34 changes: 29 additions & 5 deletions src/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import "@babylonjs/core/Loading/loadingScreen";

import {ActionManager, ExecuteCodeAction} from "@babylonjs/core/Actions";

import {MeshBuilder} from "@babylonjs/core/Meshes/meshBuilder";
import {Tools} from "@babylonjs/core/Misc/tools";
import {StandardMaterial} from "@babylonjs/core/Materials/standardMaterial";
import {DirectionalLight} from "@babylonjs/core/Lights/directionalLight";
import {HemisphericLight} from "@babylonjs/core/Lights/hemisphericLight";

import "../styles/index.scss";
import {createGrassBlade} from "./grassBlade";
import {ArcRotateCamera, DirectionalLight, HemisphericLight, MeshBuilder, StandardMaterial} from "@babylonjs/core";
import {createGrassMaterial} from "./grassMaterial";

import perlinNoise from "../assets/perlin.png";
Expand All @@ -18,12 +23,19 @@ import {UI} from "./ui";
import {createCharacterController} from "./character";
import {ThinInstancePatch} from "./thinInstancePatch";
import {Mesh} from "@babylonjs/core/Meshes/mesh";
import {ArcRotateCamera} from "@babylonjs/core/Cameras/arcRotateCamera";

import windSound from "../assets/wind.mp3";

import "@babylonjs/core/Audio/audioSceneComponent";
import "@babylonjs/core/Audio/audioEngine";
import {Sound} from "@babylonjs/core/Audio/sound";

const canvas = document.getElementById("renderer") as HTMLCanvasElement;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const engine = new Engine(canvas);
const engine = new Engine(canvas, true, undefined, true);
engine.displayLoadingUI();

const scene = new Scene(engine);
Expand All @@ -38,6 +50,11 @@ scene.actionManager.registerAction(new ExecuteCodeAction(ActionManager.OnKeyUpTr
inputMap.set(e.sourceEvent.key, e.sourceEvent.type == "keydown");
}));

new Sound("wind", windSound, scene, null, {
loop: true,
autoplay: true
})

const camera = new ArcRotateCamera("camera", 0, 1.4, 15, Vector3.Zero(), scene);
camera.minZ = 0.1;
camera.attachControl();
Expand All @@ -64,15 +81,15 @@ lowQualityGrassBlade.material = material;

const patchSize = 10;
const patchResolution = patchSize * 5;
const fieldRadius = 18;
const fieldRadius = 17;

const bladeMeshFromLod = new Array<Mesh>(2);
bladeMeshFromLod[0] = lowQualityGrassBlade;
bladeMeshFromLod[1] = highQualityGrassBlade;

const grassScatterer = new ThinInstanceScatterer(bladeMeshFromLod, fieldRadius, patchSize, patchResolution, (patch: ThinInstancePatch) => {
const distance = Vector3.Distance(patch.position, camera.position);
return distance < patchSize * 2 ? 1 : 0;
return distance < patchSize * 3 ? 1 : 0;
});

const ground = MeshBuilder.CreateGround("ground", {
Expand All @@ -87,6 +104,13 @@ ground.material = groundMaterial;

const ui = new UI(scene);

document.addEventListener("keypress", (e) => {
if (e.key === "p") {
// take screenshot
Tools.CreateScreenshot(engine, camera, {precision: 1});
}
});

let clock = 0;

function updateScene() {
Expand All @@ -97,7 +121,7 @@ function updateScene() {
material.setVector3("cameraPosition", camera.position);
material.setFloat("time", clock);

ui.setText(`${grassScatterer.getNbThinInstances().toLocaleString()} grass blades | ${engine.getFps().toFixed(0)} FPS`);
ui.setText(`${grassScatterer.getNbThinInstances().toLocaleString()} grass blades\n${grassScatterer.getNbVertices().toLocaleString()} vertices | ${engine.getFps().toFixed(0)} FPS`);

grassScatterer.update(character.position);
}
Expand Down
3 changes: 2 additions & 1 deletion src/ts/modules.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
declare module "*.glsl";
declare module "*.png";
declare module "*.glb";
declare module "*.glb";
declare module "*.mp3"
2 changes: 1 addition & 1 deletion src/ts/skybox.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Scene} from "@babylonjs/core/scene";
import {SkyMaterial} from "@babylonjs/materials";
import {Vector3} from "@babylonjs/core/Maths/math.vector";
import {MeshBuilder} from "@babylonjs/core";
import {MeshBuilder} from "@babylonjs/core/Meshes/meshBuilder";

export function createSkybox(scene: Scene, sunPosition: Vector3) {
const skyMaterial = new SkyMaterial("skyMaterial", scene);
Expand Down
8 changes: 8 additions & 0 deletions src/ts/thinInstancePatch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {Matrix, Quaternion, Vector3} from "@babylonjs/core/Maths/math.vector";
import {Mesh} from "@babylonjs/core/Meshes/mesh";
import "@babylonjs/core/Meshes/thinInstanceMesh";
import {TransformNode} from "@babylonjs/core/Meshes/transformNode";

export class ThinInstancePatch {
private baseMesh: Mesh | null = null;
Expand Down Expand Up @@ -46,12 +48,18 @@ export class ThinInstancePatch {

public createThinInstances(baseMesh: Mesh) {
this.clearThinInstances();
if(this.baseMesh !== null) this.baseMesh.dispose();
this.baseMesh = baseMesh.clone();
this.baseMesh.makeGeometryUnique();
this.baseMesh.isVisible = true;
this.baseMesh.thinInstanceSetBuffer("matrix", this.matrixBuffer, 16);
}

public getTransform(): TransformNode {
if(this.baseMesh === null) throw new Error("Cannot get the transform of a patch without thin instances");
return this.baseMesh;
}

public getNbThinInstances() {
if(this.baseMesh === null) return 0;
return this.baseMesh.thinInstanceCount;
Expand Down
24 changes: 17 additions & 7 deletions src/ts/thinInstanceScatterer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import {Vector3} from "@babylonjs/core/Maths/math.vector";
import {ThinInstancePatch} from "./thinInstancePatch";

export class ThinInstanceScatterer {
readonly meshesFromLod: Mesh[];
readonly map = new Map<Vector3, [ThinInstancePatch, number]>();
readonly patchSize: number;
readonly patchResolution: number;
readonly radius: number;
private readonly meshesFromLod: Mesh[];
private readonly nbVertexFromLod: number[];
private readonly map = new Map<Vector3, [ThinInstancePatch, number]>();
private readonly patchSize: number;
private readonly patchResolution: number;
private readonly radius: number;

private lodUpdateCadence = 1;

Expand All @@ -16,6 +17,7 @@ export class ThinInstanceScatterer {

constructor(meshesFromLod: Mesh[], radius: number, patchSize: number, patchResolution: number, computeLodLevel = (patch: ThinInstancePatch) => 0) {
this.meshesFromLod = meshesFromLod;
this.nbVertexFromLod = this.meshesFromLod.map((mesh) => mesh.getTotalVertices());
this.patchSize = patchSize;
this.patchResolution = patchResolution;
this.radius = radius;
Expand All @@ -26,8 +28,8 @@ export class ThinInstanceScatterer {
const radiusSquared = x * x + z * z;
if (radiusSquared >= this.radius * this.radius) continue;

const patchPosition = new Vector3(x * patchSize, 0, z * patchSize);
const patch = new ThinInstancePatch(patchPosition, patchSize, patchResolution);
const patchPosition = new Vector3(x * this.patchSize, 0, z * this.patchSize);
const patch = new ThinInstancePatch(patchPosition, this.patchSize, this.patchResolution);
const patchLod = this.computeLodLevel(patch);

this.map.set(patchPosition, [patch, patchLod]);
Expand Down Expand Up @@ -69,4 +71,12 @@ export class ThinInstanceScatterer {
}
return count;
}

public getNbVertices() {
let count = 0;
for (const [patch, patchLod] of this.map.values()) {
count += this.nbVertexFromLod[patchLod] * patch.getNbThinInstances();
}
return count;
}
}
2 changes: 1 addition & 1 deletion src/ts/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class UI {
container.width = "370px";
container.height = "100px";
container.cornerRadius = 5;
container.background = "rgba(0, 0, 0, 0.7)";
container.background = "rgba(0, 0, 0, 0.9)";
container.horizontalAlignment = 0;
container.verticalAlignment = 1;
container.left = "20px";
Expand Down
2 changes: 1 addition & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const config = {
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
},
{
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif|glb|obj)$/i,
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif|glb|obj|mp3)$/i,
type: "asset"
},
{
Expand Down

0 comments on commit 7cc2940

Please sign in to comment.