|  |  |  | 
 | 
					
						
							|  |  |  | 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; |