transparent block support
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-19 19:40:15 -04:00
parent ef0adbe3e6
commit a375e1a686
10 changed files with 197 additions and 43 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 144 KiB

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Mon May 19 14:18:34 EDT 2025 #Mon May 19 19:19:06 EDT 2025
buildNumber=626 buildNumber=627

View File

@ -1906,6 +1906,7 @@ Major lighting shader organization rework
Material albedo work Material albedo work
Editor entities don't use charges on placing blocks Editor entities don't use charges on placing blocks
Overhaul material loading - uses queued textures now Overhaul material loading - uses queued textures now
Transparent block support

View File

@ -1,5 +1,8 @@
package electrosphere.client.block.cells; package electrosphere.client.block.cells;
import java.util.LinkedList;
import java.util.List;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3i; import org.joml.Vector3i;
@ -38,7 +41,7 @@ public class BlockDrawCell {
int lod; int lod;
//the main entity for the cell //the main entity for the cell
Entity modelEntity; List<Entity> modelEntities = new LinkedList<Entity>();
/** /**
* The data for generating the visuals * The data for generating the visuals
@ -132,9 +135,11 @@ public class BlockDrawCell {
} }
this.chunkData = currentChunk; this.chunkData = currentChunk;
} }
Entity toDelete = this.modelEntity; List<Entity> toDelete = this.modelEntities;
modelEntity = BlockChunkEntity.clientCreateBlockChunkEntity(chunkData, notifyTarget, toDelete, lod, atlas, this.hasPolygons()); this.modelEntities = BlockChunkEntity.clientCreateBlockChunkEntity(chunkData, notifyTarget, toDelete, lod, atlas, this.hasPolygons());
ClientEntityUtils.initiallyPositionEntity(modelEntity, this.getRealPos(), new Quaterniond()); for(Entity ent : modelEntities){
ClientEntityUtils.initiallyPositionEntity(ent, this.getRealPos(), new Quaterniond());
}
this.setHasGenerated(true); this.setHasGenerated(true);
} }
@ -180,27 +185,29 @@ public class BlockDrawCell {
* Destroys a drawcell including its physics * Destroys a drawcell including its physics
*/ */
public void destroy(){ public void destroy(){
if(modelEntity != null){ if(this.modelEntities != null){
Globals.clientState.clientScene.registerBehaviorTree(new BehaviorTree(){ for(Entity ent : this.modelEntities){
int framesSimulated = 0; Globals.clientState.clientScene.registerBehaviorTree(new BehaviorTree(){
public void simulate(float deltaTime) { int framesSimulated = 0;
if(framesSimulated < FRAMES_TO_WAIT_BEFORE_DESTRUCTION){ public void simulate(float deltaTime) {
framesSimulated++; if(framesSimulated < FRAMES_TO_WAIT_BEFORE_DESTRUCTION){
} else { framesSimulated++;
ClientEntityUtils.destroyEntity(modelEntity); } else {
Globals.clientState.clientScene.deregisterBehaviorTree(this); ClientEntityUtils.destroyEntity(ent);
Globals.clientState.clientScene.deregisterBehaviorTree(this);
}
} }
} });
}); }
} }
} }
/** /**
* Gets the entity for the cell * Gets the list of entities for the cell
* @return The entity if it exists, null otherwise * @return The list of entities if it exists, null otherwise
*/ */
public Entity getEntity(){ public List<Entity> getEntities(){
return modelEntity; return this.modelEntities;
} }
/** /**

View File

@ -616,7 +616,8 @@ public class ClientBlockCellManager {
public boolean shouldDestroy(WorldOctTreeNode<BlockDrawCell> node){ public boolean shouldDestroy(WorldOctTreeNode<BlockDrawCell> node){
return return
node.getData() != null && node.getData() != null &&
node.getData().getEntity() != null node.getData().getEntities() != null &&
node.getData().getEntities().size() > 0
; ;
} }
@ -849,8 +850,8 @@ public class ClientBlockCellManager {
*/ */
public boolean hasGeneratedPhysics(int worldX, int worldY, int worldZ){ public boolean hasGeneratedPhysics(int worldX, int worldY, int worldZ){
BlockDrawCell cell = this.getDrawCell(worldX, worldY, worldZ); BlockDrawCell cell = this.getDrawCell(worldX, worldY, worldZ);
if(cell != null && cell.getEntity() != null){ if(cell != null && cell.getEntities() != null && cell.getEntities().size() > 0){
return PhysicsEntityUtils.containsDBody(cell.getEntity()); return PhysicsEntityUtils.containsDBody(cell.getEntities().get(0));
} }
return false; return false;
} }

View File

@ -1,6 +1,7 @@
package electrosphere.controls.cursor; package electrosphere.controls.cursor;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Vector3d; import org.joml.Vector3d;
@ -477,8 +478,9 @@ public class CursorState {
* @param fab The fab * @param fab The fab
*/ */
public void setSelectedFab(BlockFab fab){ public void setSelectedFab(BlockFab fab){
Map<Integer,Boolean> solidsMap = Globals.gameConfigCurrent.getBlockData().getSolidsMap();
QueuedModel queuedModel = new QueuedModel(() -> { QueuedModel queuedModel = new QueuedModel(() -> {
return BlockMeshgen.generateBlockModel(BlockMeshgen.rasterize(fab)); return BlockMeshgen.generateBlockModel(BlockMeshgen.rasterize(fab,false,solidsMap));
}); });
Globals.assetManager.queuedAsset(queuedModel); Globals.assetManager.queuedAsset(queuedModel);
EntityCreationUtils.makeEntityDrawablePreexistingModel(playerFabCursor, queuedModel.getPromisedPath()); EntityCreationUtils.makeEntityDrawablePreexistingModel(playerFabCursor, queuedModel.getPromisedPath());

View File

@ -135,6 +135,7 @@ public class Config {
config.raceMap = FileUtils.loadObjectFromAssetPath("Data/game/races.json", RaceMap.class); config.raceMap = FileUtils.loadObjectFromAssetPath("Data/game/races.json", RaceMap.class);
config.voxelData = FileUtils.loadObjectFromAssetPath("Data/game/voxelTypes.json", VoxelData.class); config.voxelData = FileUtils.loadObjectFromAssetPath("Data/game/voxelTypes.json", VoxelData.class);
config.blockData = FileUtils.loadObjectFromAssetPath("Data/game/blockTypes.json", BlockData.class); config.blockData = FileUtils.loadObjectFromAssetPath("Data/game/blockTypes.json", BlockData.class);
config.blockData.constructSolidsMap();
config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/entity/projectile.json", ProjectileTypeHolder.class); config.projectileTypeHolder = FileUtils.loadObjectFromAssetPath("Data/entity/projectile.json", ProjectileTypeHolder.class);
config.hintData = FileUtils.loadObjectFromAssetPath("Data/tutorial/hints.json", HintDefinition.class); config.hintData = FileUtils.loadObjectFromAssetPath("Data/tutorial/hints.json", HintDefinition.class);
config.surfaceAudioCollection = FileUtils.loadObjectFromAssetPath("Data/audio/surface.json", SurfaceAudioCollection.class); config.surfaceAudioCollection = FileUtils.loadObjectFromAssetPath("Data/audio/surface.json", SurfaceAudioCollection.class);

View File

@ -1,14 +1,24 @@
package electrosphere.data.block; package electrosphere.data.block;
import java.util.HashMap;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* A list of all block types in game * A list of all block types in game
*/ */
public class BlockData { public class BlockData {
//The set of all voxel types
/**
* The set of all voxel types
*/
Set<BlockType> types; Set<BlockType> types;
/**
* The map of block id -> boolean that stores whether the block should cutout transparent or blended transparent
*/
Map<Integer,Boolean> solidsMap = new HashMap<Integer,Boolean>();
/** /**
* Gets all block types * Gets all block types
* @return The set of all block types * @return The set of all block types
@ -44,4 +54,27 @@ public class BlockData {
} }
return null; return null;
} }
/**
* Constructs the solids map
*/
public void constructSolidsMap(){
for(BlockType type : this.types){
if(type.transparent != null && type.isTransparent()){
solidsMap.put(type.getId(), false);
} else {
solidsMap.put(type.getId(), true);
}
}
}
/**
* Gets the solids map
* @return The solids map
*/
public Map<Integer,Boolean> getSolidsMap(){
return this.solidsMap;
}
} }

View File

@ -1,5 +1,8 @@
package electrosphere.entity.types.terrain; package electrosphere.entity.types.terrain;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -16,6 +19,7 @@ import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.queue.QueuedModel; import electrosphere.engine.assetmanager.queue.QueuedModel;
import electrosphere.engine.threads.ThreadCounts; import electrosphere.engine.threads.ThreadCounts;
import electrosphere.entity.ClientEntityUtils; import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.DrawableUtils;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
@ -47,36 +51,40 @@ public class BlockChunkEntity {
* @param hasPolygons true if the chunk has polygons to generate a model with, false otherwise * @param hasPolygons true if the chunk has polygons to generate a model with, false otherwise
* @return The block chunk entity * @return The block chunk entity
*/ */
public static Entity clientCreateBlockChunkEntity( public static List<Entity> clientCreateBlockChunkEntity(
BlockChunkData chunkData, BlockChunkData chunkData,
BlockDrawCell notifyTarget, BlockDrawCell notifyTarget,
Entity toDelete, List<Entity> toDelete,
int levelOfDetail, int levelOfDetail,
BlockTextureAtlas atlas, BlockTextureAtlas atlas,
boolean hasPolygons boolean hasPolygons
){ ){
Globals.profiler.beginAggregateCpuSample("BlockChunk.clientCreateBlockChunkEntity"); Globals.profiler.beginAggregateCpuSample("BlockChunk.clientCreateBlockChunkEntity");
List<Entity> rVal = new LinkedList<Entity>();
Entity rVal = EntityCreationUtils.createClientSpatialEntity();
Map<Integer,Boolean> solidsMap = Globals.gameConfigCurrent.getBlockData().getSolidsMap();
//
//Create the entity for rendering solid blocks
Entity solidsEnt = EntityCreationUtils.createClientSpatialEntity();
if(hasPolygons && chunkData.getType() != null && chunkData.getMetadata() != null){ if(hasPolygons && chunkData.getType() != null && chunkData.getMetadata() != null){
generationService.submit(() -> { generationService.submit(() -> {
BlockMeshData data; BlockMeshData data;
try { try {
data = BlockMeshgen.rasterize(chunkData); data = BlockMeshgen.rasterize(chunkData, true, solidsMap);
if(Globals.clientState.clientScene.containsEntity(rVal)){ if(Globals.clientState.clientScene.containsEntity(solidsEnt)){
String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> { String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> {
return BlockMeshgen.generateBlockModel(data); return BlockMeshgen.generateBlockModel(data);
})); }));
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath); EntityCreationUtils.makeEntityDrawablePreexistingModel(solidsEnt, modelPath);
if(levelOfDetail == BlockChunkData.LOD_FULL_RES){ if(levelOfDetail == BlockChunkData.LOD_FULL_RES){
PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(rVal, data); PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(solidsEnt, data);
CollisionObjUtils.clientPositionCharacter(rVal, new Vector3d(EntityUtils.getPosition(rVal)), new Quaterniond()); CollisionObjUtils.clientPositionCharacter(solidsEnt, new Vector3d(EntityUtils.getPosition(solidsEnt)), new Quaterniond());
} else { } else {
EntityCreationUtils.bypassShadowPass(rVal); EntityCreationUtils.bypassShadowPass(solidsEnt);
EntityCreationUtils.bypassVolumetics(rVal); EntityCreationUtils.bypassVolumetics(solidsEnt);
} }
rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true); solidsEnt.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true);
} else { } else {
LoggerInterface.loggerEngine.DEBUG("Finished generating block polygons; however, entity has already been deleted."); LoggerInterface.loggerEngine.DEBUG("Finished generating block polygons; however, entity has already been deleted.");
} }
@ -84,7 +92,9 @@ public class BlockChunkEntity {
notifyTarget.alertToGeneration(); notifyTarget.alertToGeneration();
} }
if(toDelete != null){ if(toDelete != null){
ClientEntityUtils.destroyEntity(toDelete); for(Entity target : toDelete){
ClientEntityUtils.destroyEntity(target);
}
} }
} catch (Error e){ } catch (Error e){
LoggerInterface.loggerEngine.ERROR(e); LoggerInterface.loggerEngine.ERROR(e);
@ -97,11 +107,68 @@ public class BlockChunkEntity {
notifyTarget.alertToGeneration(); notifyTarget.alertToGeneration();
} }
if(toDelete != null){ if(toDelete != null){
ClientEntityUtils.destroyEntity(toDelete); for(Entity target : toDelete){
ClientEntityUtils.destroyEntity(target);
}
} }
} }
solidsEnt.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
rVal.add(solidsEnt);
//
//Create the entity for rendering solid blocks
Entity transparentEnt = EntityCreationUtils.createClientSpatialEntity();
if(hasPolygons && chunkData.getType() != null && chunkData.getMetadata() != null){
generationService.submit(() -> {
BlockMeshData data;
try {
data = BlockMeshgen.rasterize(chunkData, false, solidsMap);
if(Globals.clientState.clientScene.containsEntity(transparentEnt)){
String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> {
return BlockMeshgen.generateBlockModel(data);
}));
EntityCreationUtils.makeEntityDrawablePreexistingModel(transparentEnt, modelPath);
if(levelOfDetail == BlockChunkData.LOD_FULL_RES){
PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(transparentEnt, data);
CollisionObjUtils.clientPositionCharacter(transparentEnt, new Vector3d(EntityUtils.getPosition(transparentEnt)), new Quaterniond());
} else {
EntityCreationUtils.bypassShadowPass(transparentEnt);
EntityCreationUtils.bypassVolumetics(transparentEnt);
}
transparentEnt.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true);
DrawableUtils.makeEntityTransparent(transparentEnt);
} else {
LoggerInterface.loggerEngine.DEBUG("Finished generating block polygons; however, entity has already been deleted.");
}
if(notifyTarget != null){
notifyTarget.alertToGeneration();
}
if(toDelete != null){
for(Entity target : toDelete){
ClientEntityUtils.destroyEntity(target);
}
}
} catch (Error e){
LoggerInterface.loggerEngine.ERROR(e);
} catch(Exception e){
LoggerInterface.loggerEngine.ERROR(e);
}
});
} else {
if(notifyTarget != null){
notifyTarget.alertToGeneration();
}
if(toDelete != null){
for(Entity target : toDelete){
ClientEntityUtils.destroyEntity(target);
}
}
}
transparentEnt.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
rVal.add(transparentEnt);
rVal.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
return rVal; return rVal;
} }

View File

@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import org.joml.Vector2f; import org.joml.Vector2f;
import org.joml.Vector3f; import org.joml.Vector3f;
@ -66,18 +67,48 @@ public class BlockMeshgen {
static final int SAMPLER_DATA_SIZE = 1; static final int SAMPLER_DATA_SIZE = 1;
/**
* Checks whether this block should be rasterized or not
* @param data The data
* @param x The x coordinate
* @param y The y coordinate
* @param z The z coordinate
* @param solids true to rasterize solids, false to rasterize transparents
* @param solidsMap The map of block type to solid status
* @return true if it should be rasterized, false otherwise
*/
protected static boolean shouldRasterize(BlockMeshgenData data, int x, int y, int z, boolean solids, Map<Integer,Boolean> solidsMap){
if(data.isEmpty(x, y, z)){
return false;
}
if(solidsMap == null){
return true;
}
return solids == solidsMap.get((int)data.getType(x, y, z));
}
/** /**
* Calculates the quad meshes for the provided data * Calculates the quad meshes for the provided data
* @param quadMeshes The quad mesh list to fill * @param quadMeshes The quad mesh list to fill
* @param data The block data * @param data The block data
*/ */
protected static void fillQuadMeshes(List<QuadMesh> quadMeshes, BlockMeshgenData data){ protected static void fillQuadMeshes(List<QuadMesh> quadMeshes, BlockMeshgenData data){
BlockMeshgen.fillQuadMeshes(quadMeshes, data, true, null);
}
/**
* Calculates the quad meshes for the provided data
* @param quadMeshes The quad mesh list to fill
* @param data The block data
*/
protected static void fillQuadMeshes(List<QuadMesh> quadMeshes, BlockMeshgenData data, boolean solids, Map<Integer,Boolean> solidsMap){
Vector3i dimensions = data.getDimensions(); Vector3i dimensions = data.getDimensions();
for(int z = 0; z < dimensions.z; z++){ for(int z = 0; z < dimensions.z; z++){
for(int x = 0; x < dimensions.x; x++){ for(int x = 0; x < dimensions.x; x++){
QuadMesh currentQuad = null; QuadMesh currentQuad = null;
for(int y = 0; y < dimensions.y; y++){ for(int y = 0; y < dimensions.y; y++){
if(data.isEmpty(x, y, z)){ if(!BlockMeshgen.shouldRasterize(data,x,y,z,solids,solidsMap)){
if(currentQuad == null){ if(currentQuad == null){
continue; continue;
} else { } else {
@ -361,14 +392,16 @@ public class BlockMeshgen {
/** /**
* Rasterizes a block chunk data into mesh data * Rasterizes a block chunk data into mesh data
* @param chunkData The block chunk data * @param chunkData The block chunk data
* @param solids true to rasterize solid blocks, false to rasterize transparent blocks
* @param solidsMap The solids map
* @return The mesh data * @return The mesh data
*/ */
public static BlockMeshData rasterize(BlockMeshgenData chunkData){ public static BlockMeshData rasterize(BlockMeshgenData chunkData, boolean solids, Map<Integer,Boolean> solidsMap){
BlockMeshData rVal = new BlockMeshData(); BlockMeshData rVal = new BlockMeshData();
//calculate quad meshes //calculate quad meshes
List<QuadMesh> quadMeshes = new LinkedList<QuadMesh>(); List<QuadMesh> quadMeshes = new LinkedList<QuadMesh>();
BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData); BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData, solids, solidsMap);
//allocate lists to store mesh data in //allocate lists to store mesh data in
List<Vector3f> verts = new LinkedList<Vector3f>(); List<Vector3f> verts = new LinkedList<Vector3f>();
@ -470,6 +503,15 @@ public class BlockMeshgen {
return rVal; return rVal;
} }
/**
* Rasterizes a block chunk data into mesh data
* @param chunkData The block chunk data
* @return The mesh data
*/
public static BlockMeshData rasterize(BlockMeshgenData chunkData){
return BlockMeshgen.rasterize(chunkData, true, null);
}
/** /**
* Copies vertex and index data from the combined array into a single shape * Copies vertex and index data from the combined array into a single shape
* @param verts The list of vertices * @param verts The list of vertices