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.
		
		
		
		
		
			
		
			
				
	
	
		
			228 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			GDScript
		
	
			
		
		
	
	
			228 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			GDScript
		
	
| const Faces = preload("./Faces.gd")
 | |
| const VoxData = preload("./VoxFormat/VoxData.gd")
 | |
| const vox_to_godot = Basis(Vector3.RIGHT, Vector3.FORWARD, Vector3.UP)
 | |
| 
 | |
| # Names for the faces by orientation
 | |
| enum FaceOrientation {
 | |
|     Top = 0,
 | |
|     Bottom = 1,
 | |
|     Left = 2,
 | |
|     Right = 3,
 | |
|     Front = 4,
 | |
|     Back = 5,
 | |
| }
 | |
| 
 | |
| # An Array(FaceOrientation) of all possible face orientations
 | |
| const face_orientations :Array = [
 | |
|     FaceOrientation.Top,
 | |
|     FaceOrientation.Bottom,
 | |
|     FaceOrientation.Left,
 | |
|     FaceOrientation.Right,
 | |
|     FaceOrientation.Front,
 | |
|     FaceOrientation.Back
 | |
| ]
 | |
| 
 | |
| # An Array(int) of the depth axis by orientation
 | |
| const depth_axis :Array = [
 | |
|     Vector3.AXIS_Z,
 | |
|     Vector3.AXIS_Z,
 | |
|     Vector3.AXIS_X,
 | |
|     Vector3.AXIS_X,
 | |
|     Vector3.AXIS_Y,
 | |
|     Vector3.AXIS_Y,
 | |
| ]
 | |
| 
 | |
| # An Array(int) of the width axis by orientation
 | |
| const width_axis :Array = [
 | |
|     Vector3.AXIS_Y,
 | |
|     Vector3.AXIS_Y,
 | |
|     Vector3.AXIS_Z,
 | |
|     Vector3.AXIS_Z,
 | |
|     Vector3.AXIS_X,
 | |
|     Vector3.AXIS_X,
 | |
| ]
 | |
| 
 | |
| # An Array(int) of height axis by orientation
 | |
| const height_axis :Array = [
 | |
|     Vector3.AXIS_X,
 | |
|     Vector3.AXIS_X,
 | |
|     Vector3.AXIS_Y,
 | |
|     Vector3.AXIS_Y,
 | |
|     Vector3.AXIS_Z,
 | |
|     Vector3.AXIS_Z,
 | |
| ]
 | |
| 
 | |
| # An Array(Vector3) describing what vectors to use to check for face occlusion
 | |
| # by orientation
 | |
| const face_checks :Array = [
 | |
|     Vector3(0, 0, 1),
 | |
|     Vector3(0, 0, -1),
 | |
|     Vector3(-1, 0, 0),
 | |
|     Vector3(1, 0, 0),
 | |
|     Vector3(0, -1, 0),
 | |
|     Vector3(0, 1, 0),
 | |
| ]
 | |
| 
 | |
| # An array of the face meshes by orientation
 | |
| const face_meshes :Array = [
 | |
|     Faces.Front,
 | |
|     Faces.Back,
 | |
|     Faces.Left,
 | |
|     Faces.Right,
 | |
|     Faces.Bottom,
 | |
|     Faces.Top,
 | |
| ]
 | |
| 
 | |
| # An Array(Vector3) describing what normals to use by orientation
 | |
| const normals :Array = [
 | |
|     Vector3(0, 1, 0),
 | |
|     Vector3(0, -1, 0),
 | |
|     Vector3(-1, 0, 0),
 | |
|     Vector3(1, 0, 0),
 | |
|     Vector3(0, 0, 1),
 | |
|     Vector3(0, 0, -1),
 | |
| ]
 | |
| 
 | |
| # The SurfaceTool the object will use to generate the mesh
 | |
| var st :SurfaceTool = SurfaceTool.new()
 | |
| 
 | |
| # A Dictonary[Vector3]int of the voxel data for the visible faces of the
 | |
| # current slice
 | |
| var faces :Dictionary
 | |
| 
 | |
| # Minimum extends of the volume
 | |
| var mins :Vector3 = Vector3(1000000, 1000000, 1000000)
 | |
| 
 | |
| # Maximum extends of the volume
 | |
| var maxs :Vector3 = Vector3(-1000000,-1000000,-1000000)
 | |
| 
 | |
| # Generate a mesh for the given voxel_data with single-pass greedy face merging
 | |
| # Primary RefCounted: https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/
 | |
| # Secondary RefCounted: https://www.gedge.ca/dev/2014/08/17/greedy-voxel-meshing
 | |
| # voxel_data is a dict[Vector3]int
 | |
| func generate(vox :VoxData, voxel_data :Dictionary, scale :float, snaptoground : bool):
 | |
|     # Remeber, MagicaVoxel thinks Y is the depth axis. We convert to the correct
 | |
|     # coordinate space when we generate the faces.
 | |
|     st.begin(Mesh.PRIMITIVE_TRIANGLES)
 | |
|     
 | |
|     # Short-circut empty models
 | |
|     if voxel_data.size() == 0:
 | |
|         return st.commit()
 | |
|     
 | |
|     # Convert voxel data to raw color values
 | |
|     for v in voxel_data:
 | |
|         voxel_data[v] = vox.colors[voxel_data[v]]
 | |
|     
 | |
|     # Find bounds
 | |
|     for v in voxel_data:
 | |
|         mins.x = min(mins.x, v.x)
 | |
|         mins.y = min(mins.y, v.y)
 | |
|         mins.z = min(mins.z, v.z)
 | |
|         maxs.x = max(maxs.x, v.x)
 | |
|         maxs.y = max(maxs.y, v.y)
 | |
|         maxs.z = max(maxs.z, v.z)
 | |
|     
 | |
|     # Itterate over all face orientations to reduce problem to 3 dimensions
 | |
|     for o in face_orientations:
 | |
|         generate_geometry_for_orientation(voxel_data, o, scale, snaptoground)
 | |
| 
 | |
|     # Finish the mesh and material and return
 | |
|     var material = StandardMaterial3D.new()
 | |
|     material.vertex_color_is_srgb = true
 | |
|     material.vertex_color_use_as_albedo = true
 | |
|     material.roughness = 1
 | |
|     st.set_material(material)
 | |
|     return st.commit()
 | |
| 
 | |
| # Generates all of the geometry for a given face orientation
 | |
| func generate_geometry_for_orientation(voxel_data :Dictionary, o :int, scale :float, snaptoground :bool) -> void:
 | |
|     # Sweep through the volume along the depth reducing the problem to 2 dimensional
 | |
|     var da :int = depth_axis[o]
 | |
|     for slice in range(mins[da], maxs[da]+1):
 | |
|         var faces :Dictionary = query_slice_faces(voxel_data, o, slice)
 | |
|         if faces.size() > 0:
 | |
|             generate_geometry(faces, o, slice, scale, snaptoground)
 | |
| 
 | |
| # Returns the voxels in the set voxel_data with a visible face along the slice
 | |
| # for the given orientation
 | |
| func query_slice_faces(voxel_data :Dictionary, o :int, slice :float) -> Dictionary:
 | |
|     var ret :Dictionary = Dictionary()
 | |
|     var da = depth_axis[o]
 | |
|     for v in voxel_data:
 | |
|         if v[da] == slice and voxel_data.has(v + face_checks[o]) == false:
 | |
|             ret[v] = voxel_data[v]
 | |
|     return ret
 | |
| 
 | |
| # Generates geometry for the given orientation for the set of faces
 | |
| func generate_geometry(faces :Dictionary, o :int, slice :float, scale :float, snaptoground :bool) -> void:
 | |
|     var da :int = depth_axis[o]
 | |
|     var wa :int = width_axis[o]
 | |
|     var ha :int = height_axis[o]
 | |
|     var v :Vector3 = Vector3()
 | |
|     v[da] = slice
 | |
|     
 | |
|     # Itterate the rows of the sparse volume
 | |
|     v[ha] = mins[ha]
 | |
|     while v[ha] <= maxs[ha]:
 | |
|         # Itterate over the voxels of the row
 | |
|         v[wa] = mins[wa]
 | |
|         while v[wa] <= maxs[wa]:
 | |
|             if faces.has(v):
 | |
|                 generate_geometry_for_face(faces, v, o, scale, snaptoground)
 | |
|             v[wa] += 1.0
 | |
|         v[ha] += 1.0
 | |
| 
 | |
| # Generates the geometry for the given face and orientation and scale and returns
 | |
| # the set of remaining faces
 | |
| func generate_geometry_for_face(faces :Dictionary, face :Vector3, o :int, scale :float, snaptoground :bool) -> Dictionary:
 | |
|     var da :int = depth_axis[o]
 | |
|     var wa :int = width_axis[o]
 | |
|     var ha :int = height_axis[o]
 | |
|     
 | |
|     # Greedy face merging
 | |
|     var width :int = width_query(faces, face, o)
 | |
|     var height :int = height_query(faces, face, o, width)
 | |
|     var grow :Vector3 = Vector3(1, 1, 1)
 | |
|     grow[wa] *= width
 | |
|     grow[ha] *= height
 | |
|     
 | |
|     # Generate geometry
 | |
|     var yoffset = Vector3(0,0,0);
 | |
|     if snaptoground : yoffset = Vector3(0, -mins.z * scale, 0);
 | |
| 
 | |
|     st.set_color(faces[face])
 | |
|     st.set_normal(normals[o])
 | |
|     for vert in face_meshes[o]:
 | |
|         st.add_vertex(yoffset + vox_to_godot * ((vert * grow) + face) * scale)
 | |
|     
 | |
|     # Remove these faces from the pool
 | |
|     var v :Vector3 = Vector3()
 | |
|     v[da] = face[da]
 | |
|     for iy in range(height):
 | |
|         v[ha] = face[ha] + float(iy)
 | |
|         for ix in range(width):
 | |
|             v[wa] = face[wa] + float(ix)
 | |
|             faces.erase(v)
 | |
|     
 | |
|     return faces
 | |
| 
 | |
| # Returns the number of voxels wide the run starting at face is with respect to
 | |
| # the set of faces and orientation
 | |
| func width_query(faces :Dictionary, face :Vector3, o :int) -> int:
 | |
|     var wd :int = width_axis[o]
 | |
|     var v :Vector3 = face
 | |
|     while faces.has(v) and faces[v] == faces[face]:
 | |
|         v[wd] += 1.0
 | |
|     return int(v[wd] - face[wd])
 | |
| 
 | |
| # Returns the number of voxels high the run starting at face is with respect to
 | |
| # the set of faces and orientation, with the given width
 | |
| func height_query(faces :Dictionary, face :Vector3, o :int, width :int) -> int:
 | |
|     var hd :int = height_axis[o]
 | |
|     var c :Color = faces[face]
 | |
|     var v :Vector3 = face
 | |
|     v[hd] += 1.0
 | |
|     while faces.has(v) and faces[v] == c and width_query(faces, v, o) >= width:
 | |
|         v[hd] += 1.0
 | |
|     return int(v[hd] - face[hd])
 |