exporting block prefabs to compressed files
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-04-26 11:07:33 -04:00
parent 20c53b151b
commit 69a5e07818
8 changed files with 268 additions and 8 deletions

View File

@ -1539,6 +1539,9 @@ Variable block editing size
Increase block cursor size Increase block cursor size
Block area selection Block area selection
(04/26/2025)
Exporting block prefabs to compressed files

View File

@ -19,10 +19,16 @@ public class BlockChunkData {
*/ */
public static final int TOTAL_DATA_WIDTH = CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH; public static final int TOTAL_DATA_WIDTH = CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH * CHUNK_DATA_WIDTH;
/**
* 2 - block type
* 2 - metadata
*/
public static final int BYTES_PER_BLOCK = 2 * 2;
/** /**
* Size of a buffer that stores this chunk's data * Size of a buffer that stores this chunk's data
*/ */
public static final int BUFFER_SIZE = TOTAL_DATA_WIDTH * 2 * 2; public static final int BUFFER_SIZE = TOTAL_DATA_WIDTH * BYTES_PER_BLOCK;
/** /**
* The number of blocks to place within each unit of distance * The number of blocks to place within each unit of distance

View File

@ -1,10 +1,13 @@
package electrosphere.client.block; package electrosphere.client.block;
import java.io.File;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.joml.Vector3i; import org.joml.Vector3i;
import electrosphere.controls.cursor.CursorState; import electrosphere.client.interact.select.AreaSelection;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.game.data.block.BlockFab;
/** /**
* Class for selecting blocks on the client * Class for selecting blocks on the client
@ -68,8 +71,51 @@ public class ClientBlockSelection {
} }
} }
} }
AreaSelection selection = AreaSelection.createRect(minPos, maxPos);
Globals.cursorState.selectRectangularArea(selection);
}
CursorState.selectRectangularArea(minPos, maxPos); /**
* Exports currently selected area of voxels
*/
public static void exportSelection(){
AreaSelection selection = Globals.cursorState.getAreaSelection();
Vector3i startChunk = Globals.clientWorldData.convertRealToWorldSpace(selection.getRectStart());
Vector3i endChunk = Globals.clientWorldData.convertRealToWorldSpace(selection.getRectEnd());
if(!startChunk.equals(endChunk)){
throw new Error("Unsupported case! Selected are coverts multiple chunks.. " + startChunk + " " + endChunk);
}
Vector3i blockStart = Globals.clientWorldData.convertRealToBlockSpace(selection.getRectStart());
Vector3i blockEnd = Globals.clientWorldData.convertRealToBlockSpace(selection.getRectEnd());
BlockChunkData chunk = Globals.clientBlockManager.getChunkDataAtWorldPoint(startChunk, 0);
if(chunk == null){
throw new Error("Failed to grab chunk at " + startChunk);
}
int blockCount = (blockEnd.x - blockStart.x) * (blockEnd.y - blockStart.y) * (blockEnd.z - blockStart.z);
short[] types = new short[blockCount];
short[] metadata = new short[blockCount];
int i = 0;
for(int x = blockStart.x; x < blockEnd.x; x++){
for(int y = blockStart.y; y < blockEnd.y; y++){
for(int z = blockStart.z; z < blockEnd.z; z++){
types[i] = chunk.getType(x, y, z);
metadata[i] = chunk.getMetadata(x, y, z);
i++;
}
}
}
Vector3i dimensions = new Vector3i(
(blockEnd.x - blockStart.x),
(blockEnd.y - blockStart.y),
(blockEnd.z - blockStart.z)
);
BlockFab fab = BlockFab.create(dimensions, types, metadata);
File exportLoc = new File("./struct.block");
fab.save(exportLoc);
} }
} }

View File

@ -0,0 +1,75 @@
package electrosphere.client.interact.select;
import org.joml.Vector3d;
/**
* An area of space that is selected by the client
*/
public class AreaSelection {
/**
* The type of selection
*/
public static enum AreaSelectionType {
/**
* A rectangle
*/
RECTANGULAR,
}
/**
* The type of selection
*/
private AreaSelectionType type;
/**
* The start point of the rectangular selection
*/
private Vector3d rectStart;
/**
* The end point of the rectangular selection
*/
private Vector3d rectEnd;
/**
* Creates a rectangular selection
* @param start The start point
* @param end The end point
* @return The selection
*/
public static AreaSelection createRect(Vector3d start, Vector3d end){
AreaSelection rVal = new AreaSelection();
rVal.type = AreaSelectionType.RECTANGULAR;
rVal.rectStart = start;
rVal.rectEnd = end;
return rVal;
}
/**
* Gets the type of area
* @return The type of area
*/
public AreaSelectionType getType() {
return type;
}
/**
* Gets the start point of the rectangular selection
* @return The start point
*/
public Vector3d getRectStart() {
return rectStart;
}
/**
* Gets the end point of the rectangular selection
* @return The end point
*/
public Vector3d getRectEnd() {
return rectEnd;
}
}

View File

@ -15,6 +15,9 @@ public class ImGuiAreaTab {
if(ImGui.button("Select all voxels")){ if(ImGui.button("Select all voxels")){
ClientBlockSelection.selectAllBlocks(); ClientBlockSelection.selectAllBlocks();
} }
if(ImGui.button("Export Selected Blocks")){
ClientBlockSelection.exportSelection();
}
} }
} }

View File

@ -7,6 +7,7 @@ import org.joml.Vector3i;
import electrosphere.client.block.BlockChunkData; import electrosphere.client.block.BlockChunkData;
import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.client.interact.select.AreaSelection;
import electrosphere.collision.CollisionEngine; import electrosphere.collision.CollisionEngine;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.engine.assetmanager.AssetDataStrings;
@ -59,6 +60,11 @@ public class CursorState {
*/ */
private int blockSize = MIN_BLOCK_SIZE; private int blockSize = MIN_BLOCK_SIZE;
/**
* The current selection of the area selector
*/
private AreaSelection areaCursorSelection = null;
/** /**
* Creates the cursor entities * Creates the cursor entities
*/ */
@ -154,12 +160,12 @@ public class CursorState {
/** /**
* Selects a rectangular area * Selects a rectangular area
* @param startPos The start position of the area * @param selection The area selection to encompass the cursor
* @param endPos The end position of the area
*/ */
public static void selectRectangularArea(Vector3d startPos, Vector3d endPos){ public void selectRectangularArea(AreaSelection selection){
Vector3d center = new Vector3d(startPos).add(endPos).mul(0.5f); this.areaCursorSelection = selection;
Vector3d scale = new Vector3d(startPos).sub(endPos).absolute(); Vector3d center = new Vector3d(areaCursorSelection.getRectStart()).add(areaCursorSelection.getRectEnd()).mul(0.5f);
Vector3d scale = new Vector3d(areaCursorSelection.getRectStart()).sub(areaCursorSelection.getRectEnd()).absolute();
EntityCreationUtils.makeEntityDrawable(Globals.playerAreaCursor, AssetDataStrings.UNITCUBE); EntityCreationUtils.makeEntityDrawable(Globals.playerAreaCursor, AssetDataStrings.UNITCUBE);
Actor areaCursorActor = EntityUtils.getActor(Globals.playerAreaCursor); Actor areaCursorActor = EntityUtils.getActor(Globals.playerAreaCursor);
areaCursorActor.addTextureMask(new ActorTextureMask("cube", Arrays.asList(new String[]{"Textures/transparent_red.png"}))); areaCursorActor.addTextureMask(new ActorTextureMask("cube", Arrays.asList(new String[]{"Textures/transparent_red.png"})));
@ -261,4 +267,12 @@ public class CursorState {
return blockSize; return blockSize;
} }
/**
* Gets the currently selected area
* @return The currently selected area
*/
public AreaSelection getAreaSelection(){
return this.areaCursorSelection;
}
} }

View File

@ -0,0 +1,85 @@
package electrosphere.game.data.block;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import org.joml.Vector3i;
import electrosphere.client.block.BlockChunkData;
import electrosphere.util.FileUtils;
/**
* A collection of blocks
*/
public class BlockFab {
/**
* Size of the header for a block fab file
* 3 * 4 for the dimensions at the front of the file
*/
public static final int HEADER_SIZE = 3 * 4;
/**
* Dimensions of the block fab
*/
Vector3i dimensions;
/**
* Block type data
*/
short[] types;
/**
* Block metadata
*/
short[] metadata;
/**
* Creates a block fab
* @param dimensions The dimensions of the fab
* @param types The block types
* @param metadata The block metadata
* @return The block fab
*/
public static BlockFab create(Vector3i dimensions, short[] types, short[] metadata){
BlockFab rVal = new BlockFab();
rVal.dimensions = dimensions;
rVal.types = types;
rVal.metadata = metadata;
return rVal;
}
/**
* Saves this fab to a file
* @param file The file
*/
public void save(File file){
int blockCount = dimensions.x * dimensions.y * dimensions.z;
ByteBuffer buff = ByteBuffer.allocate(HEADER_SIZE + blockCount * BlockChunkData.BYTES_PER_BLOCK);
IntBuffer intView = buff.asIntBuffer();
intView.put(dimensions.x);
intView.put(dimensions.y);
intView.put(dimensions.z);
buff.position(HEADER_SIZE);
ShortBuffer shortView = buff.asShortBuffer();
shortView.put(types);
shortView.put(metadata);
shortView.flip();
File exportLoc = new File("./struct.block");
try {
FileUtils.writeBufferToCompressedFile(exportLoc, buff);
} catch (IOException e) {
throw new Error("Failed to export selected blocks to a file!");
}
}
}

View File

@ -33,6 +33,8 @@ import java.security.NoSuchAlgorithmException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@ -526,6 +528,32 @@ public class FileUtils {
} }
} }
/**
* Writes a ByteBuffer to a file and compresses it
* @param file The file
* @param buff The buffer
*/
public static void writeBufferToCompressedFile(File file, ByteBuffer buff) throws IOException {
try (GZIPOutputStream outStream = new GZIPOutputStream(Files.newOutputStream(file.toPath()))){
outStream.write(buff.array());
}
}
/**
* Writes a ByteBuffer to a file and compresses it
* @param file The file
* @param buff The buffer
*/
public static ByteBuffer readBufferFromCompressedFile(File file) throws IOException {
ByteBuffer buff = null;
try (GZIPInputStream inStream = new GZIPInputStream(Files.newInputStream(file.toPath()))){
byte[] bytes = inStream.readAllBytes();
buff = ByteBuffer.allocate(bytes.length);
buff.put(bytes);
buff.flip();
}
return buff;
}