Skip to content

Commit fb8a22a

Browse files
author
gamedevgrunt
committed
Added high performance grass, opaque thininstances or fully transparent clones. Grass snaps to terrain.
1 parent 29e2459 commit fb8a22a

13 files changed

Lines changed: 912 additions & 58 deletions

File tree

‎shaders/env/grass.fragment.fx‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
precision highp float;
2+
varying vec2 vUV;
3+
uniform sampler2D textureSampler;
4+
5+
void main(void) {
6+
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
7+
8+
vec2 flippedUV = vec2(vUV.x, 1.0 - vUV.y);
9+
// vec2 flippedUV = vec2(vUV.x, vUV.y);
10+
11+
gl_FragColor = texture2D(textureSampler, flippedUV);
12+
gl_FragColor.r = gl_FragColor.r * 0.4;
13+
gl_FragColor.g = gl_FragColor.g * 0.4;
14+
gl_FragColor.b = gl_FragColor.b * 0.4;
15+
gl_FragColor.a = gl_FragColor.a;
16+
}
17+
18+

‎shaders/env/grass.vertex.fx‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
precision highp float;
2+
attribute vec3 position;
3+
attribute vec2 uv;
4+
attribute vec4 color; // Vertex color attribute
5+
6+
uniform mat4 worldViewProjection;
7+
uniform mat4 viewProjection;
8+
uniform float time; // Add a time uniform to control the waving effect
9+
uniform float frequency;
10+
uniform float amplitude;
11+
12+
varying vec2 vUV;
13+
varying vec4 vColor; // Varying to pass color to the fragment shader
14+
15+
// // Instancing attributes
16+
// attribute vec4 world0;
17+
// attribute vec4 world1;
18+
// attribute vec4 world2;
19+
// attribute vec4 world3;
20+
21+
// uncomment for instanced
22+
// #include<instancesDeclaration>
23+
24+
void main(void) {
25+
26+
float heightThreshold = 0.0;
27+
float wave = 0.0;
28+
29+
if (color.r > 0.1) {
30+
// if (position.y > heightThreshold) {
31+
wave = sin(position.x * 1.0 + time) * 0.5; // Adjust frequency and amplitude of the wave
32+
// }
33+
}
34+
35+
vColor = color; // Pass vertex color to the fragment shader
36+
37+
vec3 displacedPosition = position + vec3(0.0, wave, 0.0);
38+
39+
40+
vUV = uv;
41+
// gl_Position = worldViewProjection * vec4(position, 1.0);
42+
gl_Position = worldViewProjection * vec4(displacedPosition, 1.0);
43+
44+
// gl_Position = viewProjection * worldMatrix * vec4(displacedPosition, 1.0);
45+
46+
// uncomment for instanced
47+
// #include<instancesVertex>
48+
// gl_Position = worldViewProjection * finalWorld * vec4(displacedPosition, 1.0);
49+
50+
51+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
precision highp float;
2+
3+
4+
#define FOGMODE_NONE 0.
5+
#define FOGMODE_EXP 1.
6+
#define FOGMODE_EXP2 2.
7+
#define FOGMODE_LINEAR 3.
8+
#define E 2.71828
9+
10+
uniform vec4 vFogInfos;
11+
uniform vec3 vFogColor;
12+
varying float fFogDistance;
13+
14+
float CalcFogFactor()
15+
{
16+
float fogCoeff = 1.0;
17+
float fogStart = vFogInfos.y;
18+
float fogEnd = vFogInfos.z;
19+
float fogDensity = vFogInfos.w;
20+
21+
if (FOGMODE_LINEAR == vFogInfos.x)
22+
{
23+
fogCoeff = (fogEnd - fFogDistance) / (fogEnd - fogStart);
24+
}
25+
else if (FOGMODE_EXP == vFogInfos.x)
26+
{
27+
fogCoeff = 1.0 / pow(E, fFogDistance * fogDensity);
28+
}
29+
else if (FOGMODE_EXP2 == vFogInfos.x)
30+
{
31+
fogCoeff = 1.0 / pow(E, fFogDistance * fFogDistance * fogDensity * fogDensity);
32+
}
33+
34+
return clamp(fogCoeff, 0.0, 1.0);
35+
}
36+
37+
varying vec2 vUV;
38+
uniform sampler2D textureSampler;
39+
void main(void) {
40+
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
41+
42+
// gl_FragColor = texture2D(textureSampler, vUV);
43+
44+
vec2 flippedUV = vec2(vUV.x, 1.0 - vUV.y);
45+
gl_FragColor = texture2D(textureSampler, flippedUV);
46+
47+
vec4 defaultColor = vec4(1.0, 0.0, 0.0, 1.0);
48+
float fog = CalcFogFactor();
49+
vec4 vFogColor4 = vec4(vFogColor, 1.0);
50+
// vFogColor4 = vFogColor4 * 1.0;
51+
gl_FragColor = fog * gl_FragColor + (1.0 - fog) * vFogColor4;
52+
// gl_FragColor = (fog * 2.) * defaultColor;
53+
54+
55+
gl_FragColor.r = gl_FragColor.r * 0.4;
56+
gl_FragColor.g = gl_FragColor.g * 0.4;
57+
gl_FragColor.b = gl_FragColor.b * 0.4;
58+
gl_FragColor.a = gl_FragColor.a;
59+
60+
61+
}
62+
63+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
precision highp float;
2+
attribute vec3 position;
3+
attribute vec2 uv;
4+
attribute vec4 color; // Vertex color attribute
5+
6+
// attribute vec4 world0;
7+
// attribute vec4 world1;
8+
// attribute vec4 world2;
9+
// attribute vec4 world3;
10+
11+
uniform mat4 worldViewProjection;
12+
uniform float time; // Add a time uniform to control the waving effect
13+
uniform float frequency;
14+
uniform float amplitude;
15+
16+
varying vec2 vUV;
17+
varying vec4 vColor; // Varying to pass color to the fragment shader
18+
19+
uniform mat4 view;
20+
varying float fFogDistance;
21+
22+
#include<instancesDeclaration>
23+
uniform mat4 viewProjection;
24+
25+
void main(void) {
26+
#include<instancesVertex>
27+
28+
vec4 worldPosition = world * vec4(position, 1.0);
29+
fFogDistance = (view * worldPosition).z;
30+
31+
float heightThreshold = 0.0;
32+
float wave = 0.0;
33+
34+
if (color.r > 0.1) {
35+
// if (position.y > heightThreshold) {
36+
wave = sin(position.x * 1.0 + time) * 0.5 * ((color.r + 0.1) / 2.0) ; // Adjust frequency and amplitude of the wave
37+
// }
38+
}
39+
40+
vColor = color; // Pass vertex color to the fragment shader
41+
42+
vec3 displacedPosition = position + vec3(0.0, wave, 0.0);
43+
44+
45+
vUV = uv;
46+
47+
// gl_Position = worldViewProjection * vec4(position, 1.0);
48+
// gl_Position = worldViewProjection * finalWorld * vec4(displacedPosition, 1.0);
49+
// gl_Position = worldViewProjection * finalWorld * vec4(displacedPosition, 1.0);
50+
51+
// gl_Position = worldViewProjection * finalWorld * vec4(displacedPosition, 1.0);
52+
53+
// worldPosition
54+
gl_Position = viewProjection * finalWorld * vec4(displacedPosition, 1.0);
55+
}
56+

‎src/scene/gen/procedural/grid/grid.js‎

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export function createGrid(scene, meshes) {
4141
const gridTracker = createGridTracker(scene, meshes);
4242
let tools = createTools(scene, meshes, gridTracker, grid);
4343

44+
45+
4446
// Player starts on this cell, the mesh is already filled in.
4547
// gridTracker[Math.floor(gridSize / 2)][Math.floor(gridSize / 2)] = true; // Fill a cell
4648

@@ -49,59 +51,80 @@ export function createGrid(scene, meshes) {
4951
}
5052

5153
// Grid Input Actions //
52-
// grid.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, (evt) => {
53-
// if (ON_MOBILE) {
54-
// const { width, height } = scene.getEngine().getRenderingCanvas();
55-
// if (evt.pointerY >= height * 0.6) {
56-
// console.log("clicked bottom");
57-
// CANVASES[1].style.zIndex = 1;
58-
// return;
59-
// }
60-
// }
61-
// const pickedPoint = evt.additionalData.pickedPoint;
62-
// if (!pickedPoint) return;
63-
64-
// const xIndex = Math.round(pickedPoint.x / cellSize);
65-
// const zIndex = Math.round(pickedPoint.z / cellSize);
66-
// let gridTrackerIndex = convert(xIndex, zIndex);
67-
// // console.log(`Clicked gridTrackerIndex: (${gridTrackerIndex.x}, ${gridTrackerIndex.z})`);
68-
// // console.log(`Clicked cell center: (${xIndex}, ${zIndex})`);
69-
70-
// // Create or remove the floor mesh based on click type
71-
// if (evt.sourceEvent.button === 0) { // Left click
72-
// // Do current Selected Tool
73-
// tools.selectedTool.click(xIndex, zIndex, gridTrackerIndex, gridTracker, pickedPoint);
74-
75-
// // let floor = scene.getMeshByName(`floor_${xIndex}_${zIndex}`);
76-
// // if (!floor) {
77-
// // let removeFunction = function () {
78-
// // disposeAnimation(scene, floor);
79-
// // gridTracker[gridTrackerIndex.x][gridTrackerIndex.z] = false;
80-
// // removeAllWalls(gridTrackerIndex.x, gridTrackerIndex.z);
81-
// // updateCellAndSurronding(gridTrackerIndex, meshes);
82-
// // };
83-
// // floor = createFloor(scene, xIndex, zIndex, cellSize, meshes, removeFunction);
84-
// // // Position at the exact center of the cell
85-
// // floor.position = new BABYLON.Vector3((xIndex + 0.5) * cellSize - cellSize / 2, 0.1, (zIndex + 0.5) * cellSize - cellSize / 2);
86-
87-
// // let completeFunction = function () {
88-
// // gridTracker[gridTrackerIndex.x][gridTrackerIndex.z] = true;
89-
// // updateCellAndSurronding(gridTrackerIndex, meshes);
90-
// // }
91-
// // floor.completeFunction = completeFunction;
92-
// // entryAnimationFloor(scene, floor, meshes);
93-
94-
95-
// // }
96-
// }// else if (evt.sourceEvent.button === 2) { // Right click
97-
// // const floor = scene.getMeshByName(`floor_${x}_${z}`);
98-
// // if (floor) {
99-
// // // Scale down and remove
100-
// // floor.scaling = new BABYLON.Vector3(0, 0, 0); // Animate this for a smoother effect
101-
// // setTimeout(() => floor.dispose(), 300); // Delay to show scaling
102-
// // }
103-
// // }
104-
// }));
54+
grid.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, (evt) => {
55+
if (ON_MOBILE) {
56+
const { width, height } = scene.getEngine().getRenderingCanvas();
57+
if (evt.pointerY >= height * 0.6) {
58+
console.log("clicked bottom");
59+
CANVASES[1].style.zIndex = 1;
60+
return;
61+
}
62+
}
63+
const pickedPoint = evt.additionalData.pickedPoint;
64+
if (!pickedPoint) return;
65+
66+
const xIndex = Math.round(pickedPoint.x / cellSize);
67+
const zIndex = Math.round(pickedPoint.z / cellSize);
68+
let gridTrackerIndex = convert(xIndex, zIndex);
69+
// console.log(`Clicked gridTrackerIndex: (${gridTrackerIndex.x}, ${gridTrackerIndex.z})`);
70+
// console.log(`Clicked cell center: (${xIndex}, ${zIndex})`);
71+
72+
// Create or remove the floor mesh based on click type
73+
if (evt.sourceEvent.button === 0) { // Left click
74+
75+
// Clicked Entire Building - Brings up Bubble Menu
76+
// // Swap Building Type
77+
// // Or Edit Position
78+
// const gizmoManager = new BABYLON.GizmoManager(scene);
79+
// gizmoManager.positionGizmoEnabled = true;
80+
// gizmoManager.rotationGizmoEnabled = true;
81+
// // gizmoManager.scaleGizmoEnabled = true;
82+
// gizmoManager.boundingBoxGizmoEnabled = true;
83+
// gizmoManager.usePointerToAttachGizmos = false;
84+
// gizmoManager.attachToMesh(gridTracker.parentNode);
85+
// // gizmoManager.attachableMeshes = [gridTracker.parentNode];
86+
87+
// Clicked Individual
88+
89+
// Clicked Terrain
90+
// Do current Selected Tool
91+
// tools.selectedTool.click(xIndex, zIndex, gridTrackerIndex, gridTracker, pickedPoint);
92+
93+
// let floor = scene.getMeshByName(`floor_${xIndex}_${zIndex}`);
94+
// if (!floor) {
95+
// let removeFunction = function () {
96+
// disposeAnimation(scene, floor);
97+
// gridTracker[gridTrackerIndex.x][gridTrackerIndex.z] = false;
98+
// removeAllWalls(gridTrackerIndex.x, gridTrackerIndex.z);
99+
// updateCellAndSurronding(gridTrackerIndex, meshes);
100+
// };
101+
// floor = createFloor(scene, xIndex, zIndex, cellSize, meshes, removeFunction);
102+
// // Position at the exact center of the cell
103+
// floor.position = new BABYLON.Vector3((xIndex + 0.5) * cellSize - cellSize / 2, 0.1, (zIndex + 0.5) * cellSize - cellSize / 2);
104+
105+
// let completeFunction = function () {
106+
// gridTracker[gridTrackerIndex.x][gridTrackerIndex.z] = true;
107+
// updateCellAndSurronding(gridTrackerIndex, meshes);
108+
// }
109+
// floor.completeFunction = completeFunction;
110+
// entryAnimationFloor(scene, floor, meshes);
111+
112+
113+
// }
114+
}// else if (evt.sourceEvent.button === 2) { // Right click
115+
// const floor = scene.getMeshByName(`floor_${x}_${z}`);
116+
// if (floor) {
117+
// // Scale down and remove
118+
// floor.scaling = new BABYLON.Vector3(0, 0, 0); // Animate this for a smoother effect
119+
// setTimeout(() => floor.dispose(), 300); // Delay to show scaling
120+
// }
121+
// }
122+
}));
123+
124+
125+
126+
var parentNode = new BABYLON.TransformNode("parentNode", scene);
127+
gridTracker.parentNode = parentNode;
105128

106129

107130

@@ -121,6 +144,9 @@ export function createGrid(scene, meshes) {
121144
}
122145
if (pointerInfo.type === BABYLON.PointerEventTypes.POINTERUP) {
123146
leftHeldDown = false;
147+
if (pointerInfo.event.button == 0) {
148+
if (tools.selectedTool.mouseUp) tools.selectedTool.mouseUp(gridTracker);
149+
}
124150
}
125151

126152
if (pointerInfo.type === BABYLON.PointerEventTypes.POINTERMOVE) {

‎src/scene/gen/procedural/grid/gridTracker.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ function updateWallsForCell(x, z) {
5959
wall?.dispose();
6060
const cornerInfo = detectCorner(x, z, direction);
6161
wall = createWall2Story(x, z, direction, cellSize, key, cornerInfo);
62+
wall.parent = gridTracker.parentNode;
6263
entryAnimationWall(scene, wall);
6364

6465
} else {

‎src/scene/gen/procedural/grid/place/wall/wall.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { cellSize, gridSize } from "../../constants.js";
44
export function createWallOnly(x, z, direction, size, key, meshes) {
55
const wall = meshes['wall'][0].createInstance(`${key}_wall_${direction}`);
66
wall.isPickable = false;
7+
wall.alwaysSelectAsActiveMesh = true;
78
wall.parent = null;
89
// const wall = BABYLON.MeshBuilder.CreateBox(`${key}_wall_${direction}`, { width: size, height: 20, depth: 1 }, scene);
910
// positions for using mesh builder
@@ -71,11 +72,14 @@ export function createWall2Story(x, z, direction, size, key, cornerInfo) {
7172
wall = meshes['wall'][0].createInstance(`${key}_wall_${direction}`);
7273
}
7374
wall.isPickable = false;
75+
wall.alwaysSelectAsActiveMesh = true;
76+
7477
wall.parent = null;
7578

7679
//create base
7780
let base = meshes['base'][0].createInstance("base_clone");
7881
base.isPickable = false;
82+
base.alwaysSelectAsActiveMesh = true;
7983
base.parent = wall;
8084
base.position = new BABYLON.Vector3(0, 0, 0);
8185
base.rotation = new BABYLON.Vector3(0, 0, 0);
@@ -84,13 +88,16 @@ export function createWall2Story(x, z, direction, size, key, cornerInfo) {
8488
// second story
8589
let secondStory = meshes['wall'][1].createInstance("base_clone");
8690
secondStory.isPickable = false;
91+
secondStory.alwaysSelectAsActiveMesh = true;
8792
secondStory.parent = wall;
8893
secondStory.position = new BABYLON.Vector3(0, 0, 2);
8994
secondStory.rotation = new BABYLON.Vector3(0, 0, 0);
9095
secondStory.scaling = new BABYLON.Vector3(1, 1, 1);
9196

9297
//roof
9398
let roof = createRoof(cornerInfo);
99+
roof.isPickable = false;
100+
roof.alwaysSelectAsActiveMesh = true;
94101
roof.parent = wall;
95102
roof.position = new BABYLON.Vector3(0, 0, 4);
96103
roof.rotation = new BABYLON.Vector3(0, 0, 0);

0 commit comments

Comments
 (0)