mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-08-03 01:35:25 +00:00
Isometric test
This commit is contained in:
parent
70d22b2be8
commit
1434f7aa6d
806 changed files with 18966 additions and 15 deletions
136
addons/func_godot/src/core/func_godot.gd
Normal file
136
addons/func_godot/src/core/func_godot.gd
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
class_name FuncGodot extends RefCounted
|
||||
|
||||
var map_data:= FuncGodotMapData.new()
|
||||
var map_parser:= FuncGodotMapParser.new(map_data)
|
||||
var geo_generator = preload("res://addons/func_godot/src/core/func_godot_geo_generator.gd").new(map_data)
|
||||
var map_settings: FuncGodotMapSettings = null:
|
||||
set(new):
|
||||
if not new or new == map_settings: return
|
||||
surface_gatherer.map_settings = new
|
||||
map_settings = new
|
||||
var surface_gatherer:= FuncGodotSurfaceGatherer.new(map_data, map_settings)
|
||||
|
||||
func load_map(filename: String, keep_tb_groups: bool) -> void:
|
||||
map_parser.load_map(filename, keep_tb_groups)
|
||||
|
||||
func get_texture_list() -> PackedStringArray:
|
||||
var g_textures: PackedStringArray
|
||||
var tex_count: int = map_data.textures.size()
|
||||
|
||||
g_textures.resize(tex_count)
|
||||
for i in range(tex_count):
|
||||
g_textures.set(i, map_data.textures[i].name)
|
||||
|
||||
return g_textures
|
||||
|
||||
func set_entity_definitions(entity_defs: Dictionary) -> void:
|
||||
for i in range(entity_defs.size()):
|
||||
var classname: String = entity_defs.keys()[i]
|
||||
var spawn_type: int = entity_defs.values()[i].get("spawn_type", FuncGodotMapData.FuncGodotEntitySpawnType.ENTITY)
|
||||
var origin_type: int = entity_defs.values()[i].get("origin_type", FuncGodotMapData.FuncGodotEntityOriginType.BOUNDS_CENTER)
|
||||
var metadata_inclusion_flags: int = entity_defs.values()[i].get("metadata_inclusion_flags", FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags.NONE)
|
||||
map_data.set_entity_types_by_classname(classname, spawn_type, origin_type, metadata_inclusion_flags)
|
||||
|
||||
func get_texture_info(texture_name: String) -> FuncGodotMapData.FuncGodotTextureType:
|
||||
if texture_name == map_settings.origin_texture:
|
||||
return FuncGodotMapData.FuncGodotTextureType.ORIGIN
|
||||
return FuncGodotMapData.FuncGodotTextureType.NORMAL
|
||||
|
||||
func generate_geometry(texture_dict: Dictionary) -> void:
|
||||
var keys: Array = texture_dict.keys()
|
||||
for key in keys:
|
||||
var val: Vector2 = texture_dict[key]
|
||||
map_data.set_texture_info(key, val.x, val.y, get_texture_info(key))
|
||||
geo_generator.run()
|
||||
|
||||
func get_entity_dicts() -> Array:
|
||||
var ent_dicts: Array
|
||||
for entity in map_data.entities:
|
||||
var dict: Dictionary
|
||||
dict["brush_count"] = entity.brushes.size()
|
||||
|
||||
# TODO: This is a horrible remnant of the worldspawn layer system, remove it.
|
||||
var brush_indices: PackedInt64Array
|
||||
brush_indices.resize(entity.brushes.size())
|
||||
for b in range(entity.brushes.size()):
|
||||
brush_indices[b] = b
|
||||
|
||||
dict["brush_indices"] = brush_indices
|
||||
dict["center"] = Vector3(entity.center.y, entity.center.z, entity.center.x)
|
||||
dict["properties"] = entity.properties
|
||||
|
||||
ent_dicts.append(dict)
|
||||
|
||||
return ent_dicts
|
||||
|
||||
func gather_texture_surfaces(texture_name: String) -> Dictionary:
|
||||
var sg: FuncGodotSurfaceGatherer = FuncGodotSurfaceGatherer.new(map_data, map_settings)
|
||||
sg.reset_params()
|
||||
sg.split_type = FuncGodotSurfaceGatherer.SurfaceSplitType.ENTITY
|
||||
const MFlags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags
|
||||
sg.metadata_skip_flags = MFlags.TEXTURES | MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP
|
||||
sg.set_texture_filter(texture_name)
|
||||
sg.set_clip_filter_texture(map_settings.clip_texture)
|
||||
sg.set_skip_filter_texture(map_settings.skip_texture)
|
||||
sg.set_origin_filter_texture(map_settings.origin_texture)
|
||||
sg.run()
|
||||
return {
|
||||
surfaces = fetch_surfaces(sg),
|
||||
metadata = sg.out_metadata,
|
||||
}
|
||||
|
||||
func gather_entity_convex_collision_surfaces(entity_idx: int) -> void:
|
||||
surface_gatherer.reset_params()
|
||||
surface_gatherer.split_type = FuncGodotSurfaceGatherer.SurfaceSplitType.BRUSH
|
||||
surface_gatherer.entity_filter_idx = entity_idx
|
||||
surface_gatherer.set_origin_filter_texture(map_settings.origin_texture)
|
||||
surface_gatherer.run()
|
||||
|
||||
func gather_entity_concave_collision_surfaces(entity_idx: int) -> void:
|
||||
surface_gatherer.reset_params()
|
||||
surface_gatherer.split_type = FuncGodotSurfaceGatherer.SurfaceSplitType.NONE
|
||||
surface_gatherer.entity_filter_idx = entity_idx
|
||||
const MFlags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags
|
||||
surface_gatherer.metadata_skip_flags |= MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP
|
||||
surface_gatherer.set_skip_filter_texture(map_settings.skip_texture)
|
||||
surface_gatherer.set_origin_filter_texture(map_settings.origin_texture)
|
||||
surface_gatherer.run()
|
||||
|
||||
func fetch_surfaces(sg: FuncGodotSurfaceGatherer) -> Array:
|
||||
var surfs: Array[FuncGodotMapData.FuncGodotFaceGeometry] = sg.out_surfaces
|
||||
var surf_array: Array
|
||||
|
||||
for surf in surfs:
|
||||
if surf == null or surf.vertices.size() == 0:
|
||||
surf_array.append(null)
|
||||
continue
|
||||
|
||||
var vertices: PackedVector3Array
|
||||
var normals: PackedVector3Array
|
||||
var tangents: PackedFloat64Array
|
||||
var uvs: PackedVector2Array
|
||||
for v in surf.vertices:
|
||||
vertices.append(Vector3(v.vertex.y, v.vertex.z, v.vertex.x) * map_settings.scale_factor)
|
||||
normals.append(Vector3(v.normal.y, v.normal.z, v.normal.x))
|
||||
tangents.append(v.tangent.y)
|
||||
tangents.append(v.tangent.z)
|
||||
tangents.append(v.tangent.x)
|
||||
tangents.append(v.tangent.w)
|
||||
uvs.append(Vector2(v.uv.x, v.uv.y))
|
||||
|
||||
var indices: PackedInt32Array
|
||||
if surf.indicies.size() > 0:
|
||||
indices.append_array(surf.indicies)
|
||||
|
||||
var brush_array: Array
|
||||
brush_array.resize(Mesh.ARRAY_MAX)
|
||||
|
||||
brush_array[Mesh.ARRAY_VERTEX] = vertices
|
||||
brush_array[Mesh.ARRAY_NORMAL] = normals
|
||||
brush_array[Mesh.ARRAY_TANGENT] = tangents
|
||||
brush_array[Mesh.ARRAY_TEX_UV] = uvs
|
||||
brush_array[Mesh.ARRAY_INDEX] = indices
|
||||
|
||||
surf_array.append(brush_array)
|
||||
|
||||
return surf_array
|
||||
1
addons/func_godot/src/core/func_godot.gd.uid
Normal file
1
addons/func_godot/src/core/func_godot.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bvstd30rkrap
|
||||
381
addons/func_godot/src/core/func_godot_geo_generator.gd
Normal file
381
addons/func_godot/src/core/func_godot_geo_generator.gd
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
extends RefCounted
|
||||
|
||||
# Min distance between two verts in a brush before they're merged. Higher values fix angled brushes near extents.
|
||||
const CMP_EPSILON:= 0.008
|
||||
|
||||
const UP_VECTOR:= Vector3(0.0, 0.0, 1.0)
|
||||
const RIGHT_VECTOR:= Vector3(0.0, 1.0, 0.0)
|
||||
const FORWARD_VECTOR:= Vector3(1.0, 0.0, 0.0)
|
||||
|
||||
var map_data: FuncGodotMapData
|
||||
|
||||
var wind_entity_idx: int = 0
|
||||
var wind_brush_idx: int = 0
|
||||
var wind_face_idx: int = 0
|
||||
var wind_face_center: Vector3
|
||||
var wind_face_basis: Vector3
|
||||
var wind_face_normal: Vector3
|
||||
|
||||
func _init(in_map_data: FuncGodotMapData) -> void:
|
||||
map_data = in_map_data
|
||||
|
||||
func sort_vertices_by_winding(a: FuncGodotMapData.FuncGodotFaceVertex, b: FuncGodotMapData.FuncGodotFaceVertex) -> bool:
|
||||
var face: FuncGodotMapData.FuncGodotFace = map_data.entities[wind_entity_idx].brushes[wind_brush_idx].faces[wind_face_idx]
|
||||
var face_geo: FuncGodotMapData.FuncGodotFaceGeometry = map_data.entity_geo[wind_entity_idx].brushes[wind_brush_idx].faces[wind_face_idx]
|
||||
|
||||
var u: Vector3 = wind_face_basis.normalized()
|
||||
var v: Vector3 = u.cross(wind_face_normal).normalized()
|
||||
|
||||
var loc_a: Vector3 = a.vertex - wind_face_center
|
||||
var a_pu: float = loc_a.dot(u)
|
||||
var a_pv: float = loc_a.dot(v)
|
||||
|
||||
var loc_b: Vector3 = b.vertex - wind_face_center
|
||||
var b_pu: float = loc_b.dot(u)
|
||||
var b_pv: float = loc_b.dot(v)
|
||||
|
||||
var a_angle: float = atan2(a_pv, a_pu)
|
||||
var b_angle: float = atan2(b_pv, b_pu)
|
||||
|
||||
return a_angle < b_angle
|
||||
|
||||
# returns null if no intersection, else intersection vertex.
|
||||
func intersect_face(f0: FuncGodotMapData.FuncGodotFace, f1: FuncGodotMapData.FuncGodotFace, f2: FuncGodotMapData.FuncGodotFace) -> Variant:
|
||||
var n0:= f0.plane_normal
|
||||
var n1:= f1.plane_normal
|
||||
var n2:= f2.plane_normal
|
||||
var denom: float = n0.cross(n1).dot(n2)
|
||||
if denom > 0.0:
|
||||
return (n1.cross(n2) * f0.plane_dist + n2.cross(n0) * f1.plane_dist + n0.cross(n1) * f2.plane_dist) / denom
|
||||
return null
|
||||
|
||||
func vertex_in_hull(faces: Array[FuncGodotMapData.FuncGodotFace], vertex: Vector3) -> bool:
|
||||
for face in faces:
|
||||
var proj: float = face.plane_normal.dot(vertex)
|
||||
if proj > face.plane_dist and absf(face.plane_dist - proj) > CMP_EPSILON:
|
||||
return false
|
||||
return true
|
||||
|
||||
func get_standard_uv(vertex: Vector3, face: FuncGodotMapData.FuncGodotFace, texture_width: int, texture_height: int) -> Vector2:
|
||||
var uv_out: Vector2
|
||||
var du:= absf(face.plane_normal.dot(UP_VECTOR))
|
||||
var dr:= absf(face.plane_normal.dot(RIGHT_VECTOR))
|
||||
var df:= absf(face.plane_normal.dot(FORWARD_VECTOR))
|
||||
|
||||
if du >= dr and du >= df:
|
||||
uv_out = Vector2(vertex.x, -vertex.y)
|
||||
elif dr >= du and dr >= df:
|
||||
uv_out = Vector2(vertex.x, -vertex.z)
|
||||
elif df >= du and df >= dr:
|
||||
uv_out = Vector2(vertex.y, -vertex.z)
|
||||
|
||||
var angle: float = deg_to_rad(face.uv_extra.rot)
|
||||
uv_out = Vector2(
|
||||
uv_out.x * cos(angle) - uv_out.y * sin(angle),
|
||||
uv_out.x * sin(angle) + uv_out.y * cos(angle))
|
||||
|
||||
uv_out.x /= texture_width
|
||||
uv_out.y /= texture_height
|
||||
|
||||
uv_out.x /= face.uv_extra.scale_x
|
||||
uv_out.y /= face.uv_extra.scale_y
|
||||
|
||||
uv_out.x += face.uv_standard.x / texture_width
|
||||
uv_out.y += face.uv_standard.y / texture_height
|
||||
|
||||
return uv_out
|
||||
|
||||
func get_valve_uv(vertex: Vector3, face: FuncGodotMapData.FuncGodotFace, texture_width: int, texture_height: int) -> Vector2:
|
||||
var uv_out: Vector2
|
||||
var u_axis:= face.uv_valve.u.axis
|
||||
var v_axis:= face.uv_valve.v.axis
|
||||
var u_shift:= face.uv_valve.u.offset
|
||||
var v_shift:= face.uv_valve.v.offset
|
||||
|
||||
uv_out.x = u_axis.dot(vertex);
|
||||
uv_out.y = v_axis.dot(vertex);
|
||||
|
||||
uv_out.x /= texture_width;
|
||||
uv_out.y /= texture_height;
|
||||
|
||||
uv_out.x /= face.uv_extra.scale_x;
|
||||
uv_out.y /= face.uv_extra.scale_y;
|
||||
|
||||
uv_out.x += u_shift / texture_width;
|
||||
uv_out.y += v_shift / texture_height;
|
||||
|
||||
return uv_out
|
||||
|
||||
func get_standard_tangent(face: FuncGodotMapData.FuncGodotFace) -> Vector4:
|
||||
var du:= face.plane_normal.dot(UP_VECTOR)
|
||||
var dr:= face.plane_normal.dot(RIGHT_VECTOR)
|
||||
var df:= face.plane_normal.dot(FORWARD_VECTOR)
|
||||
var dua:= absf(du)
|
||||
var dra:= absf(dr)
|
||||
var dfa:= absf(df)
|
||||
|
||||
var u_axis: Vector3
|
||||
var v_sign: float = 0.0
|
||||
|
||||
if dua >= dra and dua >= dfa:
|
||||
u_axis = FORWARD_VECTOR
|
||||
v_sign = signf(du)
|
||||
elif dra >= dua and dra >= dfa:
|
||||
u_axis = FORWARD_VECTOR
|
||||
v_sign = -signf(dr)
|
||||
elif dfa >= dua and dfa >= dra:
|
||||
u_axis = RIGHT_VECTOR
|
||||
v_sign = signf(df)
|
||||
|
||||
v_sign *= signf(face.uv_extra.scale_y);
|
||||
u_axis = u_axis.rotated(face.plane_normal, deg_to_rad(-face.uv_extra.rot) * v_sign)
|
||||
|
||||
return Vector4(u_axis.x, u_axis.y, u_axis.z, v_sign)
|
||||
|
||||
func get_valve_tangent(face: FuncGodotMapData.FuncGodotFace) -> Vector4:
|
||||
var u_axis:= face.uv_valve.u.axis.normalized()
|
||||
var v_axis:= face.uv_valve.v.axis.normalized()
|
||||
var v_sign = -signf(face.plane_normal.cross(u_axis).dot(v_axis))
|
||||
return Vector4(u_axis.x, u_axis.y, u_axis.z, v_sign)
|
||||
|
||||
func generate_brush_vertices(entity_idx: int, brush_idx: int) -> void:
|
||||
var entity: FuncGodotMapData.FuncGodotEntity = map_data.entities[entity_idx]
|
||||
var brush: FuncGodotMapData.FuncGodotBrush = entity.brushes[brush_idx]
|
||||
var face_count: int = brush.faces.size()
|
||||
|
||||
var entity_geo: FuncGodotMapData.FuncGodotEntityGeometry = map_data.entity_geo[entity_idx]
|
||||
var brush_geo: FuncGodotMapData.FuncGodotBrushGeometry = entity_geo.brushes[brush_idx]
|
||||
|
||||
var phong: bool = entity.properties.get("_phong", "0") == "1"
|
||||
var phong_angle_str: String = entity.properties.get("_phong_angle", "89")
|
||||
var phong_angle: float = float(phong_angle_str) if phong_angle_str.is_valid_float() else 89.0
|
||||
|
||||
for f0 in range(face_count):
|
||||
var face: FuncGodotMapData.FuncGodotFace = brush.faces[f0]
|
||||
var face_geo: FuncGodotMapData.FuncGodotFaceGeometry = brush_geo.faces[f0]
|
||||
var texture: FuncGodotMapData.FuncGodotTextureData = map_data.textures[face.texture_idx]
|
||||
|
||||
for f1 in range(face_count):
|
||||
for f2 in range(face_count):
|
||||
var vertex = intersect_face(brush.faces[f0], brush.faces[f1], brush.faces[f2])
|
||||
if not vertex is Vector3:
|
||||
continue
|
||||
if not vertex_in_hull(brush.faces, vertex):
|
||||
continue
|
||||
|
||||
var merged: bool = false
|
||||
for f3 in range(f0):
|
||||
var other_face_geo : FuncGodotMapData.FuncGodotFaceGeometry = brush_geo.faces[f3]
|
||||
for i in range(len(other_face_geo.vertices)):
|
||||
if other_face_geo.vertices[i].vertex.distance_to(vertex) < CMP_EPSILON:
|
||||
vertex = other_face_geo.vertices[i].vertex
|
||||
merged = true;
|
||||
break
|
||||
|
||||
if merged:
|
||||
break
|
||||
|
||||
var normal: Vector3 = face.plane_normal
|
||||
if phong:
|
||||
var threshold:= cos((phong_angle + 0.01) * 0.0174533)
|
||||
if face.plane_normal.dot(brush.faces[f1].plane_normal) > threshold:
|
||||
normal += brush.faces[f1].plane_normal
|
||||
if face.plane_normal.dot(brush.faces[f2].plane_normal) > threshold:
|
||||
normal += brush.faces[f2].plane_normal
|
||||
normal = normal.normalized()
|
||||
|
||||
var uv: Vector2
|
||||
var tangent: Vector4
|
||||
if face.is_valve_uv:
|
||||
uv = get_valve_uv(vertex, face, texture.width, texture.height)
|
||||
tangent = get_valve_tangent(face)
|
||||
else:
|
||||
uv = get_standard_uv(vertex, face, texture.width, texture.height)
|
||||
tangent = get_standard_tangent(face)
|
||||
|
||||
# Check for a duplicate vertex in the current face.
|
||||
var duplicate_idx: int = -1
|
||||
for i in range(face_geo.vertices.size()):
|
||||
if face_geo.vertices[i].vertex == vertex:
|
||||
duplicate_idx = i
|
||||
break
|
||||
|
||||
if duplicate_idx < 0:
|
||||
var new_face_vert:= FuncGodotMapData.FuncGodotFaceVertex.new()
|
||||
new_face_vert.vertex = vertex
|
||||
new_face_vert.normal = normal
|
||||
new_face_vert.tangent = tangent
|
||||
new_face_vert.uv = uv
|
||||
face_geo.vertices.append(new_face_vert)
|
||||
elif phong:
|
||||
face_geo.vertices[duplicate_idx].normal += normal
|
||||
|
||||
# maybe optimisable?
|
||||
for face_geo in brush_geo.faces:
|
||||
for i in range(face_geo.vertices.size()):
|
||||
face_geo.vertices[i].normal = face_geo.vertices[i].normal.normalized()
|
||||
|
||||
func run() -> void:
|
||||
map_data.entity_geo.resize(map_data.entities.size())
|
||||
for i in range(map_data.entity_geo.size()):
|
||||
map_data.entity_geo[i] = FuncGodotMapData.FuncGodotEntityGeometry.new()
|
||||
|
||||
for e in range(map_data.entities.size()):
|
||||
var entity: FuncGodotMapData.FuncGodotEntity = map_data.entities[e]
|
||||
var entity_geo: FuncGodotMapData.FuncGodotEntityGeometry = map_data.entity_geo[e]
|
||||
entity_geo.brushes.resize(entity.brushes.size())
|
||||
for i in range(entity_geo.brushes.size()):
|
||||
entity_geo.brushes[i] = FuncGodotMapData.FuncGodotBrushGeometry.new()
|
||||
|
||||
for b in range(entity.brushes.size()):
|
||||
var brush: FuncGodotMapData.FuncGodotBrush = entity.brushes[b]
|
||||
var brush_geo: FuncGodotMapData.FuncGodotBrushGeometry = entity_geo.brushes[b]
|
||||
brush_geo.faces.resize(brush.faces.size())
|
||||
for i in range(brush_geo.faces.size()):
|
||||
brush_geo.faces[i] = FuncGodotMapData.FuncGodotFaceGeometry.new()
|
||||
|
||||
var generate_vertices_task = func(e):
|
||||
var entity: FuncGodotMapData.FuncGodotEntity = map_data.entities[e]
|
||||
var entity_geo: FuncGodotMapData.FuncGodotEntityGeometry = map_data.entity_geo[e]
|
||||
var entity_mins: Vector3 = Vector3.INF
|
||||
var entity_maxs: Vector3 = Vector3.INF
|
||||
var origin_mins: Vector3 = Vector3.INF
|
||||
var origin_maxs: Vector3 = -Vector3.INF
|
||||
|
||||
entity.center = Vector3.ZERO
|
||||
|
||||
for b in range(entity.brushes.size()):
|
||||
var brush: FuncGodotMapData.FuncGodotBrush = entity.brushes[b]
|
||||
brush.center = Vector3.ZERO
|
||||
var vert_count: int = 0
|
||||
|
||||
# Check if this is a special brush (eg: origin)
|
||||
var brush_texture_type: FuncGodotMapData.FuncGodotTextureType = FuncGodotMapData.FuncGodotTextureType.NORMAL
|
||||
if brush.faces.size() > 0:
|
||||
brush_texture_type = map_data.textures[brush.faces[0].texture_idx].type
|
||||
|
||||
# Check that all the faces match the same type
|
||||
for face_idx in range(1,brush.faces.size()):
|
||||
if map_data.textures[brush.faces[face_idx].texture_idx].type != brush_texture_type:
|
||||
brush_texture_type = FuncGodotMapData.FuncGodotTextureType.NORMAL # Reset face type if it doesn't match
|
||||
break
|
||||
|
||||
generate_brush_vertices(e, b)
|
||||
|
||||
var brush_geo: FuncGodotMapData.FuncGodotBrushGeometry = map_data.entity_geo[e].brushes[b]
|
||||
for face in brush_geo.faces:
|
||||
for vert in face.vertices:
|
||||
if entity_mins != Vector3.INF:
|
||||
entity_mins = entity_mins.min(vert.vertex)
|
||||
else:
|
||||
entity_mins = vert.vertex
|
||||
if entity_maxs != Vector3.INF:
|
||||
entity_maxs = entity_maxs.max(vert.vertex)
|
||||
else:
|
||||
entity_maxs = vert.vertex
|
||||
|
||||
if brush_texture_type == FuncGodotMapData.FuncGodotTextureType.ORIGIN:
|
||||
if origin_mins != Vector3.INF:
|
||||
origin_mins = origin_mins.min(vert.vertex)
|
||||
else:
|
||||
origin_mins = vert.vertex
|
||||
if origin_maxs != Vector3.INF:
|
||||
origin_maxs = origin_maxs.max(vert.vertex)
|
||||
else:
|
||||
origin_maxs = vert.vertex
|
||||
|
||||
brush.center += vert.vertex
|
||||
vert_count += 1
|
||||
|
||||
if vert_count > 0:
|
||||
brush.center /= float(vert_count)
|
||||
|
||||
# Default origin type is BOUNDS_CENTER
|
||||
if entity_maxs != Vector3.INF and entity_mins != Vector3.INF:
|
||||
entity.center = entity_maxs - ((entity_maxs - entity_mins) * 0.5)
|
||||
|
||||
if entity.origin_type != FuncGodotMapData.FuncGodotEntityOriginType.BOUNDS_CENTER and entity.brushes.size() > 0:
|
||||
match entity.origin_type:
|
||||
FuncGodotMapData.FuncGodotEntityOriginType.ABSOLUTE, FuncGodotMapData.FuncGodotEntityOriginType.RELATIVE:
|
||||
if 'origin' in entity.properties:
|
||||
var origin_comps: PackedFloat64Array = entity.properties['origin'].split_floats(' ')
|
||||
if origin_comps.size() > 2:
|
||||
if entity.origin_type == FuncGodotMapData.FuncGodotEntityOriginType.ABSOLUTE:
|
||||
entity.center = Vector3(origin_comps[0], origin_comps[1], origin_comps[2])
|
||||
else: # OriginType.RELATIVE
|
||||
entity.center += Vector3(origin_comps[0], origin_comps[1], origin_comps[2])
|
||||
|
||||
FuncGodotMapData.FuncGodotEntityOriginType.BRUSH:
|
||||
if origin_mins != Vector3.INF:
|
||||
entity.center = origin_maxs - ((origin_maxs - origin_mins) * 0.5)
|
||||
|
||||
FuncGodotMapData.FuncGodotEntityOriginType.BOUNDS_MINS:
|
||||
entity.center = entity_mins
|
||||
|
||||
FuncGodotMapData.FuncGodotEntityOriginType.BOUNDS_MAXS:
|
||||
entity.center = entity_maxs
|
||||
|
||||
FuncGodotMapData.FuncGodotEntityOriginType.AVERAGED:
|
||||
entity.center = Vector3.ZERO
|
||||
for b in range(entity.brushes.size()):
|
||||
entity.center += entity.brushes[b].center
|
||||
entity.center /= float(entity.brushes.size())
|
||||
|
||||
var generate_vertices_task_id:= WorkerThreadPool.add_group_task(generate_vertices_task, map_data.entities.size(), 4, true)
|
||||
WorkerThreadPool.wait_for_group_task_completion(generate_vertices_task_id)
|
||||
|
||||
# wind face vertices
|
||||
for e in range(map_data.entities.size()):
|
||||
var entity: FuncGodotMapData.FuncGodotEntity = map_data.entities[e]
|
||||
var entity_geo: FuncGodotMapData.FuncGodotEntityGeometry = map_data.entity_geo[e]
|
||||
|
||||
for b in range(entity.brushes.size()):
|
||||
var brush: FuncGodotMapData.FuncGodotBrush = entity.brushes[b]
|
||||
var brush_geo: FuncGodotMapData.FuncGodotBrushGeometry = entity_geo.brushes[b]
|
||||
|
||||
for f in range(brush.faces.size()):
|
||||
var face: FuncGodotMapData.FuncGodotFace = brush.faces[f]
|
||||
var face_geo: FuncGodotMapData.FuncGodotFaceGeometry = brush_geo.faces[f]
|
||||
|
||||
if face_geo.vertices.size() < 3:
|
||||
continue
|
||||
|
||||
wind_entity_idx = e
|
||||
wind_brush_idx = b
|
||||
wind_face_idx = f
|
||||
|
||||
wind_face_basis = face_geo.vertices[1].vertex - face_geo.vertices[0].vertex
|
||||
wind_face_center = Vector3.ZERO
|
||||
wind_face_normal = face.plane_normal
|
||||
|
||||
for v in face_geo.vertices:
|
||||
wind_face_center += v.vertex
|
||||
|
||||
wind_face_center /= face_geo.vertices.size()
|
||||
|
||||
face_geo.vertices.sort_custom(sort_vertices_by_winding)
|
||||
wind_entity_idx = 0
|
||||
|
||||
# index face vertices
|
||||
var index_faces_task:= func(e):
|
||||
var entity_geo: FuncGodotMapData.FuncGodotEntityGeometry = map_data.entity_geo[e]
|
||||
|
||||
for b in range(entity_geo.brushes.size()):
|
||||
var brush_geo: FuncGodotMapData.FuncGodotBrushGeometry = entity_geo.brushes[b]
|
||||
|
||||
for f in range(brush_geo.faces.size()):
|
||||
var face_geo: FuncGodotMapData.FuncGodotFaceGeometry = brush_geo.faces[f]
|
||||
|
||||
if face_geo.vertices.size() < 3:
|
||||
continue
|
||||
|
||||
var i_count: int = 0
|
||||
face_geo.indicies.resize((face_geo.vertices.size() - 2) * 3)
|
||||
for i in range(face_geo.vertices.size() - 2):
|
||||
face_geo.indicies[i_count] = 0
|
||||
face_geo.indicies[i_count + 1] = i + 1
|
||||
face_geo.indicies[i_count + 2] = i + 2
|
||||
i_count += 3
|
||||
|
||||
var index_faces_task_id:= WorkerThreadPool.add_group_task(index_faces_task, map_data.entities.size(), 4, true)
|
||||
WorkerThreadPool.wait_for_group_task_completion(index_faces_task_id)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cb0c2fn35hqov
|
||||
158
addons/func_godot/src/core/func_godot_map_data.gd
Normal file
158
addons/func_godot/src/core/func_godot_map_data.gd
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
class_name FuncGodotMapData extends RefCounted
|
||||
|
||||
var entities: Array[FuncGodotMapData.FuncGodotEntity]
|
||||
var entity_geo: Array[FuncGodotMapData.FuncGodotEntityGeometry]
|
||||
var textures: Array[FuncGodotMapData.FuncGodotTextureData]
|
||||
|
||||
func register_texture(name: String) -> int:
|
||||
for i in range(textures.size()):
|
||||
if textures[i].name == name:
|
||||
return i
|
||||
|
||||
textures.append(FuncGodotTextureData.new(name))
|
||||
return textures.size() - 1
|
||||
|
||||
func set_texture_info(name: String, width: int, height: int, type: FuncGodotTextureType) -> void:
|
||||
for i in range(textures.size()):
|
||||
if textures[i].name == name:
|
||||
textures[i].width = width
|
||||
textures[i].height = height
|
||||
textures[i].type = type
|
||||
return
|
||||
|
||||
func find_texture(texture_name: String) -> int:
|
||||
for i in range(textures.size()):
|
||||
if textures[i].name == texture_name:
|
||||
return i
|
||||
return -1
|
||||
|
||||
func set_entity_types_by_classname(classname: String, spawn_type: int, origin_type: int, meta_flags: int) -> void:
|
||||
for entity in entities:
|
||||
if entity.properties.has("classname") and entity.properties["classname"] == classname:
|
||||
entity.metadata_inclusion_flags = meta_flags as FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags
|
||||
entity.spawn_type = spawn_type as FuncGodotMapData.FuncGodotEntitySpawnType
|
||||
if entity.spawn_type == FuncGodotMapData.FuncGodotEntitySpawnType.ENTITY:
|
||||
entity.origin_type = origin_type as FuncGodotMapData.FuncGodotEntityOriginType
|
||||
else:
|
||||
entity.origin_type = FuncGodotMapData.FuncGodotEntityOriginType.AVERAGED
|
||||
|
||||
func clear() -> void:
|
||||
entities.clear()
|
||||
entity_geo.clear()
|
||||
textures.clear()
|
||||
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
# Nested Types
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
enum FuncGodotEntitySpawnType {
|
||||
WORLDSPAWN = 0,
|
||||
MERGE_WORLDSPAWN = 1,
|
||||
ENTITY = 2
|
||||
}
|
||||
|
||||
enum FuncGodotEntityOriginType {
|
||||
AVERAGED = 0,
|
||||
ABSOLUTE = 1,
|
||||
RELATIVE = 2,
|
||||
BRUSH = 3,
|
||||
BOUNDS_CENTER = 4,
|
||||
BOUNDS_MINS = 5,
|
||||
BOUNDS_MAXS = 6,
|
||||
}
|
||||
|
||||
enum FuncGodotEntityMetadataInclusionFlags {
|
||||
NONE = 0,
|
||||
ENTITY_INDEX_RANGES = 1,
|
||||
TEXTURES = 2,
|
||||
VERTEX = 4,
|
||||
FACE_POSITION = 8,
|
||||
FACE_NORMAL = 16,
|
||||
COLLISION_SHAPE_TO_FACE_RANGE_MAP = 32,
|
||||
}
|
||||
|
||||
enum FuncGodotTextureType {
|
||||
NORMAL = 0,
|
||||
ORIGIN = 1
|
||||
}
|
||||
|
||||
class FuncGodotFacePoints:
|
||||
var v0: Vector3
|
||||
var v1: Vector3
|
||||
var v2: Vector3
|
||||
|
||||
class FuncGodotValveTextureAxis:
|
||||
var axis: Vector3
|
||||
var offset: float
|
||||
|
||||
class FuncGodotValveUV:
|
||||
var u: FuncGodotValveTextureAxis
|
||||
var v: FuncGodotValveTextureAxis
|
||||
|
||||
func _init() -> void:
|
||||
u = FuncGodotValveTextureAxis.new()
|
||||
v = FuncGodotValveTextureAxis.new()
|
||||
|
||||
class FuncGodotFaceUVExtra:
|
||||
var rot: float
|
||||
var scale_x: float
|
||||
var scale_y: float
|
||||
|
||||
class FuncGodotFace:
|
||||
var plane_points: FuncGodotFacePoints
|
||||
var plane_normal: Vector3
|
||||
var plane_dist: float
|
||||
var texture_idx: int
|
||||
var is_valve_uv: bool
|
||||
var uv_standard: Vector2
|
||||
var uv_valve: FuncGodotValveUV
|
||||
var uv_extra: FuncGodotFaceUVExtra
|
||||
|
||||
func _init() -> void:
|
||||
plane_points = FuncGodotFacePoints.new()
|
||||
uv_valve = FuncGodotValveUV.new()
|
||||
uv_extra = FuncGodotFaceUVExtra.new()
|
||||
|
||||
class FuncGodotBrush:
|
||||
var faces: Array[FuncGodotFace]
|
||||
var center: Vector3
|
||||
|
||||
class FuncGodotEntity:
|
||||
var properties: Dictionary
|
||||
var brushes: Array[FuncGodotBrush]
|
||||
var center: Vector3
|
||||
var spawn_type: FuncGodotEntitySpawnType
|
||||
var origin_type: FuncGodotEntityOriginType
|
||||
var metadata_inclusion_flags: FuncGodotEntityMetadataInclusionFlags
|
||||
|
||||
class FuncGodotFaceVertex:
|
||||
var vertex: Vector3
|
||||
var normal: Vector3
|
||||
var uv: Vector2
|
||||
var tangent: Vector4
|
||||
|
||||
func duplicate() -> FuncGodotFaceVertex:
|
||||
var new_vert := FuncGodotFaceVertex.new()
|
||||
new_vert.vertex = vertex
|
||||
new_vert.normal = normal
|
||||
new_vert.uv = uv
|
||||
new_vert.tangent = tangent
|
||||
return new_vert
|
||||
|
||||
class FuncGodotFaceGeometry:
|
||||
var vertices: Array[FuncGodotFaceVertex]
|
||||
var indicies: Array[int]
|
||||
|
||||
class FuncGodotBrushGeometry:
|
||||
var faces: Array[FuncGodotFaceGeometry]
|
||||
|
||||
class FuncGodotEntityGeometry:
|
||||
var brushes: Array[FuncGodotBrushGeometry]
|
||||
|
||||
class FuncGodotTextureData:
|
||||
var name: String
|
||||
var width: int
|
||||
var height: int
|
||||
var type: FuncGodotTextureType
|
||||
|
||||
func _init(in_name: String):
|
||||
name = in_name
|
||||
1
addons/func_godot/src/core/func_godot_map_data.gd.uid
Normal file
1
addons/func_godot/src/core/func_godot_map_data.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://ct3rx5npjd00s
|
||||
326
addons/func_godot/src/core/func_godot_map_parser.gd
Normal file
326
addons/func_godot/src/core/func_godot_map_parser.gd
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
class_name FuncGodotMapParser extends RefCounted
|
||||
|
||||
var scope:= FuncGodotMapParser.ParseScope.FILE
|
||||
var comment: bool = false
|
||||
var entity_idx: int = -1
|
||||
var brush_idx: int = -1
|
||||
var face_idx: int = -1
|
||||
var component_idx: int = 0
|
||||
var prop_key: String = ""
|
||||
var current_property: String = ""
|
||||
var valve_uvs: bool = false
|
||||
|
||||
var current_face: FuncGodotMapData.FuncGodotFace
|
||||
var current_brush: FuncGodotMapData.FuncGodotBrush
|
||||
var current_entity: FuncGodotMapData.FuncGodotEntity
|
||||
|
||||
var map_data: FuncGodotMapData
|
||||
var _keep_tb_groups: bool = false
|
||||
|
||||
func _init(in_map_data: FuncGodotMapData) -> void:
|
||||
map_data = in_map_data
|
||||
|
||||
func load_map(map_file: String, keep_tb_groups: bool) -> bool:
|
||||
current_face = FuncGodotMapData.FuncGodotFace.new()
|
||||
current_brush = FuncGodotMapData.FuncGodotBrush.new()
|
||||
current_entity = FuncGodotMapData.FuncGodotEntity.new()
|
||||
|
||||
scope = FuncGodotMapParser.ParseScope.FILE
|
||||
comment = false
|
||||
entity_idx = -1
|
||||
brush_idx = -1
|
||||
face_idx = -1
|
||||
component_idx = 0
|
||||
valve_uvs = false
|
||||
_keep_tb_groups = keep_tb_groups
|
||||
|
||||
var lines: PackedStringArray = []
|
||||
|
||||
var map: FileAccess = FileAccess.open(map_file, FileAccess.READ)
|
||||
|
||||
if map == null:
|
||||
printerr("Error: Failed to open map file (" + map_file + ")")
|
||||
return false
|
||||
|
||||
if map_file.ends_with(".import"):
|
||||
while not map.eof_reached():
|
||||
var line: String = map.get_line()
|
||||
if line.begins_with("path"):
|
||||
map.close()
|
||||
line = line.replace("path=", "");
|
||||
line = line.replace('"', '')
|
||||
var map_data: String = (load(line) as QuakeMapFile).map_data
|
||||
if map_data.is_empty():
|
||||
printerr("Error: Failed to open map file (" + line + ")")
|
||||
return false
|
||||
lines = map_data.split("\n")
|
||||
break
|
||||
else:
|
||||
while not map.eof_reached():
|
||||
var line: String = map.get_line()
|
||||
lines.append(line)
|
||||
|
||||
for line in lines:
|
||||
if comment:
|
||||
comment = false
|
||||
var tokens := split_string(line, [" ", "\t"], true)
|
||||
for s in tokens:
|
||||
token(s)
|
||||
|
||||
return true
|
||||
|
||||
func split_string(s: String, delimeters: Array[String], allow_empty: bool = true) -> Array[String]:
|
||||
var parts: Array[String] = []
|
||||
|
||||
var start := 0
|
||||
var i := 0
|
||||
|
||||
while i < s.length():
|
||||
if s[i] in delimeters:
|
||||
if allow_empty or start < i:
|
||||
parts.push_back(s.substr(start, i - start))
|
||||
start = i + 1
|
||||
i += 1
|
||||
|
||||
if allow_empty or start < i:
|
||||
parts.push_back(s.substr(start, i - start))
|
||||
|
||||
return parts
|
||||
|
||||
func set_scope(new_scope: FuncGodotMapParser.ParseScope) -> void:
|
||||
"""
|
||||
match new_scope:
|
||||
ParseScope.FILE:
|
||||
print("Switching to file scope.")
|
||||
ParseScope.ENTITY:
|
||||
print("Switching to entity " + str(entity_idx) + "scope")
|
||||
ParseScope.PROPERTY_VALUE:
|
||||
print("Switching to property value scope")
|
||||
ParseScope.BRUSH:
|
||||
print("Switching to brush " + str(brush_idx) + " scope")
|
||||
ParseScope.PLANE_0:
|
||||
print("Switching to face " + str(face_idx) + " plane 0 scope")
|
||||
ParseScope.PLANE_1:
|
||||
print("Switching to face " + str(face_idx) + " plane 1 scope")
|
||||
ParseScope.PLANE_2:
|
||||
print("Switching to face " + str(face_idx) + " plane 2 scope")
|
||||
ParseScope.TEXTURE:
|
||||
print("Switching to texture scope")
|
||||
ParseScope.U:
|
||||
print("Switching to U scope")
|
||||
ParseScope.V:
|
||||
print("Switching to V scope")
|
||||
ParseScope.VALVE_U:
|
||||
print("Switching to Valve U scope")
|
||||
ParseScope.VALVE_V:
|
||||
print("Switching to Valve V scope")
|
||||
ParseScope.ROT:
|
||||
print("Switching to rotation scope")
|
||||
ParseScope.U_SCALE:
|
||||
print("Switching to U scale scope")
|
||||
ParseScope.V_SCALE:
|
||||
print("Switching to V scale scope")
|
||||
"""
|
||||
scope = new_scope
|
||||
|
||||
func token(buf_str: String) -> void:
|
||||
if comment:
|
||||
return
|
||||
elif buf_str == "//":
|
||||
comment = true
|
||||
return
|
||||
|
||||
match scope:
|
||||
FuncGodotMapParser.ParseScope.FILE:
|
||||
if buf_str == "{":
|
||||
entity_idx += 1
|
||||
brush_idx = -1
|
||||
set_scope(FuncGodotMapParser.ParseScope.ENTITY)
|
||||
FuncGodotMapParser.ParseScope.ENTITY:
|
||||
if buf_str.begins_with('"'):
|
||||
prop_key = buf_str.substr(1)
|
||||
if prop_key.ends_with('"'):
|
||||
prop_key = prop_key.left(-1)
|
||||
set_scope(FuncGodotMapParser.ParseScope.PROPERTY_VALUE)
|
||||
elif buf_str == "{":
|
||||
brush_idx += 1
|
||||
face_idx = -1
|
||||
set_scope(FuncGodotMapParser.ParseScope.BRUSH)
|
||||
elif buf_str == "}":
|
||||
commit_entity()
|
||||
set_scope(FuncGodotMapParser.ParseScope.FILE)
|
||||
FuncGodotMapParser.ParseScope.PROPERTY_VALUE:
|
||||
var is_first = buf_str[0] == '"'
|
||||
var is_last = buf_str.right(1) == '"'
|
||||
|
||||
if is_first:
|
||||
if current_property != "":
|
||||
current_property = ""
|
||||
|
||||
if not is_last:
|
||||
current_property += buf_str + " "
|
||||
else:
|
||||
current_property += buf_str
|
||||
|
||||
if is_last:
|
||||
current_entity.properties[prop_key] = current_property.substr(1, len(current_property) - 2)
|
||||
set_scope(FuncGodotMapParser.ParseScope.ENTITY)
|
||||
FuncGodotMapParser.ParseScope.BRUSH:
|
||||
if buf_str == "(":
|
||||
face_idx += 1
|
||||
component_idx = 0
|
||||
set_scope(FuncGodotMapParser.ParseScope.PLANE_0)
|
||||
elif buf_str == "}":
|
||||
commit_brush()
|
||||
set_scope(FuncGodotMapParser.ParseScope.ENTITY)
|
||||
FuncGodotMapParser.ParseScope.PLANE_0:
|
||||
if buf_str == ")":
|
||||
component_idx = 0
|
||||
set_scope(FuncGodotMapParser.ParseScope.PLANE_1)
|
||||
else:
|
||||
match component_idx:
|
||||
0:
|
||||
current_face.plane_points.v0.x = float(buf_str)
|
||||
1:
|
||||
current_face.plane_points.v0.y = float(buf_str)
|
||||
2:
|
||||
current_face.plane_points.v0.z = float(buf_str)
|
||||
|
||||
component_idx += 1
|
||||
FuncGodotMapParser.ParseScope.PLANE_1:
|
||||
if buf_str != "(":
|
||||
if buf_str == ")":
|
||||
component_idx = 0
|
||||
set_scope(FuncGodotMapParser.ParseScope.PLANE_2)
|
||||
else:
|
||||
match component_idx:
|
||||
0:
|
||||
current_face.plane_points.v1.x = float(buf_str)
|
||||
1:
|
||||
current_face.plane_points.v1.y = float(buf_str)
|
||||
2:
|
||||
current_face.plane_points.v1.z = float(buf_str)
|
||||
|
||||
component_idx += 1
|
||||
FuncGodotMapParser.ParseScope.PLANE_2:
|
||||
if buf_str != "(":
|
||||
if buf_str == ")":
|
||||
component_idx = 0
|
||||
set_scope(FuncGodotMapParser.ParseScope.TEXTURE)
|
||||
else:
|
||||
match component_idx:
|
||||
0:
|
||||
current_face.plane_points.v2.x = float(buf_str)
|
||||
1:
|
||||
current_face.plane_points.v2.y = float(buf_str)
|
||||
2:
|
||||
current_face.plane_points.v2.z = float(buf_str)
|
||||
|
||||
component_idx += 1
|
||||
FuncGodotMapParser.ParseScope.TEXTURE:
|
||||
current_face.texture_idx = map_data.register_texture(buf_str)
|
||||
set_scope(FuncGodotMapParser.ParseScope.U)
|
||||
FuncGodotMapParser.ParseScope.U:
|
||||
if buf_str == "[":
|
||||
valve_uvs = true
|
||||
component_idx = 0
|
||||
set_scope(FuncGodotMapParser.ParseScope.VALVE_U)
|
||||
else:
|
||||
valve_uvs = false
|
||||
current_face.uv_standard.x = float(buf_str)
|
||||
set_scope(FuncGodotMapParser.ParseScope.V)
|
||||
FuncGodotMapParser.ParseScope.V:
|
||||
current_face.uv_standard.y = float(buf_str)
|
||||
set_scope(FuncGodotMapParser.ParseScope.ROT)
|
||||
FuncGodotMapParser.ParseScope.VALVE_U:
|
||||
if buf_str == "]":
|
||||
component_idx = 0
|
||||
set_scope(FuncGodotMapParser.ParseScope.VALVE_V)
|
||||
else:
|
||||
match component_idx:
|
||||
0:
|
||||
current_face.uv_valve.u.axis.x = float(buf_str)
|
||||
1:
|
||||
current_face.uv_valve.u.axis.y = float(buf_str)
|
||||
2:
|
||||
current_face.uv_valve.u.axis.z = float(buf_str)
|
||||
3:
|
||||
current_face.uv_valve.u.offset = float(buf_str)
|
||||
|
||||
component_idx += 1
|
||||
FuncGodotMapParser.ParseScope.VALVE_V:
|
||||
if buf_str != "[":
|
||||
if buf_str == "]":
|
||||
set_scope(FuncGodotMapParser.ParseScope.ROT)
|
||||
else:
|
||||
match component_idx:
|
||||
0:
|
||||
current_face.uv_valve.v.axis.x = float(buf_str)
|
||||
1:
|
||||
current_face.uv_valve.v.axis.y = float(buf_str)
|
||||
2:
|
||||
current_face.uv_valve.v.axis.z = float(buf_str)
|
||||
3:
|
||||
current_face.uv_valve.v.offset = float(buf_str)
|
||||
|
||||
component_idx += 1
|
||||
FuncGodotMapParser.ParseScope.ROT:
|
||||
current_face.uv_extra.rot = float(buf_str)
|
||||
set_scope(FuncGodotMapParser.ParseScope.U_SCALE)
|
||||
FuncGodotMapParser.ParseScope.U_SCALE:
|
||||
current_face.uv_extra.scale_x = float(buf_str)
|
||||
set_scope(FuncGodotMapParser.ParseScope.V_SCALE)
|
||||
FuncGodotMapParser.ParseScope.V_SCALE:
|
||||
current_face.uv_extra.scale_y = float(buf_str)
|
||||
commit_face()
|
||||
set_scope(FuncGodotMapParser.ParseScope.BRUSH)
|
||||
|
||||
func commit_entity() -> void:
|
||||
if current_entity.properties.has('_tb_type') and map_data.entities.size() > 0:
|
||||
map_data.entities[0].brushes.append_array(current_entity.brushes)
|
||||
current_entity.brushes.clear()
|
||||
if !_keep_tb_groups:
|
||||
current_entity = FuncGodotMapData.FuncGodotEntity.new()
|
||||
return
|
||||
|
||||
var new_entity:= FuncGodotMapData.FuncGodotEntity.new()
|
||||
new_entity.spawn_type = FuncGodotMapData.FuncGodotEntitySpawnType.ENTITY
|
||||
new_entity.properties = current_entity.properties
|
||||
new_entity.brushes = current_entity.brushes
|
||||
map_data.entities.append(new_entity)
|
||||
|
||||
current_entity = FuncGodotMapData.FuncGodotEntity.new()
|
||||
|
||||
func commit_brush() -> void:
|
||||
current_entity.brushes.append(current_brush)
|
||||
current_brush = FuncGodotMapData.FuncGodotBrush.new()
|
||||
|
||||
func commit_face() -> void:
|
||||
var v0v1: Vector3 = current_face.plane_points.v1 - current_face.plane_points.v0
|
||||
var v1v2: Vector3 = current_face.plane_points.v2 - current_face.plane_points.v1
|
||||
current_face.plane_normal = v1v2.cross(v0v1).normalized()
|
||||
current_face.plane_dist = current_face.plane_normal.dot(current_face.plane_points.v0)
|
||||
current_face.is_valve_uv = valve_uvs
|
||||
|
||||
current_brush.faces.append(current_face)
|
||||
current_face = FuncGodotMapData.FuncGodotFace.new()
|
||||
|
||||
# Nested
|
||||
enum ParseScope{
|
||||
FILE,
|
||||
COMMENT,
|
||||
ENTITY,
|
||||
PROPERTY_VALUE,
|
||||
BRUSH,
|
||||
PLANE_0,
|
||||
PLANE_1,
|
||||
PLANE_2,
|
||||
TEXTURE,
|
||||
U,
|
||||
V,
|
||||
VALVE_U,
|
||||
VALVE_V,
|
||||
ROT,
|
||||
U_SCALE,
|
||||
V_SCALE
|
||||
}
|
||||
1
addons/func_godot/src/core/func_godot_map_parser.gd.uid
Normal file
1
addons/func_godot/src/core/func_godot_map_parser.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cg2iiom3svtw0
|
||||
217
addons/func_godot/src/core/func_godot_surface_gatherer.gd
Normal file
217
addons/func_godot/src/core/func_godot_surface_gatherer.gd
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
class_name FuncGodotSurfaceGatherer extends RefCounted
|
||||
|
||||
var map_data: FuncGodotMapData
|
||||
var map_settings: FuncGodotMapSettings
|
||||
var split_type: SurfaceSplitType = SurfaceSplitType.NONE
|
||||
var entity_filter_idx: int = -1
|
||||
var texture_filter_idx: int = -1
|
||||
var clip_filter_texture_idx: int
|
||||
var skip_filter_texture_idx: int
|
||||
var origin_filter_texture_idx: int
|
||||
var metadata_skip_flags: int
|
||||
|
||||
var out_surfaces: Array[FuncGodotMapData.FuncGodotFaceGeometry]
|
||||
var out_metadata: Dictionary
|
||||
|
||||
func _init(in_map_data: FuncGodotMapData, in_map_settings: FuncGodotMapSettings) -> void:
|
||||
map_data = in_map_data
|
||||
map_settings = in_map_settings
|
||||
|
||||
func set_texture_filter(texture_name: String) -> void:
|
||||
texture_filter_idx = map_data.find_texture(texture_name)
|
||||
|
||||
func set_clip_filter_texture(texture_name: String) -> void:
|
||||
clip_filter_texture_idx = map_data.find_texture(texture_name)
|
||||
|
||||
func set_skip_filter_texture(texture_name: String) -> void:
|
||||
skip_filter_texture_idx = map_data.find_texture(texture_name)
|
||||
|
||||
func set_origin_filter_texture(texture_name: String) -> void:
|
||||
origin_filter_texture_idx = map_data.find_texture(texture_name)
|
||||
|
||||
func filter_entity(entity_idx: int) -> bool:
|
||||
if entity_filter_idx != -1 and entity_idx != entity_filter_idx:
|
||||
return true
|
||||
return false
|
||||
|
||||
func filter_face(entity_idx: int, brush_idx: int, face_idx: int) -> bool:
|
||||
var face: FuncGodotMapData.FuncGodotFace = map_data.entities[entity_idx].brushes[brush_idx].faces[face_idx]
|
||||
var face_geo: FuncGodotMapData.FuncGodotFaceGeometry = map_data.entity_geo[entity_idx].brushes[brush_idx].faces[face_idx]
|
||||
|
||||
if face_geo.vertices.size() < 3:
|
||||
return true
|
||||
|
||||
# Omit faces textured with Clip
|
||||
if clip_filter_texture_idx != -1 and face.texture_idx == clip_filter_texture_idx:
|
||||
return true
|
||||
|
||||
# Omit faces textured with Skip
|
||||
if skip_filter_texture_idx != -1 and face.texture_idx == skip_filter_texture_idx:
|
||||
return true
|
||||
|
||||
# Omit faces textured with Origin
|
||||
if origin_filter_texture_idx != -1 and face.texture_idx == origin_filter_texture_idx:
|
||||
return true
|
||||
|
||||
# Omit filtered texture indices
|
||||
if texture_filter_idx != -1 and face.texture_idx != texture_filter_idx:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
func run() -> void:
|
||||
out_surfaces.clear()
|
||||
var texture_names: Array[StringName] = []
|
||||
var textures: PackedInt32Array = []
|
||||
var vertices: PackedVector3Array = []
|
||||
var positions: PackedVector3Array = []
|
||||
var normals: PackedVector3Array = []
|
||||
var shape_index_ranges: Array[Vector2i] = []
|
||||
var entity_index_ranges: Array[Vector2i] = []
|
||||
|
||||
var index_offset: int = 0
|
||||
var entity_face_range: Vector2i = Vector2i.ZERO
|
||||
const MFlags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags
|
||||
var build_entity_index_ranges: bool = not metadata_skip_flags & MFlags.ENTITY_INDEX_RANGES
|
||||
var surf: FuncGodotMapData.FuncGodotFaceGeometry
|
||||
|
||||
if split_type == SurfaceSplitType.NONE:
|
||||
surf = add_surface()
|
||||
index_offset = len(out_surfaces) - 1
|
||||
|
||||
for e in range(map_data.entities.size()):
|
||||
var entity:= map_data.entities[e]
|
||||
var entity_geo:= map_data.entity_geo[e]
|
||||
var shape_face_range := Vector2i.ZERO
|
||||
var total_entity_tris := 0
|
||||
var include_normals_metadata: bool = not metadata_skip_flags & MFlags.FACE_NORMAL and entity.metadata_inclusion_flags & MFlags.FACE_NORMAL
|
||||
var include_vertices_metadata: bool = not metadata_skip_flags & MFlags.VERTEX and entity.metadata_inclusion_flags & MFlags.VERTEX
|
||||
var include_textures_metadata: bool = not metadata_skip_flags & MFlags.TEXTURES and entity.metadata_inclusion_flags & MFlags.TEXTURES
|
||||
var include_positions_metadata: bool = not metadata_skip_flags & MFlags.FACE_POSITION and entity.metadata_inclusion_flags & MFlags.FACE_POSITION
|
||||
var include_shape_range_metadata: bool = not metadata_skip_flags & MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP and entity.metadata_inclusion_flags & MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP
|
||||
|
||||
if filter_entity(e):
|
||||
continue
|
||||
|
||||
if split_type == SurfaceSplitType.ENTITY:
|
||||
if entity.spawn_type == FuncGodotMapData.FuncGodotEntitySpawnType.MERGE_WORLDSPAWN:
|
||||
add_surface()
|
||||
surf = out_surfaces[0]
|
||||
index_offset = surf.vertices.size()
|
||||
else:
|
||||
surf = add_surface()
|
||||
index_offset = surf.vertices.size()
|
||||
|
||||
for b in range(entity.brushes.size()):
|
||||
var brush:= entity.brushes[b]
|
||||
var brush_geo:= entity_geo.brushes[b]
|
||||
var total_brush_tris:= 0
|
||||
|
||||
if split_type == SurfaceSplitType.BRUSH:
|
||||
index_offset = 0
|
||||
surf = add_surface()
|
||||
|
||||
for f in range(brush.faces.size()):
|
||||
var face_geo: FuncGodotMapData.FuncGodotFaceGeometry = brush_geo.faces[f]
|
||||
var face: FuncGodotMapData.FuncGodotFace = brush.faces[f]
|
||||
var num_tris = face_geo.vertices.size() - 2
|
||||
|
||||
if filter_face(e, b, f):
|
||||
continue
|
||||
|
||||
for v in range(face_geo.vertices.size()):
|
||||
var vert: FuncGodotMapData.FuncGodotFaceVertex = face_geo.vertices[v].duplicate()
|
||||
|
||||
if entity.spawn_type == FuncGodotMapData.FuncGodotEntitySpawnType.ENTITY:
|
||||
vert.vertex -= entity.center
|
||||
|
||||
surf.vertices.append(vert)
|
||||
|
||||
if include_normals_metadata:
|
||||
var normal := Vector3(face.plane_normal.y, face.plane_normal.z, face.plane_normal.x)
|
||||
for i in num_tris:
|
||||
normals.append(normal)
|
||||
if include_shape_range_metadata or build_entity_index_ranges:
|
||||
total_brush_tris += num_tris
|
||||
if include_textures_metadata:
|
||||
var texname := StringName(map_data.textures[face.texture_idx].name)
|
||||
var index: int
|
||||
if texture_names.is_empty():
|
||||
texture_names.append(texname)
|
||||
index = 0
|
||||
elif texture_names.back() == texname:
|
||||
# Common case, faces with textures are next to each other
|
||||
index = texture_names.size() - 1
|
||||
else:
|
||||
var texture_name_index: int = texture_names.find(texname)
|
||||
if texture_name_index == -1:
|
||||
index = texture_names.size()
|
||||
texture_names.append(texname)
|
||||
else:
|
||||
index = texture_name_index
|
||||
# Metadata addresses triangles, so we have to duplicate the info for each tri
|
||||
for i in num_tris:
|
||||
textures.append(index)
|
||||
|
||||
var avg_vertex_pos := Vector3.ZERO
|
||||
var avg_vertex_pos_ct: int = 0
|
||||
for i in range(num_tris * 3):
|
||||
surf.indicies.append(face_geo.indicies[i] + index_offset)
|
||||
var vertex: Vector3 = surf.vertices[surf.indicies.back()].vertex
|
||||
vertex = Vector3(vertex.y, vertex.z, vertex.x) * map_settings.scale_factor
|
||||
if include_vertices_metadata:
|
||||
vertices.append(vertex)
|
||||
if include_positions_metadata:
|
||||
avg_vertex_pos_ct += 1
|
||||
avg_vertex_pos += vertex
|
||||
if avg_vertex_pos_ct == 3:
|
||||
avg_vertex_pos /= 3
|
||||
positions.append(avg_vertex_pos)
|
||||
avg_vertex_pos = Vector3.ZERO
|
||||
avg_vertex_pos_ct = 0
|
||||
|
||||
index_offset += face_geo.vertices.size()
|
||||
|
||||
if include_shape_range_metadata:
|
||||
shape_face_range.x = shape_face_range.y
|
||||
shape_face_range.y = shape_face_range.x + total_brush_tris
|
||||
shape_index_ranges.append(shape_face_range)
|
||||
|
||||
if build_entity_index_ranges:
|
||||
total_entity_tris += total_brush_tris
|
||||
|
||||
if build_entity_index_ranges:
|
||||
entity_face_range.x = entity_face_range.y
|
||||
entity_face_range.y = entity_face_range.x + total_entity_tris
|
||||
entity_index_ranges.append(entity_face_range)
|
||||
|
||||
out_metadata = {
|
||||
textures = textures,
|
||||
texture_names = texture_names,
|
||||
normals = normals,
|
||||
vertices = vertices,
|
||||
positions = positions,
|
||||
shape_index_ranges = shape_index_ranges,
|
||||
}
|
||||
if build_entity_index_ranges:
|
||||
out_metadata["entity_index_ranges"] = entity_index_ranges
|
||||
|
||||
func add_surface() -> FuncGodotMapData.FuncGodotFaceGeometry:
|
||||
var surf:= FuncGodotMapData.FuncGodotFaceGeometry.new()
|
||||
out_surfaces.append(surf)
|
||||
return surf
|
||||
|
||||
func reset_params() -> void:
|
||||
split_type = SurfaceSplitType.NONE
|
||||
entity_filter_idx = -1
|
||||
texture_filter_idx = -1
|
||||
clip_filter_texture_idx = -1
|
||||
skip_filter_texture_idx = -1
|
||||
metadata_skip_flags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags.ENTITY_INDEX_RANGES
|
||||
|
||||
# nested
|
||||
enum SurfaceSplitType{
|
||||
NONE,
|
||||
ENTITY,
|
||||
BRUSH
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://df8y3hiimomt5
|
||||
Loading…
Add table
Add a link
Reference in a new issue