Files
tactical-shooter/client/tools/validate_map/validate_textures.gd
T
shawn e7299b17e9 Phase 7: netfox + godot-jolt stack upgrade
Stack installed:
- netfox v1.35.3 (core + extras + noray + internals)
- godot-jolt v0.16.0-stable

Architecture:
- Server: ENet transport (works headless, no netfox deps)
- Client/Editor: netfox rollback (RollbackSynchronizer, TickInterpolator)

New/modified:
- docs/migration-netfox-plan.md — migration architecture
- scripts/network/network_manager.gd — netfox-aware ENet fallback
- scripts/network/player.gd — clean base player
- client/characters/player_netfox.gd — rollback player w/ WeaponManager
- client/characters/input/player_net_input.gd — BaseNetInput subclass
- client/characters/character/fps_character_controller.gd — netfox input feed
- client/weapons/ — weapon data, registry, TacticalWeaponHitscan, WeaponManager
- client/scripts/round_replicator.gd — client-side round state bridge
- server/scripts/round_manager.gd — improved state machine
- server/scripts/plugin_api/plugin_manager.gd — refined plugin system
- config: enemy_tag, ally_tag for meatball targeting

Removed: old C++ SimulationServer GDExtension (replaced by netfox rollback)
2026-07-02 17:39:22 -04:00

158 lines
6.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@tool
extends RefCounted
# Validator: Texture Size
#
# Checks:
# - All textures referenced by materials in the scene are ≤ 1024×1024
# - Checks StandardMaterial3D, ORMMaterial3D, and ShaderMaterial textures
# - Warns on textures > 512×512 (suggested for secondary surfaces)
# - Skips LightmapGI textures (baked lightmaps are handled separately)
const MAX_TEXTURE_SIZE := 1024
const WARN_TEXTURE_SIZE := 512
func validate(scene_root: Node, scene_path: String) -> Dictionary:
var errors: Array[String] = []
var warnings: Array[String] = []
var inspected := 0
var oversized: Array[String] = []
# Collect all MeshInstance3D nodes and their materials
var mesh_instances: Array[MeshInstance3D] = []
_collect_mesh_instances(scene_root, mesh_instances)
# Track visited textures to avoid duplicate reports
var visited_textures: Dictionary = {}
for mi in mesh_instances:
var mesh: Mesh = mi.mesh
if mesh == null:
continue
for surf_idx in mesh.get_surface_count():
var mat := mesh.surface_get_material(surf_idx)
if mat == null:
# Check override material on MeshInstance3D
mat = mi.get_surface_override_material(surf_idx)
if mat == null:
# Check material_override on MeshInstance3D
mat = mi.material_override
if mat == null:
continue
_check_material_textures(mat, visited_textures, errors, warnings, inspected)
# Check material_override if no per-surface materials
if mesh_instances.is_empty():
_find_materials_on_node(scene_root, visited_textures, errors, warnings, inspected)
# Check LightmapGI light_data texture sizes separately
var lightmaps: Array[LightmapGI] = []
_find_nodes_by_type(scene_root, "LightmapGI", lightmaps)
for lm in lightmaps:
if lm.light_data != null:
var lightmap_tex = lm.light_data.get_texture()
if lightmap_tex != null and not visited_textures.has(lightmap_tex.resource_path):
visited_textures[lightmap_tex.resource_path] = true
var size := _get_texture_size(lightmap_tex)
if size > 0:
print(" Lightmap texture: ", lightmap_tex.resource_path, "", size, "×")
if size > MAX_TEXTURE_SIZE:
warnings.append("Lightmap texture exceeds %d: %s (%d×)" % [MAX_TEXTURE_SIZE, lightmap_tex.resource_path, size])
print("")
print(" Textures inspected: ", inspected)
if oversized.is_empty():
print(" ✓ All textures within ", MAX_TEXTURE_SIZE, "×", MAX_TEXTURE_SIZE, " limit")
else:
for o in oversized:
print("", o)
return {
"pass": errors.is_empty(),
"errors": errors,
"warnings": warnings,
}
func _check_material_textures(mat: Material, visited: Dictionary, errors: Array, warnings: Array, inspected: int) -> void:
if mat is StandardMaterial3D:
var smat := mat as StandardMaterial3D
_check_single_texture(smat.albedo_texture, "albedo", smat.resource_path, visited, errors, warnings, inspected)
_check_single_texture(smat.normal_texture, "normal", smat.resource_path, visited, errors, warnings, inspected)
_check_single_texture(smat.roughness_texture, "roughness", smat.resource_path, visited, errors, warnings, inspected)
_check_single_texture(smat.metallic_texture, "metallic", smat.resource_path, visited, errors, warnings, inspected)
_check_single_texture(smat.emission_texture, "emission", smat.resource_path, visited, errors, warnings, inspected)
_check_single_texture(smat.ao_texture, "ambient_occlusion", smat.resource_path, visited, errors, warnings, inspected)
elif mat is ORMMaterial3D:
var orm := mat as ORMMaterial3D
_check_single_texture(orm.albedo_texture, "albedo", orm.resource_path, visited, errors, warnings, inspected)
_check_single_texture(orm.normal_texture, "normal", orm.resource_path, visited, errors, warnings, inspected)
_check_single_texture(orm.orm_texture, "ORM", orm.resource_path, visited, errors, warnings, inspected)
_check_single_texture(orm.emission_texture, "emission", orm.resource_path, visited, errors, warnings, inspected)
elif mat is ShaderMaterial:
var sm := mat as ShaderMaterial
# ShaderMaterial textures are set via shader params — check all Texture2D params
var param_list = sm.get_shader_parameter_list() if sm.shader else []
for param in param_list:
var val = sm.get_shader_parameter(param)
if val is Texture2D:
_check_single_texture(val as Texture2D, param, sm.resource_path, visited, errors, warnings, inspected)
func _check_single_texture(tex: Texture2D, slot_name: String, material_path: String, visited: Dictionary, errors: Array, warnings: Array, inspected: int) -> void:
if tex == null:
return
var path := tex.resource_path
if path.is_empty():
path = tex.name # built-in/placeholder texture
if visited.has(path):
return
visited[path] = true
inspected += 1
var size := _get_texture_size(tex)
if size <= 0:
return
var label := "%s/%s" % [material_path.get_file(), slot_name]
if size > MAX_TEXTURE_SIZE:
errors.append("Texture exceeds %d×%d: %s (%s) — %d× (path: %s)" % [MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, label, path, size, path])
elif size > WARN_TEXTURE_SIZE:
warnings.append("Texture > %d×%d: %s (%s) — %d× (consider downscaling)" % [WARN_TEXTURE_SIZE, WARN_TEXTURE_SIZE, label, path, size])
func _get_texture_size(tex: Texture2D) -> int:
# Return the larger dimension
return max(tex.get_width(), tex.get_height())
func _collect_mesh_instances(node: Node, result: Array) -> void:
if node is MeshInstance3D:
result.append(node)
for child in node.get_children():
_collect_mesh_instances(child, result)
func _find_nodes_by_type(root: Node, expected_type: String, output) -> void:
if root.get_class() == expected_type:
output.append(root)
for child in root.get_children():
_find_nodes_by_type(child, expected_type, output)
func _find_materials_on_node(node: Node, visited: Dictionary, errors: Array, warnings: Array, inspected: int) -> void:
# Fallback: walk all nodes and check their material_override
for child in node.get_children():
if child.has_method("get_material_override"):
var mat = child.get("material_override")
if mat != null and mat is Material:
_check_material_textures(mat, visited, errors, warnings, inspected)
_find_materials_on_node(child, visited, errors, warnings, inspected)