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;