js chunkgen + fixes + scriptengine work
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
86e61b7a0e
commit
cc60818e35
@ -4,6 +4,7 @@ import { HookManager } from '/Scripts/engine/hooks/hook-manager'
|
||||
import { SceneLoader } from '/Scripts/engine/scene/scene-loader'
|
||||
import { Engine } from '/Scripts/types/engine'
|
||||
import { clientHooks } from '/Scripts/client/clienthooks'
|
||||
import { ChunkGeneratorManager } from '/Scripts/server/chunk/chunkgeneratormanager'
|
||||
|
||||
/**
|
||||
* The core engine values
|
||||
@ -13,6 +14,7 @@ export const engine: Engine = {
|
||||
singletons: {},
|
||||
hookManager: new HookManager(),
|
||||
sceneLoader: new SceneLoader(),
|
||||
chunkGeneratorManager: new ChunkGeneratorManager(),
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,6 +27,7 @@ export const ENGINE_onInit = () => {
|
||||
let client: NamespaceClient = Client
|
||||
engine.sceneLoader.engine = engine
|
||||
engine.hookManager.engine = engine
|
||||
engine.chunkGeneratorManager.engine = engine
|
||||
|
||||
//load global hooks
|
||||
clientHooks.forEach(hook => {
|
||||
|
||||
39
assets/Scripts/server/chunk/chunkgeneratormanager.ts
Normal file
39
assets/Scripts/server/chunk/chunkgeneratormanager.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { TestGen } from "/Scripts/server/chunk/generators/testgen"
|
||||
import { Engine } from "/Scripts/types/engine"
|
||||
import { ChunkGenerator, VoxelFunction } from "/Scripts/types/host/server/chunk/chunkgenerator"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Manages all the chunk generators defined script-side
|
||||
*/
|
||||
export class ChunkGeneratorManager {
|
||||
|
||||
/**
|
||||
* The parent engine object
|
||||
*/
|
||||
engine: Engine
|
||||
|
||||
/**
|
||||
* The list of registered chunk generators
|
||||
*/
|
||||
readonly registeredGenerators: ChunkGenerator[] = [
|
||||
TestGen,
|
||||
]
|
||||
|
||||
/**
|
||||
* Gets the voxel function for the tag
|
||||
* @param tag The tag
|
||||
* @returns The voxel function if it exists, null otherwise
|
||||
*/
|
||||
readonly getVoxelFunction = (tag: string): VoxelFunction => {
|
||||
let rVal: VoxelFunction = null
|
||||
this.registeredGenerators.forEach(generator => {
|
||||
if(generator.getTag() === tag){
|
||||
rVal = generator.getVoxelFunction(this.engine)
|
||||
}
|
||||
})
|
||||
return rVal
|
||||
}
|
||||
|
||||
}
|
||||
159
assets/Scripts/server/chunk/generators/testgen.ts
Normal file
159
assets/Scripts/server/chunk/generators/testgen.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import { Engine } from "/Scripts/types/engine";
|
||||
import { CHUNK_WIDTH, ChunkGenerator, GeneratedVoxel, VoxelFunction } from "/Scripts/types/host/server/chunk/chunkgenerator";
|
||||
import { BiomeData } from "/Scripts/types/host/server/data/biomedata";
|
||||
|
||||
|
||||
/**
|
||||
* Converts a chunk coordinate to a real coordinate
|
||||
* @param chunk The chunk pos
|
||||
* @param world The world pos
|
||||
* @returns The real pos
|
||||
*/
|
||||
const voxelToReal = (chunk: number, world: number) => {
|
||||
return world * CHUNK_WIDTH + chunk
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the voxel on the surface
|
||||
* @return The voxel
|
||||
*/
|
||||
const getSurfaceVoxel = (
|
||||
voxel: GeneratedVoxel,
|
||||
worldX: number, worldY: number, worldZ: number,
|
||||
chunkX: number, chunkY: number, chunkZ: number,
|
||||
realX: number, realY: number, realZ: number,
|
||||
surfacePercent: number,
|
||||
surfaceHeight: number,
|
||||
surfaceBiome: BiomeData
|
||||
) => {
|
||||
voxel.weight = surfacePercent * 2 - 1;
|
||||
voxel.type = 2;
|
||||
return voxel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the voxel below the surface
|
||||
* @return The voxel
|
||||
*/
|
||||
const getSubsurfaceVoxel = (
|
||||
voxel: GeneratedVoxel,
|
||||
worldX: number, worldY: number, worldZ: number,
|
||||
chunkX: number, chunkY: number, chunkZ: number,
|
||||
realX: number, realY: number, realZ: number,
|
||||
surfacePercent: number,
|
||||
surfaceHeight: number,
|
||||
surfaceBiome: BiomeData
|
||||
) => {
|
||||
if(realY < surfaceHeight - 5){
|
||||
voxel.weight = 1;
|
||||
voxel.type = 6;
|
||||
} else {
|
||||
voxel.weight = 1;
|
||||
voxel.type = 1;
|
||||
}
|
||||
return voxel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the voxel above the service
|
||||
* @return The voxel
|
||||
*/
|
||||
const getOverSurfaceVoxel = (
|
||||
voxel: GeneratedVoxel,
|
||||
worldX: number, worldY: number, worldZ: number,
|
||||
chunkX: number, chunkY: number, chunkZ: number,
|
||||
realX: number, realY: number, realZ: number,
|
||||
surfacePercent: number,
|
||||
surfaceHeight: number,
|
||||
surfaceBiome: BiomeData
|
||||
) => {
|
||||
voxel.weight = -1;
|
||||
voxel.type = 0;
|
||||
return voxel;
|
||||
}
|
||||
|
||||
/**
|
||||
* A test generator
|
||||
*/
|
||||
export const TestGen: ChunkGenerator = {
|
||||
|
||||
/**
|
||||
* Gets the tag for this generator
|
||||
* @returns The tag
|
||||
*/
|
||||
getTag: () => "test",
|
||||
|
||||
/**
|
||||
* The elevation function
|
||||
* @param worldX The world x coordinate
|
||||
* @param worldZ The world z coordinate
|
||||
* @param chunkX The chunk x coordinate
|
||||
* @param chunkZ The chunk z coordinate
|
||||
*/
|
||||
getElevation: (worldX: number, worldZ: number, chunkX: number, chunkZ: number): number => {
|
||||
return 1
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the function to actually get voxels
|
||||
* @param engine The engine reference
|
||||
*/
|
||||
getVoxelFunction: (
|
||||
engine: Engine
|
||||
): VoxelFunction => {
|
||||
const rVal = (
|
||||
worldX: number, worldY: number, worldZ: number,
|
||||
chunkX: number, chunkY: number, chunkZ: number,
|
||||
stride: number,
|
||||
surfaceHeight: number,
|
||||
surfaceBiome: BiomeData
|
||||
): GeneratedVoxel => {
|
||||
let rVal: GeneratedVoxel = {
|
||||
type: 0,
|
||||
weight: -1,
|
||||
}
|
||||
|
||||
const realX = voxelToReal(chunkX,worldX)
|
||||
const realY = voxelToReal(chunkY,worldY)
|
||||
const realZ = voxelToReal(chunkZ,worldZ)
|
||||
const strideMultiplier = engine.classes.math.static.pow(2,stride)
|
||||
// const strideMultiplier = 1
|
||||
const heightDiff = realY - surfaceHeight
|
||||
const surfacePercent = (surfaceHeight - realY) / strideMultiplier
|
||||
|
||||
if(heightDiff < -strideMultiplier){
|
||||
return getSubsurfaceVoxel(
|
||||
rVal,
|
||||
worldX, worldY, worldZ,
|
||||
chunkX, chunkY, chunkZ,
|
||||
realX, realY, realZ,
|
||||
surfacePercent,
|
||||
surfaceHeight,
|
||||
surfaceBiome
|
||||
);
|
||||
} else if(heightDiff > 0) {
|
||||
return getOverSurfaceVoxel(
|
||||
rVal,
|
||||
worldX, worldY, worldZ,
|
||||
chunkX, chunkY, chunkZ,
|
||||
realX, realY, realZ,-
|
||||
surfacePercent,
|
||||
surfaceHeight,
|
||||
surfaceBiome
|
||||
);
|
||||
} else {
|
||||
return getSurfaceVoxel(
|
||||
rVal,
|
||||
worldX, worldY, worldZ,
|
||||
chunkX, chunkY, chunkZ,
|
||||
realX, realY, realZ,
|
||||
surfacePercent,
|
||||
surfaceHeight,
|
||||
surfaceBiome
|
||||
);
|
||||
}
|
||||
}
|
||||
return rVal
|
||||
},
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { HookManager } from "/Scripts/engine/hooks/hook-manager";
|
||||
import { SceneLoader } from "/Scripts/engine/scene/scene-loader";
|
||||
import { ChunkGeneratorManager } from "/Scripts/server/chunk/chunkgeneratormanager";
|
||||
import { SingletonsMap } from "/Scripts/types/host/singletons";
|
||||
import { StaticClasses } from "/Scripts/types/host/static-classes";
|
||||
|
||||
@ -29,5 +30,10 @@ export interface Engine {
|
||||
*/
|
||||
readonly sceneLoader: SceneLoader,
|
||||
|
||||
/**
|
||||
* The chunk generator manager
|
||||
*/
|
||||
readonly chunkGeneratorManager: ChunkGeneratorManager,
|
||||
|
||||
}
|
||||
|
||||
|
||||
95
assets/Scripts/types/host/server/chunk/chunkgenerator.ts
Normal file
95
assets/Scripts/types/host/server/chunk/chunkgenerator.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { Engine } from "/Scripts/types/engine";
|
||||
import { BiomeData } from "/Scripts/types/host/server/data/biomedata";
|
||||
|
||||
|
||||
/**
|
||||
* The width of a chunk
|
||||
*/
|
||||
export const CHUNK_WIDTH = 16;
|
||||
|
||||
/**
|
||||
* A voxel generated by a ChunkGenerator
|
||||
*/
|
||||
export interface GeneratedVoxel {
|
||||
|
||||
/**
|
||||
* The id of the voxel type
|
||||
*/
|
||||
type: number,
|
||||
|
||||
/**
|
||||
* The weight of the voxel
|
||||
*/
|
||||
weight: number,
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a voxel for a given position
|
||||
* @param worldX The world x coordinate of the chunk
|
||||
* @param worldY The world y coordinate of the chunk
|
||||
* @param worldZ The world z coordinate of the chunk
|
||||
* @param chunkX The chunk x coordinate
|
||||
* @param chunkY The chunk y coordinate
|
||||
* @param chunkZ The chunk z coordinate
|
||||
* @param stride The stride of the data (this is used for LOD, it ranges 0->4 with 0 being the highest level of detail and 4 being the lowest)
|
||||
* @param surfaceHeight The height of the surface at the given x,z coordinate
|
||||
* @param terrainModel The terrain model
|
||||
* @param surfaceBiome The surface biome
|
||||
* @returns The voxel at the provided position
|
||||
*/
|
||||
export type VoxelFunction = (
|
||||
worldX: number, worldY: number, worldZ: number,
|
||||
chunkX: number, chunkY: number, chunkZ: number,
|
||||
stride: number,
|
||||
surfaceHeight: number,
|
||||
surfaceBiome: BiomeData
|
||||
) => GeneratedVoxel;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets a chunk for a given world position
|
||||
* @param worldX The world x coordinate of the chunk
|
||||
* @param worldY The world y coordinate of the chunk
|
||||
* @param worldZ The world z coordinate of the chunk
|
||||
* @param stride The stride of the data (this is used for LOD, it ranges 0->4 with 0 being the highest level of detail and 4 being the lowest)
|
||||
* @param surfaceHeight The height of the surface at the given x,z coordinate
|
||||
* @param terrainModel The terrain model
|
||||
* @param surfaceBiome The surface biome
|
||||
* @returns The voxel at the provided position
|
||||
*/
|
||||
export type ChunkFunction = (
|
||||
worldX: number, worldY: number, worldZ: number,
|
||||
chunkX: number, chunkY: number, chunkZ: number,
|
||||
stride: number,
|
||||
surfaceHeight: number,
|
||||
surfaceBiome: BiomeData
|
||||
) => number[][][];
|
||||
|
||||
/**
|
||||
* A chunk generator
|
||||
*/
|
||||
export interface ChunkGenerator {
|
||||
|
||||
/**
|
||||
* Gets the tag for this generator
|
||||
* @returns The tag
|
||||
*/
|
||||
getTag: () => string
|
||||
|
||||
/**
|
||||
* Retrieves the elevation for the world at a given x,z coordinate
|
||||
* @param worldX The world x coordinate
|
||||
* @param worldZ The world z coordinate
|
||||
* @param chunkX The x coordinate of the chunk within the specified world coordinate
|
||||
* @param chunkZ The z coordinate of the chunk within the specified world coordinate
|
||||
* @returns The elevation at that specific position
|
||||
*/
|
||||
getElevation: (worldX: number, worldZ: number, chunkX: number, chunkZ: number) => number
|
||||
|
||||
/**
|
||||
* The function to get a voxel for a given position
|
||||
*/
|
||||
getVoxelFunction: (engine: Engine) => VoxelFunction
|
||||
|
||||
}
|
||||
95
assets/Scripts/types/host/server/data/biomedata.ts
Normal file
95
assets/Scripts/types/host/server/data/biomedata.ts
Normal file
@ -0,0 +1,95 @@
|
||||
|
||||
|
||||
/**
|
||||
* Describes a type of foliage that can be generated in a biome
|
||||
*/
|
||||
export interface BiomeFoliageDescription {
|
||||
|
||||
/**
|
||||
* The list of entity IDs in the type
|
||||
*/
|
||||
entityIDs: string[],
|
||||
|
||||
/**
|
||||
* How regular the placement of the foliage is.
|
||||
* Low values make the placement more random.
|
||||
* High values make the placement more orderly (aligned with a grid).
|
||||
*/
|
||||
regularity: number,
|
||||
|
||||
/**
|
||||
* The percentage of the ground to cover with foliage
|
||||
*/
|
||||
threshold: number,
|
||||
|
||||
/**
|
||||
* The priority of this type in particular
|
||||
*/
|
||||
priority: number,
|
||||
|
||||
/**
|
||||
* The scale of the noise used to place foliage
|
||||
*/
|
||||
scale: number,
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The parameters for generating the surface of a biome
|
||||
*/
|
||||
export interface BiomeSurfaceGenerationParams {
|
||||
|
||||
/**
|
||||
* The tag for the generation algorithm to generate the surface of the biome
|
||||
*/
|
||||
surfaceGenTag: string,
|
||||
|
||||
/**
|
||||
* The offset added to the generated heightfield to get the final height of the surface at a given position.
|
||||
* This is most useful for cases like plateaus where you really want to put the surface noise higher than its surroundings.
|
||||
*/
|
||||
heightOffset: number,
|
||||
|
||||
/**
|
||||
* The different foliage types that can be generated on this surface
|
||||
*/
|
||||
foliageDescriptions: BiomeFoliageDescription[],
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Biome data
|
||||
*/
|
||||
export interface BiomeData {
|
||||
|
||||
/**
|
||||
* The id of the biome type
|
||||
*/
|
||||
id: string,
|
||||
|
||||
/**
|
||||
* The display name of the biome
|
||||
*/
|
||||
displayName: string,
|
||||
|
||||
/**
|
||||
* True if this is an aerial biome
|
||||
*/
|
||||
isAerial: boolean,
|
||||
|
||||
/**
|
||||
* True if this is a surface biome
|
||||
*/
|
||||
isSurface: boolean,
|
||||
|
||||
/**
|
||||
* True if this is a subterranean biome
|
||||
*/
|
||||
isSubterranean: boolean,
|
||||
|
||||
/**
|
||||
* The parameters for generating the surface of this biome
|
||||
*/
|
||||
surfaceGenerationParams: BiomeSurfaceGenerationParams,
|
||||
|
||||
}
|
||||
@ -46,6 +46,11 @@ export interface StaticClasses {
|
||||
*/
|
||||
readonly levelEditorUtils?: Class<ClientLevelEditorUtils>,
|
||||
|
||||
/**
|
||||
* Math functions
|
||||
*/
|
||||
readonly math?: Class<Math>
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
14
assets/Scripts/types/host/util/mathutils.ts
Normal file
14
assets/Scripts/types/host/util/mathutils.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Math functions
|
||||
*/
|
||||
export interface Math {
|
||||
|
||||
/**
|
||||
* Computes an arbitrary power of a number
|
||||
* @param num1 The number
|
||||
* @param num2 The power
|
||||
* @returns The result of the power
|
||||
*/
|
||||
readonly pow: (num1: number, num2: number) => number
|
||||
|
||||
}
|
||||
@ -963,6 +963,15 @@ Fix foliage rendering
|
||||
Fix async physics gen on client
|
||||
Convert volumetric + shadow pass to entity tags
|
||||
|
||||
(11/09/2024)
|
||||
Fix eighth res chunk radius
|
||||
Fix chunk gen debug ui regenerate button
|
||||
Script-defined chunk generators
|
||||
Script engine synchronization utility
|
||||
Convert ScriptEngine to service
|
||||
ScriptEngine full re-initialization signal
|
||||
Add surface width to test generator
|
||||
|
||||
|
||||
# TODO
|
||||
|
||||
|
||||
@ -14,9 +14,11 @@ public class ClientScriptUtils {
|
||||
* @param args The arguments provided alongside the signal
|
||||
*/
|
||||
public static void fireSignal(String signalName, Object ... args){
|
||||
if(Globals.scriptEngine != null && Globals.scriptEngine.isInitialized()){
|
||||
Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args);
|
||||
}
|
||||
Globals.scriptEngine.executeSynchronously(() -> {
|
||||
if(Globals.scriptEngine != null && Globals.scriptEngine.isInitialized()){
|
||||
Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -97,6 +97,9 @@ public class ClientTerrainCache {
|
||||
this.cacheList.clear();
|
||||
this.cacheMapFullRes.clear();
|
||||
this.cacheMapHalfRes.clear();
|
||||
this.cacheMapQuarterRes.clear();
|
||||
this.cacheMapEighthRes.clear();
|
||||
this.cacheMapSixteenthRes.clear();
|
||||
this.chunkPositionMap.clear();
|
||||
lock.release();
|
||||
}
|
||||
|
||||
@ -98,6 +98,11 @@ public class ClientDrawCellManager {
|
||||
*/
|
||||
Map<FloatingChunkTreeNode<DrawCell>,Boolean> evaluationMap = new HashMap<FloatingChunkTreeNode<DrawCell>,Boolean>();
|
||||
|
||||
/**
|
||||
* All draw cells currently tracked
|
||||
*/
|
||||
List<DrawCell> activeCells = new LinkedList<DrawCell>();
|
||||
|
||||
/**
|
||||
* Tracks whether the cell manager updated last frame or not
|
||||
*/
|
||||
@ -227,6 +232,7 @@ public class ClientDrawCellManager {
|
||||
child.getMinBound().z
|
||||
);
|
||||
DrawCell drawCell = DrawCell.generateTerrainCell(cellWorldPos);
|
||||
activeCells.add(drawCell);
|
||||
child.convertToLeaf(drawCell);
|
||||
evaluationMap.put(child,true);
|
||||
});
|
||||
@ -246,6 +252,7 @@ public class ClientDrawCellManager {
|
||||
|
||||
//do creations
|
||||
DrawCell drawCell = DrawCell.generateTerrainCell(node.getMinBound());
|
||||
activeCells.add(drawCell);
|
||||
newLeaf.convertToLeaf(drawCell);
|
||||
|
||||
//update neighbors
|
||||
@ -544,7 +551,7 @@ public class ClientDrawCellManager {
|
||||
(
|
||||
node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD
|
||||
&&
|
||||
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST
|
||||
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST
|
||||
)
|
||||
||
|
||||
(
|
||||
@ -591,7 +598,7 @@ public class ClientDrawCellManager {
|
||||
(
|
||||
node.getLevel() == this.chunkTree.getMaxLevel() - EIGHTH_RES_LOD
|
||||
&&
|
||||
this.getMinDistance(pos, node) <= EIGHTH_RES_DIST
|
||||
this.getMinDistance(pos, node) <= SIXTEENTH_RES_DIST
|
||||
)
|
||||
||
|
||||
(
|
||||
@ -631,6 +638,7 @@ public class ClientDrawCellManager {
|
||||
node.getChildren().forEach(child -> recursivelyDestroy(child));
|
||||
}
|
||||
if(node.getData() != null){
|
||||
activeCells.remove(node.getData());
|
||||
node.getData().destroy();
|
||||
}
|
||||
}
|
||||
@ -659,6 +667,7 @@ public class ClientDrawCellManager {
|
||||
* Evicts all cells
|
||||
*/
|
||||
public void evictAll(){
|
||||
this.recursivelyDestroy(this.chunkTree.getRoot());
|
||||
this.chunkTree.clear();
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package electrosphere.client.ui.menu.debug;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.signal.Signal.SignalType;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiWindow;
|
||||
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
|
||||
import electrosphere.server.datacell.GriddedDataCellManager;
|
||||
@ -45,10 +46,19 @@ public class ImGuiTestGen {
|
||||
|
||||
//regenerate the test area
|
||||
if(ImGui.button("Regenerate")){
|
||||
GriddedDataCellManager gridManager = (GriddedDataCellManager)Globals.realmManager.first().getDataCellManager();
|
||||
gridManager.evictAll();
|
||||
Globals.clientDrawCellManager.evictAll();
|
||||
Globals.clientTerrainManager.evictAll();
|
||||
//recompile script engine
|
||||
Globals.signalSystem.post(SignalType.SCRIPT_RECOMPILE, () -> {
|
||||
|
||||
//run once script recompilation has completed
|
||||
|
||||
//clear server
|
||||
GriddedDataCellManager gridManager = (GriddedDataCellManager)Globals.realmManager.first().getDataCellManager();
|
||||
gridManager.evictAll();
|
||||
|
||||
//clear client
|
||||
Globals.clientDrawCellManager.evictAll();
|
||||
Globals.clientTerrainManager.evictAll();
|
||||
});
|
||||
}
|
||||
|
||||
//set macro data scale in terrain model
|
||||
@ -82,7 +92,7 @@ public class ImGuiTestGen {
|
||||
}
|
||||
|
||||
//Toggles whether the client draws cell manager should update or not
|
||||
if(ImGui.button("Toggle ClientDrawCellManager updates")){
|
||||
if(ImGui.button("Toggle ClientDrawCellManager updates " + (Globals.clientDrawCellManager.getShouldUpdate() ? "off" : "on"))){
|
||||
Globals.clientDrawCellManager.setShouldUpdate(!Globals.clientDrawCellManager.getShouldUpdate());
|
||||
}
|
||||
|
||||
|
||||
@ -481,8 +481,6 @@ public class Globals {
|
||||
skyboxColors = new ArrayList<Vector3f>();
|
||||
//load asset manager
|
||||
assetManager = new AssetManager();
|
||||
//script engine
|
||||
scriptEngine = new ScriptEngine();
|
||||
//ai manager
|
||||
aiManager = new AIManager(0);
|
||||
//realm & data cell manager
|
||||
@ -526,6 +524,7 @@ public class Globals {
|
||||
Globals.signalSystem = (SignalSystem)serviceManager.registerService(new SignalSystem());
|
||||
Globals.elementService = (ElementService)serviceManager.registerService(new ElementService());
|
||||
Globals.particleService = (ParticleService)serviceManager.registerService(new ParticleService());
|
||||
Globals.scriptEngine = (ScriptEngine)serviceManager.registerService(new ScriptEngine());
|
||||
serviceManager.instantiate();
|
||||
//
|
||||
//End service manager
|
||||
@ -537,6 +536,7 @@ public class Globals {
|
||||
Globals.signalSystem.registerService(SignalType.YOGA_DESTROY, Globals.elementService);
|
||||
Globals.signalSystem.registerService(SignalType.UI_MODIFICATION, Globals.elementService);
|
||||
Globals.signalSystem.registerService(SignalType.RENDERING_ENGINE_READY, Globals.particleService);
|
||||
Globals.signalSystem.registerService(SignalType.SCRIPT_RECOMPILE, Globals.scriptEngine);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -291,7 +291,13 @@ public class Main {
|
||||
RenderingEngine.recaptureIfNecessary();
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///
|
||||
/// S Y N C H R O N O U S S I G N A L H A N D L I N G
|
||||
///
|
||||
Globals.scriptEngine.handleAllSignals();
|
||||
|
||||
|
||||
///
|
||||
///
|
||||
|
||||
@ -14,6 +14,7 @@ import electrosphere.net.parser.net.message.TerrainMessage;
|
||||
import electrosphere.net.server.ServerConnectionHandler;
|
||||
import electrosphere.renderer.ui.elements.Window;
|
||||
import electrosphere.server.saves.SaveUtils;
|
||||
import electrosphere.server.terrain.generation.TestGenerationChunkGenerator;
|
||||
|
||||
/**
|
||||
* Loads the chunk generation testing realm
|
||||
@ -42,6 +43,17 @@ public class ChunkGenerationTestLoading {
|
||||
loadingWindow.setVisible(true);
|
||||
});
|
||||
|
||||
//wait on script engine to load
|
||||
if(TestGenerationChunkGenerator.DEFAULT_USE_JAVASCRIPT){
|
||||
WindowUtils.updateLoadingWindow("Waiting on scripting engine");
|
||||
while(!Globals.scriptEngine.isInitialized()){
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(1);
|
||||
} catch (InterruptedException ex) {}
|
||||
}
|
||||
WindowUtils.updateLoadingWindow("LOADING");
|
||||
}
|
||||
|
||||
|
||||
|
||||
String saveName = "generation_testing";
|
||||
|
||||
@ -11,7 +11,7 @@ public class EngineInitLoading {
|
||||
* Loads the core assets of the scripting engine from disk and initializes the engine
|
||||
*/
|
||||
protected static void loadScriptingEngine(Object[] params){
|
||||
Globals.scriptEngine.init();
|
||||
Globals.scriptEngine.initScripts();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -37,6 +37,11 @@ public class Signal {
|
||||
REQUEST_CHUNK_EDIT,
|
||||
CHUNK_EDITED,
|
||||
|
||||
//
|
||||
//Script
|
||||
//
|
||||
SCRIPT_RECOMPILE,
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -124,8 +124,11 @@ public class SceneLoader {
|
||||
}
|
||||
//load scripts
|
||||
if(!isLevelEditor && file.getInitScriptPath() != null){
|
||||
int sceneInstanceId = Globals.scriptEngine.initScene(file.getInitScriptPath());
|
||||
realm.setSceneInstanceId(sceneInstanceId);
|
||||
Realm finalRealm = realm;
|
||||
Globals.scriptEngine.executeSynchronously(() -> {
|
||||
int sceneInstanceId = Globals.scriptEngine.initScene(file.getInitScriptPath());
|
||||
finalRealm.setSceneInstanceId(sceneInstanceId);
|
||||
});
|
||||
}
|
||||
//TODO: integrate scripts for client side of scenes
|
||||
// for(String scriptPath : file.getScriptPaths()){
|
||||
|
||||
@ -2,6 +2,8 @@ package electrosphere.game.data.biome;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.graalvm.polyglot.HostAccess.Export;
|
||||
|
||||
/**
|
||||
* Data about a given biome
|
||||
*/
|
||||
@ -10,11 +12,13 @@ public class BiomeData {
|
||||
/**
|
||||
* The id of the biome
|
||||
*/
|
||||
@Export
|
||||
String id;
|
||||
|
||||
/**
|
||||
* The display name of the biome
|
||||
*/
|
||||
@Export
|
||||
String displayName;
|
||||
|
||||
/**
|
||||
@ -25,21 +29,25 @@ public class BiomeData {
|
||||
/**
|
||||
* True if this region applies above the surface
|
||||
*/
|
||||
@Export
|
||||
Boolean isAerial;
|
||||
|
||||
/**
|
||||
* True if this region applies to the surface
|
||||
*/
|
||||
@Export
|
||||
Boolean isSurface;
|
||||
|
||||
/**
|
||||
* True if this region applies below the surface
|
||||
*/
|
||||
@Export
|
||||
Boolean isSubterranean;
|
||||
|
||||
/**
|
||||
* The surface generation params
|
||||
*/
|
||||
@Export
|
||||
BiomeSurfaceGenerationParams surfaceGenerationParams;
|
||||
|
||||
|
||||
|
||||
@ -2,34 +2,41 @@ package electrosphere.game.data.biome;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.graalvm.polyglot.HostAccess.Export;
|
||||
|
||||
/**
|
||||
* Describes behavior for spawning a specific type of foliage in the biome
|
||||
*/
|
||||
public class BiomeFoliageDescription {
|
||||
|
||||
/**
|
||||
* The liust of entity IDs of this foliage type in particular
|
||||
* The list of entity IDs of this foliage type in particular
|
||||
*/
|
||||
@Export
|
||||
List<String> entityIDs;
|
||||
|
||||
/**
|
||||
* How regular the placement of foliage is. Low values will create very uneven foliage, while high values will place them along a grid.
|
||||
*/
|
||||
@Export
|
||||
Double regularity;
|
||||
|
||||
/**
|
||||
* The percentage of the ground to cover with foliage
|
||||
*/
|
||||
@Export
|
||||
Double threshold;
|
||||
|
||||
/**
|
||||
* The priority of this floor element in particular
|
||||
*/
|
||||
@Export
|
||||
Double priority;
|
||||
|
||||
/**
|
||||
* The scale of the noise used to place foliage
|
||||
*/
|
||||
@Export
|
||||
Double scale;
|
||||
|
||||
/**
|
||||
|
||||
@ -2,6 +2,8 @@ package electrosphere.game.data.biome;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.graalvm.polyglot.HostAccess.Export;
|
||||
|
||||
/**
|
||||
* Params for the surface generation algorithm
|
||||
*/
|
||||
@ -10,11 +12,13 @@ public class BiomeSurfaceGenerationParams {
|
||||
/**
|
||||
* The tag for the generation algorithm for generating the surface
|
||||
*/
|
||||
@Export
|
||||
String surfaceGenTag;
|
||||
|
||||
/**
|
||||
* The offset from baseline for height generation with this biome
|
||||
*/
|
||||
@Export
|
||||
Float heightOffset;
|
||||
|
||||
/**
|
||||
@ -25,6 +29,7 @@ public class BiomeSurfaceGenerationParams {
|
||||
/**
|
||||
* The list of foliage descriptions available to this biome type
|
||||
*/
|
||||
@Export
|
||||
List<BiomeFoliageDescription> foliageDescriptions;
|
||||
|
||||
/**
|
||||
|
||||
@ -153,7 +153,7 @@ public class ServerWorldData {
|
||||
|
||||
//test terrain gen
|
||||
{
|
||||
TestGenerationChunkGenerator chunkGen = new TestGenerationChunkGenerator(serverWorldData);
|
||||
TestGenerationChunkGenerator chunkGen = new TestGenerationChunkGenerator(serverWorldData, TestGenerationChunkGenerator.DEFAULT_USE_JAVASCRIPT);
|
||||
serverTerrainManager = new ServerTerrainManager(serverWorldData, 0, chunkGen);
|
||||
serverTerrainManager.genTestData(chunkGen);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Engine;
|
||||
@ -19,15 +20,18 @@ import electrosphere.client.ui.menu.script.ScriptMenuUtils;
|
||||
import electrosphere.client.ui.menu.tutorial.TutorialMenus;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.Main;
|
||||
import electrosphere.engine.signal.Signal;
|
||||
import electrosphere.engine.signal.SignalServiceImpl;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.script.translation.JSServerUtils;
|
||||
import electrosphere.script.utils.ScriptMathInterface;
|
||||
import electrosphere.util.FileUtils;
|
||||
import electrosphere.util.math.SpatialMathUtils;
|
||||
|
||||
/**
|
||||
* Interface for executing scripts in the game engine
|
||||
*/
|
||||
public class ScriptEngine {
|
||||
public class ScriptEngine extends SignalServiceImpl {
|
||||
|
||||
//the default namespaces for
|
||||
public static String SCRIPT_NAMESPACE_ENGINE = "engine"; //namespace for the engine functions exposed to the script engine
|
||||
@ -67,6 +71,11 @@ public class ScriptEngine {
|
||||
*/
|
||||
boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Locks the script engine to enforce synchronization
|
||||
*/
|
||||
ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
//The files that are loaded on init to bootstrap the script engine
|
||||
static final String[] filesToLoadOnInit = new String[]{
|
||||
//polyfills
|
||||
@ -98,6 +107,7 @@ public class ScriptEngine {
|
||||
{"menuUtils",ScriptMenuUtils.class},
|
||||
{"voxelUtils",ScriptClientVoxelUtils.class},
|
||||
{"levelEditorUtils",ScriptLevelEditorUtils.class},
|
||||
{"math",ScriptMathInterface.class},
|
||||
};
|
||||
|
||||
//singletons from the host that are provided to the javascript context
|
||||
@ -107,10 +117,18 @@ public class ScriptEngine {
|
||||
{"loggerScripts",LoggerInterface.loggerScripts},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ScriptEngine(){
|
||||
super("ScriptEngine");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the engine
|
||||
*/
|
||||
public void init(){
|
||||
public void initScripts(){
|
||||
//init datastructures
|
||||
sourceMap = new HashMap<String,Source>();
|
||||
initialized = false;
|
||||
@ -320,6 +338,22 @@ public class ScriptEngine {
|
||||
invokeMemberFunction("COMPILER", "run");
|
||||
}
|
||||
|
||||
/**
|
||||
* Recompiles the scripting engine
|
||||
*/
|
||||
private void recompile(Runnable onCompletion){
|
||||
Thread recompileThread = new Thread(() -> {
|
||||
Globals.scriptEngine.executeSynchronously(() -> {
|
||||
this.initScripts();
|
||||
});
|
||||
if(onCompletion != null){
|
||||
onCompletion.run();
|
||||
}
|
||||
});
|
||||
recompileThread.setName("Recompile Script Engine");
|
||||
recompileThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a scene script
|
||||
* @param scenePath The scene's init script path
|
||||
@ -398,6 +432,40 @@ public class ScriptEngine {
|
||||
invokeFunction("require", filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a function on a member of arbitrary depth on the engine object
|
||||
* @param memberName The member name
|
||||
* @param functionName The function's name
|
||||
* @param className The class of the expected return value
|
||||
* @param args The args to pass to the function
|
||||
* @return The results of the invocation or null if there was no result
|
||||
*/
|
||||
public Value invokeEngineMember(String memberName, String functionName, Object ... args){
|
||||
Value member = this.engineObject.getMember(memberName);
|
||||
if(member == null){
|
||||
throw new Error("Member is null!");
|
||||
}
|
||||
Value function = member.getMember(functionName);
|
||||
if(function == null || !function.canExecute()){
|
||||
throw new Error("Function is not executable! " + function);
|
||||
}
|
||||
Value executionResult = function.execute(args);
|
||||
if(executionResult == null){
|
||||
return null;
|
||||
}
|
||||
return executionResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes some code synchronously that requires script engine access
|
||||
* @param function The function
|
||||
*/
|
||||
public void executeSynchronously(Runnable function){
|
||||
lock.lock();
|
||||
function.run();
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines host members within javascript context
|
||||
*/
|
||||
@ -431,5 +499,23 @@ public class ScriptEngine {
|
||||
return this.initialized;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean handle(Signal signal){
|
||||
boolean rVal = false;
|
||||
switch(signal.getType()){
|
||||
case SCRIPT_RECOMPILE: {
|
||||
if(signal.getData() != null && signal.getData() instanceof Runnable){
|
||||
this.recompile((Runnable)signal.getData());
|
||||
} else {
|
||||
this.recompile(null);
|
||||
}
|
||||
rVal = true;
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package electrosphere.script.utils;
|
||||
|
||||
import org.graalvm.polyglot.HostAccess.Export;
|
||||
|
||||
/**
|
||||
* Script access to specific math functions
|
||||
*/
|
||||
public class ScriptMathInterface {
|
||||
|
||||
/**
|
||||
* Power function
|
||||
* @param val1 The number
|
||||
* @param val2 The exponent
|
||||
* @return The power
|
||||
*/
|
||||
@Export
|
||||
public static double pow(double val1, double val2){
|
||||
return Math.pow(val1,val2);
|
||||
}
|
||||
|
||||
}
|
||||
@ -421,9 +421,6 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
|
||||
/**
|
||||
* Evicts all loaded chunks.
|
||||
* <p>
|
||||
* Note: Does not save to disk.
|
||||
* </p>
|
||||
*/
|
||||
public void evictAll(){
|
||||
//TODO: improve to make have less performance impact
|
||||
|
||||
@ -274,11 +274,13 @@ public class Realm {
|
||||
*/
|
||||
public void fireSignal(String signalName, Object ... args){
|
||||
if(Globals.scriptEngine != null && Globals.scriptEngine.isInitialized()){
|
||||
if(this.sceneInstanceId != NO_SCENE_INSTANCE){
|
||||
Globals.scriptEngine.fireSignal(signalName, sceneInstanceId, args);
|
||||
} else {
|
||||
Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args);
|
||||
}
|
||||
Globals.scriptEngine.executeSynchronously(() -> {
|
||||
if(this.sceneInstanceId != NO_SCENE_INSTANCE){
|
||||
Globals.scriptEngine.fireSignal(signalName, sceneInstanceId, args);
|
||||
} else {
|
||||
Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ package electrosphere.server.terrain.generation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.graalvm.polyglot.Value;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.game.data.biome.BiomeData;
|
||||
import electrosphere.game.data.biome.BiomeSurfaceGenerationParams;
|
||||
@ -12,6 +14,7 @@ import electrosphere.server.terrain.generation.heightmap.HeightmapGenerator;
|
||||
import electrosphere.server.terrain.generation.heightmap.HillsGen;
|
||||
import electrosphere.server.terrain.generation.heightmap.PlainsGen;
|
||||
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
|
||||
import electrosphere.server.terrain.generation.interfaces.GeneratedVoxel;
|
||||
import electrosphere.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.server.terrain.models.TerrainModel;
|
||||
|
||||
@ -29,6 +32,21 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
* The default biome index
|
||||
*/
|
||||
public static final int DEFAULT_BIOME_INDEX = 1;
|
||||
|
||||
/**
|
||||
* The width of the surface in number of voxels
|
||||
*/
|
||||
public static final int SURFACE_VOXEL_WIDTH = 2;
|
||||
|
||||
/**
|
||||
* Tag for the test generator
|
||||
*/
|
||||
public static final String SCRIPT_GEN_TEST_TAG = "test";
|
||||
|
||||
/**
|
||||
* Controls the default setting for whether to use javascript or not
|
||||
*/
|
||||
public static final boolean DEFAULT_USE_JAVASCRIPT = false;
|
||||
|
||||
/**
|
||||
* The terreain model for the generator
|
||||
@ -45,14 +63,20 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
*/
|
||||
Map<String,HeightmapGenerator> tagGeneratorMap = new HashMap<String,HeightmapGenerator>();
|
||||
|
||||
/**
|
||||
* Tracks whether to use javascript generation or not
|
||||
*/
|
||||
boolean useJavascript = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public TestGenerationChunkGenerator(ServerWorldData serverWorldData){
|
||||
public TestGenerationChunkGenerator(ServerWorldData serverWorldData, boolean useJavascript){
|
||||
this.serverWorldData = serverWorldData;
|
||||
registerGenerator(new EmptySkyGen());
|
||||
registerGenerator(new HillsGen());
|
||||
registerGenerator(new PlainsGen());
|
||||
this.useJavascript = useJavascript;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,22 +125,61 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
GeneratedVoxel voxel = this.getVoxel(finalWorldX, finalWorldY, finalWorldZ, finalChunkX, finalChunkY, finalChunkZ, heightfield[x][z], this.terrainModel, surfaceBiome);
|
||||
weights[x][y][z] = voxel.weight;
|
||||
values[x][y][z] = voxel.type;
|
||||
if(this.useJavascript){
|
||||
Globals.scriptEngine.executeSynchronously(() -> {
|
||||
Value getVoxelFunc = Globals.scriptEngine.invokeEngineMember("chunkGeneratorManager", "getVoxelFunction", SCRIPT_GEN_TEST_TAG);
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
Value result = getVoxelFunc.execute(
|
||||
finalWorldX, finalWorldY, finalWorldZ,
|
||||
finalChunkX, finalChunkY, finalChunkZ,
|
||||
stride,
|
||||
heightfield[x][z],
|
||||
surfaceBiome
|
||||
);
|
||||
if(result != null){
|
||||
weights[x][y][z] = result.getMember("weight").asFloat();
|
||||
values[x][y][z] = result.getMember("type").asInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION; x++){
|
||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator - Generate slice");
|
||||
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
|
||||
for(int z = 0; z < ServerTerrainChunk.CHUNK_DIMENSION; z++){
|
||||
int finalWorldX = worldX + ((x * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalWorldY = worldY + ((y * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalWorldZ = worldZ + ((z * strideValue) / ServerTerrainChunk.CHUNK_DIMENSION);
|
||||
int finalChunkX = (x * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
int finalChunkY = (y * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
int finalChunkZ = (z * strideValue) % ServerTerrainChunk.CHUNK_DIMENSION;
|
||||
GeneratedVoxel voxel = this.getVoxel(
|
||||
finalWorldX, finalWorldY, finalWorldZ,
|
||||
finalChunkX, finalChunkY, finalChunkZ,
|
||||
stride,
|
||||
heightfield[x][z],
|
||||
surfaceBiome
|
||||
);
|
||||
if(voxel != null){
|
||||
weights[x][y][z] = voxel.weight;
|
||||
values[x][y][z] = voxel.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
Globals.profiler.endCpuSample();
|
||||
}
|
||||
} catch(Exception ex){
|
||||
ex.printStackTrace();
|
||||
@ -156,16 +219,16 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
* @param chunkX The chunk x pos
|
||||
* @param chunkY The chunk y pos
|
||||
* @param chunkZ The chunk z pos
|
||||
* @param stride The stride of the data
|
||||
* @param surfaceHeight The height of the surface at x,z
|
||||
* @param terrainModel The terrain model
|
||||
* @param surfaceBiome The surface biome of the chunk
|
||||
* @return The value of the chunk
|
||||
*/
|
||||
private GeneratedVoxel getVoxel(
|
||||
int worldX, int worldY, int worldZ,
|
||||
int chunkX, int chunkY, int chunkZ,
|
||||
int stride,
|
||||
float surfaceHeight,
|
||||
TerrainModel terrainModel,
|
||||
BiomeData surfaceBiome
|
||||
){
|
||||
Globals.profiler.beginAggregateCpuSample("TestGenerationChunkGenerator.getChunkValue");
|
||||
@ -179,24 +242,26 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
double realY = this.serverWorldData.convertVoxelToRealSpace(chunkY,worldY);
|
||||
double realZ = this.serverWorldData.convertVoxelToRealSpace(chunkZ,worldZ);
|
||||
|
||||
double flooredSurfaceHeight = Math.floor(surfaceHeight);
|
||||
double strideMultiplier = Math.pow(2,stride);
|
||||
double heightDiff = realY - surfaceHeight;
|
||||
double surfacePercent = TestGenerationChunkGenerator.getSurfaceWeight(surfaceHeight,realY,strideMultiplier);
|
||||
Globals.profiler.endCpuSample();
|
||||
if(realY < surfaceHeight - 1){
|
||||
if(heightDiff < -strideMultiplier * SURFACE_VOXEL_WIDTH){
|
||||
return getSubsurfaceVoxel(
|
||||
worldX, worldY, worldZ,
|
||||
chunkX, chunkY, chunkZ,
|
||||
realX, realY, realZ,
|
||||
surfaceHeight, flooredSurfaceHeight,
|
||||
terrainModel,
|
||||
surfacePercent,
|
||||
surfaceHeight,
|
||||
surfaceBiome
|
||||
);
|
||||
} else if(realY > flooredSurfaceHeight) {
|
||||
} else if(heightDiff > 0) {
|
||||
return getOverSurfaceVoxel(
|
||||
worldX, worldY, worldZ,
|
||||
chunkX, chunkY, chunkZ,
|
||||
realX, realY, realZ,
|
||||
surfaceHeight, flooredSurfaceHeight,
|
||||
terrainModel,
|
||||
realX, realY, realZ,-
|
||||
surfacePercent,
|
||||
surfaceHeight,
|
||||
surfaceBiome
|
||||
);
|
||||
} else {
|
||||
@ -204,8 +269,8 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
worldX, worldY, worldZ,
|
||||
chunkX, chunkY, chunkZ,
|
||||
realX, realY, realZ,
|
||||
surfaceHeight, flooredSurfaceHeight,
|
||||
terrainModel,
|
||||
surfacePercent,
|
||||
surfaceHeight,
|
||||
surfaceBiome
|
||||
);
|
||||
}
|
||||
@ -219,12 +284,12 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
int worldX, int worldY, int worldZ,
|
||||
int chunkX, int chunkY, int chunkZ,
|
||||
double realX, double realY, double realZ,
|
||||
float surfaceHeight, double flooredSurfaceHeight,
|
||||
TerrainModel terrainModel,
|
||||
double surfacePercent,
|
||||
float surfaceHeight,
|
||||
BiomeData surfaceBiome
|
||||
){
|
||||
GeneratedVoxel voxel = new GeneratedVoxel();
|
||||
voxel.weight = (float)(surfaceHeight - flooredSurfaceHeight) * 2 - 1;
|
||||
voxel.weight = (float)surfacePercent * 2 - 1;
|
||||
voxel.type = 2;
|
||||
return voxel;
|
||||
}
|
||||
@ -237,8 +302,8 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
int worldX, int worldY, int worldZ,
|
||||
int chunkX, int chunkY, int chunkZ,
|
||||
double realX, double realY, double realZ,
|
||||
float surfaceHeight, double flooredSurfaceHeight,
|
||||
TerrainModel terrainModel,
|
||||
double surfacePercent,
|
||||
float surfaceHeight,
|
||||
BiomeData surfaceBiome
|
||||
){
|
||||
GeneratedVoxel voxel = new GeneratedVoxel();
|
||||
@ -260,8 +325,8 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
int worldX, int worldY, int worldZ,
|
||||
int chunkX, int chunkY, int chunkZ,
|
||||
double realX, double realY, double realZ,
|
||||
float surfaceHeight, double flooredSurfaceHeight,
|
||||
TerrainModel terrainModel,
|
||||
double surfacePercent,
|
||||
float surfaceHeight,
|
||||
BiomeData surfaceBiome
|
||||
){
|
||||
GeneratedVoxel voxel = new GeneratedVoxel();
|
||||
@ -271,17 +336,14 @@ public class TestGenerationChunkGenerator implements ChunkGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* A voxel that was generated
|
||||
* Calculates the weight of a voxel on the surface based on the surface height, the position of the voxel, and the stride multiplier
|
||||
* @param surfaceHeight The surface height
|
||||
* @param realPosY The position of the voxel
|
||||
* @param strideMultiplier The stride multiplier
|
||||
* @return The weight of the voxel
|
||||
*/
|
||||
static class GeneratedVoxel {
|
||||
/**
|
||||
* The type of the voxel
|
||||
*/
|
||||
int type;
|
||||
/**
|
||||
* The weight of the voxel
|
||||
*/
|
||||
float weight;
|
||||
protected static double getSurfaceWeight(double surfaceHeight, double realPosY, double strideMultiplier){
|
||||
return ((surfaceHeight - realPosY) / strideMultiplier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package electrosphere.server.terrain.generation.interfaces;
|
||||
|
||||
import org.graalvm.polyglot.HostAccess.Export;
|
||||
|
||||
/**
|
||||
* A voxel generated by a chunk generator
|
||||
*/
|
||||
public class GeneratedVoxel {
|
||||
|
||||
/**
|
||||
* The type of the voxel
|
||||
*/
|
||||
@Export
|
||||
public int type;
|
||||
|
||||
/**
|
||||
* The weight of the voxel
|
||||
*/
|
||||
@Export
|
||||
public float weight;
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package electrosphere.server.terrain.generation.interfaces;
|
||||
|
||||
import electrosphere.game.data.biome.BiomeData;
|
||||
|
||||
/**
|
||||
* A script-defined chunk generator
|
||||
*/
|
||||
public interface JSChunkGenerator {
|
||||
|
||||
/**
|
||||
* Gets the tag for this generator
|
||||
* @returns The tag
|
||||
*/
|
||||
public String getTag();
|
||||
|
||||
/**
|
||||
* Retrieves the elevation for the world at a given x,z coordinate
|
||||
* @param worldX The world x coordinate
|
||||
* @param worldZ The world z coordinate
|
||||
* @param chunkX The x coordinate of the chunk within the specified world coordinate
|
||||
* @param chunkZ The z coordinate of the chunk within the specified world coordinate
|
||||
* @returns The elevation at that specific position
|
||||
*/
|
||||
public float getElevation(int worldX, int worldZ, int chunkX, int chunkZ);
|
||||
|
||||
/**
|
||||
* The function to get a voxel for a given position
|
||||
*/
|
||||
public GeneratedVoxel getVoxel(
|
||||
int worldX, int worldY, int worldZ,
|
||||
int chunkX, int chunkY, int chunkZ,
|
||||
int stride,
|
||||
double surfaceHeight,
|
||||
BiomeData surfaceBiome
|
||||
);
|
||||
|
||||
}
|
||||
@ -84,6 +84,9 @@ public class ServerChunkCache {
|
||||
lock.acquireUninterruptibly();
|
||||
cacheMapFullRes.clear();
|
||||
cacheMapHalfRes.clear();
|
||||
cacheMapQuarterRes.clear();
|
||||
cacheMapEighthRes.clear();
|
||||
cacheMapSixteenthRes.clear();
|
||||
lock.release();
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package electrosphere.server.terrain.generation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import electrosphere.test.annotations.FastTest;
|
||||
import electrosphere.test.annotations.UnitTest;
|
||||
|
||||
/**
|
||||
* Unit tests for the test generation chunk generator
|
||||
*/
|
||||
public class TestGenerationChunkGeneratorTests {
|
||||
|
||||
@UnitTest
|
||||
@FastTest
|
||||
public void getSurfaceWeight_ValueTests(){
|
||||
assertEquals(0.5,TestGenerationChunkGenerator.getSurfaceWeight(0.5, 0, 1));
|
||||
assertEquals(0.1,TestGenerationChunkGenerator.getSurfaceWeight(0.1, 0, 1));
|
||||
assertEquals(0.9,TestGenerationChunkGenerator.getSurfaceWeight(0.9, 0, 1));
|
||||
assertEquals(0.95,TestGenerationChunkGenerator.getSurfaceWeight(1.9, 0, 2));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user