Skip to content

Commit

Permalink
added butterflies on habitable planets
Browse files Browse the repository at this point in the history
  • Loading branch information
BarthPaleologue committed Jan 13, 2024
1 parent 8bbe567 commit 98e2086
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 3 deletions.
Binary file added src/asset/butterfly.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions src/shaders/butterflyMaterial/butterflyFragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
precision highp float;

uniform float time;

uniform vec3 lightDirection;

uniform sampler2D butterflyTexture;

varying vec3 vPosition;
varying vec2 vUV;

varying mat4 normalMatrix;
varying vec3 vNormal;

varying vec3 vOriginalWorldPosition;

// src: https://gist.github.com/mairod/a75e7b44f68110e1576d77419d608786
vec3 hueShift( vec3 color, float hueAdjust ){
const vec3 kRGBToYPrime = vec3 (0.299, 0.587, 0.114);
const vec3 kRGBToI = vec3 (0.596, -0.275, -0.321);
const vec3 kRGBToQ = vec3 (0.212, -0.523, 0.311);

const vec3 kYIQToR = vec3 (1.0, 0.956, 0.621);
const vec3 kYIQToG = vec3 (1.0, -0.272, -0.647);
const vec3 kYIQToB = vec3 (1.0, -1.107, 1.704);

float YPrime = dot (color, kRGBToYPrime);
float I = dot (color, kRGBToI);
float Q = dot (color, kRGBToQ);
float hue = atan (Q, I);
float chroma = sqrt (I * I + Q * Q);

hue += hueAdjust;

Q = chroma * sin (hue);
I = chroma * cos (hue);

vec3 yIQ = vec3 (YPrime, I, Q);

return vec3( dot (yIQ, kYIQToR), dot (yIQ, kYIQToG), dot (yIQ, kYIQToB) );
}

void main() {
vec4 finalColor = texture(butterflyTexture, vUV);
if(finalColor.a < 0.1) discard;

finalColor.rgb = hueShift(finalColor.rgb, vOriginalWorldPosition.x * 10.0 + vOriginalWorldPosition.z * 10.0);

vec3 normalW = normalize((normalMatrix * vec4(vNormal, 0.0)).xyz);

float ndl1 = max(dot(normalW, lightDirection), 0.0);
float ndl2 = max(dot(-normalW, lightDirection), 0.0);
float ndl = ndl1 + ndl2;

// ambient lighting
ndl = clamp(ndl + 0.1, 0.0, 1.0);

gl_FragColor = vec4(finalColor.rgb * ndl, 1.0);// apply color and lighting
}
78 changes: 78 additions & 0 deletions src/shaders/butterflyMaterial/butterflyVertex.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
precision highp float;

attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;

uniform mat4 viewProjection;

//uniform mat4 world;

uniform vec3 playerPosition;

uniform float time;

varying vec3 vPosition;
varying vec2 vUV;

varying mat4 normalMatrix;
varying vec3 vNormal;

varying vec3 vOriginalWorldPosition;

#include "../utils/rotateAround.glsl";

float easeOut(float t, float a) {
return 1.0 - pow(1.0 - t, a);
}

float easeIn(float t, float alpha) {
return pow(t, alpha);
}

#include "../utils/remap.glsl";

#include<instancesDeclaration>

void main() {
#include<instancesVertex>

vec3 objectWorld = vec3(finalWorld[3].x, finalWorld[3].y, finalWorld[3].z);
vOriginalWorldPosition = objectWorld;

// high frequency movement for wing flap
objectWorld.y += 0.1 * sin(5.0 * time + objectWorld.x * 10.0 + objectWorld.z * 10.0);
// low frequency movement of larger amplitude for general movement
objectWorld.y += 0.5 * sin(0.2 * time + objectWorld.x * 15.0 + objectWorld.z * 15.0);

vec3 butterflyForward = vec3(1.0, 0.0, 0.0);

float rotationY = sin(0.5 * time + vOriginalWorldPosition.x * 10.0 + vOriginalWorldPosition.z * 10.0) * 3.14;
vec3 rotatedPosition = rotateAround(position, vec3(0.0, 1.0, 0.0), rotationY);
butterflyForward = rotateAround(butterflyForward, vec3(0.0, 1.0, 0.0), rotationY);

vec3 flyPosition = rotateAround(rotatedPosition, butterflyForward, sign(position.z) * cos(10.0 * time + objectWorld.x * 10.0 + objectWorld.z * 10.0));
flyPosition.y += 3.0;

objectWorld += butterflyForward * 0.5 * sin(0.5 * time + vOriginalWorldPosition.x * 10.0 + vOriginalWorldPosition.z * 10.0);

// avoid the player
vec3 playerToButterfly = objectWorld - playerPosition;
playerToButterfly.y = 0.0;
float distanceToPlayer = length(playerToButterfly);
if (distanceToPlayer < 2.0) {
objectWorld += normalize(playerToButterfly) * (2.0 - distanceToPlayer);
}

finalWorld[3].xyz = objectWorld;

vec4 outPosition = viewProjection * finalWorld * vec4(flyPosition, 1.0);
gl_Position = outPosition;

vPosition = flyPosition;
vUV = uv;

normalMatrix = transpose(inverse(finalWorld));

vNormal = normal;
}
9 changes: 7 additions & 2 deletions src/ts/planemos/telluricPlanemo/terrain/chunks/planetChunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Camera } from "@babylonjs/core/Cameras/camera";
import { IPatch } from "../instancePatch/iPatch";
import { createGrassBlade } from "../../../../proceduralAssets/grass/grassBlade";
import { TelluricPlanemoModel } from "../../telluricPlanemoModel";
import { createButterfly } from "../../../../proceduralAssets/butterfly/butterfly";

export class PlanetChunk implements Transformable {
public readonly mesh: Mesh;
Expand Down Expand Up @@ -134,15 +135,19 @@ export class PlanetChunk implements Transformable {

this.onRecieveVertexDataObservable.notifyObservers();

const rockPatch = new ThinInstancePatch(this.parent, randomDownSample(alignedInstancesMatrixBuffer, 2));
const rockPatch = new ThinInstancePatch(this.parent, randomDownSample(alignedInstancesMatrixBuffer, 4));
rockPatch.createInstances(Assets.Rock);
this.instancePatches.push(rockPatch);

if(this.planetModel.physicalProperties.pressure > 0 && this.planetModel.physicalProperties.oceanLevel > 0) {
const treePatch = new ThinInstancePatch(this.parent, randomDownSample(instancesMatrixBuffer, 3));
const treePatch = new ThinInstancePatch(this.parent, randomDownSample(instancesMatrixBuffer, 6));
treePatch.createInstances(Assets.Tree);
this.instancePatches.push(treePatch);

const butterflyPatch = new ThinInstancePatch(this.parent, instancesMatrixBuffer);
butterflyPatch.createInstances(createButterfly(this.mesh.getScene()));
this.instancePatches.push(butterflyPatch);

/*const grassPatch = new ThinInstancePatch(this.parent, instancesMatrixBuffer);
grassPatch.createInstances(createGrassBlade(this.mesh.getScene(), 3));
this.instancePatches.push(grassPatch);*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function handle_build(data: TransferBuildData): void {
const size = data.planetDiameter / 2 ** data.depth;
const space_between_vertices = size / nbSubdivisions;
//console.log(data.depth, space_between_vertices);
const scatter_per_square_meter = space_between_vertices < Settings.MIN_DISTANCE_BETWEEN_VERTICES ? 0.01 : 0;
const scatter_per_square_meter = space_between_vertices < Settings.MIN_DISTANCE_BETWEEN_VERTICES ? 0.02 : 0;

const flat_area = size * size;
const max_nb_instances = Math.floor(flat_area * scatter_per_square_meter * 2.0);
Expand Down
92 changes: 92 additions & 0 deletions src/ts/proceduralAssets/butterfly/butterfly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { VertexData } from "@babylonjs/core/Meshes/mesh.vertexData";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { Scene } from "@babylonjs/core/scene";
import { createButterflyMaterial } from "./butterflyMaterial";

export function createButterfly(scene: Scene) {
const positions = new Float32Array(6 * 3);
const indices = new Uint32Array(4 * 3);
const uvs = new Float32Array(6 * 2);

// butter fly is made of 4 triangles (2 squares touching each other)
// 0--1
// | /|
// |/ |
// 2--3
positions[0] = 0;
positions[1] = 0;
positions[2] = -1;

positions[3] = 1;
positions[4] = 0;
positions[5] = -1;

positions[6] = 0;
positions[7] = 0.0;
positions[8] = 0.0;

positions[9] = 1;
positions[10] = 0;
positions[11] = 0;

positions[12] = 0;
positions[13] = 0;
positions[14] = 1;

positions[15] = 1;
positions[16] = 0;
positions[17] = 1;

// first square
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;

indices[3] = 1;
indices[4] = 3;
indices[5] = 2;

// second square
indices[6] = 2;
indices[7] = 3;
indices[8] = 4;

indices[9] = 3;
indices[10] = 5;
indices[11] = 4;

// uvs (0,0) is bottom left, (1,1) is top right
uvs[0] = 0;
uvs[1] = 0;

uvs[2] = 0;
uvs[3] = 1;

uvs[4] = 0.5;
uvs[5] = 0;

uvs[6] = 0.5;
uvs[7] = 1;

uvs[8] = 1;
uvs[9] = 0;

uvs[10] = 1;
uvs[11] = 1;

const vertexData = new VertexData();
vertexData.positions = positions;
vertexData.indices = indices;
vertexData.uvs = uvs;

const mesh = new Mesh("butterfly", scene);
vertexData.applyToMesh(mesh);
mesh.createNormals(false);

mesh.scaling.scaleInPlace(0.2);
mesh.bakeCurrentTransformIntoVertices();

mesh.material = createButterflyMaterial(scene);

return mesh;
}
46 changes: 46 additions & 0 deletions src/ts/proceduralAssets/butterfly/butterflyMaterial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Scene } from "@babylonjs/core/scene";
import { Effect } from "@babylonjs/core/Materials/effect";
import { ShaderMaterial } from "@babylonjs/core/Materials/shaderMaterial";
import butterflyFragment from "../../../shaders/butterflyMaterial/butterflyFragment.glsl";
import butterflyVertex from "../../../shaders/butterflyMaterial/butterflyVertex.glsl";

import butterflyTexture from "../../../asset/butterfly.png";
import { Texture } from "@babylonjs/core/Materials/Textures/texture";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { PointLight } from "@babylonjs/core/Lights/pointLight";

export function createButterflyMaterial(scene: Scene, player?: TransformNode) {
const shaderName = "butterflyMaterial";
Effect.ShadersStore[`${shaderName}FragmentShader`] = butterflyFragment;
Effect.ShadersStore[`${shaderName}VertexShader`] = butterflyVertex;

const butterflyMaterial = new ShaderMaterial(shaderName, scene, shaderName, {
attributes: ["position", "normal", "uv"],
uniforms: ["world", "worldView", "worldViewProjection", "view", "projection", "viewProjection", "time", "lightDirection", "playerPosition"],
defines: ["#define INSTANCES"],
samplers: ["butterflyTexture"]
});

butterflyMaterial.setTexture("butterflyTexture", new Texture(butterflyTexture, scene));
butterflyMaterial.backFaceCulling = false;

let elapsedSeconds = 0;
scene.onBeforeRenderObservable.add(() => {
elapsedSeconds += scene.getEngine().getDeltaTime() / 1000;

if(scene.activeCamera === null) throw new Error("Active camera is null");

const star = scene.lights[1];
if(!(star instanceof PointLight)) throw new Error("Could not find star light");

const lightDirection = star.position.subtract(scene.activeCamera.globalPosition).normalize();
butterflyMaterial.setVector3("lightDirection", lightDirection);

const playerPosition = player?.position ?? new Vector3(0, 0, 0);
butterflyMaterial.setVector3("playerPosition", playerPosition);
butterflyMaterial.setFloat("time", elapsedSeconds);
});

return butterflyMaterial;
}

0 comments on commit 98e2086

Please sign in to comment.