exporting block prefabs to compressed files
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
This commit is contained in:
parent
20c53b151b
commit
69a5e07818
@ -1539,6 +1539,9 @@ Variable block editing size
|
||||
Increase block cursor size
|
||||
Block area selection
|
||||
|
||||
(04/26/2025)
|
||||
Exporting block prefabs to compressed files
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -19,10 +19,16 @@ public class BlockChunkData {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
package electrosphere.client.block;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.controls.cursor.CursorState;
|
||||
import electrosphere.client.interact.select.AreaSelection;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.game.data.block.BlockFab;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -15,6 +15,9 @@ public class ImGuiAreaTab {
|
||||
if(ImGui.button("Select all voxels")){
|
||||
ClientBlockSelection.selectAllBlocks();
|
||||
}
|
||||
if(ImGui.button("Export Selected Blocks")){
|
||||
ClientBlockSelection.exportSelection();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.client.block.BlockChunkData;
|
||||
import electrosphere.client.entity.camera.CameraEntityUtils;
|
||||
import electrosphere.client.interact.select.AreaSelection;
|
||||
import electrosphere.collision.CollisionEngine;
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.assetmanager.AssetDataStrings;
|
||||
@ -59,6 +60,11 @@ public class CursorState {
|
||||
*/
|
||||
private int blockSize = MIN_BLOCK_SIZE;
|
||||
|
||||
/**
|
||||
* The current selection of the area selector
|
||||
*/
|
||||
private AreaSelection areaCursorSelection = null;
|
||||
|
||||
/**
|
||||
* Creates the cursor entities
|
||||
*/
|
||||
@ -154,12 +160,12 @@ public class CursorState {
|
||||
|
||||
/**
|
||||
* Selects a rectangular area
|
||||
* @param startPos The start position of the area
|
||||
* @param endPos The end position of the area
|
||||
* @param selection The area selection to encompass the cursor
|
||||
*/
|
||||
public static void selectRectangularArea(Vector3d startPos, Vector3d endPos){
|
||||
Vector3d center = new Vector3d(startPos).add(endPos).mul(0.5f);
|
||||
Vector3d scale = new Vector3d(startPos).sub(endPos).absolute();
|
||||
public void selectRectangularArea(AreaSelection selection){
|
||||
this.areaCursorSelection = selection;
|
||||
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);
|
||||
Actor areaCursorActor = EntityUtils.getActor(Globals.playerAreaCursor);
|
||||
areaCursorActor.addTextureMask(new ActorTextureMask("cube", Arrays.asList(new String[]{"Textures/transparent_red.png"})));
|
||||
@ -260,5 +266,13 @@ public class CursorState {
|
||||
public int getBlockSize(){
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently selected area
|
||||
* @return The currently selected area
|
||||
*/
|
||||
public AreaSelection getAreaSelection(){
|
||||
return this.areaCursorSelection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
85
src/main/java/electrosphere/game/data/block/BlockFab.java
Normal file
85
src/main/java/electrosphere/game/data/block/BlockFab.java
Normal 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!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,6 +33,8 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user