UV Mapping for Destructable Walls
This commit is contained in:
BIN
demo/assets/models/soup_can/soup_can.glb
Normal file
BIN
demo/assets/models/soup_can/soup_can.glb
Normal file
Binary file not shown.
42
demo/assets/models/soup_can/soup_can.glb.import
Normal file
42
demo/assets/models/soup_can/soup_can.glb.import
Normal file
@@ -0,0 +1,42 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://04ag2c3lcicg"
|
||||
path="res://.godot/imported/soup_can.glb-789b0529ee38a7f623de5835c9b37487.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/soup_can/soup_can.glb"
|
||||
dest_files=["res://.godot/imported/soup_can.glb-789b0529ee38a7f623de5835c9b37487.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=false
|
||||
animation/fps=30
|
||||
animation/trimming=false
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=2
|
||||
gltf/embedded_image_handling=1
|
||||
BIN
demo/assets/models/soup_can/soup_can_AORoughMetal.png
Normal file
BIN
demo/assets/models/soup_can/soup_can_AORoughMetal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 707 KiB |
@@ -2,17 +2,20 @@
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dwhdp7spars2b"
|
||||
path.s3tc="res://.godot/imported/texture.png-59546a6b6ae00ae598d09228d4c66fd2.s3tc.ctex"
|
||||
uid="uid://ck5tahw45mdyg"
|
||||
path.s3tc="res://.godot/imported/soup_can_AORoughMetal.png-43e2632063d2d19b56c9d0c621da1d76.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "35b96d6af8c926765a1b0dfedb661ac0"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/textures/texture.png"
|
||||
dest_files=["res://.godot/imported/texture.png-59546a6b6ae00ae598d09228d4c66fd2.s3tc.ctex"]
|
||||
source_file="res://assets/models/soup_can/soup_can_AORoughMetal.png"
|
||||
dest_files=["res://.godot/imported/soup_can_AORoughMetal.png-43e2632063d2d19b56c9d0c621da1d76.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
BIN
demo/assets/models/soup_can/soup_can_BaseColor.png
Normal file
BIN
demo/assets/models/soup_can/soup_can_BaseColor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 368 KiB |
@@ -2,17 +2,20 @@
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://doknmohl75xnp"
|
||||
path.s3tc="res://.godot/imported/t_floormetal1.png-42986c698f9d8b89446ac25d30252b9c.s3tc.ctex"
|
||||
uid="uid://dfacteo6e26uj"
|
||||
path.s3tc="res://.godot/imported/soup_can_BaseColor.png-6727e8f8d3ac7d832b3d936fe9e3ce00.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "1860f397e89d1e452194ed3fd622504d"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/textures/t_floormetal1.png"
|
||||
dest_files=["res://.godot/imported/t_floormetal1.png-42986c698f9d8b89446ac25d30252b9c.s3tc.ctex"]
|
||||
source_file="res://assets/models/soup_can/soup_can_BaseColor.png"
|
||||
dest_files=["res://.godot/imported/soup_can_BaseColor.png-6727e8f8d3ac7d832b3d936fe9e3ce00.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
BIN
demo/assets/models/soup_can/soup_can_Normal.png
Normal file
BIN
demo/assets/models/soup_can/soup_can_Normal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 201 KiB |
44
demo/assets/models/soup_can/soup_can_Normal.png.import
Normal file
44
demo/assets/models/soup_can/soup_can_Normal.png.import
Normal file
@@ -0,0 +1,44 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ct7pciueuljsr"
|
||||
path.s3tc="res://.godot/imported/soup_can_Normal.png-ecfc9a38b9ec7072ea5bc1197d66919c.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "eeb4f586a588a0287b05a6d4499c3676"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/models/soup_can/soup_can_Normal.png"
|
||||
dest_files=["res://.godot/imported/soup_can_Normal.png-ecfc9a38b9ec7072ea5bc1197d66919c.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=1
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=1
|
||||
roughness/src_normal="res://assets/models/soup_can/soup_can_Normal.png"
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
@@ -11,7 +11,9 @@ enum ExtrudeDirection {
|
||||
@export var verts2d: PackedVector2Array
|
||||
@export var meshInstance3d: MeshInstance3D
|
||||
@export var depth: float = 0.1
|
||||
@export var depth_position_offset: Vector3 = Vector3.ZERO
|
||||
@export var original_vertices = PackedVector3Array()
|
||||
@export var original_normals = PackedVector3Array()
|
||||
@export var original_uvs = PackedVector2Array()
|
||||
@export_category("TrechBroom User Values")
|
||||
@export var extrusion_direction: ExtrudeDirection
|
||||
@export var hole_proximity: float
|
||||
@@ -28,7 +30,6 @@ var static_body: StaticBody3D
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
parentNode = get_node("..")
|
||||
inner_polygons = Holes.new()
|
||||
inner_rtree = RectangleTree.new()
|
||||
@@ -43,6 +44,9 @@ func _exit_tree() -> void:
|
||||
func _func_godot_apply_properties(entity_properties: Dictionary) -> void:
|
||||
meshInstance3d = _find_mesh_body()
|
||||
var verticies = meshInstance3d.mesh.surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
|
||||
original_vertices = self.get_meta("func_godot_mesh_data")["vertices"]
|
||||
original_normals = self.get_meta("func_godot_mesh_data")["normals"]
|
||||
original_uvs = self.get_meta("func_godot_mesh_data")["uvs"]
|
||||
|
||||
extrusion_direction = entity_properties["extrude_direction"]
|
||||
hole_proximity = entity_properties["hole_proximity"]
|
||||
@@ -63,8 +67,6 @@ func _func_godot_apply_properties(entity_properties: Dictionary) -> void:
|
||||
elif d < depth_min:
|
||||
depth_min = d
|
||||
depth = -(abs(depth_min) + abs(depth_max))
|
||||
|
||||
depth_position_offset = Vector3.BACK * (depth/2)
|
||||
elif extrusion_direction == ExtrudeDirection.RIGHT:
|
||||
for vert in verticies:
|
||||
verts_2d.append(Vector2(vert.z, vert.y))
|
||||
@@ -76,8 +78,6 @@ func _func_godot_apply_properties(entity_properties: Dictionary) -> void:
|
||||
elif d < depth_min:
|
||||
depth_min = d
|
||||
depth = -(abs(depth_min) + abs(depth_max))
|
||||
|
||||
depth_position_offset = Vector3.RIGHT * (depth/2)
|
||||
elif extrusion_direction == ExtrudeDirection.UP:
|
||||
for vert in verticies:
|
||||
verts_2d.append(Vector2(vert.x, vert.z))
|
||||
@@ -89,8 +89,6 @@ func _func_godot_apply_properties(entity_properties: Dictionary) -> void:
|
||||
elif d < depth_min:
|
||||
depth_min = d
|
||||
depth = -(abs(depth_min) + abs(depth_max))
|
||||
|
||||
depth_position_offset = Vector3.UP * (depth/2)
|
||||
|
||||
var outer_boudary = Geometry2D.convex_hull(verts_2d)
|
||||
verts2d = outer_boudary
|
||||
@@ -102,12 +100,11 @@ func _draw():
|
||||
vectors.append_array(outer_polygon)
|
||||
vectors.append_array(inner_polygons.get_hole_verticies())
|
||||
|
||||
var meshGenerator = GeoPolyMesh.new(vector_indexes, vectors, extrusion_direction, depth)
|
||||
var meshGenerator = GeoPolyMesh.new(vector_indexes, vectors, extrusion_direction, original_vertices, original_uvs, original_normals, depth)
|
||||
var commited_mesh = meshGenerator.commit_mesh(meshInstance3d.get_active_material(0))
|
||||
meshGenerator.free()
|
||||
|
||||
meshInstance3d.mesh = commited_mesh
|
||||
meshInstance3d.position += depth_position_offset
|
||||
meshInstance3d.create_trimesh_collision()
|
||||
static_body = _find_static_body()
|
||||
|
||||
@@ -131,7 +128,7 @@ func _deffered_draw(mesh: Mesh, task_id: int):
|
||||
static_body = _find_static_body()
|
||||
|
||||
func _generate_mesh(vector_indexes: PackedInt32Array, vectors: PackedVector2Array):
|
||||
var meshGenerator = GeoPolyMesh.new(vector_indexes, vectors, extrusion_direction, depth)
|
||||
var meshGenerator = GeoPolyMesh.new(vector_indexes, vectors, extrusion_direction, original_vertices, original_uvs, original_normals, depth)
|
||||
var commited_mesh = meshGenerator.commit_mesh(meshInstance3d.get_active_material(0))
|
||||
meshGenerator.free()
|
||||
|
||||
|
||||
29
demo/assets/scripts/environment/destructable/faces.gd
Normal file
29
demo/assets/scripts/environment/destructable/faces.gd
Normal file
@@ -0,0 +1,29 @@
|
||||
extends RefCounted
|
||||
|
||||
class_name Faces
|
||||
|
||||
var faces: Array[Face]
|
||||
|
||||
func _init(verts: PackedVector3Array, uvs: PackedVector2Array, normals: PackedVector3Array) -> void:
|
||||
assert(len(verts) == len(uvs), "number of verts vs uvs do not match")
|
||||
assert(len(normals)*3 == len(verts), "missing normal data")
|
||||
|
||||
for i in range(0, len(normals)):
|
||||
var start_index = i*3
|
||||
|
||||
var v0 = verts[start_index]
|
||||
var v1 = verts[start_index+1]
|
||||
var v2 = verts[start_index+2]
|
||||
|
||||
var varray = PackedVector3Array([v0, v1, v2])
|
||||
|
||||
var u0 = uvs[start_index]
|
||||
var u1 = uvs[start_index+1]
|
||||
var u2 = uvs[start_index+2]
|
||||
|
||||
var uarray = PackedVector2Array([u0, u1, u2])
|
||||
|
||||
self.faces.append(Face.new(varray, uarray, normals[i]))
|
||||
|
||||
func get_faces() -> Array[Face]:
|
||||
return faces
|
||||
@@ -0,0 +1 @@
|
||||
uid://bw2u2opw8uy82
|
||||
@@ -9,6 +9,8 @@ var uvs = PackedVector2Array()
|
||||
var normals = PackedVector3Array()
|
||||
var array_mesh = ArrayMesh.new()
|
||||
|
||||
var original_face_data: Faces
|
||||
|
||||
var _v_indexes = []
|
||||
var _v_points = []
|
||||
|
||||
@@ -17,24 +19,25 @@ var edges = {}
|
||||
var depth_vector: Vector3
|
||||
var extrude_direction: DestructableWall.ExtrudeDirection
|
||||
|
||||
func _init(vector_indexes: PackedInt32Array, vector_points: PackedVector2Array, extrude_direction: DestructableWall.ExtrudeDirection, depth: float = -0.1):
|
||||
func _init(vector_indexes: PackedInt32Array, vector_points: PackedVector2Array, extrude_direction: DestructableWall.ExtrudeDirection, original_vectors: PackedVector3Array, original_uvs: PackedVector2Array, original_normals: PackedVector3Array, depth: float = -0.1):
|
||||
assert(len(vector_indexes) % 3 == 0 && len(vector_indexes) != 0, "Number of vertex points is not divisible by 3, invalid triangle verticies")
|
||||
surface_array.resize(Mesh.ARRAY_MAX)
|
||||
original_face_data = Faces.new(original_vectors, original_uvs, original_normals)
|
||||
|
||||
self._v_indexes = vector_indexes
|
||||
self.extrude_direction = extrude_direction
|
||||
|
||||
if extrude_direction == DestructableWall.ExtrudeDirection.BACK:
|
||||
for point in vector_points:
|
||||
self._v_points.append(Vector3(point.x, point.y, 0))
|
||||
depth_vector = Vector3.BACK * depth
|
||||
elif extrude_direction == DestructableWall.ExtrudeDirection.RIGHT:
|
||||
for point in vector_points:
|
||||
self._v_points.append(Vector3(0, point.y, point.x))
|
||||
self._v_points.append(Vector3(point.x, point.y, -depth/2))
|
||||
elif extrude_direction == DestructableWall.ExtrudeDirection.RIGHT:
|
||||
depth_vector = Vector3.LEFT * depth
|
||||
for point in vector_points:
|
||||
self._v_points.append(Vector3(depth/2, point.y, point.x))
|
||||
elif extrude_direction == DestructableWall.ExtrudeDirection.UP:
|
||||
for point in vector_points:
|
||||
self._v_points.append(Vector3(point.x, 0, point.y))
|
||||
self._v_points.append(Vector3(point.x, depth/2, point.y))
|
||||
depth_vector = Vector3.DOWN * depth
|
||||
|
||||
|
||||
@@ -71,6 +74,38 @@ func _pre_process_edges():
|
||||
else:
|
||||
self.edges[[min_index, max_index]] += 1
|
||||
|
||||
func in_triag(u, v, w):
|
||||
return u >= 0 and u <= 1 and v >= 0 and v <= 1 and w >= 0 and w <= 1
|
||||
|
||||
func _reduce_significance(value: float):
|
||||
return snapped(value, 0.0001)
|
||||
|
||||
func _new_interpolated_uv_point(new_vector: Vector3, normal: Vector3) -> Vector2:
|
||||
# get faces that have the same normal e.g faces that the point can fall into
|
||||
var faces = self.original_face_data.get_faces()
|
||||
|
||||
var possible_faces: Array[Face] = []
|
||||
|
||||
for f in faces:
|
||||
if normal.dot(f.normal) > 0.99:
|
||||
possible_faces.append(f)
|
||||
|
||||
for ps in possible_faces:
|
||||
var ps_face_verts = ps.vectors
|
||||
var b_points = Geometry3D.get_triangle_barycentric_coords(new_vector, ps_face_verts[0], ps_face_verts[1], ps_face_verts[2])
|
||||
|
||||
var u = self._reduce_significance(b_points[0])
|
||||
var v = self._reduce_significance(b_points[1])
|
||||
var w = self._reduce_significance(b_points[2])
|
||||
|
||||
|
||||
var uv_coords = ps.uvs
|
||||
if in_triag(u, v, w):
|
||||
return (uv_coords[0] * u) + (uv_coords[1] * v) + (uv_coords[2] * w)
|
||||
|
||||
# for destructable walls, if we couldn't interpolate a uv, this is fine
|
||||
return Vector2.ZERO
|
||||
|
||||
func get_loops():
|
||||
var unvisted_edges = self.get_outline_edge()
|
||||
var loops = []
|
||||
@@ -167,34 +202,16 @@ func _draw_sides():
|
||||
|
||||
var n1 = self._calc_triangle_normal(v2, v1, v0)
|
||||
self.verts.append_array([v0, v1, v2, v1, v3, v2])
|
||||
_append_uvs4(v0, v1, v2, v3, n1)
|
||||
_append_uvs3(v0, v1, v2, n1)
|
||||
_append_uvs3(v1, v3, v2, n1)
|
||||
self.normals.append_array([n1, n1, n1, n1, n1, n1])
|
||||
|
||||
func _append_uvs4(v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, normal: Vector3):
|
||||
if normal == Vector3.BACK or normal == Vector3.FORWARD:
|
||||
self.uvs.append_array([Vector2(-v0.x, -v0.y), Vector2(-v1.x, -v1.y), Vector2(-v2.x, -v2.y), Vector2(-v1.x, -v1.y), Vector2(-v3.x, -v3.y), Vector2(-v2.x, -v2.y)])
|
||||
return
|
||||
elif normal == Vector3.RIGHT or normal == Vector3.LEFT:
|
||||
self.uvs.append_array([Vector2(-v0.z, -v0.y), Vector2(-v1.z, -v1.y), Vector2(-v2.z, -v2.y), Vector2(-v1.z, -v1.y), Vector2(-v3.z, -v3.y), Vector2(-v2.z, -v2.y)])
|
||||
return
|
||||
elif normal == Vector3.UP or normal == Vector3.DOWN:
|
||||
self.uvs.append_array([Vector2(-v0.x, -v0.z), Vector2(-v1.x, -v1.z), Vector2(-v2.x, -v2.z), Vector2(-v1.x, -v1.z), Vector2(-v3.x, -v3.z), Vector2(-v2.x, -v2.z)])
|
||||
return
|
||||
|
||||
self.uvs.append_array([Vector2(-v0.x, -v0.y), Vector2(-v1.x, -v1.y), Vector2(-v2.x, -v2.y), Vector2(-v1.x, -v1.y), Vector2(-v3.x, -v3.y), Vector2(-v2.x, -v2.y)])
|
||||
|
||||
func _append_uvs3(v0: Vector3, v1: Vector3, v2: Vector3, normal: Vector3):
|
||||
if normal == Vector3.BACK or normal == Vector3.FORWARD:
|
||||
self.uvs.append_array([Vector2(-v0.x, -v0.y), Vector2(-v1.x, -v1.y), Vector2(-v2.x, -v2.y)])
|
||||
return
|
||||
elif normal == Vector3.RIGHT or normal == Vector3.LEFT:
|
||||
self.uvs.append_array([Vector2(-v0.z, -v0.y), Vector2(-v1.z, -v1.y), Vector2(-v2.z, -v2.y)])
|
||||
return
|
||||
elif normal == Vector3.UP or normal == Vector3.DOWN:
|
||||
self.uvs.append_array([Vector2(-v0.x, -v0.z), Vector2(-v1.x, -v1.z), Vector2(-v2.x, -v2.z)])
|
||||
return
|
||||
var uv0 = self._new_interpolated_uv_point(v0, normal)
|
||||
var uv1 = self._new_interpolated_uv_point(v1, normal)
|
||||
var uv2 = self._new_interpolated_uv_point(v2, normal)
|
||||
|
||||
self.uvs.append_array([Vector2(-v0.x, -v0.y), Vector2(-v1.x, -v1.y), Vector2(-v2.x, -v2.y)])
|
||||
self.uvs.append_array([uv0, uv1, uv2])
|
||||
|
||||
# front face
|
||||
func _draw_mesh():
|
||||
@@ -204,10 +221,9 @@ func _draw_mesh():
|
||||
|
||||
var normal = self._calc_triangle_normal(vector3C, vector3B, vector3A)
|
||||
|
||||
|
||||
self.verts.append_array([vector3A, vector3B, vector3C])
|
||||
_append_uvs3(vector3A, vector3B, vector3C, normal)
|
||||
|
||||
|
||||
self.normals.append_array([normal, normal, normal])
|
||||
|
||||
# insert each front face into the mesh "clockwise"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
extends Resource
|
||||
|
||||
class_name Face
|
||||
|
||||
var vectors: PackedVector3Array
|
||||
var uvs: PackedVector2Array
|
||||
var normal: Vector3
|
||||
|
||||
func _init(vectors: PackedVector3Array, uvs: PackedVector2Array, normal: Vector3):
|
||||
assert(len(vectors) % 3 == 0, "not a face")
|
||||
assert(len(uvs) == len(vectors), "missing uvs for vectors")
|
||||
self.vectors = vectors.duplicate()
|
||||
self.uvs = uvs.duplicate()
|
||||
self.normal = normal
|
||||
@@ -0,0 +1 @@
|
||||
uid://bto8ov1esykmk
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 7.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user