@@ -16,7 +16,8 @@ const BLOCKS = {
1616
1717
1818const CHUNK_SIZE = 16 ;
19- const WORLD_HEIGHT = 128 ;
19+ const WORLD_HEIGHT = 96 ;
20+ const SEA_LEVEL = 28 ;
2021const GROUND_LEVEL = 31 ;
2122const CHUNK_GENERATION_HEIGHT = GROUND_LEVEL + 1 ;
2223
@@ -166,80 +167,136 @@ function smoothstep(edge0, edge1, x) {
166167 return x * x * ( 3 - 2 * x ) ;
167168}
168169
170+ function placeTree ( chunkData , x , y , z , worldX , worldZ , terrainNoise ) {
171+ const trunkHeight = 2 + Math . floor ( ( terrainNoise ( worldX * 0.2 , worldZ * 0.2 ) + 1 ) * 1.5 ) ;
172+
173+ // --- Trunk ---
174+ for ( let i = 0 ; i < trunkHeight ; i ++ ) {
175+ if ( y + i >= WORLD_HEIGHT ) continue ;
176+ chunkData [ x ] [ y + i ] [ z ] = BLOCKS [ "log" ] ;
177+ }
178+
179+ const top = y + trunkHeight ;
180+
181+ // --- 3×3 Leaf Square ---
182+ for ( let lx = - 1 ; lx <= 1 ; lx ++ ) {
183+ for ( let lz = - 1 ; lz <= 1 ; lz ++ ) {
184+ const dx = x + lx ;
185+ const dy = top ;
186+ const dz = z + lz ;
187+
188+ if ( dx < 0 || dx >= CHUNK_SIZE ||
189+ dy < 0 || dy >= WORLD_HEIGHT ||
190+ dz < 0 || dz >= CHUNK_SIZE ) continue ;
191+
192+ if ( chunkData [ dx ] [ dy ] [ dz ] . id === 0 ) {
193+ chunkData [ dx ] [ dy ] [ dz ] = BLOCKS [ "leaf" ] ;
194+ }
195+ }
196+ }
197+
198+ // --- Single Top Leaf ---
199+ const topLeafY = top + 1 ;
200+ if ( topLeafY < WORLD_HEIGHT && chunkData [ x ] [ topLeafY ] [ z ] . id === 0 ) {
201+ chunkData [ x ] [ topLeafY ] [ z ] = BLOCKS [ "leaf" ] ;
202+ }
203+ }
204+
205+ class TerrainPointInfo {
206+ constructor ( temperature , humidity , mountainFactor , desertFactor , forestFactor , plainsFactor , terrainHeight ) {
207+ this . temperature = temperature ;
208+ this . humidity = humidity ;
209+ this . mountainFactor = mountainFactor ;
210+ this . desertFactor = desertFactor ;
211+ this . forestFactor = forestFactor ;
212+ this . plainsFactor = plainsFactor ;
213+ this . terrainHeight = terrainHeight ;
214+ }
215+ }
216+
217+ function getTerrainPointInfo ( worldX , worldZ , terrainNoise , tempNoise , humidityNoise ) {
218+ // BIOME NOISE
219+ const temperature = tempNoise ( worldX * 0.001 , worldZ * 0.001 ) ;
220+ const humidity = humidityNoise ( worldX * 0.001 , worldZ * 0.001 ) ;
221+
222+ // BIOME BLENDING
223+ const mountainFactor = smoothstep ( - 0.2 , 0.4 , - temperature ) ; // Mountains appear in colder regions
224+ const desertFactor = smoothstep ( 0.2 , 0.7 , temperature ) * smoothstep ( 0.3 , - 0.3 , humidity ) ; // Deserts appear in hot + dry regions
225+ const forestFactor = smoothstep ( 0.1 , 0.7 , humidity ) ; // Forests appear in humid regions
226+ let plainsFactor = 1.0 - Math . max ( mountainFactor , desertFactor , forestFactor ) ; // Plains are default
227+
228+ plainsFactor = Math . max ( plainsFactor , 0 ) ;
229+
230+ // DOMAIN WARPING
231+ const warpX = terrainNoise ( worldX * 0.002 , worldZ * 0.002 ) * 30 ;
232+ const warpZ = terrainNoise ( ( worldX + 1000 ) * 0.002 , ( worldZ + 1000 ) * 0.002 ) * 30 ;
233+
234+ const nx = worldX + warpX ;
235+ const nz = worldZ + warpZ ;
236+
237+ // BASE TERRAIN
238+ const baseTerrain = fbm ( terrainNoise , nx * 0.003 , nz * 0.003 , 5 , 0.5 , 2.0 ) ;
239+
240+ // RIDGED MOUNTAINS
241+ const mountains = ridgedFBM ( terrainNoise , nx * 0.008 , nz * 0.008 , 5 ) ;
242+
243+ // BIOME HEIGHTS
244+ const plainsHeight = baseTerrain * 8 ;
245+ const forestHeight = baseTerrain * 12 ;
246+ const desertHeight = baseTerrain * 5 ;
247+ const mountainHeight = baseTerrain * 15 + mountains * 45 ;
248+
249+ // FINAL BLENDED HEIGHT
250+ const terrainHeight = Math . floor (
251+ plainsHeight * plainsFactor +
252+ forestHeight * forestFactor +
253+ desertHeight * desertFactor +
254+ mountainHeight * mountainFactor
255+ ) + SEA_LEVEL ;
256+
257+ return new TerrainPointInfo ( temperature , humidity , mountainFactor , desertFactor , forestFactor , plainsFactor , terrainHeight ) ;
258+ }
259+
169260function GenerateChunk ( chunkX , chunkZ , terrainNoise , tempNoise , humidityNoise ) {
170261
171262 if ( chunkX % CHUNK_SIZE != 0 || chunkZ % CHUNK_SIZE != 0 ) {
172263 console . error ( `Chunk coordinates: ${ chunkX } , ${ chunkZ } must be multiples of 16` ) ;
173264 return null ;
174265 }
175266
176- const SEA_LEVEL = 28 ;
177- const MAX_TREES_PER_CHUNK = 5 ;
178- let treesInChunk = 0 ;
267+ const TREE_CELL_SIZE = 6 ;
179268
180269 let chunkData = new Array ( CHUNK_SIZE ) ;
181-
182270 for ( let x = 0 ; x < CHUNK_SIZE ; x ++ ) {
183271
184272 chunkData [ x ] = new Array ( WORLD_HEIGHT ) ;
185273
186274 for ( let y = 0 ; y < WORLD_HEIGHT ; y ++ ) {
187275 chunkData [ x ] [ y ] = new Array ( CHUNK_SIZE ) ;
276+ for ( let z = 0 ; z < CHUNK_SIZE ; z ++ ) {
277+ chunkData [ x ] [ y ] [ z ] = BLOCKS [ "air" ] ;
278+ }
188279 }
280+ }
189281
282+ for ( let x = 0 ; x < CHUNK_SIZE ; x ++ ) {
190283 for ( let z = 0 ; z < CHUNK_SIZE ; z ++ ) {
284+
191285 const worldX = chunkX + x ;
192286 const worldZ = chunkZ + z ;
193287
194- // BIOME NOISE
195- const temperature = tempNoise ( worldX * 0.001 , worldZ * 0.001 ) ;
196- const humidity = humidityNoise ( worldX * 0.001 , worldZ * 0.001 ) ;
197-
198- // BIOME BLENDING
199- const mountainFactor = smoothstep ( - 0.2 , 0.4 , - temperature ) ; // Mountains appear in colder regions
200- const desertFactor = smoothstep ( 0.2 , 0.7 , temperature ) * smoothstep ( 0.3 , - 0.3 , humidity ) ; // Deserts appear in hot + dry regions
201- const forestFactor = smoothstep ( 0.1 , 0.7 , humidity ) ; // Forests appear in humid regions
202- let plainsFactor = 1.0 - Math . max ( mountainFactor , desertFactor , forestFactor ) ; // Plains are default
203-
204- plainsFactor = Math . max ( plainsFactor , 0 ) ;
205-
206- // DOMAIN WARPING
207- const warpX = terrainNoise ( worldX * 0.002 , worldZ * 0.002 ) * 30 ;
208- const warpZ = terrainNoise ( ( worldX + 1000 ) * 0.002 , ( worldZ + 1000 ) * 0.002 ) * 30 ;
209-
210- const nx = worldX + warpX ;
211- const nz = worldZ + warpZ ;
212-
213- // BASE TERRAIN
214- const baseTerrain = fbm ( terrainNoise , nx * 0.003 , nz * 0.003 , 5 , 0.5 , 2.0 ) ;
215-
216- // RIDGED MOUNTAINS
217- const mountains = ridgedFBM ( terrainNoise , nx * 0.008 , nz * 0.008 , 5 ) ;
218-
219- // BIOME HEIGHTS
220- const plainsHeight = baseTerrain * 8 ;
221- const forestHeight = baseTerrain * 12 ;
222- const desertHeight = baseTerrain * 5 ;
223- const mountainHeight = baseTerrain * 15 + mountains * 45 ;
224-
225- // FINAL BLENDED HEIGHT
226- const terrainHeight = Math . floor (
227- plainsHeight * plainsFactor +
228- forestHeight * forestFactor +
229- desertHeight * desertFactor +
230- mountainHeight * mountainFactor
231- ) + SEA_LEVEL ;
288+ const tpi = getTerrainPointInfo ( worldX , worldZ , terrainNoise , tempNoise , humidityNoise )
232289
233290 // SURFACE BLOCKS
234291 let surfaceBlock = "gras" ;
235292 let subsurfaceBlock = "dir" ;
236293
237- if ( desertFactor > 0.5 ) {
294+ if ( tpi . desertFactor > 0.5 ) {
238295 surfaceBlock = "san" ;
239296 subsurfaceBlock = "san" ;
240297 }
241298
242- if ( mountainFactor > 0.6 ) {
299+ if ( tpi . mountainFactor > 0.6 ) {
243300 surfaceBlock = "cob" ;
244301 subsurfaceBlock = "cob" ;
245302 }
@@ -254,34 +311,47 @@ function GenerateChunk(chunkX, chunkZ, terrainNoise, tempNoise, humidityNoise) {
254311 chunkData [ x ] [ y ] [ z ] = BLOCKS [ "bedroc" ] ;
255312 }
256313
257- else if ( y < terrainHeight - 4 ) {
314+ else if ( y < tpi . terrainHeight - 4 ) {
258315 chunkData [ x ] [ y ] [ z ] = BLOCKS [ "cob" ] ;
259316 }
260317
261- else if ( y < terrainHeight ) {
262- if ( mountainFactor > 0.6 && y < mountainCobHeight ) {
318+ else if ( y < tpi . terrainHeight ) {
319+ if ( tpi . mountainFactor > 0.6 && y < mountainCobHeight ) {
263320 subsurfaceBlock = "dir" ; // for mountains, transition from dir to cob
264321 }
265322 chunkData [ x ] [ y ] [ z ] = BLOCKS [ subsurfaceBlock ] ;
266323 }
267324
268- else if ( y === terrainHeight ) {
269- if ( mountainFactor > 0.6 && y < mountainCobHeight ) {
325+ else if ( y === tpi . terrainHeight ) {
326+ if ( tpi . mountainFactor > 0.6 && y < mountainCobHeight ) {
270327 surfaceBlock = "gras" ; // for mountains, transition from gras to cob
271328 }
272329 chunkData [ x ] [ y ] [ z ] = BLOCKS [ surfaceBlock ] ;
273330 }
274331
275332 else {
276- chunkData [ x ] [ y ] [ z ] = BLOCKS [ "air" ] ;
333+ // chunkData[x][y][z] = BLOCKS["air"];
277334 }
278335 }
279336
280337 // TREES
281- const treeNoise = terrainNoise ( worldX * 0.1 , worldZ * 0.1 ) ;
282- if ( forestFactor > 0.7 && treeNoise > 0.6 && MAX_TREES_PER_CHUNK > treesInChunk && terrainHeight < mountainCobHeight ) {
283- chunkData [ x ] [ terrainHeight + 1 ] [ z ] = BLOCKS [ "log" ] ;
284- treesInChunk ++ ;
338+ const forestDensity = terrainNoise ( worldX * 0.01 , worldZ * 0.01 ) ;
339+
340+ const cellX = Math . floor ( worldX / TREE_CELL_SIZE ) ;
341+ const cellZ = Math . floor ( worldZ / TREE_CELL_SIZE ) ;
342+
343+ const localX = Math . floor ( ( terrainNoise ( cellX * 17 , cellZ * 17 ) + 1 ) * 0.5 * TREE_CELL_SIZE ) ;
344+ const localZ = Math . floor ( ( terrainNoise ( cellX * 29 , cellZ * 29 ) + 1 ) * 0.5 * TREE_CELL_SIZE ) ;
345+
346+ const treeX = cellX * TREE_CELL_SIZE + localX ;
347+ const treeZ = cellZ * TREE_CELL_SIZE + localZ ;
348+
349+ if ( worldX === treeX && worldZ === treeZ && tpi . forestFactor > 0.5 && forestDensity > 0.2 && tpi . terrainHeight < mountainCobHeight ) {
350+ const isOnChunkEdge = x === 0 || x === CHUNK_SIZE - 1 || z === 0 || z === CHUNK_SIZE - 1 ;
351+
352+ if ( ! isOnChunkEdge ) {
353+ placeTree ( chunkData , x , tpi . terrainHeight + 1 , z , worldX , worldZ , terrainNoise ) ;
354+ }
285355 }
286356 }
287357 }
@@ -604,6 +674,24 @@ class ChunkManager {
604674 chunk . data = GenerateChunk ( chunk . x * CHUNK_SIZE , chunk . z * CHUNK_SIZE , this . terrainNoise , this . tempNoise , this . humidityNoise ) ;
605675 }
606676 }
677+
678+ getBlock ( wx , wy , wz ) {
679+ const cx = worldToChunkCoord ( wx ) ;
680+ const cz = worldToChunkCoord ( wz ) ;
681+ const lx = worldToLocal ( wx ) ;
682+ const lz = worldToLocal ( wz ) ;
683+
684+ const key = chunkKey ( cx , cz ) ;
685+ const chunk = this . chunks . get ( key ) ;
686+ if ( ! chunk ) return BLOCKS [ "air" ] ;
687+
688+ if ( wy < 0 || wy >= WORLD_HEIGHT ) {
689+ //console.warn(`Y coordinate ${wy} is out of bounds`);
690+ return BLOCKS [ "air" ] ;
691+ }
692+
693+ return chunk . data [ lx ] [ wy ] [ lz ] ;
694+ }
607695}
608696
609697function getAffectedChunks ( wx , wy , wz ) {
@@ -623,6 +711,14 @@ function getAffectedChunks(wx, wy, wz) {
623711 return chunks ;
624712}
625713
714+ function TopPositionInWorld ( xmin , xmax , zmin , zmax , chunkManager ) {
715+ const x = Math . floor ( Math . random ( ) * ( xmax - xmin ) + xmin ) ;
716+ const z = Math . floor ( Math . random ( ) * ( zmax - zmin ) + zmin ) ;
717+
718+ const tpi = getTerrainPointInfo ( x , z , chunkManager . terrainNoise , chunkManager . tempNoise , chunkManager . humidityNoise ) ;
719+ const y = tpi . terrainHeight ;
626720
721+ return new THREE . Vector3 ( x , y + 2 , z ) ;
722+ }
627723
628- export const BLOCK_MANAGER = { Chunk, ChunkManager, GenerateChunk, RenderChunk, worldToChunkCoord, chunkKey, BLOCKS , CHUNK_SIZE , WORLD_HEIGHT , GROUND_LEVEL , CHUNK_GENERATION_HEIGHT , FACE_DEFINITIONS } ;
724+ export const BLOCK_MANAGER = { Chunk, ChunkManager, TerrainPointInfo , GenerateChunk, RenderChunk, worldToChunkCoord, chunkKey, TopPositionInWorld , BLOCKS , CHUNK_SIZE , WORLD_HEIGHT , GROUND_LEVEL , CHUNK_GENERATION_HEIGHT , FACE_DEFINITIONS } ;
0 commit comments