terrain work

This commit is contained in:
austin 2025-05-04 18:14:26 -04:00
parent c9ac7496a5
commit c9ce7264fa
11 changed files with 198 additions and 28 deletions

View File

@ -23,5 +23,6 @@
+ bug fixes
- Window does not play nice with its minWidth/minHeight being set differently
- When inventory utils is used to create an item with charges in the inventory, the ui does not reflect the corrent number of charges (maybe not setting the value correctly??)
+ unreproducible bugs

View File

@ -1671,6 +1671,11 @@ ClientSynchronizationManager un-deleted entity IDs when client receives creation
MoveTo tree doesn't overwrite published status
Fix AIManager.shutdown call not null checking
Small explore node height offset
Terrain items
Digging produces terrain item form
Terrain items can be placed to place terrain

View File

@ -129,6 +129,7 @@ public class Config {
//create procedural item types
ItemDataMap.loadSpawnItems(config.itemMap, config.objectTypeLoader);
ItemDataMap.generateBlockItems(config.itemMap, config.blockData);
ItemDataMap.generateVoxelItems(config.itemMap, config.voxelData);
//construct the sourcing map
config.itemSourcingMap = ItemSourcingMap.parse(config);

View File

@ -16,6 +16,7 @@ import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.common.item.SpawnItemDescription;
import electrosphere.game.data.graphics.GraphicsTemplate;
import electrosphere.game.data.graphics.NonproceduralModel;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.RenderUtils;
@ -190,6 +191,73 @@ public class Item extends CommonEntityType {
return rVal;
}
/**
* Gets the id of the item type for a given voxel type
* @param voxelType The voxel type
* @return The id of the corresponding item data
*/
public static String getVoxelTypeId(VoxelType voxelType){
return "vox:" + voxelType.getName();
}
/**
* Creates item data from a voxel type
* @param description The voxel type
* @return The item data
*/
public static Item createVoxelItem(VoxelType voxelType){
Item rVal = new Item();
rVal.setId(Item.getVoxelTypeId(voxelType));
if(voxelType.getTexture() != null){
rVal.iconPath = voxelType.getTexture();
} else {
rVal.iconPath = Item.DEFAULT_ITEM_ICON_PATH;
}
NonproceduralModel modelData = new NonproceduralModel();
modelData.setPath(AssetDataStrings.MODEL_BLOCK_SINGLE);
GraphicsTemplate blockItemGraphicsTemplate = new GraphicsTemplate();
blockItemGraphicsTemplate.setModel(modelData);
rVal.setGraphicsTemplate(blockItemGraphicsTemplate);
//set uniforms for the model
//TODO: texture work for single voxel
// Map<String,Map<String,Object>> meshUniformMap = new HashMap<String,Map<String,Object>>();
// Map<String,Object> uniforms = new HashMap<String,Object>();
// if(Globals.voxelTextureAtlas.getVoxelTypeOffset(voxelType.getId()) == BlockTextureAtlas.MISSING && voxelType.getId() != BlockChunkData.BLOCK_TYPE_EMPTY){
// LoggerInterface.loggerEngine.WARNING("Block type " + voxelType.getId() + " missing in BlockTextureAtlas");
// }
// uniforms.put("blockAtlasIndex",Globals.voxelTextureAtlas.getVoxelTypeOffset(voxelType.getId()));
// meshUniformMap.put(RenderUtils.MESH_NAME_BLOCK_SINGLE,uniforms);
// modelData.setUniforms(meshUniformMap);
//set item class
rVal.equipData = new EquipData();
rVal.equipData.equipClass = "tool";
//set usage
ItemUsage usage = new ItemUsage();
usage.setVoxelId(voxelType.getId());
usage.setOnlyOnMouseDown(true);
rVal.setSecondaryUsage(usage);
rVal.setPrimaryUsage(usage);
//set stacking data
rVal.setMaxStack(MAX_BLOCK_STACK);
//attach common tokens
List<String> tokens = new LinkedList<String>(Arrays.asList(DEFAULT_TOKENS));
tokens.add(CursorState.CURSOR_TOKEN);
rVal.setTokens(tokens);
return rVal;
}
/**
* the idle animation for the item
* @return

View File

@ -13,6 +13,8 @@ import electrosphere.game.data.block.BlockData;
import electrosphere.game.data.block.BlockType;
import electrosphere.game.data.common.CommonEntityMap;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.voxel.VoxelData;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.util.FileUtils;
/**
@ -145,4 +147,17 @@ public class ItemDataMap {
}
}
/**
* Loads all voxel types as items
* @param itemDataMap The item data map
* @param voxelData The data on all voxel types
*/
public static void generateVoxelItems(ItemDataMap itemDataMap, VoxelData voxelData){
for(VoxelType voxelType : voxelData.getTypes()){
Item spawnItem = Item.createVoxelItem(voxelType);
//create spawn items
itemDataMap.putType(spawnItem.getId(), spawnItem);
}
}
}

View File

@ -15,6 +15,11 @@ public class ItemUsage {
*/
Integer blockId;
/**
* If defined, this item will place the voxel type on use
*/
Integer voxelId;
/**
* The hook to fire on the client when this item is used
*/
@ -109,6 +114,22 @@ public class ItemUsage {
public void setSuppressServerRequest(Boolean suppressServerRequest) {
this.suppressServerRequest = suppressServerRequest;
}
/**
* Gets the voxel id
* @return The voxel id
*/
public Integer getVoxelId() {
return voxelId;
}
/**
* Sets the voxel id
* @param voxelId The voxel id
*/
public void setVoxelId(Integer voxelId) {
this.voxelId = voxelId;
}

View File

@ -460,7 +460,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId());
Realm realm = Globals.realmManager.getPlayerRealm(player);
Vector3d location = new Vector3d(message.getrealLocationX(), message.getrealLocationY(), message.getrealLocationZ());
TerrainEditing.destroyTerrain(realm, location, message.getvalue(), message.getterrainWeight());
TerrainEditing.destroyTerrain(realm, player.getPlayerEntity(), location, message.getvalue(), message.getterrainWeight());
}
/**

View File

@ -1,12 +1,13 @@
package electrosphere.renderer.ui;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
import org.joml.Vector2i;
@ -32,7 +33,7 @@ import electrosphere.renderer.ui.events.DragEvent.DragEventType;
import electrosphere.renderer.ui.events.NavigationEvent.NavigationEventType;
/**
* The main interface for working with the ui system
* The main interface for working with the ui systemd
*/
public class ElementService extends SignalServiceImpl {
@ -51,8 +52,8 @@ public class ElementService extends SignalServiceImpl {
);
}
Map<String,Element> elementMap = new HashMap<String,Element>();
List<Element> elementList = new LinkedList<Element>();
Map<String,Element> elementMap = new ConcurrentHashMap<String,Element>();
List<Element> elementList = new CopyOnWriteArrayList<Element>();
FocusableElement currentFocusedElement = null;
DraggableElement currentDragElement = null;

View File

@ -365,11 +365,11 @@ public class ServerWorldData {
* @param position The real coordinate
* @return The world space coordinate
*/
public Vector3i convertRealToWorldSpace(Vector3d position){
public static Vector3i convertRealToWorldSpace(Vector3d position){
return new Vector3i(
convertRealToChunkSpace(position.x),
convertRealToChunkSpace(position.y),
convertRealToChunkSpace(position.z)
ServerWorldData.convertRealToChunkSpace(position.x),
ServerWorldData.convertRealToChunkSpace(position.y),
ServerWorldData.convertRealToChunkSpace(position.z)
);
}
@ -380,9 +380,9 @@ public class ServerWorldData {
*/
public static Vector3i convertRealToLocalBlockSpace(Vector3d position){
return new Vector3i(
convertRealToLocalBlockSpace(position.x),
convertRealToLocalBlockSpace(position.y),
convertRealToLocalBlockSpace(position.z)
ServerWorldData.convertRealToLocalBlockSpace(position.x),
ServerWorldData.convertRealToLocalBlockSpace(position.y),
ServerWorldData.convertRealToLocalBlockSpace(position.z)
);
}

View File

@ -1,9 +1,19 @@
package electrosphere.server.physics.terrain.editing;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.game.data.item.Item;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerWorldData;
import electrosphere.server.datacell.interfaces.VoxelCellManager;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
@ -16,6 +26,16 @@ public class TerrainEditing {
* the minimum value before hard setting to 0
*/
static final float MINIMUM_FULL_VALUE = 0.01f;
/**
* The default magnitude
*/
public static final float DEFAULT_MAGNITUDE = 1.1f;
/**
* The default weight
*/
public static final float DEFAULT_WEIGHT = 1.0f;
/**
* Performs a terrain chunk edit. Basically has a sphere around the provided position that it attempts to add value to.
@ -47,8 +67,8 @@ public class TerrainEditing {
for(i = 0; i < numPlacesToCheck; i++){
//calculate position of edit
Vector3d offsetPos = new Vector3d(position).add(xOffsetSet[i],yOffsetSet[i],zOffsetSet[i]);
Vector3i chunkPos = realm.getServerWorldData().convertRealToWorldSpace(offsetPos);
Vector3i voxelPos = realm.getServerWorldData().convertRealToVoxelSpace(offsetPos);
Vector3i chunkPos = ServerWorldData.convertRealToWorldSpace(offsetPos);
Vector3i voxelPos = ServerWorldData.convertRealToVoxelSpace(offsetPos);
//get distance from true center point of sphere to current voxel position in world space
float distance = (float)new Vector3d(Math.floor(offsetPos.x),Math.floor(offsetPos.y),Math.floor(offsetPos.z)).distance(position);
float currentPositionMagnitude = editMagnitude - distance;
@ -61,6 +81,9 @@ public class TerrainEditing {
voxelPos.x >= 0 &&
voxelPos.y >= 0 &&
voxelPos.z >= 0 &&
chunkPos.x >= 0 &&
chunkPos.y >= 0 &&
chunkPos.z >= 0 &&
currentPositionMagnitude > 0 &&
(data = voxelCellManager.getChunkAtPosition(chunkPos)) != null
){
@ -78,11 +101,13 @@ public class TerrainEditing {
/**
* Performs a terrain chunk edit. Basically has a sphere around the provided position that it attempts to remove value from.
* @param realm The realm to destroy terrain within
* @param sourceEntity The entity performing the destruction
* @param position The position to perform the edit
* @param editMagnitude The magnitude of the edit to perform
* @param weight The weight of the sphere to apply the edit to
*/
public static void destroyTerrain(Realm realm, Vector3d position, float editMagnitude, float weight){
public static void destroyTerrain(Realm realm, Entity sourceEntity, Vector3d position, float editMagnitude, float weight){
if(position != null && realm != null && realm.getDataCellManager() instanceof VoxelCellManager){
VoxelCellManager voxelCellManager = (VoxelCellManager) realm.getDataCellManager();
//calculate kernel size
@ -102,11 +127,12 @@ public class TerrainEditing {
}
}
}
Map<Integer,Float> typeAmountMap = new HashMap<Integer,Float>();
for(i = 0; i < numPlacesToCheck; i++){
//calculate position of edit
Vector3d offsetPos = new Vector3d(position).add(xOffsetSet[i],yOffsetSet[i],zOffsetSet[i]);
Vector3i chunkPos = realm.getServerWorldData().convertRealToWorldSpace(offsetPos);
Vector3i voxelPos = realm.getServerWorldData().convertRealToVoxelSpace(offsetPos);
Vector3i chunkPos = ServerWorldData.convertRealToWorldSpace(offsetPos);
Vector3i voxelPos = ServerWorldData.convertRealToVoxelSpace(offsetPos);
//get distance from true center point of sphere to current voxel position in world space
float distance = (float)new Vector3d(Math.floor(offsetPos.x),Math.floor(offsetPos.y),Math.floor(offsetPos.z)).distance(position);
float currentPositionMagnitude = editMagnitude - distance;
@ -122,17 +148,37 @@ public class TerrainEditing {
currentPositionMagnitude > 0 &&
(data = voxelCellManager.getChunkAtPosition(chunkPos)) != null
){
int typeAtPos = voxelCellManager.getVoxelTypeAtLocalPosition(chunkPos, voxelPos);
int originalTypeAtPos = voxelCellManager.getVoxelTypeAtLocalPosition(chunkPos, voxelPos);
float current = data.getWeights()[voxelPos.x][voxelPos.y][voxelPos.z];
//hard clamp so it doesn't go over 1
float finalValue = Math.max(Math.min(current + weight / distance,1),-1);
int finalType = originalTypeAtPos;
if(finalValue < MINIMUM_FULL_VALUE && current >= MINIMUM_FULL_VALUE){
finalValue = -1;
typeAtPos = ServerTerrainChunk.VOXEL_TYPE_AIR;
finalType = ServerTerrainChunk.VOXEL_TYPE_AIR;
}
voxelCellManager.editChunk(chunkPos, voxelPos, finalValue, finalType);
//keep track of how much we're editing each chunk so we can add items to inventories
if(typeAmountMap.containsKey(originalTypeAtPos)){
typeAmountMap.put(originalTypeAtPos,typeAmountMap.get(originalTypeAtPos) + (finalValue - current));
} else {
typeAmountMap.put(originalTypeAtPos,(finalValue - current));
}
voxelCellManager.editChunk(chunkPos, voxelPos, finalValue, typeAtPos);
}
}
if(sourceEntity == null){
throw new Error("Does not support destroying terrain without a source entity");
}
for(Entry<Integer,Float> typeEntry : typeAmountMap.entrySet()){
VoxelType voxelType = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(typeEntry.getKey());
Item voxelItem = Globals.gameConfigCurrent.getItemMap().getItem(Item.getVoxelTypeId(voxelType));
int count = -(int)(float)typeEntry.getValue();
InventoryUtils.serverCreateInventoryItem(sourceEntity, voxelItem.getId(), count);
}
}
}

View File

@ -9,6 +9,7 @@ import electrosphere.entity.Entity;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.entity.state.equip.ServerToolbarState;
import electrosphere.entity.state.furniture.ServerDoorState;
import electrosphere.entity.state.item.ServerChargeState;
import electrosphere.entity.state.life.ServerLifeTree;
import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
@ -24,6 +25,7 @@ import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerWorldData;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.physics.block.editing.ServerBlockEditing;
import electrosphere.server.physics.terrain.editing.TerrainEditing;
import electrosphere.server.utils.ServerScriptUtils;
/**
@ -43,7 +45,9 @@ public class PlayerActions {
int itemActionCodeState = message.getitemActionCodeState();
ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(playerEntity);
if(serverToolbarState != null && serverToolbarState.getRealWorldItem() != null){
Item item = Globals.gameConfigCurrent.getItemMap().getItem(serverToolbarState.getRealWorldItem());
Entity realWorldItemEnt = serverToolbarState.getRealWorldItem();
Entity inventoryItemEnt = serverToolbarState.getInInventoryItem();
Item item = Globals.gameConfigCurrent.getItemMap().getItem(realWorldItemEnt);
CreatureData creatureData = Globals.gameConfigCurrent.getCreatureTypeLoader().getType(CreatureUtils.getType(playerEntity));
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(playerEntity);
@ -66,7 +70,7 @@ public class PlayerActions {
item.getSecondaryUsage().getOnlyOnMouseDown() == null ||
(itemActionCodeState == ItemActions.ITEM_ACTION_CODE_STATE_ON && item.getSecondaryUsage().getOnlyOnMouseDown())
){
PlayerActions.secondaryUsage(playerEntity, item, message);
PlayerActions.secondaryUsage(playerEntity, inventoryItemEnt, item, message);
}
}
}
@ -90,10 +94,11 @@ public class PlayerActions {
/**
* Performs various secondary usages
* @param playerEntity The player's entity
* @param itemEnt The item entity
* @param item The item data
* @param message The message
*/
private static void secondaryUsage(Entity playerEntity, Item item, InventoryMessage message){
private static void secondaryUsage(Entity playerEntity, Entity itemEnt, Item item, InventoryMessage message){
ItemUsage secondaryUsage = item.getSecondaryUsage();
Realm playerRealm = Globals.realmManager.getEntityRealm(playerEntity);
@ -103,20 +108,27 @@ public class PlayerActions {
CommonEntityUtils.serverSpawnBasicObject(playerRealm, spawnPos, secondaryUsage.getSpawnEntityId());
}
//voxel block editing
//block editing
if(secondaryUsage.getBlockId() != null){
ServerWorldData serverWorldData = playerRealm.getServerWorldData();
//clamp the placement pos to the block grid..
Vector3d placementPos = new Vector3d(message.getviewTargetX(),message.getviewTargetY(),message.getviewTargetZ());
Vector3i worldPos = serverWorldData.convertRealToWorldSpace(placementPos);
Vector3i blockPos = serverWorldData.convertRealToLocalBlockSpace(placementPos);
Vector3i worldPos = ServerWorldData.convertRealToWorldSpace(placementPos);
Vector3i blockPos = ServerWorldData.convertRealToLocalBlockSpace(placementPos);
//actually edit
ServerBlockEditing.editBlockChunk(playerRealm, worldPos, blockPos, (short)(int)secondaryUsage.getBlockId(), (short)0);
LoggerInterface.loggerEngine.DEBUG("Place block type " + secondaryUsage.getBlockId() + " at " + placementPos + " -> " + worldPos + " " + blockPos);
}
//voxel editing
if(secondaryUsage.getVoxelId() != null){
Vector3d placementPos = new Vector3d(message.getviewTargetX(),message.getviewTargetY(),message.getviewTargetZ());
//actually edit
TerrainEditing.editTerrain(playerRealm, placementPos, TerrainEditing.DEFAULT_MAGNITUDE, secondaryUsage.getVoxelId(), TerrainEditing.DEFAULT_WEIGHT);
ServerChargeState.getServerChargeState(itemEnt).attemptAddCharges(-1);
LoggerInterface.loggerEngine.DEBUG("Place voxel type " + secondaryUsage.getVoxelId() + " at " + placementPos);
}
}