Skip to content

Commit 462cf1c

Browse files
committed
VMF: Optimize merged faces cleanup
1 parent e29336a commit 462cf1c

9 files changed

Lines changed: 82 additions & 65 deletions

File tree

addons/godotvmf/entities/func_detail.gd

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
class_name func_detail extends VMFEntityNode
33

44
func _entity_setup(entity: VMFEntity):
5-
var mesh = get_mesh();
6-
mesh.lightmap_unwrap(global_transform, config.import.lightmap_texel_size);
7-
8-
$MeshInstance3D.cast_shadow = entity.data.get("disableshadows", 0) == 0;
5+
var mesh = get_mesh(true, false);
96

107
if !mesh or mesh.get_surface_count() == 0:
118
queue_free();
129
return;
1310

11+
var unwrap_err = mesh.lightmap_unwrap(global_transform, config.import.lightmap_texel_size);
12+
if unwrap_err != OK:
13+
VMFLogger.warn("func_detail %s: lightmap_unwrap failed (%d), skipping UV2" % [entity.id, unwrap_err]);
14+
15+
$MeshInstance3D.cast_shadow = entity.data.get("disableshadows", 0) == 0;
1416
$MeshInstance3D.set_mesh(mesh);
1517
$MeshInstance3D/StaticBody3D/CollisionShape3D.shape = $MeshInstance3D.mesh.create_trimesh_shape();

addons/godotvmf/entities/prop_static.gd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ func _entity_setup(e: VMFEntity):
3030
model_instance.visibility_range_end_margin = fade_margin;
3131

3232
var geometry_node := get_vmfnode().geometry as Node3D;
33+
if not geometry_node:
34+
var node := Node3D.new();
35+
node.name = "Geometry";
36+
get_vmfnode().add_child(node);
37+
node.set_owner(get_vmfnode().owner);
38+
return;
39+
3340
var models_node := geometry_node.get_node_or_null("StaticProps") as Node3D;
3441

3542
if not models_node:

addons/godotvmf/shaders/WorldVertexTransitionMaterial.gd.uid

Lines changed: 0 additions & 1 deletion
This file was deleted.

addons/godotvmf/shaders/WorldVertexTransitionMaterial.gdshader.uid

Lines changed: 0 additions & 1 deletion
This file was deleted.

addons/godotvmf/src/structs/vmf_displacement_info.gd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ func get_color(x: float, y: float) -> Color:
6868
var index = y + x * verts_count;
6969

7070
if alphas.size() == 0:
71-
return Color8(255, 0, 0);
71+
return Color8(0, 0, 0);
7272

73-
return Color8(int(alphas[index]), 0, 0);
73+
return Color8(255 - int(alphas[index]), 0, 0);
7474

7575
func get_vertices() -> Array[Vector3]:
7676
if side.vertices.size() < 3:

addons/godotvmf/src/structs/vmf_side.gd

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ func _init(raw: Dictionary, _solid: VMFSolid) -> void:
2626
smoothing_groups = raw.get("smoothing_groups", 0);
2727
solid = _solid;
2828
plane = raw.plane.value;
29+
if id == 18590:
30+
print("Debug plane points: ", raw.plane);
2931

3032
vaxis = VMFTexCoord.new(raw.vaxis);
3133
uaxis = VMFTexCoord.new(raw.uaxis);
@@ -59,47 +61,9 @@ func filter_existing_vertices(solid: VMFSolid) -> void:
5961

6062
vertices = PackedVector3Array(new_vertices);
6163

62-
## Internal method. Calculates the vertices of this side if they are not already calculated.
63-
## Called automatically from VMFSolid
64+
## Internal method. Called from VMFSolid after vertices have been populated either
65+
## via vertices_plus (in _init) or via VMFSolid._compute_vertices_from_planes().
6466
func calculate_vertices() -> void:
65-
# Already calculated
66-
if vertices.size() > 0:
67-
if is_displacement:
68-
dispinfo.calculate_vertices();
69-
return;
70-
71-
var raw_vertices: Array[Vector3] = [];
72-
var cache = {};
73-
74-
var is_vertice_exists = func(vector: Vector3):
75-
var hash_value: int = hash(Vector3i(vector));
76-
if hash_value in cache: return true;
77-
78-
cache[hash_value] = 1;
79-
return false;
80-
81-
for side2 in solid.sides:
82-
if side2 == self: continue;
83-
84-
for side3 in solid.sides:
85-
if side2 == side3 or side3 == self: continue;
86-
var vertex = solid.get_planes_intersection_point(self, side2, side3);
87-
88-
if vertex == null or is_vertice_exists.call(vertex): continue;
89-
raw_vertices.append(vertex as Vector3);
90-
91-
raw_vertices = raw_vertices.filter(func(vertex):
92-
return not solid.sides.any(func(s: VMFSide): return s.plane.distance_to(vertex) > 0.01);
93-
);
94-
95-
var side_normal: Vector3 = plane.normal;
96-
var center := (plane_points[0] + plane_points[1] + plane_points[2]) / 3.0;
97-
var vector_sorter: VMFVectorSorter = VMFVectorSorter.new(side_normal, center);
98-
99-
raw_vertices.sort_custom(vector_sorter.sort);
100-
101-
vertices = PackedVector3Array(raw_vertices);
102-
10367
if is_displacement:
10468
dispinfo.calculate_vertices();
10569

addons/godotvmf/src/structs/vmf_solid.gd

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ class_name VMFSolid extends RefCounted
33
var id: int = -1;
44
var sides: Array[VMFSide] = [];
55
var has_displacement: bool = false;
6-
var intersections: Dictionary = {};
76
var min: Vector3 = Vector3(INF, INF, INF);
87
var max: Vector3 = Vector3(-INF, -INF, -INF);
98
var vertices: Array[Vector3] = [];
@@ -31,23 +30,66 @@ func define_sides(raw: Dictionary) -> void:
3130

3231
if side_instance.is_displacement:
3332
has_displacement = true;
34-
33+
34+
var needs_computation := false;
35+
for s in sides:
36+
if s.vertices.size() == 0:
37+
needs_computation = true;
38+
break;
39+
40+
if needs_computation:
41+
_compute_vertices_from_planes();
42+
3543
for side in sides:
3644
side.calculate_vertices();
3745

38-
intersections = {};
3946
vertices = [];
4047

41-
func get_planes_intersection_point(side: VMFSide, side2: VMFSide, side3: VMFSide) -> Variant:
42-
var d: Array[int] = [side.id, side2.id, side3.id];
43-
d.sort();
48+
for side in sides:
49+
for v in side.vertices:
50+
if v.x < min.x: min.x = v.x;
51+
if v.y < min.y: min.y = v.y;
52+
if v.z < min.z: min.z = v.z;
53+
if v.x > max.x: max.x = v.x;
54+
if v.y > max.y: max.y = v.y;
55+
if v.z > max.z: max.z = v.z;
56+
57+
## Computes vertices for all sides that don't have pre-computed vertices (i.e. no vertices_plus).
58+
## Iterates all unique plane triples once (C(n,3)), tests each intersection against all planes,
59+
## then distributes valid vertices to the three involved sides and sorts them.
60+
func _compute_vertices_from_planes() -> void:
61+
var n := sides.size();
62+
var side_verts: Array[Dictionary] = [];
63+
side_verts.resize(n);
64+
for i in range(n):
65+
side_verts[i] = {};
66+
67+
for i in range(n):
68+
for j in range(i + 1, n):
69+
for k in range(j + 1, n):
70+
var v: Variant = sides[i].plane.intersect_3(sides[j].plane, sides[k].plane);
71+
if v == null: continue;
72+
var vertex := v as Vector3;
73+
74+
var valid := true;
75+
for s in sides:
76+
if s.plane.distance_to(vertex) > 0.01:
77+
valid = false;
78+
break;
79+
if not valid: continue;
80+
81+
var vhash := hash(Vector3i(vertex));
82+
side_verts[i][vhash] = vertex;
83+
side_verts[j][vhash] = vertex;
84+
side_verts[k][vhash] = vertex;
4485

45-
var ihash := hash(d);
46-
var is_intersection_defined: bool = ihash in intersections;
86+
for i in range(n):
87+
if sides[i].vertices.size() > 0: continue;
4788

48-
if is_intersection_defined:
49-
return intersections[ihash];
50-
else:
51-
var vertex: Variant = side.plane.intersect_3(side2.plane, side3.plane);
52-
intersections[ihash] = vertex;
53-
return vertex;
89+
var raw: Array = side_verts[i].values();
90+
var side_normal := sides[i].plane.normal;
91+
var pp := sides[i].plane_points;
92+
var center := (pp[0] + pp[1] + pp[2]) / 3.0;
93+
var sorter := VMFVectorSorter.new(side_normal, center);
94+
raw.sort_custom(sorter.sort);
95+
sides[i].vertices = PackedVector3Array(raw);

addons/godotvmf/src/vmf_tool.gd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ static func remove_merged_faces(brush_a: VMFSolid, brushes: Array[VMFSolid]) ->
173173
for brush_b in brushes:
174174
if brush_a == brush_b: continue;
175175

176+
if brush_a.max.x < brush_b.min.x or brush_b.max.x < brush_a.min.x: continue;
177+
if brush_a.max.y < brush_b.min.y or brush_b.max.y < brush_a.min.y: continue;
178+
if brush_a.max.z < brush_b.min.z or brush_b.max.z < brush_a.min.z: continue;
179+
176180
for side_a in brush_a.sides:
177181
var a_removed = false;
178182

addons/godotvmf/utils/vdf_parser.gd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ static var _vector2Regex := RegEx.create_from_string('^([-\\d\\.e]+)\\s([-\\d\\.
88
static var _colorRegex := RegEx.create_from_string('^([-\\d\\.e]+)\\s([-\\d\\.e]+)\\s([-\\d\\.e]+)\\s([-\\d\\.e]+)$');
99
static var _uvRegex := RegEx.create_from_string('\\[([-\\d\\.e]+)\\s([-\\d\\.e]+)\\s([-\\d\\.e]+)\\s([-\\d\\.e]+)\\]\\s([-\\d\\.e]+)');
1010
static var _planeRegex := RegEx.create_from_string('\\(([\\d\\-\\.e]+\\s[\\d\\-\\.e]+\\s[\\d\\-\\.e]+)\\)\\s?\\(([\\d\\-\\.e]+\\s[\\d\\-\\.e]+\\s[\\d\\-\\.e]+)\\)\\s?\\(([\\d\\-\\.e]+\\s[\\d\\-\\.e]+\\s[\\d\\-\\.e]+)\\)');
11-
static var _commentRegex := RegEx.create_from_string('\\s+?\\/\\/.+');
11+
static var _commentRegex := RegEx.create_from_string('\\s*\\/\\/.+');
1212

1313
## Returns Dictionary representation of the Valve Data Format file located at [file_path].
1414
## Returns null and prints an error if the file could not be read or parsed.
@@ -60,7 +60,7 @@ static func parse_value(line: String) -> Variant:
6060

6161
var v = Plane(plane[0], plane[1], plane[2]);
6262

63-
if not v:
63+
if v.normal == Vector3.ZERO:
6464
push_error('ValveFormatParser: Failed to create plane from: ' + line);
6565
return null;
6666

@@ -71,7 +71,7 @@ static func parse_value(line: String) -> Variant:
7171
return float(line);
7272
return line;
7373
static func define_structure(hierarchy: Array, line: String, keys_to_lower = false):
74-
var _name := line.strip_edges().replace('{', '').replace('"', '');
74+
var _name := line.strip_edges().replace('{', '').replace('"', '').strip_edges();
7575
if keys_to_lower:
7676
_name = _name.to_lower();
7777
var newStruct = {};

0 commit comments

Comments
 (0)