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.
		
		
		
		
		
			
		
			
				
	
	
		
			308 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			GDScript
		
	
			
		
		
	
	
			308 lines
		
	
	
		
			12 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;
 |