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
		
	
	
		
			10 KiB
		
	
	
	
		
			GDScript
		
	
			
		
		
	
	
			308 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;
 |