v2.2.0: Textur-Bake-Button (tileable, fuer Triplanar in UE) + UE-Material-Anleitung
- Bake BaseColor/Normal/Roughness/AO als PNG - Nahtlos tileable via 4D-Torus-Projektion der UV (cos/sin u,v) - Cycles-Bake mit Zustands-Restore, temporaere Bake-Plane + Cleanup - docs/UE_Material_Triplanar.md: WorldAlignedTexture/Normal-Verdrahtung - _deselect_all gegen transiente None-Refs nach Objekt-Removal gehaertet - Live gegen Blender 5.1.2 getestet (4 Maps, Normal jetzt mit Struktur), Headless gruen Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,15 @@ aussen (signed volume > 0), UV0 + UV1 vorhanden, und gibt eine Zusammenfassung
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Changelog v2.2.0
|
||||||
|
|
||||||
|
- **Textur-Bake-Button:** backt ein nahtlos **tileables** Stylized-Rock-Set
|
||||||
|
(BaseColor / Normal / Roughness / AO) als PNG. Nahtlos durch **4D-Torus-
|
||||||
|
Projektion** der UV (`cos/sin` von U und V) → tilet in beide Richtungen ohne
|
||||||
|
Naht, ideal fuer **Triplanar** in Unreal.
|
||||||
|
- Anleitung zur UE-Material-Verdrahtung: siehe
|
||||||
|
[docs/UE_Material_Triplanar.md](docs/UE_Material_Triplanar.md).
|
||||||
|
|
||||||
## Changelog v2.1.0
|
## Changelog v2.1.0
|
||||||
|
|
||||||
- **FBX-Export-Button (UE-ready):** exportiert jeden Rock / jede LOD-Gruppe als
|
- **FBX-Export-Button (UE-ready):** exportiert jeden Rock / jede LOD-Gruppe als
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
schema_version = "1.0.0"
|
schema_version = "1.0.0"
|
||||||
|
|
||||||
id = "stylized_rock_generator"
|
id = "stylized_rock_generator"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
name = "Stylized Rock Generator"
|
name = "Stylized Rock Generator"
|
||||||
tagline = "Batch-Generator fuer stylized Rocks mit UE5-Export"
|
tagline = "Batch-Generator fuer stylized Rocks mit UE5-Export"
|
||||||
maintainer = "D4rkst3r"
|
maintainer = "D4rkst3r"
|
||||||
|
|||||||
Vendored
+4
-4
@@ -7,7 +7,7 @@
|
|||||||
"id": "stylized_rock_generator",
|
"id": "stylized_rock_generator",
|
||||||
"name": "Stylized Rock Generator",
|
"name": "Stylized Rock Generator",
|
||||||
"tagline": "Batch-Generator fuer stylized Rocks mit UE5-Export",
|
"tagline": "Batch-Generator fuer stylized Rocks mit UE5-Export",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"type": "add-on",
|
"type": "add-on",
|
||||||
"maintainer": "D4rkst3r",
|
"maintainer": "D4rkst3r",
|
||||||
"license": [
|
"license": [
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
"Mesh",
|
"Mesh",
|
||||||
"Modeling"
|
"Modeling"
|
||||||
],
|
],
|
||||||
"archive_url": "./stylized_rock_generator-2.1.0.zip",
|
"archive_url": "./stylized_rock_generator-2.2.0.zip",
|
||||||
"archive_size": 11477,
|
"archive_size": 14107,
|
||||||
"archive_hash": "sha256:76911905d9e283ce2cbe85ab341194e3fcfcb431eb7cf3816b96e6d2358214ae"
|
"archive_hash": "sha256:4a4a83e9f6955f0deb0fe56dce59d8e540424df21982639e457db1b3533d9cf5"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,101 @@
|
|||||||
|
# Unreal-Material: Stylized Rock (Triplanar / World-Aligned)
|
||||||
|
|
||||||
|
Ziel: **ein** Material fuer **alle** Rocks. Dank Triplanar (world-aligned) wird die
|
||||||
|
Textur nach Weltposition projiziert – unabhaengig von den UVs jedes einzelnen
|
||||||
|
Steins. Du musst also nichts pro Rock texturieren.
|
||||||
|
|
||||||
|
Die vier Maps kommen aus dem Addon (Panel *Rock Gen → Textur-Bake*):
|
||||||
|
|
||||||
|
| Datei | Inhalt | UE sRGB | Compression |
|
||||||
|
|------------------|-------------|---------|--------------------|
|
||||||
|
| `*_BC.png` | BaseColor | **an** | Default (BC7/DXT1) |
|
||||||
|
| `*_N.png` | Normal | aus | **Normalmap** |
|
||||||
|
| `*_R.png` | Roughness | aus | Default/Masks |
|
||||||
|
| `*_AO.png` | AO / Cavity | aus | Default/Masks |
|
||||||
|
|
||||||
|
> Die Normal Map ist OpenGL (+Y). In UE beim Import **„Flip Green Channel"**
|
||||||
|
> aktivieren (oder gruen einmalig invertieren), sonst wirkt das Relief eingedellt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Texturen importieren
|
||||||
|
|
||||||
|
1. Die vier PNGs in den Content Browser ziehen.
|
||||||
|
2. Fuer `_N`: Compression = **Normalmap**, sRGB aus, **Flip Green Channel** an.
|
||||||
|
3. Fuer `_R` und `_AO`: sRGB **aus** (lineare Daten), Compression „Masks" ist ok.
|
||||||
|
4. `_BC`: sRGB **an** (Standard).
|
||||||
|
|
||||||
|
*(Optional, spart Samples: `_AO`, `_R` und ggf. Metallic in eine „ORM"-Textur
|
||||||
|
packen – R=AO, G=Roughness, B=Metallic. Dann unten die einzelnen Sampler durch
|
||||||
|
einen ORM-Sampler + Component-Mask ersetzen.)*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Material anlegen
|
||||||
|
|
||||||
|
Neues Material `M_Rock_Stylized`, *Two Sided* aus, *Shading Model* Default Lit.
|
||||||
|
|
||||||
|
### Parameter (fuer Instanzen)
|
||||||
|
- **ScalarParameter** `WorldSize` (Default `256`) → Weltgroesse einer Kachel in
|
||||||
|
cm. Kleiner = Textur wirkt groesser/detaillierter.
|
||||||
|
- (optional) **ScalarParameter** `NormalIntensity` (Default `1.0`).
|
||||||
|
|
||||||
|
### BaseColor / Roughness / AO (World Aligned Texture)
|
||||||
|
Fuer jede der drei Maps einen **`WorldAlignedTexture`**-Node:
|
||||||
|
|
||||||
|
```
|
||||||
|
TextureObject(T_..._BC) ─┐
|
||||||
|
WorldSize (Scalar) ──────► WorldAlignedTexture ─► XYZTexture ─► BaseColor
|
||||||
|
```
|
||||||
|
|
||||||
|
- `WorldAlignedTexture` → Eingang **TextureObject** = die jeweilige Textur
|
||||||
|
(als *TextureObject*-Node, nicht *TextureSample*).
|
||||||
|
- Eingang **WorldSize** = `WorldSize`-Parameter.
|
||||||
|
- Ausgang **XYZTexture** verbinden:
|
||||||
|
- `T_..._BC` → **Base Color**
|
||||||
|
- `T_..._R` → **Roughness**
|
||||||
|
- `T_..._AO` → **Ambient Occlusion**
|
||||||
|
|
||||||
|
### Normal (World Aligned Normal)
|
||||||
|
Normalen brauchen den speziellen Node **`WorldAlignedNormal`**:
|
||||||
|
|
||||||
|
```
|
||||||
|
TextureObject(T_..._N) ─► WorldAlignedNormal ─► XYZNormal ─► (FlattenNormal / Normal)
|
||||||
|
WorldSize ─────────────►
|
||||||
|
```
|
||||||
|
|
||||||
|
- Ausgang **XYZTexture (Normal)** → **Normal**.
|
||||||
|
- Intensitaet optional ueber `FlattenNormal` (Flatness = `1 - NormalIntensity`)
|
||||||
|
oder einen Multiply auf den XY-Anteil.
|
||||||
|
|
||||||
|
> `WorldAlignedTexture`/`WorldAlignedNormal` haben einen Haken
|
||||||
|
> **„Use World Space Normal to Blend"** bzw. eine `WorldSpaceNormal`-Eingabe –
|
||||||
|
> Default passt fuer Rocks. Der Node blendet automatisch die drei Projektions-
|
||||||
|
> achsen (Triplanar).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Feinschliff (optional)
|
||||||
|
|
||||||
|
- **Farbvariation pro Instanz:** BaseColor mit `PerInstanceRandom` * einer leichten
|
||||||
|
Tint-Range multiplizieren → jeder Rock leicht anders, gleiche Textur.
|
||||||
|
- **Nahtlos in der Hoehe:** Da die Bake-Textur bereits nahtlos tilet, entstehen bei
|
||||||
|
Triplanar keine sichtbaren Kanten.
|
||||||
|
- **LODs:** Beim FBX-Import „Import LODs" an – das Material gilt automatisch fuer alle
|
||||||
|
LOD-Stufen.
|
||||||
|
- **Lightmaps:** Der UV1-Kanal (Lightmap) aus dem Addon wird von UE fuer statische
|
||||||
|
Beleuchtung genutzt; das Triplanar-Material nutzt ihn nicht, das ist korrekt so.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Kurzfassung der Verdrahtung
|
||||||
|
|
||||||
|
```
|
||||||
|
WorldSize(Scalar)
|
||||||
|
├─► WorldAlignedTexture(BC) ─► XYZTexture ─► BaseColor
|
||||||
|
├─► WorldAlignedTexture(R) ─► XYZTexture ─► Roughness
|
||||||
|
├─► WorldAlignedTexture(AO) ─► XYZTexture ─► Ambient Occlusion
|
||||||
|
└─► WorldAlignedNormal(N) ─► XYZNormal ─► Normal
|
||||||
|
```
|
||||||
|
|
||||||
|
Ein Material, liegt auf jedem Rock. Fertig.
|
||||||
+258
-3
@@ -1,7 +1,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Stylized Rock Generator",
|
"name": "Stylized Rock Generator",
|
||||||
"author": "D4rkst3r",
|
"author": "D4rkst3r",
|
||||||
"version": (2, 1, 0),
|
"version": (2, 2, 0),
|
||||||
"blender": (4, 2, 0),
|
"blender": (4, 2, 0),
|
||||||
"location": "View3D > Sidebar > Rock Gen",
|
"location": "View3D > Sidebar > Rock Gen",
|
||||||
"description": (
|
"description": (
|
||||||
@@ -107,9 +107,12 @@ def _face_count(obj):
|
|||||||
|
|
||||||
|
|
||||||
def _deselect_all(context):
|
def _deselect_all(context):
|
||||||
for o in context.view_layer.objects:
|
for o in list(context.view_layer.objects):
|
||||||
if o.select_get():
|
try:
|
||||||
o.select_set(False)
|
o.select_set(False)
|
||||||
|
except (ReferenceError, AttributeError, RuntimeError):
|
||||||
|
# Kann nach dem Entfernen eines Objekts kurzzeitig ungueltig sein
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _ensure_object_mode(context):
|
def _ensure_object_mode(context):
|
||||||
@@ -728,6 +731,25 @@ class RockGenSettings(PropertyGroup):
|
|||||||
description="Ab dieser Rock-Anzahl wird der modale (nicht-blockierende) Modus genutzt",
|
description="Ab dieser Rock-Anzahl wird der modale (nicht-blockierende) Modus genutzt",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# --- Textur-Bake (tileable, fuer Triplanar in UE) ---
|
||||||
|
bake_dir: StringProperty(
|
||||||
|
name="Bake-Ordner", default="", subtype='DIR_PATH',
|
||||||
|
description="Zielordner fuer die gebackenen Textur-PNGs",
|
||||||
|
)
|
||||||
|
bake_prefix: StringProperty(
|
||||||
|
name="Textur-Prefix", default="T_Rock_Stylized",
|
||||||
|
description="Dateiname-Prefix (UE-Konvention T_)",
|
||||||
|
)
|
||||||
|
bake_resolution: EnumProperty(
|
||||||
|
name="Aufloesung",
|
||||||
|
items=[('512', "512", ""), ('1024', "1024", ""), ('2048', "2048", "")],
|
||||||
|
default='1024',
|
||||||
|
)
|
||||||
|
bake_color: BoolProperty(name="BaseColor", default=True)
|
||||||
|
bake_normal: BoolProperty(name="Normal", default=True)
|
||||||
|
bake_roughness: BoolProperty(name="Roughness", default=True)
|
||||||
|
bake_ao: BoolProperty(name="AO / Cavity", default=True)
|
||||||
|
|
||||||
# --- FBX-Export ---
|
# --- FBX-Export ---
|
||||||
export_dir: StringProperty(
|
export_dir: StringProperty(
|
||||||
name="Export-Ordner", default="", subtype='DIR_PATH',
|
name="Export-Ordner", default="", subtype='DIR_PATH',
|
||||||
@@ -911,6 +933,226 @@ class OBJECT_OT_export_rocks_fbx(Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Textur-Bake (nahtlos tileable via 4D-Torus-Projektion, fuer Triplanar in UE)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _build_bake_material():
|
||||||
|
"""Baut ein prozedurales, nahtlos tileables Stylized-Rock-Material.
|
||||||
|
|
||||||
|
Trick: Die 2D-UV wird als (cos u, sin u, cos v, sin v) auf einen Torus
|
||||||
|
projiziert und als 4D-Noise gesampelt -> tilet in U und V ohne Naht.
|
||||||
|
Liefert (mat, sockets) mit den fertigen Ausgangs-Sockets fuer die Bake-Passes.
|
||||||
|
"""
|
||||||
|
mat = bpy.data.materials.new("__RockBakeMat")
|
||||||
|
mat.use_nodes = True
|
||||||
|
nt = mat.node_tree
|
||||||
|
nodes, links = nt.nodes, nt.links
|
||||||
|
nodes.clear()
|
||||||
|
|
||||||
|
out = nodes.new("ShaderNodeOutputMaterial")
|
||||||
|
bsdf = nodes.new("ShaderNodeBsdfPrincipled")
|
||||||
|
emit = nodes.new("ShaderNodeEmission")
|
||||||
|
texco = nodes.new("ShaderNodeTexCoord")
|
||||||
|
sep = nodes.new("ShaderNodeSeparateXYZ")
|
||||||
|
links.new(texco.outputs["UV"], sep.inputs[0])
|
||||||
|
|
||||||
|
def _circle(coord_socket):
|
||||||
|
mul = nodes.new("ShaderNodeMath"); mul.operation = 'MULTIPLY'
|
||||||
|
mul.inputs[1].default_value = TWO_PI
|
||||||
|
links.new(coord_socket, mul.inputs[0])
|
||||||
|
cos = nodes.new("ShaderNodeMath"); cos.operation = 'COSINE'
|
||||||
|
links.new(mul.outputs[0], cos.inputs[0])
|
||||||
|
sin = nodes.new("ShaderNodeMath"); sin.operation = 'SINE'
|
||||||
|
links.new(mul.outputs[0], sin.inputs[0])
|
||||||
|
return cos.outputs[0], sin.outputs[0]
|
||||||
|
|
||||||
|
cu, su = _circle(sep.outputs["X"])
|
||||||
|
cv, sv = _circle(sep.outputs["Y"])
|
||||||
|
comb = nodes.new("ShaderNodeCombineXYZ")
|
||||||
|
links.new(cu, comb.inputs[0])
|
||||||
|
links.new(su, comb.inputs[1])
|
||||||
|
links.new(cv, comb.inputs[2])
|
||||||
|
|
||||||
|
def _torus_noise(scale, detail):
|
||||||
|
n = nodes.new("ShaderNodeTexNoise")
|
||||||
|
n.noise_dimensions = '4D'
|
||||||
|
n.inputs["Scale"].default_value = scale
|
||||||
|
n.inputs["Detail"].default_value = detail
|
||||||
|
links.new(comb.outputs[0], n.inputs["Vector"])
|
||||||
|
links.new(sv, n.inputs["W"])
|
||||||
|
return n
|
||||||
|
|
||||||
|
noise_base = _torus_noise(2.5, 6.0) # grobe Formvariation
|
||||||
|
noise_cavity = _torus_noise(5.0, 6.0) # feinere Risse/Cavity
|
||||||
|
noise_detail = _torus_noise(9.0, 8.0) # Mikro-Detail fuer die Normal Map
|
||||||
|
|
||||||
|
# Cavity/AO: Kontrast per MapRange
|
||||||
|
ao = nodes.new("ShaderNodeMapRange")
|
||||||
|
links.new(noise_cavity.outputs["Fac"], ao.inputs["Value"])
|
||||||
|
ao.inputs["From Min"].default_value = 0.3
|
||||||
|
ao.inputs["From Max"].default_value = 0.7
|
||||||
|
ao.inputs["To Min"].default_value = 0.35
|
||||||
|
ao.inputs["To Max"].default_value = 1.0
|
||||||
|
ao.clamp = True
|
||||||
|
|
||||||
|
# BaseColor: kombinierter Fac (Form * Cavity) durch ColorRamp
|
||||||
|
comb_fac = nodes.new("ShaderNodeMath"); comb_fac.operation = 'MULTIPLY'
|
||||||
|
links.new(noise_base.outputs["Fac"], comb_fac.inputs[0])
|
||||||
|
links.new(ao.outputs[0], comb_fac.inputs[1])
|
||||||
|
ramp = nodes.new("ShaderNodeValToRGB")
|
||||||
|
ramp.color_ramp.elements[0].position = 0.25
|
||||||
|
ramp.color_ramp.elements[0].color = (0.09, 0.09, 0.10, 1.0)
|
||||||
|
ramp.color_ramp.elements[1].position = 0.85
|
||||||
|
ramp.color_ramp.elements[1].color = (0.46, 0.42, 0.37, 1.0)
|
||||||
|
links.new(comb_fac.outputs[0], ramp.inputs["Fac"])
|
||||||
|
|
||||||
|
# Roughness: rauer in dunklen Bereichen
|
||||||
|
rough = nodes.new("ShaderNodeMapRange")
|
||||||
|
links.new(noise_base.outputs["Fac"], rough.inputs["Value"])
|
||||||
|
rough.inputs["To Min"].default_value = 0.9
|
||||||
|
rough.inputs["To Max"].default_value = 0.6
|
||||||
|
rough.clamp = True
|
||||||
|
|
||||||
|
# Bump -> Normal (fuer den Normal-Bake). Height = grobe Form + Mikro-Detail,
|
||||||
|
# damit die Normal Map sichtbare Struktur bekommt (nicht flach).
|
||||||
|
height_mix = nodes.new("ShaderNodeMath"); height_mix.operation = 'ADD'
|
||||||
|
height_mix.inputs[1].default_value = 0.0
|
||||||
|
hb = nodes.new("ShaderNodeMath"); hb.operation = 'MULTIPLY'
|
||||||
|
hb.inputs[1].default_value = 0.6
|
||||||
|
links.new(noise_base.outputs["Fac"], hb.inputs[0])
|
||||||
|
hd = nodes.new("ShaderNodeMath"); hd.operation = 'MULTIPLY'
|
||||||
|
hd.inputs[1].default_value = 0.4
|
||||||
|
links.new(noise_detail.outputs["Fac"], hd.inputs[0])
|
||||||
|
links.new(hb.outputs[0], height_mix.inputs[0])
|
||||||
|
links.new(hd.outputs[0], height_mix.inputs[1])
|
||||||
|
bump = nodes.new("ShaderNodeBump")
|
||||||
|
bump.inputs["Strength"].default_value = 0.9
|
||||||
|
bump.inputs["Distance"].default_value = 0.5
|
||||||
|
links.new(height_mix.outputs[0], bump.inputs["Height"])
|
||||||
|
links.new(ramp.outputs["Color"], bsdf.inputs["Base Color"])
|
||||||
|
links.new(rough.outputs[0], bsdf.inputs["Roughness"])
|
||||||
|
links.new(bump.outputs["Normal"], bsdf.inputs["Normal"])
|
||||||
|
links.new(bsdf.outputs[0], out.inputs["Surface"])
|
||||||
|
|
||||||
|
sockets = {
|
||||||
|
"out": out, "bsdf": bsdf, "emit": emit,
|
||||||
|
"color": ramp.outputs["Color"],
|
||||||
|
"roughness": rough.outputs[0],
|
||||||
|
"ao": ao.outputs[0],
|
||||||
|
}
|
||||||
|
return mat, sockets
|
||||||
|
|
||||||
|
|
||||||
|
def _bake_one(context, mat, img_node, name, folder, res, bake_type, non_color):
|
||||||
|
img = bpy.data.images.new(name, res, res, alpha=False, float_buffer=False)
|
||||||
|
if non_color:
|
||||||
|
img.colorspace_settings.name = 'Non-Color'
|
||||||
|
img_node.image = img
|
||||||
|
mat.node_tree.nodes.active = img_node
|
||||||
|
if bake_type == 'NORMAL':
|
||||||
|
bpy.ops.object.bake(type='NORMAL', normal_space='TANGENT', use_clear=True)
|
||||||
|
else:
|
||||||
|
bpy.ops.object.bake(type='EMIT', use_clear=True)
|
||||||
|
path = os.path.join(folder, name + ".png")
|
||||||
|
img.filepath_raw = path
|
||||||
|
img.file_format = 'PNG'
|
||||||
|
img.save()
|
||||||
|
bpy.data.images.remove(img)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
class OBJECT_OT_bake_rock_textures(Operator):
|
||||||
|
bl_idname = "object.bake_rock_textures"
|
||||||
|
bl_label = "Textur backen"
|
||||||
|
bl_description = "Backt ein nahtlos tileables Stylized-Rock-Textur-Set (fuer Triplanar in UE)"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
settings = context.scene.rock_gen_settings
|
||||||
|
folder = bpy.path.abspath(settings.bake_dir) if settings.bake_dir else ""
|
||||||
|
if not folder or not os.path.isdir(folder):
|
||||||
|
self.report({'ERROR'}, "Bitte einen gueltigen Bake-Ordner waehlen.")
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
res = int(settings.bake_resolution)
|
||||||
|
prefix = settings.bake_prefix.strip() or "T_Rock_Stylized"
|
||||||
|
scene = context.scene
|
||||||
|
|
||||||
|
# Zustand sichern
|
||||||
|
prev_engine = scene.render.engine
|
||||||
|
prev_active = context.view_layer.objects.active
|
||||||
|
prev_selected = [o for o in context.selected_objects]
|
||||||
|
prev_samples = getattr(scene.cycles, "samples", None) if hasattr(scene, "cycles") else None
|
||||||
|
|
||||||
|
_ensure_object_mode(context)
|
||||||
|
saved = []
|
||||||
|
plane = None
|
||||||
|
mat = None
|
||||||
|
try:
|
||||||
|
bpy.ops.mesh.primitive_plane_add(size=2.0)
|
||||||
|
plane = context.active_object
|
||||||
|
plane.name = "__RockBakePlane"
|
||||||
|
mat, sk = _build_bake_material()
|
||||||
|
plane.data.materials.clear()
|
||||||
|
plane.data.materials.append(mat)
|
||||||
|
|
||||||
|
scene.render.engine = 'CYCLES'
|
||||||
|
if hasattr(scene, "cycles"):
|
||||||
|
scene.cycles.samples = 1 # Emit/Normal sind deterministisch
|
||||||
|
scene.render.bake.margin = max(2, res // 128)
|
||||||
|
|
||||||
|
nodes = mat.node_tree.nodes
|
||||||
|
links = mat.node_tree.links
|
||||||
|
img_node = nodes.new("ShaderNodeTexImage")
|
||||||
|
|
||||||
|
_deselect_all(context)
|
||||||
|
plane.select_set(True)
|
||||||
|
context.view_layer.objects.active = plane
|
||||||
|
|
||||||
|
if settings.bake_normal:
|
||||||
|
links.new(sk["bsdf"].outputs[0], sk["out"].inputs["Surface"])
|
||||||
|
saved.append(_bake_one(context, mat, img_node, prefix + "_N",
|
||||||
|
folder, res, 'NORMAL', non_color=True))
|
||||||
|
|
||||||
|
def _emit_pass(source_socket, suffix, non_color):
|
||||||
|
links.new(source_socket, sk["emit"].inputs["Color"])
|
||||||
|
links.new(sk["emit"].outputs[0], sk["out"].inputs["Surface"])
|
||||||
|
saved.append(_bake_one(context, mat, img_node, prefix + suffix,
|
||||||
|
folder, res, 'EMIT', non_color))
|
||||||
|
|
||||||
|
if settings.bake_color:
|
||||||
|
_emit_pass(sk["color"], "_BC", non_color=False)
|
||||||
|
if settings.bake_roughness:
|
||||||
|
_emit_pass(sk["roughness"], "_R", non_color=True)
|
||||||
|
if settings.bake_ao:
|
||||||
|
_emit_pass(sk["ao"], "_AO", non_color=True)
|
||||||
|
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
print("[RockGen] Bake fehlgeschlagen: %s" % exc)
|
||||||
|
traceback.print_exc()
|
||||||
|
self.report({'ERROR'}, "Bake fehlgeschlagen: %s (Details in der Konsole)" % exc)
|
||||||
|
finally:
|
||||||
|
if plane is not None:
|
||||||
|
bpy.data.objects.remove(plane, do_unlink=True)
|
||||||
|
if mat is not None and mat.users == 0:
|
||||||
|
bpy.data.materials.remove(mat)
|
||||||
|
scene.render.engine = prev_engine
|
||||||
|
if prev_samples is not None:
|
||||||
|
scene.cycles.samples = prev_samples
|
||||||
|
_deselect_all(context)
|
||||||
|
for o in prev_selected:
|
||||||
|
try:
|
||||||
|
o.select_set(True)
|
||||||
|
except ReferenceError:
|
||||||
|
pass
|
||||||
|
context.view_layer.objects.active = prev_active
|
||||||
|
|
||||||
|
if saved:
|
||||||
|
self.report({'INFO'}, "%d Textur(en) gebacken nach %s" % (len(saved), folder))
|
||||||
|
return {'FINISHED'}
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Operatoren: Presets
|
# Operatoren: Presets
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -1099,6 +1341,18 @@ class VIEW3D_PT_rock_generator(Panel):
|
|||||||
box.prop(settings, "export_selected_only")
|
box.prop(settings, "export_selected_only")
|
||||||
box.operator("object.export_rocks_fbx", icon='EXPORT')
|
box.operator("object.export_rocks_fbx", icon='EXPORT')
|
||||||
|
|
||||||
|
box = layout.box()
|
||||||
|
box.label(text="Textur-Bake (tileable / Triplanar)")
|
||||||
|
box.prop(settings, "bake_dir")
|
||||||
|
box.prop(settings, "bake_prefix")
|
||||||
|
box.prop(settings, "bake_resolution")
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.prop(settings, "bake_color", toggle=True)
|
||||||
|
row.prop(settings, "bake_normal", toggle=True)
|
||||||
|
row.prop(settings, "bake_roughness", toggle=True)
|
||||||
|
row.prop(settings, "bake_ao", toggle=True)
|
||||||
|
box.operator("object.bake_rock_textures", icon='TEXTURE')
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Registration
|
# Registration
|
||||||
@@ -1108,6 +1362,7 @@ classes = (
|
|||||||
RockGenSettings,
|
RockGenSettings,
|
||||||
OBJECT_OT_generate_rocks,
|
OBJECT_OT_generate_rocks,
|
||||||
OBJECT_OT_export_rocks_fbx,
|
OBJECT_OT_export_rocks_fbx,
|
||||||
|
OBJECT_OT_bake_rock_textures,
|
||||||
ROCKGEN_OT_preset_save,
|
ROCKGEN_OT_preset_save,
|
||||||
ROCKGEN_OT_preset_load,
|
ROCKGEN_OT_preset_load,
|
||||||
ROCKGEN_OT_preset_delete,
|
ROCKGEN_OT_preset_delete,
|
||||||
|
|||||||
Reference in New Issue
Block a user