Terrain generator updates + Foliage work
This commit is contained in:
		
							parent
							
								
									c69caeeab9
								
							
						
					
					
						commit
						3948f1f921
					
				| @ -17,6 +17,13 @@ | |||||||
|                 } |                 } | ||||||
|             ], |             ], | ||||||
|             "modelPath" : "Models/falloak1.fbx" |             "modelPath" : "Models/falloak1.fbx" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "name" : "Green Grass", | ||||||
|  |             "tokens" : [ | ||||||
|  |                 "AMBIENT" | ||||||
|  |             ], | ||||||
|  |             "modelPath" : "Models/falloak1.fbx" | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     ] |     ] | ||||||
|  | |||||||
							
								
								
									
										19
									
								
								assets/Data/voxelTypes.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								assets/Data/voxelTypes.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | { | ||||||
|  |     "types" : [ | ||||||
|  |         { | ||||||
|  |             "id" : 0, | ||||||
|  |             "name" : "air" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "id" : 1, | ||||||
|  |             "name" : "dirt" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "id" : 2, | ||||||
|  |             "name" : "grass", | ||||||
|  |             "ambientFoliage" : [ | ||||||
|  |                 "Grass" | ||||||
|  |             ] | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								docs/TerrainEditing.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								docs/TerrainEditing.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | electrosphere.client.terrain.editing.TerrainEditing | ||||||
|  |  - Client static interface for editing terrain | ||||||
|  |  - The idea is that this provides functions you can call anywhere from client side to trigger a request to perform a terrain edit | ||||||
|  | 
 | ||||||
|  | Which leads to | ||||||
|  | 
 | ||||||
|  | electrosphere.server.terrain.editing.TerrainEditing | ||||||
|  |  - Server utility functions for actually editing terrain | ||||||
|  |  - Does the calculations of a real coordinate + radius to determine which cells to edit and how much | ||||||
|  |  - This then updates the server terrain manager with edits via the VoxelCellManager interface | ||||||
|  | 
 | ||||||
|  | VoxelCellManager interface | ||||||
|  |  - Provides an interface on top of DataCellManager to update terrain functions | ||||||
|  |  - Makes functions that must be implemented on data cell manager so implementation specific to cell manager | ||||||
|  |  - For GriddedDataCellManager, this uses a lock and updates values | ||||||
|  |  - As values are updated, they should be send 1-by-1 over the network via individual update packets to the client | ||||||
|  | 
 | ||||||
|  | When client receives voxel update packet in ClientTerrainManager, it triggers the cell to update that specific drawcell | ||||||
|  | This should also update all ambient foliage | ||||||
|  | 
 | ||||||
| @ -13,7 +13,9 @@ import org.joml.Matrix4f; | |||||||
| import org.joml.Quaterniond; | import org.joml.Quaterniond; | ||||||
| import org.joml.Vector3d; | import org.joml.Vector3d; | ||||||
| import org.joml.Vector3f; | import org.joml.Vector3f; | ||||||
|  | import org.joml.Vector3i; | ||||||
| 
 | 
 | ||||||
|  | import electrosphere.client.terrain.cache.ChunkData; | ||||||
| import electrosphere.engine.Globals; | import electrosphere.engine.Globals; | ||||||
| import electrosphere.entity.Entity; | import electrosphere.entity.Entity; | ||||||
| import electrosphere.entity.EntityCreationUtils; | import electrosphere.entity.EntityCreationUtils; | ||||||
| @ -26,7 +28,7 @@ import electrosphere.renderer.buffer.ShaderAttribute; | |||||||
| import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; | import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Manages foliage (grass, small plants, etc) that should be shown, typically instanced |  * Manages ambient foliage (grass, small plants, etc) that should be shown, typically instanced | ||||||
|  */ |  */ | ||||||
| public class ClientFoliageManager { | public class ClientFoliageManager { | ||||||
| 
 | 
 | ||||||
| @ -50,6 +52,8 @@ public class ClientFoliageManager { | |||||||
| 
 | 
 | ||||||
|     //FoliageCells that are active and have foliage that is being drawn |     //FoliageCells that are active and have foliage that is being drawn | ||||||
|     Set<FoliageCell> activeCells = new HashSet<FoliageCell>(); |     Set<FoliageCell> activeCells = new HashSet<FoliageCell>(); | ||||||
|  |     //map of position-based key to foliage cell at the position | ||||||
|  |     Map<String,FoliageCell> locationCellMap = new HashMap<String,FoliageCell>(); | ||||||
|     //The maximum distance a cell can be away from the player before being destroyed |     //The maximum distance a cell can be away from the player before being destroyed | ||||||
|     static final float CELL_DISTANCE_MAX = 25f; |     static final float CELL_DISTANCE_MAX = 25f; | ||||||
|     //The maximum number of foliage cells  |     //The maximum number of foliage cells  | ||||||
| @ -172,6 +176,16 @@ public class ClientFoliageManager { | |||||||
|         return new Quaterniond().rotationX(-Math.PI / 2.0f).rotateLocalY(Math.PI * placementRandomizer.nextFloat()); |         return new Quaterniond().rotationX(-Math.PI / 2.0f).rotateLocalY(Math.PI * placementRandomizer.nextFloat()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets a key for a foliage cell in the localCellMap | ||||||
|  |      * @param worldPosition The world position of the cell | ||||||
|  |      * @param voxelPosition The voxel position of the cell | ||||||
|  |      * @return The key for the cell | ||||||
|  |      */ | ||||||
|  |     private String getFoliageCellKey(Vector3i worldPosition, Vector3i voxelPosition){ | ||||||
|  |         return worldPosition.x + "_" + worldPosition.y + "_" + worldPosition.z + "_" + voxelPosition.x + "_" + voxelPosition.y + "_" + voxelPosition.z; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor |      * Makes an already created entity a drawable, instanced entity (client only) by backing it with an InstancedActor | ||||||
| @ -188,6 +202,62 @@ public class ClientFoliageManager { | |||||||
|         Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED); |         Globals.clientScene.registerEntityToTag(entity, EntityTags.DRAW_INSTANCED); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Evaluates a chunk to see where foliage cells should be created or updated | ||||||
|  |      * @param worldPos The world position of the chunk | ||||||
|  |      */ | ||||||
|  |     public void evaluateChunk(Vector3i worldPos){ | ||||||
|  |         ChunkData data = Globals.clientTerrainManager.getChunkDataAtWorldPoint(worldPos); | ||||||
|  |         for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){ | ||||||
|  |             //can't go to very top 'cause otherwise there would be no room to put grass | ||||||
|  |             for(int y = 0; y < ChunkData.CHUNK_SIZE - 1; y++){ | ||||||
|  |                 for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ | ||||||
|  |                     String key = getFoliageCellKey(worldPos, new Vector3i(x,y,z)); | ||||||
|  |                     if(locationCellMap.get(key) != null){ | ||||||
|  |                         //destroy if there's no longer ground or  | ||||||
|  |                         //if the cell above is now occupied or | ||||||
|  |                         //if the lower cell is no longer supporting foliage | ||||||
|  |                         if(data.getWeight(new Vector3i(x,y,z)) <= 0 || | ||||||
|  |                         data.getWeight(new Vector3i(x,y + 1,z)) > 0 || | ||||||
|  |                         !typeSupportsFoliage(data.getType(new Vector3i(x,y,z)))){ | ||||||
|  |                             //TODO: destroy | ||||||
|  |                         } else { | ||||||
|  |                             //TODO: evaluate if foliage is placed well | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         //create if current is ground and above is air | ||||||
|  |                         if( | ||||||
|  |                             data.getWeight(new Vector3i(x,y,z)) > 0 &&  | ||||||
|  |                             data.getWeight(new Vector3i(x,y + 1,z)) < 0 && | ||||||
|  |                             typeSupportsFoliage(data.getType(new Vector3i(x,y,z))) | ||||||
|  |                         ){ | ||||||
|  |                             //create foliage cell | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         //evaluate top cells if chunk above this one exists | ||||||
|  |         ChunkData aboveData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(new Vector3i(worldPos).add(0,1,0)); | ||||||
|  |         if(aboveData != null){ | ||||||
|  |             for(int x = 0; x < ChunkData.CHUNK_SIZE; x++){ | ||||||
|  |                 for(int z = 0; z < ChunkData.CHUNK_SIZE; z++){ | ||||||
|  |                     if(data.getWeight(new Vector3i(x,ChunkData.CHUNK_SIZE - 1,z)) > 0 && aboveData.getWeight(new Vector3i(x,0,z)) < 0){ | ||||||
|  |                          | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets whether the voxel type supports foliage or not | ||||||
|  |      * @param type | ||||||
|  |      * @return | ||||||
|  |      */ | ||||||
|  |     private boolean typeSupportsFoliage(int type){ | ||||||
|  |         return Globals.gameConfigCurrent.getVoxelData().getTypeFromId(type).getAmbientFoliage() != null; | ||||||
|  |     } | ||||||
|      |      | ||||||
|     /** |     /** | ||||||
|      * Evaluate all foliage cells to see if any should be deconstructed and any new ones should be created |      * Evaluate all foliage cells to see if any should be deconstructed and any new ones should be created | ||||||
| @ -196,7 +266,7 @@ public class ClientFoliageManager { | |||||||
|         Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity); |         Vector3d playerPosition = EntityUtils.getPosition(Globals.playerEntity); | ||||||
|         for(FoliageCell activeCell : activeCells){ |         for(FoliageCell activeCell : activeCells){ | ||||||
|             //if cell is outside of range of player, disable cell |             //if cell is outside of range of player, disable cell | ||||||
|             if(activeCell.position.distance(playerPosition) > CELL_DISTANCE_MAX){ |             if(Globals.clientWorldData.convertWorldToRealSpace(activeCell.worldPosition).distance(playerPosition) > CELL_DISTANCE_MAX){ | ||||||
|                 //TODO: destroy cell |                 //TODO: destroy cell | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ package electrosphere.client.foliagemanager; | |||||||
| import java.util.Set; | import java.util.Set; | ||||||
| 
 | 
 | ||||||
| import org.joml.Vector3d; | import org.joml.Vector3d; | ||||||
|  | import org.joml.Vector3i; | ||||||
| 
 | 
 | ||||||
| import electrosphere.entity.Entity; | import electrosphere.entity.Entity; | ||||||
| 
 | 
 | ||||||
| @ -10,9 +11,12 @@ import electrosphere.entity.Entity; | |||||||
|  * Contains a set of foliage entities and groups them together. |  * Contains a set of foliage entities and groups them together. | ||||||
|  */ |  */ | ||||||
| public class FoliageCell { | public class FoliageCell { | ||||||
|     //position of the foliage cell |     //position of the foliage cell in world coordinates | ||||||
|     Vector3d position; |     protected Vector3i worldPosition; | ||||||
|  |     //position of the foliage cell in local coordinates | ||||||
|  |     protected Vector3i localPosition; | ||||||
|     //constituent entities |     //constituent entities | ||||||
|     Set<Entity> containedEntities; |     protected Set<Entity> containedEntities; | ||||||
|  |      | ||||||
|      |      | ||||||
| } | } | ||||||
|  | |||||||
| @ -102,6 +102,19 @@ public class ClientWorldData { | |||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Converts a world space vector to a real space vector | ||||||
|  |      * @param position The world space vector | ||||||
|  |      * @return The real space vector | ||||||
|  |      */ | ||||||
|  |     public Vector3d convertWorldToRealSpace(Vector3i position){ | ||||||
|  |         return new Vector3d( | ||||||
|  |             convertWorldToReal(position.x), | ||||||
|  |             convertWorldToReal(position.y), | ||||||
|  |             convertWorldToReal(position.z) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public Vector3i convertRealToVoxelSpace(Vector3d position){ |     public Vector3i convertRealToVoxelSpace(Vector3d position){ | ||||||
|         return new Vector3i( |         return new Vector3i( | ||||||
|             (int)Math.floor(position.x - convertChunkToRealSpace(convertRealToChunkSpace(position.x))), |             (int)Math.floor(position.x - convertChunkToRealSpace(convertRealToChunkSpace(position.x))), | ||||||
|  | |||||||
| @ -57,9 +57,7 @@ public class ClientSimulation { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         //update foliage |         //update foliage | ||||||
|         if(Globals.clientFoliageManager != null){ |  | ||||||
|         Globals.clientFoliageManager.update(); |         Globals.clientFoliageManager.update(); | ||||||
|         } |  | ||||||
|         //tally collidables and offset position accordingly |         //tally collidables and offset position accordingly | ||||||
|         // for(Entity currentCollidable : Globals.entityManager.getEntitiesWithTag(EntityTags.COLLIDABLE)){ |         // for(Entity currentCollidable : Globals.entityManager.getEntitiesWithTag(EntityTags.COLLIDABLE)){ | ||||||
|         //     CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable); |         //     CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable); | ||||||
|  | |||||||
| @ -173,6 +173,8 @@ public class DrawCellManager { | |||||||
|                     drawable.add(targetKey); |                     drawable.add(targetKey); | ||||||
|                     //make drawable entity |                     //make drawable entity | ||||||
|                     keyCellMap.get(targetKey).generateDrawableEntity(); |                     keyCellMap.get(targetKey).generateDrawableEntity(); | ||||||
|  |                     //evaluate for foliage | ||||||
|  |                     Globals.clientFoliageManager.evaluateChunk(worldPos); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -204,6 +206,8 @@ public class DrawCellManager { | |||||||
|                 // } |                 // } | ||||||
|                 keyCellMap.get(targetKey).destroy(); |                 keyCellMap.get(targetKey).destroy(); | ||||||
|                 keyCellMap.get(targetKey).generateDrawableEntity(); |                 keyCellMap.get(targetKey).generateDrawableEntity(); | ||||||
|  |                 //evaluate for foliage | ||||||
|  |                 Globals.clientFoliageManager.evaluateChunk(worldPos); | ||||||
|             } |             } | ||||||
|             drawable.add(targetKey); |             drawable.add(targetKey); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import java.util.List; | |||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
| import java.util.concurrent.CopyOnWriteArrayList; | import java.util.concurrent.CopyOnWriteArrayList; | ||||||
| 
 | 
 | ||||||
|  | import org.joml.Vector3i; | ||||||
|  | 
 | ||||||
| import electrosphere.client.scene.ClientWorldData; | import electrosphere.client.scene.ClientWorldData; | ||||||
| import electrosphere.client.terrain.cache.ChunkData; | import electrosphere.client.terrain.cache.ChunkData; | ||||||
| import electrosphere.client.terrain.cache.ClientTerrainCache; | import electrosphere.client.terrain.cache.ClientTerrainCache; | ||||||
| @ -127,10 +129,26 @@ public class ClientTerrainManager { | |||||||
|         Globals.drawCellManager.markUpdateable(worldX, worldY, worldZ); |         Globals.drawCellManager.markUpdateable(worldX, worldY, worldZ); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     /** | ||||||
|  |      * Gets the chunk data at a given world position | ||||||
|  |      * @param worldX The x component of the world coordinate | ||||||
|  |      * @param worldY The y component of the world coordinate | ||||||
|  |      * @param worldZ The z component of the world coordinate | ||||||
|  |      * @return The chunk data if it exists, otherwise null | ||||||
|  |      */ | ||||||
|     public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ |     public ChunkData getChunkDataAtWorldPoint(int worldX, int worldY, int worldZ){ | ||||||
|         return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ); |         return terrainCache.getSubChunkDataAtPoint(worldX, worldY, worldZ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the chunk data at a given world position | ||||||
|  |      * @param worldPos The world position as a joml vector | ||||||
|  |      * @return The chunk data if it exists, otherwise null | ||||||
|  |      */ | ||||||
|  |     public ChunkData getChunkDataAtWorldPoint(Vector3i worldPos){ | ||||||
|  |         return terrainCache.getSubChunkDataAtPoint(worldPos.x, worldPos.y, worldPos.z); | ||||||
|  |     } | ||||||
|  |      | ||||||
|      |      | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -378,12 +378,9 @@ public class Globals { | |||||||
|         clientHitboxManager = new HitboxManager(); |         clientHitboxManager = new HitboxManager(); | ||||||
|         //ai manager |         //ai manager | ||||||
|         aiManager = new AIManager(); |         aiManager = new AIManager(); | ||||||
|         //data cell manager |         //realm & data cell manager | ||||||
|         realmManager = new RealmManager(); |         realmManager = new RealmManager(); | ||||||
|         // dataCellManager = new DataCellManager(); |  | ||||||
|         // griddedDataCellManager = new GriddedDataCellManager(); |  | ||||||
|         entityDataCellMapper = new EntityDataCellMapper(); |         entityDataCellMapper = new EntityDataCellMapper(); | ||||||
|         // dataCellLocationResolver = new DataCellLocationResolver(); |  | ||||||
|         //nav mesh manager |         //nav mesh manager | ||||||
|         navMeshManager = new NavMeshManager(); |         navMeshManager = new NavMeshManager(); | ||||||
|         //terrain |         //terrain | ||||||
|  | |||||||
| @ -136,7 +136,7 @@ public class Main { | |||||||
|          |          | ||||||
| 
 | 
 | ||||||
|         //debug: create terrain/world viewer |         //debug: create terrain/world viewer | ||||||
|     //    TerrainViewer.runViewer(); |        TerrainViewer.runViewer(); | ||||||
|          |          | ||||||
|         //create the drawing context |         //create the drawing context | ||||||
|         if(Globals.RUN_CLIENT && !Globals.HEADLESS){ |         if(Globals.RUN_CLIENT && !Globals.HEADLESS){ | ||||||
|  | |||||||
| @ -80,6 +80,8 @@ public class ClientLoading { | |||||||
|         Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT); |         Globals.controlHandler.setHandlerState(ControlHandler.ControlsState.NO_INPUT); | ||||||
|         //initialize the "real" objects simulation |         //initialize the "real" objects simulation | ||||||
|         initClientSimulation(); |         initClientSimulation(); | ||||||
|  |         //init foliage manager | ||||||
|  |         initFoliageManager(); | ||||||
|         //initialize the cell manager (client) |         //initialize the cell manager (client) | ||||||
|         initDrawCellManager(); |         initDrawCellManager(); | ||||||
|         //initialize the basic graphical entities of the world (skybox, camera) |         //initialize the basic graphical entities of the world (skybox, camera) | ||||||
| @ -90,8 +92,6 @@ public class ClientLoading { | |||||||
|         setSimulationsToReady(); |         setSimulationsToReady(); | ||||||
|         //init culling manager and other graphics-focused non-simulation items |         //init culling manager and other graphics-focused non-simulation items | ||||||
|         initEntityCullingManager(); |         initEntityCullingManager(); | ||||||
|         //init foliage manager |  | ||||||
|         initFoliageManager(); |  | ||||||
|         //hide cursor |         //hide cursor | ||||||
|         Globals.controlHandler.hideMouse(); |         Globals.controlHandler.hideMouse(); | ||||||
|         //make loading window disappear |         //make loading window disappear | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ import electrosphere.game.data.object.type.model.ObjectTypeLoader; | |||||||
| import electrosphere.game.data.object.type.model.ObjectTypeMap; | import electrosphere.game.data.object.type.model.ObjectTypeMap; | ||||||
| import electrosphere.game.data.projectile.ProjectileTypeHolder; | import electrosphere.game.data.projectile.ProjectileTypeHolder; | ||||||
| import electrosphere.game.data.structure.type.model.StructureTypeMap; | import electrosphere.game.data.structure.type.model.StructureTypeMap; | ||||||
|  | import electrosphere.game.data.voxel.VoxelData; | ||||||
| import electrosphere.game.server.race.model.RaceMap; | import electrosphere.game.server.race.model.RaceMap; | ||||||
| import electrosphere.game.server.symbolism.model.SymbolMap; | import electrosphere.game.server.symbolism.model.SymbolMap; | ||||||
| import electrosphere.util.FileUtils; | import electrosphere.util.FileUtils; | ||||||
| @ -32,6 +33,8 @@ public class Config { | |||||||
|     SymbolMap symbolMap; |     SymbolMap symbolMap; | ||||||
|     RaceMap raceMap; |     RaceMap raceMap; | ||||||
|     ProjectileTypeHolder projectileTypeHolder; |     ProjectileTypeHolder projectileTypeHolder; | ||||||
|  |     //data about every voxel type | ||||||
|  |     VoxelData voxelData; | ||||||
|      |      | ||||||
|     public static Config loadDefaultConfig(){ |     public static Config loadDefaultConfig(){ | ||||||
|         Config config = new Config(); |         Config config = new Config(); | ||||||
| @ -42,6 +45,7 @@ public class Config { | |||||||
|         config.objectTypeLoader = loadObjectTypes("Data/objects.json"); |         config.objectTypeLoader = loadObjectTypes("Data/objects.json"); | ||||||
|         config.symbolMap = FileUtils.loadObjectFromAssetPath("Data/symbolism.json", SymbolMap.class); |         config.symbolMap = FileUtils.loadObjectFromAssetPath("Data/symbolism.json", SymbolMap.class); | ||||||
|         config.raceMap = FileUtils.loadObjectFromAssetPath("Data/races.json", RaceMap.class); |         config.raceMap = FileUtils.loadObjectFromAssetPath("Data/races.json", RaceMap.class); | ||||||
|  |         config.voxelData = FileUtils.loadObjectFromAssetPath("Data/voxelTypes.json", VoxelData.class); | ||||||
|         config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/projectile.json", ProjectileTypeHolder.class); |         config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/projectile.json", ProjectileTypeHolder.class); | ||||||
|         config.projectileTypeHolder.init(); |         config.projectileTypeHolder.init(); | ||||||
|         return config; |         return config; | ||||||
| @ -140,4 +144,8 @@ public class Config { | |||||||
|         return projectileTypeHolder; |         return projectileTypeHolder; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public VoxelData getVoxelData(){ | ||||||
|  |         return voxelData; | ||||||
|  |     } | ||||||
|  |      | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,17 +4,26 @@ import electrosphere.game.data.foliage.type.FoliageType; | |||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * |  * The map of all types of foliage in the game | ||||||
|  * @author amaterasu |  | ||||||
|  */ |  */ | ||||||
| public class FoliageTypeMap { | public class FoliageTypeMap { | ||||||
|      |      | ||||||
|  |     //The list of all foliage types | ||||||
|     List<FoliageType> foliageList; |     List<FoliageType> foliageList; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the list of all foliage types | ||||||
|  |      * @return The list of all foliage types | ||||||
|  |      */ | ||||||
|     public List<FoliageType> getFoliageList() { |     public List<FoliageType> getFoliageList() { | ||||||
|         return foliageList; |         return foliageList; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     /** | ||||||
|  |      * Gets a foliage type by its name | ||||||
|  |      * @param name The name of the foliage type | ||||||
|  |      * @return The type object | ||||||
|  |      */ | ||||||
|     public FoliageType getFoliage(String name){ |     public FoliageType getFoliage(String name){ | ||||||
|         for(FoliageType foliage : foliageList){ |         for(FoliageType foliage : foliageList){ | ||||||
|             if(foliage.getName().matches(name)){ |             if(foliage.getName().matches(name)){ | ||||||
|  | |||||||
							
								
								
									
										47
									
								
								src/main/java/electrosphere/game/data/voxel/VoxelData.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/main/java/electrosphere/game/data/voxel/VoxelData.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | |||||||
|  | package electrosphere.game.data.voxel; | ||||||
|  | 
 | ||||||
|  | import java.util.Set; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * A list of all voxel types in game | ||||||
|  |  */ | ||||||
|  | public class VoxelData { | ||||||
|  |     //The set of all voxel types | ||||||
|  |     Set<VoxelType> types; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets all voxel types | ||||||
|  |      * @return The set of all voxel types | ||||||
|  |      */ | ||||||
|  |     public Set<VoxelType> getTypes(){ | ||||||
|  |         return types; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the voxel type by its name, or null if that type does not exist | ||||||
|  |      * @param name The name of the voxel type | ||||||
|  |      * @return The voxel type or null | ||||||
|  |      */ | ||||||
|  |     public VoxelType getTypeFromName(String name){ | ||||||
|  |         for(VoxelType type : types){ | ||||||
|  |             if(type.name.contains(name)){ | ||||||
|  |                 return type; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the voxel type by its id, or null if that type does not exist | ||||||
|  |      * @param id The id of the voxel type | ||||||
|  |      * @return The voxel type or null | ||||||
|  |      */ | ||||||
|  |     public VoxelType getTypeFromId(int id){ | ||||||
|  |         for(VoxelType type : types){ | ||||||
|  |             if(type.id == id){ | ||||||
|  |                 return type; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/main/java/electrosphere/game/data/voxel/VoxelType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/main/java/electrosphere/game/data/voxel/VoxelType.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | package electrosphere.game.data.voxel; | ||||||
|  | 
 | ||||||
|  | import java.util.Set; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Data about a particular type of voxel | ||||||
|  |  */ | ||||||
|  | public class VoxelType { | ||||||
|  |     //the id of this voxel type | ||||||
|  |     int id; | ||||||
|  |     //the name of the type | ||||||
|  |     String name; | ||||||
|  |     //any ambient foliage that can be placed on this voxel type | ||||||
|  |     Set<String> ambientFoliage; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the id of the voxel type | ||||||
|  |      * @return The id | ||||||
|  |      */ | ||||||
|  |     public int getId(){ | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the name of the voxel type | ||||||
|  |      * @return The name | ||||||
|  |      */ | ||||||
|  |     public String getName(){ | ||||||
|  |         return name; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the names of all ambient foliage that can be placed on this voxel type | ||||||
|  |      * @return The set of names | ||||||
|  |      */ | ||||||
|  |     public Set<String> getAmbientFoliage(){ | ||||||
|  |         return ambientFoliage; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -874,8 +874,13 @@ public class TerrainInterpolator { | |||||||
| //         | //         | ||||||
| //    } | //    } | ||||||
|      |      | ||||||
|      |     /** | ||||||
|     public static float[][] getBicubicInterpolatedChunk(float[][] macroValues, long[][] randomizerValues, int dynamicInterpolationRatio, float randomDampener){ |      * Gets the bicubic interpolation of an array of 5 x 5 values to an array of (dynamicInterpolationRatio + 1) x (dynamicInterpolationRatio + 1) | ||||||
|  |      * @param macroValues The array of values to sample from | ||||||
|  |      * @param dynamicInterpolationRatio The interpolation ratio | ||||||
|  |      * @return The interpolated array | ||||||
|  |      */ | ||||||
|  |     public static float[][] getBicubicInterpolatedChunk(float[][] macroValues, int dynamicInterpolationRatio){ | ||||||
|         float[][] rVal = new float[dynamicInterpolationRatio + 1][dynamicInterpolationRatio + 1]; |         float[][] rVal = new float[dynamicInterpolationRatio + 1][dynamicInterpolationRatio + 1]; | ||||||
|          |          | ||||||
|         float[][] subValues = new float[4][4]; |         float[][] subValues = new float[4][4]; | ||||||
| @ -894,14 +899,6 @@ public class TerrainInterpolator { | |||||||
|         // |         // | ||||||
|         //Inbetween phase 1 |         //Inbetween phase 1 | ||||||
|         // |         // | ||||||
|         long phase1Randomizer =  |  | ||||||
|                 randomizerValues[0][0] +  |  | ||||||
|                 randomizerValues[0][1] +  |  | ||||||
|                 randomizerValues[0][2] +  |  | ||||||
|                 randomizerValues[0][3]; |  | ||||||
|          |  | ||||||
|         Random randomizer = new Random(phase1Randomizer); |  | ||||||
|          |  | ||||||
|         float a0 = subValues[0][3] - subValues[0][2] - subValues[0][0] + subValues[0][1]; |         float a0 = subValues[0][3] - subValues[0][2] - subValues[0][0] + subValues[0][1]; | ||||||
|         float a1 = subValues[0][0] - subValues[0][1] - a0; |         float a1 = subValues[0][0] - subValues[0][1] - a0; | ||||||
|         float a2 = subValues[0][2] - subValues[0][0]; |         float a2 = subValues[0][2] - subValues[0][0]; | ||||||
| @ -909,7 +906,7 @@ public class TerrainInterpolator { | |||||||
|         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ |         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ | ||||||
|             float x = (float)i/(float)dynamicInterpolationRatio; |             float x = (float)i/(float)dynamicInterpolationRatio; | ||||||
|             float x2 = x * x; |             float x2 = x * x; | ||||||
|             inbetweenStage[0][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3 + randomizer.nextFloat() * randomDampener; |             inbetweenStage[0][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|          |          | ||||||
| @ -917,14 +914,6 @@ public class TerrainInterpolator { | |||||||
|         //Inbetween phase 2 |         //Inbetween phase 2 | ||||||
|         // |         // | ||||||
|          |          | ||||||
|         phase1Randomizer =  |  | ||||||
|                 randomizerValues[1][0] +  |  | ||||||
|                 randomizerValues[1][1] +  |  | ||||||
|                 randomizerValues[1][2] +  |  | ||||||
|                 randomizerValues[1][3]; |  | ||||||
|          |  | ||||||
|         randomizer = new Random(phase1Randomizer); |  | ||||||
|          |  | ||||||
|         a0 = subValues[1][3] - subValues[1][2] - subValues[1][0] + subValues[1][1]; |         a0 = subValues[1][3] - subValues[1][2] - subValues[1][0] + subValues[1][1]; | ||||||
|         a1 = subValues[1][0] - subValues[1][1] - a0; |         a1 = subValues[1][0] - subValues[1][1] - a0; | ||||||
|         a2 = subValues[1][2] - subValues[1][0]; |         a2 = subValues[1][2] - subValues[1][0]; | ||||||
| @ -932,7 +921,7 @@ public class TerrainInterpolator { | |||||||
|         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ |         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ | ||||||
|             float x = (float)i/(float)dynamicInterpolationRatio; |             float x = (float)i/(float)dynamicInterpolationRatio; | ||||||
|             float x2 = x * x; |             float x2 = x * x; | ||||||
|             inbetweenStage[1][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3 + randomizer.nextFloat() * randomDampener; |             inbetweenStage[1][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|          |          | ||||||
| @ -940,14 +929,6 @@ public class TerrainInterpolator { | |||||||
|         //Inbetween phase 3 |         //Inbetween phase 3 | ||||||
|         // |         // | ||||||
|          |          | ||||||
|         phase1Randomizer =  |  | ||||||
|                 randomizerValues[2][0] +  |  | ||||||
|                 randomizerValues[2][1] +  |  | ||||||
|                 randomizerValues[2][2] +  |  | ||||||
|                 randomizerValues[2][3]; |  | ||||||
|          |  | ||||||
|         randomizer = new Random(phase1Randomizer); |  | ||||||
|          |  | ||||||
|         a0 = subValues[2][3] - subValues[2][2] - subValues[2][0] + subValues[2][1]; |         a0 = subValues[2][3] - subValues[2][2] - subValues[2][0] + subValues[2][1]; | ||||||
|         a1 = subValues[2][0] - subValues[2][1] - a0; |         a1 = subValues[2][0] - subValues[2][1] - a0; | ||||||
|         a2 = subValues[2][2] - subValues[2][0]; |         a2 = subValues[2][2] - subValues[2][0]; | ||||||
| @ -955,7 +936,7 @@ public class TerrainInterpolator { | |||||||
|         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ |         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ | ||||||
|             float x = (float)i/(float)dynamicInterpolationRatio; |             float x = (float)i/(float)dynamicInterpolationRatio; | ||||||
|             float x2 = x * x; |             float x2 = x * x; | ||||||
|             inbetweenStage[2][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3 + randomizer.nextFloat() * randomDampener; |             inbetweenStage[2][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|          |          | ||||||
| @ -964,14 +945,6 @@ public class TerrainInterpolator { | |||||||
|         //Inbetween phase 4 |         //Inbetween phase 4 | ||||||
|         // |         // | ||||||
|          |          | ||||||
|         phase1Randomizer =  |  | ||||||
|                 randomizerValues[3][0] +  |  | ||||||
|                 randomizerValues[3][1] +  |  | ||||||
|                 randomizerValues[3][2] +  |  | ||||||
|                 randomizerValues[3][3]; |  | ||||||
|          |  | ||||||
|         randomizer = new Random(phase1Randomizer); |  | ||||||
|          |  | ||||||
|         a0 = subValues[3][3] - subValues[3][2] - subValues[3][0] + subValues[3][1]; |         a0 = subValues[3][3] - subValues[3][2] - subValues[3][0] + subValues[3][1]; | ||||||
|         a1 = subValues[3][0] - subValues[3][1] - a0; |         a1 = subValues[3][0] - subValues[3][1] - a0; | ||||||
|         a2 = subValues[3][2] - subValues[3][0]; |         a2 = subValues[3][2] - subValues[3][0]; | ||||||
| @ -979,7 +952,7 @@ public class TerrainInterpolator { | |||||||
|         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ |         for(int i = 0; i < dynamicInterpolationRatio + 1; i++){ | ||||||
|             float x = (float)i/(float)dynamicInterpolationRatio; |             float x = (float)i/(float)dynamicInterpolationRatio; | ||||||
|             float x2 = x * x; |             float x2 = x * x; | ||||||
|             inbetweenStage[3][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3 + randomizer.nextFloat() * randomDampener; |             inbetweenStage[3][i] = a0 * x * x2 + a1 * x2 + a2 * x + a3; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|          |          | ||||||
| @ -989,14 +962,6 @@ public class TerrainInterpolator { | |||||||
|          |          | ||||||
|          |          | ||||||
|         for(int x = 0; x < dynamicInterpolationRatio + 1; x++){ |         for(int x = 0; x < dynamicInterpolationRatio + 1; x++){ | ||||||
| //            long phase2Randomizer =  |  | ||||||
| //                randomizerValues[3][0] +  |  | ||||||
| //                randomizerValues[3][1] +  |  | ||||||
| //                randomizerValues[3][2] +  |  | ||||||
| //                randomizerValues[3][3]; |  | ||||||
|              |  | ||||||
| //            randomizer = new Random(phase2Randomizer); |  | ||||||
| 
 |  | ||||||
|             a0 = inbetweenStage[3][x] - inbetweenStage[2][x] - inbetweenStage[0][x] + inbetweenStage[1][x]; |             a0 = inbetweenStage[3][x] - inbetweenStage[2][x] - inbetweenStage[0][x] + inbetweenStage[1][x]; | ||||||
|             a1 = inbetweenStage[0][x] - inbetweenStage[1][x] - a0; |             a1 = inbetweenStage[0][x] - inbetweenStage[1][x] - a0; | ||||||
|             a2 = inbetweenStage[2][x] - inbetweenStage[0][x]; |             a2 = inbetweenStage[2][x] - inbetweenStage[0][x]; | ||||||
| @ -1004,8 +969,7 @@ public class TerrainInterpolator { | |||||||
|             for(int y = 0; y < dynamicInterpolationRatio + 1; y++){ |             for(int y = 0; y < dynamicInterpolationRatio + 1; y++){ | ||||||
|                 float i = (float)y/(float)dynamicInterpolationRatio; |                 float i = (float)y/(float)dynamicInterpolationRatio; | ||||||
|                 float i2 = i * i; |                 float i2 = i * i; | ||||||
|                 rVal[y][x] = a0 * i * i2 + a1 * i2 + a2 * i + a3 + randomizer.nextFloat() * randomDampener; |                 rVal[y][x] = a0 * i * i2 + a1 * i2 + a2 * i + a3; | ||||||
| //                rVal[x][y] = i * inbetweenStage[1][x] + (1.0f - i) * inbetweenStage[2][x]; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ public class ServerContentManager { | |||||||
|         if(!Globals.serverWorldData.isArena()){ //in other words, if not arena mode |         if(!Globals.serverWorldData.isArena()){ //in other words, if not arena mode | ||||||
|             //if on disk (has already been generated) |             //if on disk (has already been generated) | ||||||
|             //else create from scratch |             //else create from scratch | ||||||
|             EnvironmentGenerator.generatePlains(cell, worldPos, Globals.serverTerrainManager.getRandomizerAtPoint(worldPos.x, worldPos.z)); |             EnvironmentGenerator.generatePlains(cell, worldPos, 0); | ||||||
|         } |         } | ||||||
|         cell.setNavMesh( |         cell.setNavMesh( | ||||||
|             NavMeshUtils.createMeshFromChunk(Globals.serverTerrainManager.getChunk( |             NavMeshUtils.createMeshFromChunk(Globals.serverTerrainManager.getChunk( | ||||||
|  | |||||||
| @ -0,0 +1,425 @@ | |||||||
|  | package electrosphere.server.terrain.generation; | ||||||
|  | 
 | ||||||
|  | import java.util.Random; | ||||||
|  | import java.util.concurrent.CountDownLatch; | ||||||
|  | import java.util.concurrent.Executors; | ||||||
|  | import java.util.concurrent.ThreadPoolExecutor; | ||||||
|  | 
 | ||||||
|  | import electrosphere.game.terrain.processing.TerrainInterpolator; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Performs an erosion simulation that expands the heightmap and simulates drainage across the world | ||||||
|  |  */ | ||||||
|  | public class ErosionSimulation { | ||||||
|  | 
 | ||||||
|  |     //The number of threads to farm simulation chunks out to | ||||||
|  |     private static final int NUMBER_THREADS = 16; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     //The initial heightmap passed into the simulation | ||||||
|  |     private float[][] startHeightmap; | ||||||
|  | 
 | ||||||
|  |     //The actually valid data is flipped between primaryHeightmap and alternateHeightmap as the simulation runs | ||||||
|  |     //so that no more than one allocation has to happen. isStartHeightmap tracks which one contains the | ||||||
|  |     //valid data. | ||||||
|  | 
 | ||||||
|  |     //The primary data heightmap | ||||||
|  |     private float[][] primaryHeightmap; | ||||||
|  |     //The second array to hold values | ||||||
|  |     private float[][] alternateHeightmap; | ||||||
|  |     //Controls which heightmap contains the hot data | ||||||
|  |     private boolean isPrimaryHeightmap = true; | ||||||
|  | 
 | ||||||
|  |     //Keeps track of how much water is in a given location | ||||||
|  |     private float[][] primaryHydrationMap; | ||||||
|  |     private float[][] alternateHydrationMap; | ||||||
|  | 
 | ||||||
|  |     //The height at which the ocean begins. No erosion simulation will happen below this point. | ||||||
|  |     private float oceanLevel; | ||||||
|  | 
 | ||||||
|  |     //The size of the chunks of simulation that are created | ||||||
|  |     private int interpolationRatio; | ||||||
|  | 
 | ||||||
|  |     //Random for seeding worker threads | ||||||
|  |     Random rand; | ||||||
|  | 
 | ||||||
|  |     //threadpool for the step phase | ||||||
|  |     ThreadPoolExecutor threadPool; | ||||||
|  | 
 | ||||||
|  |     protected ErosionSimulation(float[][] heightmap, float oceanLevel, int interpolationRatio, long randomSeed){ | ||||||
|  |         this.interpolationRatio = interpolationRatio; | ||||||
|  |         this.startHeightmap = heightmap; | ||||||
|  |         this.primaryHeightmap = new float[heightmap.length * interpolationRatio][heightmap[0].length * interpolationRatio]; | ||||||
|  |         this.alternateHeightmap = new float[heightmap.length * interpolationRatio][heightmap[0].length * interpolationRatio]; | ||||||
|  |         this.primaryHydrationMap = new float[heightmap.length * interpolationRatio][heightmap[0].length * interpolationRatio]; | ||||||
|  |         this.alternateHydrationMap = new float[heightmap.length * interpolationRatio][heightmap[0].length * interpolationRatio]; | ||||||
|  |         this.oceanLevel = oceanLevel; | ||||||
|  |         this.rand = new Random(randomSeed); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Runs the erosion simulation | ||||||
|  |      */ | ||||||
|  |     protected void simulate(){ | ||||||
|  |         setup(); | ||||||
|  |         float totalHydration = getTotalHydration(); | ||||||
|  |         float waterLevelRatio = totalHydration / (this.primaryHydrationMap.length * this.primaryHydrationMap[0].length); | ||||||
|  |         while(totalHydration > 0){ | ||||||
|  |             CountDownLatch latch = new CountDownLatch(this.startHeightmap.length * this.startHeightmap[0].length); | ||||||
|  |             for(int x = 0; x < this.startHeightmap.length; x++){ | ||||||
|  |                 for(int y = 0; y < this.startHeightmap[0].length; y++){ | ||||||
|  |                     //queue location | ||||||
|  |                     ErosionJob job = new ErosionJob( | ||||||
|  |                         primaryHeightmap, | ||||||
|  |                         alternateHeightmap, | ||||||
|  |                         primaryHydrationMap, | ||||||
|  |                         alternateHydrationMap, | ||||||
|  |                         isPrimaryHeightmap, | ||||||
|  |                         new Vector(x,y), | ||||||
|  |                         interpolationRatio, | ||||||
|  |                         oceanLevel, | ||||||
|  |                         waterLevelRatio, | ||||||
|  |                         rand.nextLong(), | ||||||
|  |                         latch | ||||||
|  |                     ); | ||||||
|  |                     threadPool.submit(job); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             //await all jobs | ||||||
|  |             try { | ||||||
|  |                 latch.await(); | ||||||
|  |             } catch (InterruptedException e) { | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } | ||||||
|  |             totalHydration = getTotalHydration(); | ||||||
|  |             waterLevelRatio = totalHydration / (this.primaryHydrationMap.length * this.primaryHydrationMap[0].length); | ||||||
|  |             //flip primary map | ||||||
|  |             isPrimaryHeightmap = !isPrimaryHeightmap; | ||||||
|  |             System.out.println(totalHydration + " - " + waterLevelRatio); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Sets up the simulation | ||||||
|  |      */ | ||||||
|  |     private void setup(){ | ||||||
|  |         threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(NUMBER_THREADS); | ||||||
|  |         //interpolate start heightmap into full heightmap | ||||||
|  |         int sampleCount = 25; | ||||||
|  |         int[] xSampleOffset = new int[]{ | ||||||
|  |             -2,-1,0,1,2, | ||||||
|  |             -2,-1,0,1,2, | ||||||
|  |             -2,-1,0,1,2, | ||||||
|  |             -2,-1,0,1,2, | ||||||
|  |             -2,-1,0,1,2, | ||||||
|  |         }; | ||||||
|  |         int[] ySampleOffset = new int[]{ | ||||||
|  |             -2,-2,-2,-2,-2, | ||||||
|  |             -1,-1,-1,-1,-1, | ||||||
|  |             0,0,0,0,0, | ||||||
|  |             1,1,1,1,1, | ||||||
|  |             2,2,2,2,2, | ||||||
|  |         }; | ||||||
|  |         for(int x = 0; x < startHeightmap.length; x++){ | ||||||
|  |             for(int y = 0; y < startHeightmap[0].length; y++){ | ||||||
|  |                 //get the array of samples | ||||||
|  |                 float[][] sample = new float[5][5]; | ||||||
|  |                 for(int i = 0; i < sampleCount; i++){ | ||||||
|  |                     if(x + xSampleOffset[i] >= 0 && x + xSampleOffset[i] < startHeightmap.length && | ||||||
|  |                     y + ySampleOffset[i] >= 0 && y + ySampleOffset[i] < startHeightmap[0].length){ | ||||||
|  |                         //have to add 2 to xSampleOffset and ySampleOffset to get accurate position in sample array | ||||||
|  |                         sample[xSampleOffset[i] + 2][ySampleOffset[i] + 2] = startHeightmap[x + xSampleOffset[i]][y + ySampleOffset[i]]; | ||||||
|  |                     } else { | ||||||
|  |                         sample[xSampleOffset[i] + 2][ySampleOffset[i] + 2] = 0; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 float[][] interpolatedValues = TerrainInterpolator.getBicubicInterpolatedChunk(sample, interpolationRatio); | ||||||
|  |                 for(int m = 0; m < interpolationRatio; m++){ | ||||||
|  |                     for(int n = 0; n < interpolationRatio; n++){ | ||||||
|  |                         primaryHeightmap[x * interpolationRatio + m][y * interpolationRatio + n]   = interpolatedValues[m][n]; | ||||||
|  |                         alternateHeightmap[x * interpolationRatio + m][y * interpolationRatio + n] = interpolatedValues[m][n]; | ||||||
|  |                         //seed initial hydration map | ||||||
|  |                         primaryHydrationMap[x * interpolationRatio + m][y * interpolationRatio + n] = 1; | ||||||
|  |                         alternateHydrationMap[x * interpolationRatio + m][y * interpolationRatio + n] = 1; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Gets the data resulting from the erosion simulation | ||||||
|  |      * @return The data | ||||||
|  |      */ | ||||||
|  |     protected float[][] getData(){ | ||||||
|  |         if(isPrimaryHeightmap){ | ||||||
|  |             return primaryHeightmap; | ||||||
|  |         } else { | ||||||
|  |             return alternateHeightmap; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns the total hydration of the currently active map | ||||||
|  |      * @return The total hydration | ||||||
|  |      */ | ||||||
|  |     private float getTotalHydration(){ | ||||||
|  |         float sum = 0; | ||||||
|  |         float highestElevation = 0; | ||||||
|  |         float highestHydration = 0; | ||||||
|  |         if(isPrimaryHeightmap){ | ||||||
|  |             for(int x = 0; x < primaryHydrationMap.length; x++){ | ||||||
|  |                 for(int y = 0; y < primaryHydrationMap[0].length; y++){ | ||||||
|  |                     sum = sum + primaryHydrationMap[x][y]; | ||||||
|  |                     if(primaryHeightmap[x][y] > highestElevation){ | ||||||
|  |                         highestElevation = primaryHeightmap[x][y]; | ||||||
|  |                     } | ||||||
|  |                     if(primaryHydrationMap[x][y] > highestHydration){ | ||||||
|  |                         highestHydration = primaryHydrationMap[x][y]; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             for(int x = 0; x < alternateHydrationMap.length; x++){ | ||||||
|  |                 for(int y = 0; y < alternateHydrationMap[0].length; y++){ | ||||||
|  |                     sum = sum + alternateHydrationMap[x][y]; | ||||||
|  |                     if(alternateHeightmap[x][y] > highestElevation){ | ||||||
|  |                         highestElevation = alternateHeightmap[x][y]; | ||||||
|  |                     } | ||||||
|  |                     if(alternateHydrationMap[x][y] > highestHydration){ | ||||||
|  |                         highestHydration = alternateHydrationMap[x][y]; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         System.out.println("Highest elev: " + highestElevation); | ||||||
|  |         System.out.println("Highest hydra: " + highestHydration); | ||||||
|  |         return sum; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * A runnable job of simulation erosion on a single chunk | ||||||
|  |      */ | ||||||
|  |     static class ErosionJob implements Runnable { | ||||||
|  | 
 | ||||||
|  |         static final int MAX_HYDRATION = 25; | ||||||
|  | 
 | ||||||
|  |         float primaryHeightmap[][]; | ||||||
|  |         float alternateHeightmap[][]; | ||||||
|  |         float[][] primaryHydrationMap; | ||||||
|  |         float[][] alternateHydrationMap; | ||||||
|  |         boolean usePrimaryMaps; | ||||||
|  |         Vector targetLocation; | ||||||
|  |         int interpolationRatio; | ||||||
|  |         float oceanLevel; | ||||||
|  |         float waterLevelRatio; | ||||||
|  |         Random rand; | ||||||
|  |         CountDownLatch latch; | ||||||
|  | 
 | ||||||
|  |         protected ErosionJob( | ||||||
|  |             float[][] primaryHeightmap, | ||||||
|  |             float[][] alternateHeightmap, | ||||||
|  |             float[][] primaryHydrationMap, | ||||||
|  |             float[][] alternateHydrationMap, | ||||||
|  |             boolean usePrimaryMaps, | ||||||
|  |             Vector targetLocation, | ||||||
|  |             int interpolationRatio, | ||||||
|  |             float oceanLevel, | ||||||
|  |             float waterLevelRatio, | ||||||
|  |             long randomSeed, | ||||||
|  |             CountDownLatch latch | ||||||
|  |         ){ | ||||||
|  |             this.primaryHeightmap = primaryHeightmap; | ||||||
|  |             this.alternateHeightmap = alternateHeightmap; | ||||||
|  |             this.primaryHydrationMap = primaryHydrationMap; | ||||||
|  |             this.alternateHydrationMap = alternateHydrationMap; | ||||||
|  |             this.usePrimaryMaps = usePrimaryMaps; | ||||||
|  |             this.targetLocation = targetLocation; | ||||||
|  |             this.interpolationRatio = interpolationRatio; | ||||||
|  |             this.oceanLevel = oceanLevel; | ||||||
|  |             this.waterLevelRatio = waterLevelRatio; | ||||||
|  |             this.rand = new Random(randomSeed); | ||||||
|  |             this.latch = latch; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         static final int[] offsetX = new int[]{ | ||||||
|  |             -1,1,0,0 | ||||||
|  |         }; | ||||||
|  |         static final int[] offsetY = new int[]{ | ||||||
|  |             0,0,-1,1 | ||||||
|  |         }; | ||||||
|  |         @Override | ||||||
|  |         public void run() { | ||||||
|  |             for(int x = 0; x < interpolationRatio; x++){ | ||||||
|  |                 for(int y = 0; y < interpolationRatio; y++){ | ||||||
|  |                     float currentHeight = 0; | ||||||
|  |                     int targetX = targetLocation.x * interpolationRatio + x; | ||||||
|  |                     int targetY = targetLocation.y * interpolationRatio + y; | ||||||
|  |                     if(usePrimaryMaps){ | ||||||
|  |                         currentHeight = primaryHeightmap[targetX][targetY]; | ||||||
|  |                         float oldHydration = primaryHydrationMap[targetX][targetY]; | ||||||
|  |                         float newHydration = 0; | ||||||
|  |                         float highestEncounteredElevation = 0; | ||||||
|  |                         int numberHydrationHits = 0; | ||||||
|  |                         // if(targetX == 728 && targetY == 732){ | ||||||
|  |                         //     System.out.println("asdf"); | ||||||
|  |                         // } | ||||||
|  |                         //calculate total hydration | ||||||
|  |                         for(int i = 0; i < 4; i++){ | ||||||
|  |                             if(targetX + offsetX[i] >= 0 && targetX + offsetX[i] < primaryHeightmap.length && | ||||||
|  |                             targetY + offsetY[i] >= 0 && targetY + offsetY[i] < primaryHeightmap[0].length | ||||||
|  |                             ){ | ||||||
|  |                                 if(currentHeight < primaryHeightmap[targetX + offsetX[i]][targetY + offsetY[i]]){ | ||||||
|  |                                     numberHydrationHits++; | ||||||
|  |                                     float sourceHydration = primaryHydrationMap[targetX + offsetX[i]][targetY + offsetY[i]]; | ||||||
|  |                                     float percentageFromSource = calculatePercentageRunoff( | ||||||
|  |                                         targetX + offsetX[i], | ||||||
|  |                                         targetY + offsetY[i], | ||||||
|  |                                         targetX, | ||||||
|  |                                         targetY, | ||||||
|  |                                         primaryHeightmap | ||||||
|  |                                     ); | ||||||
|  |                                     newHydration = newHydration + sourceHydration * percentageFromSource; | ||||||
|  |                                 } else if(currentHeight == primaryHeightmap[targetX + offsetX[i]][targetY + offsetY[i]] && rand.nextInt() % alternateHydrationMap[targetX][targetY] > 5) { | ||||||
|  |                                     // numberHydrationHits++; | ||||||
|  |                                     // alternateHydrationMap[targetX][targetY] = alternateHydrationMap[targetX][targetY] + primaryHydrationMap[targetX + offsetX[i]][targetY + offsetY[i]]; | ||||||
|  |                                 } else { | ||||||
|  |                                     if(primaryHeightmap[targetX + offsetX[i]][targetY + offsetY[i]] > highestEncounteredElevation){ | ||||||
|  |                                         highestEncounteredElevation = primaryHeightmap[targetX + offsetX[i]][targetY + offsetY[i]]; | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         //calculate shear due to hydration | ||||||
|  |                         float shear = Math.abs(newHydration - oldHydration); | ||||||
|  |                         if(waterLevelRatio > 1.0f){ | ||||||
|  |                             shear = alternateHydrationMap[targetX][targetY] / waterLevelRatio; | ||||||
|  |                         } | ||||||
|  |                         //clamp hydration value | ||||||
|  |                         alternateHydrationMap[targetX][targetY] = Math.min(newHydration,MAX_HYDRATION); | ||||||
|  |                         if(numberHydrationHits == 4){ | ||||||
|  |                             alternateHydrationMap[targetX][targetY] = 0; | ||||||
|  |                         } | ||||||
|  |                         if(currentHeight > oceanLevel && numberHydrationHits < 4){ | ||||||
|  |                             alternateHeightmap[targetX][targetY] = Math.max(highestEncounteredElevation,currentHeight - 0.1f / shear); | ||||||
|  |                         } else { | ||||||
|  |                             //if below sea level, delete hydration | ||||||
|  |                             alternateHeightmap[targetX][targetY] = currentHeight; | ||||||
|  |                             alternateHydrationMap[targetX][targetY] = 0; | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         // if(targetX == 728 && targetY == 732){ | ||||||
|  |                         //     System.out.println("asdf"); | ||||||
|  |                         // } | ||||||
|  |                         currentHeight = alternateHeightmap[targetX][targetY]; | ||||||
|  |                         float oldHydration = primaryHydrationMap[targetX][targetY]; | ||||||
|  |                         float newHydration = 0; | ||||||
|  |                         float highestEncounteredElevation = 0; | ||||||
|  |                         int numberHydrationHits = 0; | ||||||
|  |                         //check each neighbor to see who we can interact with | ||||||
|  |                         for(int i = 0; i < 4; i++){ | ||||||
|  |                             if(targetX + offsetX[i] >= 0 && targetX + offsetX[i] < primaryHeightmap.length && | ||||||
|  |                             targetY + offsetY[i] >= 0 && targetY + offsetY[i] < primaryHeightmap[0].length | ||||||
|  |                             ){ | ||||||
|  |                                 //if the neighbor is taller, pull hydration from it | ||||||
|  |                                 if(currentHeight < alternateHeightmap[targetX + offsetX[i]][targetY + offsetY[i]]){ | ||||||
|  |                                     numberHydrationHits++; | ||||||
|  |                                     float sourceHydration = primaryHydrationMap[targetX + offsetX[i]][targetY + offsetY[i]]; | ||||||
|  |                                     float percentageFromSource = calculatePercentageRunoff( | ||||||
|  |                                         targetX + offsetX[i], | ||||||
|  |                                         targetY + offsetY[i], | ||||||
|  |                                         targetX, | ||||||
|  |                                         targetY, | ||||||
|  |                                         alternateHeightmap | ||||||
|  |                                     ); | ||||||
|  |                                     newHydration = newHydration + sourceHydration * percentageFromSource; | ||||||
|  |                                 } else if(currentHeight == alternateHeightmap[targetX + offsetX[i]][targetY + offsetY[i]] && rand.nextInt() % primaryHydrationMap[targetX][targetY] > 5) { | ||||||
|  |                                     //if the neighbor is the same height, have a chance to pull hydration from it | ||||||
|  |                                     // numberHydrationHits++; | ||||||
|  |                                     // primaryHydrationMap[targetX][targetY] = primaryHydrationMap[targetX][targetY] + alternateHydrationMap[targetX + offsetX[i]][targetY + offsetY[i]]; | ||||||
|  |                                 } else { | ||||||
|  |                                     //if the neighbor is smaller, but taller than the tallest neighbor currently encountered, record its height | ||||||
|  |                                     if(alternateHeightmap[targetX + offsetX[i]][targetY + offsetY[i]] > highestEncounteredElevation){ | ||||||
|  |                                         highestEncounteredElevation = alternateHeightmap[targetX + offsetX[i]][targetY + offsetY[i]]; | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         //calculate shear due to hydration | ||||||
|  |                         float shear = Math.abs(newHydration - oldHydration); | ||||||
|  |                         //bound the hydration by the total hydration of the map | ||||||
|  |                         //This keeps the hydration from explosively increasing unbounded | ||||||
|  |                         if(waterLevelRatio > 1.0f){ | ||||||
|  |                             shear = alternateHydrationMap[targetX][targetY] / waterLevelRatio; | ||||||
|  |                         } | ||||||
|  |                         //clamp hydration value | ||||||
|  |                         primaryHydrationMap[targetX][targetY] = Math.min(newHydration,MAX_HYDRATION); | ||||||
|  |                         //If every neighbor is taller, this is a local minimum and should be treated as a lake (removes all hydration) | ||||||
|  |                         if(numberHydrationHits == 4){ | ||||||
|  |                             primaryHydrationMap[targetX][targetY] = 0; | ||||||
|  |                         } | ||||||
|  |                         if(currentHeight > oceanLevel && numberHydrationHits < 4){ | ||||||
|  |                             primaryHeightmap[targetX][targetY] = Math.max(highestEncounteredElevation,currentHeight - 0.1f / shear); | ||||||
|  |                         } else { | ||||||
|  |                             //if below sea level, delete hydration | ||||||
|  |                             primaryHeightmap[targetX][targetY] = currentHeight; | ||||||
|  |                             primaryHydrationMap[targetX][targetY] = 0; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             latch.countDown(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Basically calculates how much runoff should go from source to destination. The percentage is based on how much of the total drop to all neighbors destination would be. | ||||||
|  |          * @param sourceX The x coordinate of the source of the water | ||||||
|  |          * @param sourceY The y coordinate of the source of the water | ||||||
|  |          * @param destinationX The x coordinate of the destination of the water | ||||||
|  |          * @param destinationY The y coordinate of the destination of the water | ||||||
|  |          * @param elevationMapToCheck The elevation map to use as reference | ||||||
|  |          * @return The percentage of water to pull from source to destination | ||||||
|  |          */ | ||||||
|  |         private float calculatePercentageRunoff(int sourceX, int sourceY, int destinationX, int destinationY, float[][] elevationMapToCheck){ | ||||||
|  |             //the difference between the source and destination points in elevation | ||||||
|  |             float heightDifferencToDestination = elevationMapToCheck[sourceX][sourceY] - elevationMapToCheck[destinationX][destinationY]; | ||||||
|  |             //the sum difference between source and all its smaller neighbors | ||||||
|  |             float totalHeightDifference = 0; | ||||||
|  |             for(int i = 0; i < 4; i++){ | ||||||
|  |                 if(sourceX + offsetX[i] >= 0 && sourceX + offsetX[i] < primaryHeightmap.length && | ||||||
|  |                 sourceY + offsetY[i] >= 0 && sourceY + offsetY[i] < primaryHeightmap[0].length | ||||||
|  |                 ){ | ||||||
|  |                     if(elevationMapToCheck[sourceX][sourceY] > elevationMapToCheck[sourceX + offsetX[i]][sourceY + offsetY[i]]){ | ||||||
|  |                         totalHeightDifference = totalHeightDifference + elevationMapToCheck[sourceX][sourceY] - elevationMapToCheck[sourceX + offsetX[i]][sourceY + offsetY[i]]; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return heightDifferencToDestination / totalHeightDifference; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Checks if a given location has all neighbors that are the same height | ||||||
|  |          * @param sourceX The location to check x coordinate | ||||||
|  |          * @param sourceY The location to check y coordinate | ||||||
|  |          * @param elevationMapToCheck The elevation map to reference | ||||||
|  |          * @return True if all neighbors are flat, false otherwise | ||||||
|  |          */ | ||||||
|  |         private boolean neighborIsFlat(int sourceX, int sourceY, float[][] elevationMapToCheck){ | ||||||
|  |             for(int i = 0; i < 4; i++){ | ||||||
|  |                 if(sourceX + offsetX[i] >= 0 && sourceX + offsetX[i] < primaryHeightmap.length && | ||||||
|  |                 sourceY + offsetY[i] >= 0 && sourceY + offsetY[i] < primaryHeightmap[0].length | ||||||
|  |                 ){ | ||||||
|  |                     if(elevationMapToCheck[sourceX][sourceY] != elevationMapToCheck[sourceX + offsetX[i]][sourceY + offsetY[i]]){ | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void createVisualization(){ | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -12,10 +12,10 @@ class Hotspot { | |||||||
|     int life_max; |     int life_max; | ||||||
|     int magnitude_current; |     int magnitude_current; | ||||||
|     int magnitude_max; |     int magnitude_max; | ||||||
|     TerrainGenerator parent; |     TectonicSimulation parent; | ||||||
|      |      | ||||||
|      |      | ||||||
|     protected Hotspot(int x, int y, int life_max, int magnitude, TerrainGenerator parent) { |     protected Hotspot(int x, int y, int life_max, int magnitude, TectonicSimulation parent) { | ||||||
|         this.x = x; |         this.x = x; | ||||||
|         this.y = y; |         this.y = y; | ||||||
|         this.life_current = 0; |         this.life_current = 0; | ||||||
|  | |||||||
| @ -10,9 +10,9 @@ import javax.swing.JPanel; | |||||||
|  */ |  */ | ||||||
| class InterpolationDisplay extends JPanel{ | class InterpolationDisplay extends JPanel{ | ||||||
| 
 | 
 | ||||||
|     TerrainGen parent; |     TerrainGenerator parent; | ||||||
| 
 | 
 | ||||||
|     protected InterpolationDisplay(TerrainGen parent){ |     protected InterpolationDisplay(TerrainGenerator parent){ | ||||||
|         this.parent = parent; |         this.parent = parent; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -22,9 +22,9 @@ class InterpolationDisplay extends JPanel{ | |||||||
|         if(parent.displayToggle == 0) { |         if(parent.displayToggle == 0) { | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 for (int y = 0; y < width; y++) { |                 for (int y = 0; y < width; y++) { | ||||||
|                     if (parent.mountainParsed[x][y] > TerrainGen.MOUNTAIN_THRESHOLD - 1) { |                     if (parent.mountainParsed[x][y] > TerrainGenerator.MOUNTAIN_THRESHOLD - 1) { | ||||||
|                         g.setColor(new Color((int) (parent.elevation[x][y] / 100.0 * 254 * (parent.brightness / 100.0)), 1, 1)); |                         g.setColor(new Color((int) (parent.elevation[x][y] / 100.0 * 254 * (parent.brightness / 100.0)), 1, 1)); | ||||||
|                     } else if (parent.oceanParsed[x][y] > TerrainGen.OCEAN_THRESHOLD - 1) { |                     } else if (parent.oceanParsed[x][y] > TerrainGenerator.OCEAN_THRESHOLD - 1) { | ||||||
|                         g.setColor( |                         g.setColor( | ||||||
|                                 new Color( |                                 new Color( | ||||||
|                                         1, |                                         1, | ||||||
| @ -39,6 +39,28 @@ class InterpolationDisplay extends JPanel{ | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else if(parent.displayToggle == 1) { |         } else if(parent.displayToggle == 1) { | ||||||
|  |             for (int x = 0; x < parent.continentPhaseDimension * parent.EROSION_INTERPOLATION_RATIO; x++) { | ||||||
|  |                 for (int y = 0; y < parent.continentPhaseDimension * parent.EROSION_INTERPOLATION_RATIO; y++) { | ||||||
|  |                     if (parent.erosionHeightmap[x][y] > TerrainGenerator.MOUNTAIN_THRESHOLD - 1) { | ||||||
|  |                         float color = Math.max(0,Math.min(parent.erosionHeightmap[x][y],100)); | ||||||
|  |                         g.setColor(new Color((int) (color / 100.0 * 254 * (parent.brightness / 100.0)), 1, 1)); | ||||||
|  |                     } else if (parent.erosionHeightmap[x][y] < TerrainGenerator.OCEAN_THRESHOLD - 1) { | ||||||
|  |                         float color = Math.max(0,Math.min(parent.erosionHeightmap[x][y],100)); | ||||||
|  |                         g.setColor( | ||||||
|  |                                 new Color( | ||||||
|  |                                         1, | ||||||
|  |                                         (int) (color / 100.0 * 254 * (parent.brightness / 100.0)), | ||||||
|  |                                         (int) (color / 100.0 * 254 * (parent.brightness / 100.0)) | ||||||
|  |                                 ) | ||||||
|  |                         ); | ||||||
|  |                     } else { | ||||||
|  |                         float color = Math.max(0,Math.min(parent.erosionHeightmap[x][y],100)); | ||||||
|  |                         g.setColor(new Color(1, (int) (color / 100.0 * 254 * (parent.brightness / 100.0)), 1)); | ||||||
|  |                     } | ||||||
|  |                     g.fillRect(x + 25, y + 25, 1, 1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else if(parent.displayToggle == 2){ | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 for (int y = 0; y < width; y++) { |                 for (int y = 0; y < width; y++) { | ||||||
|                     if (parent.precipitationChart[x][y] > 0) { |                     if (parent.precipitationChart[x][y] > 0) { | ||||||
| @ -55,7 +77,7 @@ class InterpolationDisplay extends JPanel{ | |||||||
|                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); |                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else if(parent.displayToggle == 2){ |         } else if(parent.displayToggle == 3){ | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 for (int y = 0; y < width; y++) { |                 for (int y = 0; y < width; y++) { | ||||||
| //                    if (TerrainInterpolator.precipitation_Chart[x][y] > 0) { | //                    if (TerrainInterpolator.precipitation_Chart[x][y] > 0) { | ||||||
| @ -72,7 +94,7 @@ class InterpolationDisplay extends JPanel{ | |||||||
|                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); |                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else if(parent.displayToggle == 3){ |         } else if(parent.displayToggle == 4){ | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 for (int y = 0; y < width; y++) { |                 for (int y = 0; y < width; y++) { | ||||||
|                     if (parent.climateCategory[x][y] == 0) { |                     if (parent.climateCategory[x][y] == 0) { | ||||||
| @ -143,7 +165,7 @@ class InterpolationDisplay extends JPanel{ | |||||||
|                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); |                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else if(parent.displayToggle == 4){ |         } else if(parent.displayToggle == 5){ | ||||||
|             for (int x = 0; x < width; x++) { |             for (int x = 0; x < width; x++) { | ||||||
|                 for (int y = 0; y < width; y++) { |                 for (int y = 0; y < width; y++) { | ||||||
|                     if (parent.continentIdField[x][y] > 8) { |                     if (parent.continentIdField[x][y] > 8) { | ||||||
| @ -170,7 +192,7 @@ class InterpolationDisplay extends JPanel{ | |||||||
|                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); |                     g.fillRect(x * 2 + 25, y * 2 + 25, 2, 2); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else if (parent.displayToggle == 5) { |         } else if (parent.displayToggle == 6) { | ||||||
|             Continent current = parent.continents.get(parent.current_Continent); |             Continent current = parent.continents.get(parent.current_Continent); | ||||||
|             g.drawString("dim_x: " + current.dim_x, 20, 20); |             g.drawString("dim_x: " + current.dim_x, 20, 20); | ||||||
|             g.drawString("dim_y: " + current.dim_y, 20, 30); |             g.drawString("dim_y: " + current.dim_y, 20, 30); | ||||||
| @ -182,7 +204,7 @@ class InterpolationDisplay extends JPanel{ | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } else if (parent.displayToggle == 6){ |         } else if (parent.displayToggle == 7){ | ||||||
|             // Continent current = parent.continents.get(parent.current_Continent); |             // Continent current = parent.continents.get(parent.current_Continent); | ||||||
|             // for(int x = 0; x < Region.REGION_DIMENSION; x++){ |             // for(int x = 0; x < Region.REGION_DIMENSION; x++){ | ||||||
|             //     for(int y = 0; y < Region.REGION_DIMENSION; y++){ |             //     for(int y = 0; y < Region.REGION_DIMENSION; y++){ | ||||||
|  | |||||||
| @ -0,0 +1,645 @@ | |||||||
|  | package electrosphere.server.terrain.generation; | ||||||
|  | 
 | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Iterator; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Random; | ||||||
|  | import java.util.concurrent.CountDownLatch; | ||||||
|  | import java.util.concurrent.Executors; | ||||||
|  | import java.util.concurrent.ThreadPoolExecutor; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Core continent phase terrain generator | ||||||
|  |  */ | ||||||
|  | class TectonicSimulation { | ||||||
|  | 
 | ||||||
|  |     //size of a parallelized chunk | ||||||
|  |     static final int PARALLEL_CHUNK_SIZE = 32; | ||||||
|  |     //number of threads for threadpool | ||||||
|  |     static final int THREAD_POOL_COUNT = 16; | ||||||
|  | 
 | ||||||
|  |     //the dimensions of the map | ||||||
|  |     int DIMENSION = 200; | ||||||
|  |     int[][] asthenosphereHeat; | ||||||
|  |     int[][] rockHardness; | ||||||
|  |     int[][] elevation; | ||||||
|  |     int[][] smoothedElevation; | ||||||
|  |     int currentElev[][]; | ||||||
|  |     int newElevation[][]; | ||||||
|  |     //currents used for pushing terrain elevation around the map | ||||||
|  |     Vector[][] currents; | ||||||
|  |     //hotspots that thrust rock up from the ocean floor | ||||||
|  |     List<Hotspot> spots = new ArrayList<Hotspot>(); | ||||||
|  |     int time = 0; | ||||||
|  |     int lifespan = 75000; | ||||||
|  | 
 | ||||||
|  |     Random rand; | ||||||
|  | 
 | ||||||
|  |     //thread pool for parallelized force calculation | ||||||
|  |     ThreadPoolExecutor threadPool; | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Constructor | ||||||
|  |      * @param seed Seed for random | ||||||
|  |      */ | ||||||
|  |     protected TectonicSimulation(long seed){ | ||||||
|  |         this.rand = new Random(seed); | ||||||
|  |         threadPool = (ThreadPoolExecutor)Executors.newFixedThreadPool(THREAD_POOL_COUNT); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Sets the data width | ||||||
|  |      * @param newDim The dimension of the data | ||||||
|  |      */ | ||||||
|  |     protected void setDimension(int newDim){ | ||||||
|  |         DIMENSION = newDim; | ||||||
|  |         if(DIMENSION % PARALLEL_CHUNK_SIZE != 0){ | ||||||
|  |             //this requirement is for parallelization purposes | ||||||
|  |             throw new Error("DIMENSION MUST BE A MULTIPLE OF 16!"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Sets the simulation lifespan for the Continent Phase | ||||||
|  |      * @param newLifespan The lifespan in units of simulation frames | ||||||
|  |      */ | ||||||
|  |     protected void setLifespan(int newLifespan){ | ||||||
|  |         lifespan = newLifespan; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Runs the continent phase simulation. Blocks until completed | ||||||
|  |      */ | ||||||
|  |     protected void run(){ | ||||||
|  |         allocateData(); | ||||||
|  |          | ||||||
|  |          | ||||||
|  |          | ||||||
|  |         long lastTime = System.currentTimeMillis(); | ||||||
|  |          | ||||||
|  |         //construct convection cells prior to simulation | ||||||
|  |         constructConvectionCells(); | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         //main simulation | ||||||
|  |         while(true){ | ||||||
|  |             time++; | ||||||
|  |             simulateHotspots(); | ||||||
|  |             heatToElevation(); | ||||||
|  |             applyVectorsToElevationParallel(); | ||||||
|  |             calculateSmoothedElevations(); | ||||||
|  | 
 | ||||||
|  |             //            try { | ||||||
|  | //                TimeUnit.MILLISECONDS.sleep(1); | ||||||
|  | //            } catch (InterruptedException ex) { | ||||||
|  | //            } | ||||||
|  |             if(time % 500 == 0) { | ||||||
|  |                 long new_Time = System.currentTimeMillis(); | ||||||
|  |                 long time_Delta = new_Time - lastTime; | ||||||
|  |                 lastTime = new_Time; | ||||||
|  |                 System.out.println("Progress: " + time + "/" + lifespan + " ETA: " + (time_Delta * (lifespan - time) / 1000 / 500) + "S"); | ||||||
|  |             } | ||||||
|  |             if(time > lifespan){ | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //TODO: | ||||||
|  |         //next subphase is to find large areas without continents and place ones there | ||||||
|  |         //the terrain added in this next phase will be made with a more quick and dirty implementation | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         //shutdown threadpool | ||||||
|  |         threadPool.shutdown(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Gets the raw terrain | ||||||
|  |      * @return The raw terrain | ||||||
|  |      */ | ||||||
|  |     protected int[][] getTerrain(){ | ||||||
|  |         return elevation; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Gets the terrain smoothed | ||||||
|  |      * @return The terrain smoothed | ||||||
|  |      */ | ||||||
|  |     protected int[][] getTerrainSmoothed(){ | ||||||
|  |         return smoothedElevation; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Allocates all arrays generated based on the dimension provided | ||||||
|  |      */ | ||||||
|  |     private void allocateData(){ | ||||||
|  |         asthenosphereHeat = new int[DIMENSION][DIMENSION]; | ||||||
|  |         elevation = new int[DIMENSION][DIMENSION]; | ||||||
|  |         smoothedElevation = new int[DIMENSION][DIMENSION]; | ||||||
|  |         newElevation = new int[DIMENSION][DIMENSION]; | ||||||
|  |         currents = new Vector[DIMENSION][DIMENSION]; | ||||||
|  |         currentElev = new int[DIMENSION][DIMENSION]; | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 currents[x][y] = new Vector(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * If the asthenosphere is sufficiently hot, increases elevation of position | ||||||
|  |      */ | ||||||
|  |     private void heatToElevation(){ | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 if(asthenosphereHeat[x][y] > 25){ | ||||||
|  |                     if(electrosphere.server.terrain.generation.Utilities.random_Integer(1, 10, rand) == 10){ | ||||||
|  |                         elevation[x][y] = elevation[x][y] + 1; | ||||||
|  |                         if(elevation[x][y] > 100){ | ||||||
|  |                             elevation[x][y] = 100; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Constructs convection cells in the force vector field | ||||||
|  |      */ | ||||||
|  |     private void constructConvectionCells(){ | ||||||
|  |         //controls whether the cell rotates clockwise or couterclockwise | ||||||
|  |         boolean isCellType1 = false; | ||||||
|  |         //one fourth of the width of the data set | ||||||
|  |         int fourth = DIMENSION / 4; | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 //the current position RELATIVE to the center point of the current convection cell center | ||||||
|  |                 int normalizedX = x; | ||||||
|  |                 int normalizedY = y; | ||||||
|  |                  | ||||||
|  |                 //determine relative position and whether convection cell type one or two | ||||||
|  |                 if(y < fourth || (y < fourth * 3 && y > (fourth * 2) - 1)){ | ||||||
|  |                     isCellType1 = true; | ||||||
|  |                     if(normalizedY > fourth){ | ||||||
|  |                         normalizedY = normalizedY - fourth * 2; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     isCellType1 = false; | ||||||
|  |                     if(normalizedY > fourth * 2 + 1){ | ||||||
|  |                         normalizedY = normalizedY - fourth * 3; | ||||||
|  |                     } else { | ||||||
|  |                         normalizedY = normalizedY - fourth; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 while(normalizedX > fourth){ | ||||||
|  |                     normalizedX = normalizedX - fourth; | ||||||
|  |                 } | ||||||
|  |                 if(normalizedX < 0){ | ||||||
|  |                     normalizedX = 0; | ||||||
|  |                 } | ||||||
|  |                 if(normalizedY < 0){ | ||||||
|  |                     normalizedY = 0; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 //one eighth of the width of the data set | ||||||
|  |                 int eigth = fourth / 2; | ||||||
|  |                 //Moves the relative position to be in its correct eighth | ||||||
|  |                 normalizedY = normalizedY - eigth; | ||||||
|  |                 normalizedX = normalizedX - eigth; | ||||||
|  | 
 | ||||||
|  |                 //calculates the distance from convection cell center to the relative position | ||||||
|  |                 float magnitude = (float)Math.sqrt(Math.pow(normalizedY, 2) + Math.pow(normalizedX, 2)); | ||||||
|  | 
 | ||||||
|  |                 //If the distance is small enough we stretch it along the X axis ... ? | ||||||
|  |                 if(magnitude < fourth / 10){ | ||||||
|  |                     normalizedX = normalizedX + fourth / 10; | ||||||
|  |                     magnitude = (float)Math.sqrt(Math.pow(normalizedY, 2) + Math.pow(normalizedX, 2)); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 //calculates the angle of the point relative to convection cell center | ||||||
|  |                 double offsetAngle = Math.atan2(normalizedY / magnitude, normalizedX / magnitude); | ||||||
|  |                 if(offsetAngle < 0){ | ||||||
|  |                     offsetAngle = offsetAngle + Math.PI * 2; | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 //rotate based on cell type | ||||||
|  |                 if(isCellType1){ | ||||||
|  |                     offsetAngle = offsetAngle + Math.PI / 2; | ||||||
|  |                 } else { | ||||||
|  |                     offsetAngle = offsetAngle - Math.PI / 2; | ||||||
|  |                 } | ||||||
|  |                 //normalize | ||||||
|  |                 while(offsetAngle > Math.PI * 2){ | ||||||
|  |                     offsetAngle = offsetAngle - Math.PI * 2; | ||||||
|  |                 } | ||||||
|  |                 while(offsetAngle < 0){ | ||||||
|  |                     offsetAngle = offsetAngle + Math.PI * 2; | ||||||
|  |                 } | ||||||
|  |                 //Lastly, actually set the force vector | ||||||
|  |                 currents[x][y].x = (int)(99 * Math.cos(offsetAngle)); | ||||||
|  |                 currents[x][y].y = (int)(99 * Math.sin(offsetAngle)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Moves the terrain around based on the vector field | ||||||
|  |      */ | ||||||
|  |     private void applyVectorsToElevation(){ | ||||||
|  |         //allocate new elevation array | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 newElevation[x][y] = 0; | ||||||
|  |                 currentElev[x][y] = elevation[x][y]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         //transfer terrain | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 boolean transfer = false; | ||||||
|  |                 if (Utilities.random_Integer(1, 50, rand) == 1) { | ||||||
|  |                     transfer = true; | ||||||
|  |                 } | ||||||
|  |                 int transfer_goal; | ||||||
|  |                 if(Utilities.random_Integer(1, 2, rand)==1){ | ||||||
|  |                     transfer_goal = Utilities.random_Integer(20, 60, rand); | ||||||
|  |                 } else { | ||||||
|  |                     transfer_goal = Utilities.random_Integer(0, 60, rand); | ||||||
|  |                 } | ||||||
|  |                 if(Utilities.random_Integer(1, 2, rand)==1){ | ||||||
|  |                 if (currents[x][y].x >= 0) { | ||||||
|  |                         if (transfer) { | ||||||
|  |                             if (x + 1 < DIMENSION) { | ||||||
|  |                                 while(newElevation[x + 1][y] + currentElev[x + 1][y] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                     newElevation[x + 1][y]++; | ||||||
|  |                                     currentElev[x][y]--; | ||||||
|  |                                 } | ||||||
|  |                             } else { | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         if (transfer) { | ||||||
|  |                             if (x - 1 >= 0) { | ||||||
|  |                                 while(newElevation[x - 1][y] + currentElev[x - 1][y] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                     newElevation[x - 1][y]++; | ||||||
|  |                                     currentElev[x][y]--; | ||||||
|  |                                 } | ||||||
|  |                             } else { | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     if (currents[x][y].y >= 0) { | ||||||
|  |                         if (transfer) { | ||||||
|  |                             if (y + 1 < DIMENSION) {                                   //  V    REPLACE THIS WITH GOAL | ||||||
|  |                                 while(newElevation[x][y + 1] + currentElev[x][y + 1] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                     newElevation[x][y + 1]++; | ||||||
|  |                                     currentElev[x][y]--; | ||||||
|  |                                 } | ||||||
|  |                             } else { | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         if (transfer) { | ||||||
|  |                             if (y - 1 >= 0) { | ||||||
|  |                                     while(newElevation[x][y - 1] + currentElev[x][y - 1] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                         newElevation[x][y - 1]++; | ||||||
|  |                                     currentElev[x][y]--; | ||||||
|  |                                 } | ||||||
|  |                             } else { | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         //move data from temporary array to main array | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 newElevation[x][y] = newElevation[x][y] + currentElev[x][y]; | ||||||
|  |                 while(newElevation[x][y] > 99){ | ||||||
|  |                     newElevation[x][y] = 99; | ||||||
|  |                 } | ||||||
|  |                 elevation[x][y] = newElevation[x][y]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Applies a smooth kernel to the terrain data | ||||||
|  |      */ | ||||||
|  |     private void calculateSmoothedElevations(){ | ||||||
|  |         int[][] buffer = new int[DIMENSION][DIMENSION]; | ||||||
|  |         for(int x = 1; x < DIMENSION - 2; x++){ | ||||||
|  |             for(int y = 1; y < DIMENSION - 2; y++){ | ||||||
|  |                 buffer[x][y] = elevation[x][y] * 4 * elevation[x+1][y] * 2 + elevation[x-1][y] * 2 + elevation[x][y+1] * 2 + | ||||||
|  |                         elevation[x][y-1] * 2 + elevation[x+1][y+1] + elevation[x+1][y-1] + elevation[x-1][y+1] + elevation[x-1][y-1]; | ||||||
|  |                 buffer[x][y] = (int)(buffer[x][y] / 16.0); | ||||||
|  |                 while(buffer[x][y] > 100){ | ||||||
|  |                     buffer[x][y] = buffer[x][y]/2; | ||||||
|  |                 } | ||||||
|  |                 smoothedElevation[x][y] = buffer[x][y]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         for(int x = 1; x < DIMENSION - 2; x++){ | ||||||
|  |             for(int y = 1; y < DIMENSION - 2; y++){ | ||||||
|  |                 buffer[x][y] = smoothedElevation[x][y] * 4 * smoothedElevation[x+1][y] * 2 + smoothedElevation[x-1][y] * 2 + smoothedElevation[x][y+1] * 2 + | ||||||
|  |                 smoothedElevation[x][y-1] * 2 + smoothedElevation[x+1][y+1] + smoothedElevation[x+1][y-1] + smoothedElevation[x-1][y+1] + smoothedElevation[x-1][y-1]; | ||||||
|  |                 buffer[x][y] = (int)(buffer[x][y] / 16.0); | ||||||
|  |                 while(buffer[x][y] > 100){ | ||||||
|  |                     buffer[x][y] = buffer[x][y]/2; | ||||||
|  |                 } | ||||||
|  |                 smoothedElevation[x][y] = buffer[x][y]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * simulates the hotspot logic | ||||||
|  |      */ | ||||||
|  |     private void simulateHotspots(){ | ||||||
|  |         if(spots.size() >= 1){ | ||||||
|  |             List<Hotspot> to_Remove = new ArrayList<Hotspot>(); | ||||||
|  |             Iterator<Hotspot> spot_Iterator = spots.iterator(); | ||||||
|  |             while(spot_Iterator.hasNext()){ | ||||||
|  |                 Hotspot current_Spot = spot_Iterator.next(); | ||||||
|  |                 if(current_Spot.life_current >= current_Spot.life_max){ | ||||||
|  |                     to_Remove.add(current_Spot); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             spot_Iterator = to_Remove.iterator(); | ||||||
|  |             while(spot_Iterator.hasNext()){ | ||||||
|  |                 Hotspot current_Spot = spot_Iterator.next(); | ||||||
|  |                 spots.remove(current_Spot); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(spots.size() < 5){ | ||||||
|  |             spots.add(new Hotspot( | ||||||
|  |                 Utilities.random_Integer(0, DIMENSION - 1, rand), | ||||||
|  |                 Utilities.random_Integer(0, DIMENSION - 1, rand), | ||||||
|  |                 Utilities.random_Integer(6000, 10000, rand), | ||||||
|  |                 Utilities.random_Integer(3, 5, rand), | ||||||
|  |                 this)); | ||||||
|  |         } | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 asthenosphereHeat[x][y] = 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(spots.size() >= 1){ | ||||||
|  |             Iterator<Hotspot> spot_Iterator = spots.iterator(); | ||||||
|  |             while(spot_Iterator.hasNext()){ | ||||||
|  |                 Hotspot current_Spot = spot_Iterator.next(); | ||||||
|  |                 current_Spot.simulate(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fills in the gaps not covered by the main chunks | ||||||
|  |      */ | ||||||
|  |     private void applyVectorToElevationGaps(){ | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 if(x % 16 == 0 || x % 16 == 1 || y % 16 == 0 || y % 16 == 1){ | ||||||
|  |                     boolean transfer = false; | ||||||
|  |                     if (Utilities.random_Integer(1, 50, rand) == 1) { | ||||||
|  |                         transfer = true; | ||||||
|  |                     } | ||||||
|  |                     int transfer_goal; | ||||||
|  |                     if(Utilities.random_Integer(1, 2, rand)==1){ | ||||||
|  |                         transfer_goal = Utilities.random_Integer(20, 60, rand); | ||||||
|  |                     } else { | ||||||
|  |                         transfer_goal = Utilities.random_Integer(0, 60, rand); | ||||||
|  |                     } | ||||||
|  |                     if(Utilities.random_Integer(1, 2, rand)==1){ | ||||||
|  |                         if (currents[x][y].x >= 0) { | ||||||
|  |                             if (transfer) { | ||||||
|  |                                 if (x + 1 < DIMENSION) { | ||||||
|  |                                     while(newElevation[x + 1][y] + currentElev[x + 1][y] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                         newElevation[x + 1][y]++; | ||||||
|  |                                         currentElev[x][y]--; | ||||||
|  |                                     } | ||||||
|  |                                 } else { | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             if (transfer) { | ||||||
|  |                                 if (x - 1 >= 0) { | ||||||
|  |                                     while(newElevation[x - 1][y] + currentElev[x - 1][y] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                         newElevation[x - 1][y]++; | ||||||
|  |                                         currentElev[x][y]--; | ||||||
|  |                                     } | ||||||
|  |                                 } else { | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         if (currents[x][y].y >= 0) { | ||||||
|  |                             if (transfer) { | ||||||
|  |                                 if (y + 1 < DIMENSION) {                                   //  V    REPLACE THIS WITH GOAL | ||||||
|  |                                     while(newElevation[x][y + 1] + currentElev[x][y + 1] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                         newElevation[x][y + 1]++; | ||||||
|  |                                         currentElev[x][y]--; | ||||||
|  |                                     } | ||||||
|  |                                 } else { | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             if (transfer) { | ||||||
|  |                                 if (y - 1 >= 0) { | ||||||
|  |                                         while(newElevation[x][y - 1] + currentElev[x][y - 1] < 99 && currentElev[x][y] > transfer_goal){ | ||||||
|  |                                             newElevation[x][y - 1]++; | ||||||
|  |                                         currentElev[x][y]--; | ||||||
|  |                                     } | ||||||
|  |                                 } else { | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     //latch for synchronizing parallel force vector computation | ||||||
|  |     CountDownLatch latch; | ||||||
|  |     /** | ||||||
|  |      * Moves the terrain around based on the vector field, parallelized | ||||||
|  |      */ | ||||||
|  |     private void applyVectorsToElevationParallel(){ | ||||||
|  |         //allocate new elevation array | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 newElevation[x][y] = 0; | ||||||
|  |                 currentElev[x][y] = elevation[x][y]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         latch = new CountDownLatch(DIMENSION / PARALLEL_CHUNK_SIZE * DIMENSION / PARALLEL_CHUNK_SIZE); | ||||||
|  |         //transfer terrain in main chunks | ||||||
|  |         for(int x = 0; x < DIMENSION / PARALLEL_CHUNK_SIZE; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION / PARALLEL_CHUNK_SIZE; y++){ | ||||||
|  |                 threadPool.execute(new TerrainMovementWorker( | ||||||
|  |                     DIMENSION, | ||||||
|  |                     x, | ||||||
|  |                     y, | ||||||
|  |                     new Random(rand.nextLong()), | ||||||
|  |                     currents, | ||||||
|  |                     newElevation, | ||||||
|  |                     currentElev, | ||||||
|  |                     latch | ||||||
|  |                 )); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         //await main chunks | ||||||
|  |         try { | ||||||
|  |             latch.await(); | ||||||
|  |         } catch (InterruptedException e) { | ||||||
|  |             e.printStackTrace(); | ||||||
|  |         } | ||||||
|  |         //fill in gaps | ||||||
|  |         applyVectorToElevationGaps(); | ||||||
|  |         //move data from temporary array to main array | ||||||
|  |         for(int x = 0; x < DIMENSION; x++){ | ||||||
|  |             for(int y = 0; y < DIMENSION; y++){ | ||||||
|  |                 newElevation[x][y] = newElevation[x][y] + currentElev[x][y]; | ||||||
|  |                 while(newElevation[x][y] > 99){ | ||||||
|  |                     newElevation[x][y] = 99; | ||||||
|  |                 } | ||||||
|  |                 elevation[x][y] = newElevation[x][y]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * A worker thread for simulating terrain moving due to force vector field | ||||||
|  |      */ | ||||||
|  |     static class TerrainMovementWorker implements Runnable { | ||||||
|  | 
 | ||||||
|  |         //size of data map | ||||||
|  |         int continentPhaseDimension; | ||||||
|  |         //The offsets into the data array | ||||||
|  |         int offsetX; | ||||||
|  |         int offsetY; | ||||||
|  |         //random | ||||||
|  |         Random rand; | ||||||
|  |         //force vector field | ||||||
|  |         Vector[][] currents; | ||||||
|  |         //new elevation map to fill in | ||||||
|  |         int[][] newElevation; | ||||||
|  |         //reference elevation map to pull from | ||||||
|  |         int[][] referenceElevation; | ||||||
|  |         //latch to resynchronize threads | ||||||
|  |         CountDownLatch latch; | ||||||
|  | 
 | ||||||
|  |         protected TerrainMovementWorker( | ||||||
|  |             int continentPhaseDimension, | ||||||
|  |             int offsetX, | ||||||
|  |             int offsetY, | ||||||
|  |             Random rand, | ||||||
|  |             Vector[][] currents, | ||||||
|  |             int[][] newElevation, | ||||||
|  |             int[][] referenceElevation, | ||||||
|  |             CountDownLatch latch | ||||||
|  |         ){ | ||||||
|  |             this.continentPhaseDimension = continentPhaseDimension; | ||||||
|  |             this.offsetX = offsetX; | ||||||
|  |             this.offsetY = offsetY; | ||||||
|  |             this.rand = rand; | ||||||
|  |             this.currents = currents; | ||||||
|  |             this.newElevation = newElevation; | ||||||
|  |             this.referenceElevation = referenceElevation; | ||||||
|  |             this.latch = latch; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Runs the terrain movement simulation for this worker | ||||||
|  |          */ | ||||||
|  |         @Override | ||||||
|  |         public void run() { | ||||||
|  |             for(int x = 0; x < PARALLEL_CHUNK_SIZE; x++){ | ||||||
|  |                 for(int y = 0; y < PARALLEL_CHUNK_SIZE; y++){ | ||||||
|  |                     if(x % PARALLEL_CHUNK_SIZE != 0 && x % PARALLEL_CHUNK_SIZE != 1 && y % PARALLEL_CHUNK_SIZE != 0 && y % PARALLEL_CHUNK_SIZE != 1){ | ||||||
|  |                         //current absolute position in data arrays | ||||||
|  |                         int currentX = x + offsetX * PARALLEL_CHUNK_SIZE; | ||||||
|  |                         int currentY = y + offsetY * PARALLEL_CHUNK_SIZE; | ||||||
|  | 
 | ||||||
|  |                         //roll whether should transfer terrain or not | ||||||
|  |                         boolean transfer = false; | ||||||
|  |                         if (Utilities.random_Integer(1, 50, rand) == 1) { | ||||||
|  |                             transfer = true; | ||||||
|  |                         } | ||||||
|  |                         //sets the goal of how much elevation to transfer to neighbors | ||||||
|  |                         int transferGoal; | ||||||
|  |                         if(Utilities.random_Integer(1, 2, rand)==1){ | ||||||
|  |                             transferGoal = Utilities.random_Integer(20, 60, rand); | ||||||
|  |                         } else { | ||||||
|  |                             transferGoal = Utilities.random_Integer(0, 60, rand); | ||||||
|  |                         } | ||||||
|  |                         //roll whether to transfer horizontally or vertically | ||||||
|  |                         if(Utilities.random_Integer(1, 2, rand)==1){ | ||||||
|  |                             //transfers horizontally | ||||||
|  |                             if (currents[currentX][currentY].x >= 0) { | ||||||
|  |                                 if (transfer) { | ||||||
|  |                                     if (currentX + 1 < continentPhaseDimension) { | ||||||
|  |                                         while( | ||||||
|  |                                         newElevation[currentX + 1][currentY] + referenceElevation[currentX + 1][currentY] < 99 &&  | ||||||
|  |                                         referenceElevation[currentX][currentY] > transferGoal){ | ||||||
|  |                                             newElevation[currentX + 1][currentY]++; | ||||||
|  |                                             referenceElevation[currentX][currentY]--; | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } else { | ||||||
|  |                                 if (transfer) { | ||||||
|  |                                     if (currentX - 1 >= 0) { | ||||||
|  |                                         while( | ||||||
|  |                                         newElevation[currentX - 1][currentY] + referenceElevation[currentX - 1][currentY] < 99 &&  | ||||||
|  |                                         referenceElevation[currentX][currentY] > transferGoal){ | ||||||
|  |                                             newElevation[currentX - 1][currentY]++; | ||||||
|  |                                             referenceElevation[currentX][currentY]--; | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             //transfer vertically | ||||||
|  |                             if (currents[currentX][currentY].y >= 0) { | ||||||
|  |                                 if (transfer) { | ||||||
|  |                                     if (currentY + 1 < continentPhaseDimension) {                                   //  V    REPLACE THIS WITH GOAL | ||||||
|  |                                         while( | ||||||
|  |                                         newElevation[currentX][currentY + 1] + referenceElevation[currentX][currentY + 1] < 99 &&  | ||||||
|  |                                         referenceElevation[currentX][currentY] > transferGoal){ | ||||||
|  |                                             newElevation[currentX][currentY + 1]++; | ||||||
|  |                                             referenceElevation[currentX][currentY]--; | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } else { | ||||||
|  |                                 if (transfer) { | ||||||
|  |                                     if (currentY - 1 >= 0) { | ||||||
|  |                                             while( | ||||||
|  |                                             newElevation[currentX][currentY - 1] + referenceElevation[currentX][currentY - 1] < 99 && | ||||||
|  |                                             referenceElevation[currentX][currentY] > transferGoal){ | ||||||
|  |                                             newElevation[currentX][currentY - 1]++; | ||||||
|  |                                             referenceElevation[currentX][currentY]--; | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             latch.countDown(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -6,4 +6,11 @@ package electrosphere.server.terrain.generation; | |||||||
| class Vector { | class Vector { | ||||||
|     public int x; |     public int x; | ||||||
|     public int y; |     public int y; | ||||||
|  |     protected Vector(){ | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     protected Vector(int x, int y){ | ||||||
|  |         this.x = x; | ||||||
|  |         this.y = y; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ package electrosphere.server.terrain.manager; | |||||||
| 
 | 
 | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import electrosphere.game.terrain.processing.TerrainInterpolator; | import electrosphere.game.terrain.processing.TerrainInterpolator; | ||||||
| import electrosphere.server.terrain.generation.TerrainGen; | import electrosphere.server.terrain.generation.TerrainGenerator; | ||||||
| import electrosphere.server.terrain.models.ModificationList; | import electrosphere.server.terrain.models.ModificationList; | ||||||
| import electrosphere.server.terrain.models.TerrainModel; | import electrosphere.server.terrain.models.TerrainModel; | ||||||
| import electrosphere.server.terrain.models.TerrainModification; | import electrosphere.server.terrain.models.TerrainModification; | ||||||
| @ -85,7 +85,7 @@ public class ServerTerrainManager { | |||||||
|     } |     } | ||||||
|      |      | ||||||
|     public void generate(){ |     public void generate(){ | ||||||
|         TerrainGen terrainGen = new TerrainGen(); |         TerrainGenerator terrainGen = new TerrainGenerator(); | ||||||
|         terrainGen.setInterpolationRatio(worldSizeDiscrete/200); |         terrainGen.setInterpolationRatio(worldSizeDiscrete/200); | ||||||
|         terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio); |         terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio); | ||||||
|         terrainGen.setDynamicInterpolationRatio(dynamicInterpolationRatio); |         terrainGen.setDynamicInterpolationRatio(dynamicInterpolationRatio); | ||||||
| @ -343,12 +343,9 @@ public class ServerTerrainManager { | |||||||
|             return heightmapCache.get(key); |             return heightmapCache.get(key); | ||||||
|         } else { |         } else { | ||||||
|             float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ); |             float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ); | ||||||
|             long[][] randomizer = model.getRad5RandomizerValuesAtPosition(worldX, worldZ); |  | ||||||
|             float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk( |             float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk( | ||||||
|                     macroValues, |                     macroValues, | ||||||
|                     randomizer, |                     model.getDynamicInterpolationRatio() | ||||||
|                     model.getDynamicInterpolationRatio(), |  | ||||||
|                     model.getRandomDampener() |  | ||||||
|             ); |             ); | ||||||
|             heightmapCache.put(key,heightmap); |             heightmapCache.put(key,heightmap); | ||||||
|             return heightmap; |             return heightmap; | ||||||
| @ -375,8 +372,4 @@ public class ServerTerrainManager { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     public long getRandomizerAtPoint(int worldX, int worldY){ |  | ||||||
|         return model.getRad5RandomizerValuesAtPosition(worldX, worldY)[2][2]; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,7 +13,6 @@ public class TerrainModel { | |||||||
|      |      | ||||||
|     int discreteArrayDimension; |     int discreteArrayDimension; | ||||||
|     float[][] elevation; |     float[][] elevation; | ||||||
|     long[][] chunkRandomizer; |  | ||||||
|      |      | ||||||
|     float realMountainThreshold; |     float realMountainThreshold; | ||||||
|     float realOceanThreshold; |     float realOceanThreshold; | ||||||
| @ -28,7 +27,6 @@ public class TerrainModel { | |||||||
|     public TerrainModel( |     public TerrainModel( | ||||||
|             int dimension, |             int dimension, | ||||||
|             float[][] elevation, |             float[][] elevation, | ||||||
|             long[][] chunkRandomizer, |  | ||||||
|             float realOceanThreshold, |             float realOceanThreshold, | ||||||
|             float realMountainThreshold, |             float realMountainThreshold, | ||||||
|             int dynamicInterpolationRatio |             int dynamicInterpolationRatio | ||||||
| @ -37,7 +35,6 @@ public class TerrainModel { | |||||||
|         this.dynamicInterpolationRatio = dynamicInterpolationRatio; |         this.dynamicInterpolationRatio = dynamicInterpolationRatio; | ||||||
|         this.discreteArrayDimension = dimension; |         this.discreteArrayDimension = dimension; | ||||||
|         this.elevation = elevation; |         this.elevation = elevation; | ||||||
|         this.chunkRandomizer = chunkRandomizer; |  | ||||||
|         this.realMountainThreshold = realMountainThreshold; |         this.realMountainThreshold = realMountainThreshold; | ||||||
|         this.realOceanThreshold = realOceanThreshold; |         this.realOceanThreshold = realOceanThreshold; | ||||||
|         this.modifications = new HashMap<String,ModificationList>(); |         this.modifications = new HashMap<String,ModificationList>(); | ||||||
| @ -66,7 +63,6 @@ public class TerrainModel { | |||||||
|      */ |      */ | ||||||
|     public float[][] getElevationForChunk(int x, int y){ |     public float[][] getElevationForChunk(int x, int y){ | ||||||
|          |          | ||||||
|         Random rand = new Random(chunkRandomizer[x][y]); |  | ||||||
|         //this is what we intend to return from the function |         //this is what we intend to return from the function | ||||||
|         float[][] rVal = new float[dynamicInterpolationRatio][dynamicInterpolationRatio]; |         float[][] rVal = new float[dynamicInterpolationRatio][dynamicInterpolationRatio]; | ||||||
|          |          | ||||||
| @ -308,53 +304,6 @@ public class TerrainModel { | |||||||
|          |          | ||||||
|         */ |         */ | ||||||
|      |      | ||||||
|     public long[][] getRandomizerValuesAtPosition(int x, int y){ |  | ||||||
|         long[][] rVal = new long[3][3]; |  | ||||||
|         rVal[1][1] = chunkRandomizer[x][y]; |  | ||||||
|         if(x - 1 >= 0){ |  | ||||||
|             rVal[0][1] = chunkRandomizer[x-1][y]; |  | ||||||
|             if(y - 1 >= 0){ |  | ||||||
|                 rVal[0][0] = chunkRandomizer[x-1][y-1]; |  | ||||||
|             } |  | ||||||
|             if(y + 1 < discreteArrayDimension){ |  | ||||||
|                 rVal[0][2] = chunkRandomizer[x-1][y+1]; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if(x + 1 < discreteArrayDimension){ |  | ||||||
|             rVal[2][1] = chunkRandomizer[x+1][y]; |  | ||||||
|             if(y - 1 >= 0){ |  | ||||||
|                 rVal[2][0] = chunkRandomizer[x+1][y-1]; |  | ||||||
|             } |  | ||||||
|             if(y + 1 < discreteArrayDimension){ |  | ||||||
|                 rVal[2][2] = chunkRandomizer[x+1][y+1]; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if(y - 1 >= 0){ |  | ||||||
|             rVal[1][0] = chunkRandomizer[x][y-1]; |  | ||||||
|         } |  | ||||||
|         if(y + 1 < discreteArrayDimension){ |  | ||||||
|             rVal[1][2] = chunkRandomizer[x][y+1]; |  | ||||||
|         } |  | ||||||
|         return rVal; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|      |  | ||||||
|     public long[][] getRad5RandomizerValuesAtPosition(int x, int y){ |  | ||||||
|          |  | ||||||
|         long[][] rVal = new long[5][5]; |  | ||||||
|         for(int i = -2; i < 3; i++){ |  | ||||||
|             for(int j = -2; j < 3; j++){ |  | ||||||
|                 if(x + i >= 0 && x + i < discreteArrayDimension && y + j >= 0 && y + j < discreteArrayDimension){ |  | ||||||
|                     rVal[i+2][j+2] = chunkRandomizer[x+i][y+j]; |  | ||||||
|                 } else { |  | ||||||
|                     rVal[i+2][j+2] = 0; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         return rVal; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|      |      | ||||||
|     public float getRandomDampener(){ |     public float getRandomDampener(){ | ||||||
|         return interpolationRandomDampener; |         return interpolationRandomDampener; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user