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
#Mon May 19 14:18:34 EDT 2025
buildNumber=626
#Mon May 19 19:19:06 EDT 2025
buildNumber=627

View File

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

View File

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

View File

@ -616,7 +616,8 @@ public class ClientBlockCellManager {
public boolean shouldDestroy(WorldOctTreeNode<BlockDrawCell> node){
return
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){
BlockDrawCell cell = this.getDrawCell(worldX, worldY, worldZ);
if(cell != null && cell.getEntity() != null){
return PhysicsEntityUtils.containsDBody(cell.getEntity());
if(cell != null && cell.getEntities() != null && cell.getEntities().size() > 0){
return PhysicsEntityUtils.containsDBody(cell.getEntities().get(0));
}
return false;
}

View File

@ -1,6 +1,7 @@
package electrosphere.controls.cursor;
import java.util.Arrays;
import java.util.Map;
import org.joml.Quaterniond;
import org.joml.Vector3d;
@ -477,8 +478,9 @@ public class CursorState {
* @param fab The fab
*/
public void setSelectedFab(BlockFab fab){
Map<Integer,Boolean> solidsMap = Globals.gameConfigCurrent.getBlockData().getSolidsMap();
QueuedModel queuedModel = new QueuedModel(() -> {
return BlockMeshgen.generateBlockModel(BlockMeshgen.rasterize(fab));
return BlockMeshgen.generateBlockModel(BlockMeshgen.rasterize(fab,false,solidsMap));
});
Globals.assetManager.queuedAsset(queuedModel);
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.voxelData = FileUtils.loadObjectFromAssetPath("Data/game/voxelTypes.json", VoxelData.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.hintData = FileUtils.loadObjectFromAssetPath("Data/tutorial/hints.json", HintDefinition.class);
config.surfaceAudioCollection = FileUtils.loadObjectFromAssetPath("Data/audio/surface.json", SurfaceAudioCollection.class);

View File

@ -1,14 +1,24 @@
package electrosphere.data.block;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* A list of all block types in game
*/
public class BlockData {
//The set of all voxel types
/**
* The set of all voxel 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
* @return The set of all block types
@ -44,4 +54,27 @@ public class BlockData {
}
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;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -16,6 +19,7 @@ import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.queue.QueuedModel;
import electrosphere.engine.threads.ThreadCounts;
import electrosphere.entity.ClientEntityUtils;
import electrosphere.entity.DrawableUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
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
* @return The block chunk entity
*/
public static Entity clientCreateBlockChunkEntity(
public static List<Entity> clientCreateBlockChunkEntity(
BlockChunkData chunkData,
BlockDrawCell notifyTarget,
Entity toDelete,
List<Entity> toDelete,
int levelOfDetail,
BlockTextureAtlas atlas,
boolean hasPolygons
){
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){
generationService.submit(() -> {
BlockMeshData data;
try {
data = BlockMeshgen.rasterize(chunkData);
if(Globals.clientState.clientScene.containsEntity(rVal)){
data = BlockMeshgen.rasterize(chunkData, true, solidsMap);
if(Globals.clientState.clientScene.containsEntity(solidsEnt)){
String modelPath = Globals.assetManager.queuedAsset(new QueuedModel(() -> {
return BlockMeshgen.generateBlockModel(data);
}));
EntityCreationUtils.makeEntityDrawablePreexistingModel(rVal, modelPath);
EntityCreationUtils.makeEntityDrawablePreexistingModel(solidsEnt, modelPath);
if(levelOfDetail == BlockChunkData.LOD_FULL_RES){
PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(rVal, data);
CollisionObjUtils.clientPositionCharacter(rVal, new Vector3d(EntityUtils.getPosition(rVal)), new Quaterniond());
PhysicsEntityUtils.clientAttachMultiShapeTriGeomRigidBody(solidsEnt, data);
CollisionObjUtils.clientPositionCharacter(solidsEnt, new Vector3d(EntityUtils.getPosition(solidsEnt)), new Quaterniond());
} else {
EntityCreationUtils.bypassShadowPass(rVal);
EntityCreationUtils.bypassVolumetics(rVal);
EntityCreationUtils.bypassShadowPass(solidsEnt);
EntityCreationUtils.bypassVolumetics(solidsEnt);
}
rVal.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true);
solidsEnt.putData(EntityDataStrings.HAS_UNIQUE_MODEL, true);
} else {
LoggerInterface.loggerEngine.DEBUG("Finished generating block polygons; however, entity has already been deleted.");
}
@ -84,7 +92,9 @@ public class BlockChunkEntity {
notifyTarget.alertToGeneration();
}
if(toDelete != null){
ClientEntityUtils.destroyEntity(toDelete);
for(Entity target : toDelete){
ClientEntityUtils.destroyEntity(target);
}
}
} catch (Error e){
LoggerInterface.loggerEngine.ERROR(e);
@ -97,11 +107,68 @@ public class BlockChunkEntity {
notifyTarget.alertToGeneration();
}
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();
return rVal;
}

View File

@ -6,6 +6,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.joml.Vector2f;
import org.joml.Vector3f;
@ -66,18 +67,48 @@ public class BlockMeshgen {
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
* @param quadMeshes The quad mesh list to fill
* @param data The block 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();
for(int z = 0; z < dimensions.z; z++){
for(int x = 0; x < dimensions.x; x++){
QuadMesh currentQuad = null;
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){
continue;
} else {
@ -361,14 +392,16 @@ public class BlockMeshgen {
/**
* Rasterizes a block chunk data into mesh 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
*/
public static BlockMeshData rasterize(BlockMeshgenData chunkData){
public static BlockMeshData rasterize(BlockMeshgenData chunkData, boolean solids, Map<Integer,Boolean> solidsMap){
BlockMeshData rVal = new BlockMeshData();
//calculate quad meshes
List<QuadMesh> quadMeshes = new LinkedList<QuadMesh>();
BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData);
BlockMeshgen.fillQuadMeshes(quadMeshes, chunkData, solids, solidsMap);
//allocate lists to store mesh data in
List<Vector3f> verts = new LinkedList<Vector3f>();
@ -470,6 +503,15 @@ public class BlockMeshgen {
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
* @param verts The list of vertices