You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
10 KiB
GDScript
315 lines
10 KiB
GDScript
const VoxFile = preload("./VoxFile.gd");
|
|
const VoxData = preload("./VoxFormat/VoxData.gd");
|
|
const VoxNode = preload("./VoxFormat/VoxNode.gd");
|
|
const VoxMaterial = preload("./VoxFormat/VoxMaterial.gd");
|
|
const VoxLayer = preload("./VoxFormat/VoxLayer.gd");
|
|
const CulledMeshGenerator = preload("./CulledMeshGenerator.gd");
|
|
const GreedyMeshGenerator = preload("./GreedyMeshGenerator.gd");
|
|
const debug_file = false;
|
|
const debug_models = false;
|
|
var fileKeyframeIds = [];
|
|
|
|
|
|
func import(source_path, destination_path, options, _platforms, _gen_files):
|
|
var scale = 0.1
|
|
if options.Scale:
|
|
scale = float(options.Scale)
|
|
var greedy = true
|
|
if options.has("GreedyMeshGenerator"):
|
|
greedy = bool(options.GreedyMeshGenerator)
|
|
var snaptoground = false
|
|
if options.has("SnapToGround"):
|
|
snaptoground = bool(options.SnapToGround)
|
|
var mergeKeyframes = false
|
|
if options.has("FirstKeyframeOnly"):
|
|
mergeKeyframes = not bool(options.FirstKeyframeOnly)
|
|
|
|
var file = FileAccess.open(source_path, FileAccess.READ)
|
|
|
|
if file == null:
|
|
return FileAccess.get_open_error()
|
|
|
|
var identifier = PackedByteArray([ file.get_8(), file.get_8(), file.get_8(), file.get_8() ]).get_string_from_ascii()
|
|
var version = file.get_32()
|
|
print('Importing: ', source_path, ' (scale: ', scale, ', file version: ', version, ', greedy mesh: ', greedy, ', snap to ground: ', snaptoground, ')');
|
|
|
|
var vox = VoxData.new();
|
|
if identifier == 'VOX ':
|
|
var voxFile = VoxFile.new(file);
|
|
while voxFile.has_data_to_read():
|
|
read_chunk(vox, voxFile);
|
|
file = null
|
|
|
|
fileKeyframeIds.sort()
|
|
|
|
var voxel_data = unify_voxels(vox, mergeKeyframes);
|
|
var meshes = []
|
|
for keyframeVoxels in voxel_data:
|
|
if greedy:
|
|
meshes.append(GreedyMeshGenerator.new().generate(vox, voxel_data[keyframeVoxels], scale, snaptoground))
|
|
else:
|
|
meshes.append(CulledMeshGenerator.new().generate(vox, voxel_data[keyframeVoxels], scale, snaptoground))
|
|
return meshes
|
|
|
|
|
|
func string_to_vector3(input: String) -> Vector3:
|
|
var data = input.split_floats(' ');
|
|
return Vector3(data[0], data[1], data[2]);
|
|
|
|
|
|
func byte_to_basis(data: int):
|
|
var x_ind = ((data >> 0) & 0x03);
|
|
var y_ind = ((data >> 2) & 0x03);
|
|
var indexes = [0, 1, 2];
|
|
indexes.erase(x_ind);
|
|
indexes.erase(y_ind);
|
|
var z_ind = indexes[0];
|
|
var x_sign = 1 if ((data >> 4) & 0x01) == 0 else -1;
|
|
var y_sign = 1 if ((data >> 5) & 0x01) == 0 else -1;
|
|
var z_sign = 1 if ((data >> 6) & 0x01) == 0 else -1;
|
|
var result = Basis();
|
|
result.x[0] = x_sign if x_ind == 0 else 0;
|
|
result.x[1] = x_sign if x_ind == 1 else 0;
|
|
result.x[2] = x_sign if x_ind == 2 else 0;
|
|
|
|
result.y[0] = y_sign if y_ind == 0 else 0;
|
|
result.y[1] = y_sign if y_ind == 1 else 0;
|
|
result.y[2] = y_sign if y_ind == 2 else 0;
|
|
|
|
result.z[0] = z_sign if z_ind == 0 else 0;
|
|
result.z[1] = z_sign if z_ind == 1 else 0;
|
|
result.z[2] = z_sign if z_ind == 2 else 0;
|
|
return result;
|
|
|
|
|
|
func read_chunk(vox: VoxData, file: VoxFile):
|
|
var chunk_id = file.get_string(4);
|
|
var chunk_size = file.get_32();
|
|
var childChunks = file.get_32()
|
|
|
|
file.set_chunk_size(chunk_size);
|
|
match chunk_id:
|
|
'SIZE':
|
|
vox.current_index += 1;
|
|
var model = vox.get_model();
|
|
var x = file.get_32();
|
|
var y = file.get_32();
|
|
var z = file.get_32();
|
|
model.size = Vector3(x, y, z);
|
|
if debug_file: print('SIZE ', model.size);
|
|
'XYZI':
|
|
var model = vox.get_model();
|
|
if debug_file: print('XYZI');
|
|
for _i in range(file.get_32()):
|
|
var x = file.get_8()
|
|
var y = file.get_8()
|
|
var z = file.get_8()
|
|
var c = file.get_8()
|
|
var voxel = Vector3(x, y, z)
|
|
model.voxels[voxel] = c - 1
|
|
if debug_file && debug_models: print('\t', voxel, ' ', c-1);
|
|
'RGBA':
|
|
vox.colors = []
|
|
for _i in range(256):
|
|
var r = float(file.get_8() / 255.0)
|
|
var g = float(file.get_8() / 255.0)
|
|
var b = float(file.get_8() / 255.0)
|
|
var a = float(file.get_8() / 255.0)
|
|
vox.colors.append(Color(r, g, b, a))
|
|
'nTRN':
|
|
var node_id = file.get_32();
|
|
var attributes = file.get_vox_dict();
|
|
var node = VoxNode.new(node_id, attributes);
|
|
vox.nodes[node_id] = node;
|
|
|
|
var child = file.get_32();
|
|
node.child_nodes.append(child);
|
|
|
|
file.get_32();
|
|
node.layerId = file.get_32();
|
|
var num_of_frames = file.get_32();
|
|
|
|
if debug_file:
|
|
print('nTRN[', node_id, '] -> ', child);
|
|
if (!attributes.is_empty()): print('\t', attributes);
|
|
if num_of_frames > 0:
|
|
node.transforms = {};
|
|
for _frame in range(num_of_frames):
|
|
var keyframe = 0;
|
|
var newTransform = { "position": Vector3(), "rotation": Basis() };
|
|
var frame_attributes = file.get_vox_dict();
|
|
if (frame_attributes.has('_f')):
|
|
keyframe = int(frame_attributes['_f']);
|
|
if (frame_attributes.has('_t')):
|
|
var trans = frame_attributes['_t'];
|
|
newTransform.position = string_to_vector3(trans);
|
|
if debug_file: print('\tT: ', newTransform.position);
|
|
if (frame_attributes.has('_r')):
|
|
var rot = frame_attributes['_r'];
|
|
newTransform.rotation = byte_to_basis(int(rot)).inverse();
|
|
if debug_file: print('\tR: ', newTransform.rotation);
|
|
node.transforms[keyframe] = newTransform;
|
|
if not fileKeyframeIds.has(keyframe):
|
|
fileKeyframeIds.append(keyframe);
|
|
'nGRP':
|
|
var node_id = file.get_32();
|
|
var attributes = file.get_vox_dict();
|
|
var node = VoxNode.new(node_id, attributes);
|
|
vox.nodes[node_id] = node;
|
|
|
|
var num_children = file.get_32();
|
|
for _c in num_children:
|
|
node.child_nodes.append(file.get_32());
|
|
if debug_file:
|
|
print('nGRP[', node_id, '] -> ', node.child_nodes);
|
|
if (!attributes.is_empty()): print('\t', attributes);
|
|
'nSHP':
|
|
var node_id = file.get_32();
|
|
var attributes = file.get_vox_dict();
|
|
var node = VoxNode.new(node_id, attributes);
|
|
vox.nodes[node_id] = node;
|
|
|
|
var num_models = file.get_32();
|
|
for _i in range(num_models):
|
|
var keyframe = 0;
|
|
var modelId = file.get_32();
|
|
var model_attributes = file.get_vox_dict();
|
|
if (model_attributes.has('_f')):
|
|
keyframe = int(model_attributes['_f']);
|
|
node.models[keyframe] = modelId;
|
|
if not fileKeyframeIds.has(keyframe):
|
|
fileKeyframeIds.append(keyframe);
|
|
if debug_file:
|
|
print('nSHP[', node_id, '] -> ', node.models);
|
|
if (!attributes.is_empty()): print('\t', attributes);
|
|
'MATL':
|
|
var material_id = file.get_32() - 1;
|
|
var properties = file.get_vox_dict();
|
|
vox.materials[material_id] = VoxMaterial.new(properties);
|
|
if debug_file:
|
|
print("MATL ", material_id);
|
|
print("\t", properties);
|
|
'LAYR':
|
|
var layer_id = file.get_32();
|
|
var attributes = file.get_vox_dict();
|
|
var isVisible = true;
|
|
if '_hidden' in attributes and attributes['_hidden'] == '1':
|
|
isVisible = false;
|
|
var layer = VoxLayer.new(layer_id, isVisible);
|
|
vox.layers[layer_id] = layer;
|
|
_:
|
|
if debug_file: print(chunk_id);
|
|
file.read_remaining();
|
|
|
|
|
|
func unify_voxels(vox: VoxData, mergeKeyframes: bool):
|
|
var node = vox.nodes[0];
|
|
var layeredVoxelData = get_layeredVoxels(node, vox, -1, mergeKeyframes)
|
|
return layeredVoxelData.getDataMergedFromLayers();
|
|
|
|
|
|
class LayeredVoxelData:
|
|
var data_keyframed_layered = {};
|
|
|
|
|
|
func combine(keyframeId, layerId, model):
|
|
# Make sure there's space
|
|
if not keyframeId in data_keyframed_layered:
|
|
data_keyframed_layered[keyframeId] = {}
|
|
if not layerId in data_keyframed_layered[keyframeId]:
|
|
data_keyframed_layered[keyframeId][layerId] = {}
|
|
# Add the model voxels to the data
|
|
var offset = (model.size / 2.0).floor();
|
|
for voxel in model.voxels:
|
|
data_keyframed_layered[keyframeId][layerId][voxel - offset] = model.voxels[voxel];
|
|
|
|
|
|
func combine_data(other):
|
|
for keyframeId in other.data_keyframed_layered:
|
|
if not keyframeId in data_keyframed_layered:
|
|
data_keyframed_layered[keyframeId] = {}
|
|
for layerId in other.data_keyframed_layered[keyframeId]:
|
|
if not layerId in data_keyframed_layered[keyframeId]:
|
|
data_keyframed_layered[keyframeId][layerId] = {}
|
|
for voxel in other.data_keyframed_layered[keyframeId][layerId]:
|
|
data_keyframed_layered[keyframeId][layerId][voxel] = (
|
|
other.data_keyframed_layered[keyframeId][layerId][voxel]);
|
|
|
|
|
|
func transform(transforms):
|
|
var new_data = {};
|
|
for keyframeId in data_keyframed_layered:
|
|
new_data[keyframeId] = {}
|
|
var transform = get_input_for_keyframe(keyframeId, transforms);
|
|
for layerId in data_keyframed_layered[keyframeId]:
|
|
new_data[keyframeId][layerId] = {}
|
|
for voxel in data_keyframed_layered[keyframeId][layerId]:
|
|
var half_step = Vector3(0.5, 0.5, 0.5);
|
|
var new_voxel = (
|
|
(transform.rotation * voxel+half_step-half_step).floor() +
|
|
transform.position);
|
|
new_data[keyframeId][layerId][new_voxel] = (
|
|
data_keyframed_layered[keyframeId][layerId][voxel]);
|
|
data_keyframed_layered = new_data;
|
|
|
|
|
|
func getDataMergedFromLayers():
|
|
# The result of this function
|
|
var result = {};
|
|
for keyframeId in data_keyframed_layered:
|
|
result[keyframeId] = {}
|
|
# Merge all layer data in layerId order (highest layer overrides all)
|
|
var layerIds = data_keyframed_layered[keyframeId].keys();
|
|
layerIds.sort();
|
|
for layerId in layerIds:
|
|
for voxel in data_keyframed_layered[keyframeId][layerId]:
|
|
result[keyframeId][voxel] = data_keyframed_layered[keyframeId][layerId][voxel];
|
|
# Return the merged data
|
|
return result;
|
|
|
|
|
|
static func get_input_for_keyframe(focusKeyframeId, inputCollection):
|
|
var inputKeyframeIds = inputCollection.keys();
|
|
inputKeyframeIds.sort();
|
|
inputKeyframeIds.reverse();
|
|
var result = inputKeyframeIds.back();
|
|
for inputKeyframeId in inputKeyframeIds:
|
|
if inputKeyframeId <= focusKeyframeId:
|
|
result = inputKeyframeId;
|
|
break;
|
|
return inputCollection[result];
|
|
|
|
|
|
func get_layeredVoxels(node: VoxNode, vox: VoxData, layerId: int, mergeKeyframes: bool):
|
|
var result = LayeredVoxelData.new();
|
|
|
|
# Handle layers (keeping separated and ignoring hidden)
|
|
if node.layerId in vox.layers:
|
|
if vox.layers[node.layerId].isVisible:
|
|
layerId = node.layerId;
|
|
else:
|
|
return result;
|
|
|
|
# Add all models in this node
|
|
if mergeKeyframes:
|
|
for model_index in node.models:
|
|
var modelId = node.models[model_index];
|
|
var model = vox.models[modelId];
|
|
result.combine(0, layerId, model);
|
|
elif node.models.size() > 0:
|
|
for fileKeyframeId in fileKeyframeIds:
|
|
var frameModelId = LayeredVoxelData.get_input_for_keyframe(fileKeyframeId, node.models);
|
|
var model = vox.models[frameModelId];
|
|
result.combine(fileKeyframeId, layerId, model);
|
|
|
|
# Process child nodes
|
|
for child_index in node.child_nodes:
|
|
var child = vox.nodes[child_index];
|
|
var child_data = get_layeredVoxels(child, vox, layerId, mergeKeyframes);
|
|
result.combine_data(child_data);
|
|
|
|
# Run transforms
|
|
result.transform(node.transforms);
|
|
|
|
return result;
|