@tool
extends Node3D
## Load a .ptex using MM's engine, render textures, apply to sphere.
@export var load_material: bool = false:
set(v):
if v:
_load_material()
load_material = false
@export_file("*.ptex") var ptex_path: String = "/storage/projects/code/godot/blender-bridge/test/fixtures/infected_rock.ptex"
@export var texture_size: int = 512
var _mesh_instance: MeshInstance3D
# Material Maker's engine, which this project does not currently ship. These
# were referenced without ever being declared, so the whole script failed to
# PARSE -- which is a louder failure than the missing dependency deserves and
# hid it behind a syntax error. Declared here so the script loads and says what
# is actually missing.
var mm_loader: Node = null
var mm_renderer: Node = null
func _ready() -> void:
if not Engine.is_editor_hint():
call_deferred("_load_material")
func _load_material() -> void:
print("[PtexTest] Loading: %s" % ptex_path)
if mm_loader == null or mm_renderer == null:
push_error("[PtexTest] Material Maker's engine (mm_loader / mm_renderer) "
+ "is not available in this project -- nothing to load with.")
return
var gen = await mm_loader.load_gen(ptex_path)
if gen == null:
push_error("[PtexTest] Failed to load ptex")
return
mm_renderer.add_child(gen)
print("[PtexTest] Loaded graph: %d children" % gen.get_child_count())
for i in range(5):
await get_tree().process_frame
# Find the Material node
var mat_node = null
for c in gen.get_children():
if c.has_method("get_export_profiles"):
mat_node = c
break
if mat_node == null:
push_error("[PtexTest] No material node found")
gen.queue_free()
return
# Give MM's renderer time to initialize its rendering thread + device
print("[PtexTest] Waiting for MM renderer to initialize...")
for i in range(30):
await get_tree().process_frame
print("[PtexTest] mm_renderer in tree: %s" % str(mm_renderer.is_inside_tree()))
print("[PtexTest] mm_renderer rendering_device: %s" % str(mm_renderer.rendering_device))
print("[PtexTest] Rendering textures at %dx%d..." % [texture_size, texture_size])
# Render each PBR channel directly using MM's compute shader pipeline
# Material input ports: 0=albedo, 1=metallic, 2=roughness, 3=emission, 4=normal, 5=ao
var size = Vector2i(texture_size, texture_size)
var channels = {
"albedo": 0,
"metallic": 1,
"roughness": 2,
"emission": 3,
"normal": 4,
"ao": 5
}
var textures = {}
for channel_name in channels:
var port = channels[channel_name]
if mat_node.get_source(port) != null:
print("[PtexTest] Rendering %s (port %d)..." % [channel_name, port])
var image = await mat_node.render_output(port, size)
if image:
textures[channel_name] = ImageTexture.create_from_image(image)
print("[PtexTest] %s: %dx%d" % [channel_name, image.get_width(), image.get_height()])
# Also save as PNG
var dir = "res://materials/infected_rock/"
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(dir))
image.save_png(ProjectSettings.globalize_path(dir + channel_name + ".png"))
else:
print("[PtexTest] %s: render returned null" % channel_name)
else:
print("[PtexTest] %s: not connected" % channel_name)
# Build StandardMaterial3D from rendered textures
var mat = StandardMaterial3D.new()
if textures.has("albedo"):
mat.albedo_texture = textures["albedo"]
if textures.has("metallic"):
mat.metallic = 1.0
mat.metallic_texture = textures["metallic"]
if textures.has("roughness"):
mat.roughness_texture = textures["roughness"]
if textures.has("emission"):
mat.emission_enabled = true
mat.emission_texture = textures["emission"]
mat.emission_energy_multiplier = 0.2
if textures.has("normal"):
mat.normal_enabled = true
mat.normal_texture = textures["normal"]
if textures.has("ao"):
mat.ao_enabled = true
mat.ao_texture = textures["ao"]
_ensure_sphere()
if _mesh_instance:
_mesh_instance.set_surface_override_material(0, mat)
print("[PtexTest] Applied material with %d textures" % textures.size())
print("[PtexTest] Done")
func _ensure_sphere() -> void:
for child in get_children():
if child is MeshInstance3D:
_mesh_instance = child
return
_mesh_instance = MeshInstance3D.new()
var sphere := SphereMesh.new()
sphere.radius = 1.0
sphere.height = 2.0
sphere.radial_segments = 64
sphere.rings = 32
_mesh_instance.mesh = sphere
_mesh_instance.name = "PtexSphere"
add_child(_mesh_instance)
if Engine.is_editor_hint():
_mesh_instance.owner = get_tree().edited_scene_root