Tree Gen 1.32.0: Deckung pro Stufe, mitwachsender Radius-Filter, LOD als Teilset
Drei Befunde aus der Turnhalle abgearbeitet. 1) Deckung PRO STUFE statt globaler Formel Die alte Rechnung (Dichte durch faktor^2) hielt den Setzling vor der Kahlheit, gab ihm aber die Deckung eines Altbaums. Jetzt Tabelle LEAF_STUFEN. S4 traegt die volle Dichte 170, S2 liegt 15 % darunter, S1 deutlich. Der Dichte-Anteil bei S1/S2 sieht hoch aus - "Dichte" ist Punkte pro m2 ZWEIGFLAECHE, und die waechst ueber die Stufen dramatisch. Was zaehlt ist die Anzahl: 17 / 43 / 67 / 130 Cards, Kante 0.19 / 0.29 / 0.37 / 0.45. Budget S3/S4 auf 4300 (die Spielseite hebt ASSETS.md nach dem 170er-Export entsprechend an). 2) Radius-Filter skaliert mit dem Wachstum (der S3-Befund) Der Filter trennt duenne Zweige von dickem Geaest - aber mit starren 0.04 m greift er bei jungen Baeumen nicht: gemessen sassen bei S2 81 % der Cards am Stamm, bei S3 58 %. Jetzt 48 bzw. 52 %. Dabei eine eigene Fehldeutung korrigiert: bei S1 bleiben ~88 % am Stamm, und das ist KEIN Defekt. Nachgemessen ist die zulaessige Flaeche dort fuer jeden Schwellwert identisch (0.013 m2) - der Saemling hat kein dickes Geaest, an dem sich etwas trennen liesse. Blaetter am Stiel sind bei einem Saemling der Normalfall. Ein erster Sweep hatte mich in die Irre gefuehrt, weil ich growth_t=0 auf einen AUSGEWACHSENEN Baum gesetzt hatte - der hat die volle Zweigflaeche und verhaelt sich voellig anders als ein echter Setzling. 3) LOD-Reduktion als TEILSET der LOD0-Cards Bisher wurde je Stufe neu gestreut - die Cards sassen an anderen Stellen und die Silhouette sprang beim LOD-Wechsel um. Jetzt wird ausgeduennt: jede verbleibende Card liegt genau dort, wo sie in LOD0 schon war, die Ueberlebenden werden groesser. Gearbeitet wird auf zusammenhaengenden INSELN, nicht auf Faces - eine Card muss ganz bleiben oder ganz verschwinden. Auswahl ist seed-stabil. Gemessen am reimportierten FBX: 260 / 156 / 78 / 32 Blatt-Faces, und 100 % der Cards auf LOD1-3 sitzen auf einer LOD0-Position. Der Versatz zu LOD0 ist dabei kleiner geworden als beim Neustreuen. 23 Tests bestehen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -395,7 +395,7 @@ gemessen und abgenommen:
|
||||
| | vorher | jetzt | Wirkung |
|
||||
|---|---|---|---|
|
||||
| Groesse | 0.22 | **0.85** | Bueschel statt Bonsai |
|
||||
| Dichte | 500 | **150** | wenige grosse statt viele Fussel |
|
||||
| Dichte | 500 | **170** | wenige grosse statt viele Fussel |
|
||||
| **Max Radius** | 0.02 | **0.04** | der Volumen-Hebel |
|
||||
| Kippung | 0.5 | **0.9** | Rotations-Chaos schliesst Sichtluecken |
|
||||
|
||||
@@ -403,6 +403,29 @@ gemessen und abgenommen:
|
||||
Zweigspitzen Laub, die Krone hatte nur Kontur und war innen nackt. Bei 0.04
|
||||
wachsen Bueschel auch am mittleren Geaest und die Krone bekommt Tiefe.
|
||||
|
||||
**Deckung pro Stufe, nicht per Formel.** Card-Anteil und Groesse stehen als
|
||||
Tabelle (`LEAF_STUFEN`). Der Dichte-Anteil bei S1/S2 sieht hoch aus — das
|
||||
taeuscht: „Dichte" ist Punkte pro m² *Zweigflaeche*, und die waechst ueber die
|
||||
Stufen dramatisch. Gemessen hat der S1-Setzling **0.013 m²** zulaessige Flaeche;
|
||||
ohne Anhebung bekaeme er null Cards. Was zaehlt, ist die Anzahl:
|
||||
|
||||
| Stufe | Cards | Card-Kante | Tris | Budget |
|
||||
|---|---|---|---|---|
|
||||
| S1 | 17 | 0.19 | 498 | 800 |
|
||||
| S2 | 43 | 0.29 | 754 | 1200 |
|
||||
| S3 | 67 | 0.37 | 946 | 4300 |
|
||||
| S4 | 130 | 0.45 | 1396 | 4300 |
|
||||
|
||||
**Der Radius-Filter skaliert mit.** Er trennt duenne Zweige (Laub) von dickem
|
||||
Geaest — aber ein Setzling ist *komplett* duenner als eine feste Schwelle. Mit
|
||||
starren 0.04 m sassen bei S2 **81 %** der Cards am Stamm, bei S3 58 %; jetzt
|
||||
48 bzw. 52 %.
|
||||
|
||||
> Bei S1 bleiben rund 88 % am Stamm, und das ist **kein Defekt**: gemessen ist
|
||||
> die zulaessige Flaeche dort fuer *jeden* Schwellwert identisch — der Saemling
|
||||
> hat schlicht kein dickes Geaest, an dem sich etwas trennen liesse. Blaetter am
|
||||
> Stiel sind bei einem Saemling der Normalfall.
|
||||
|
||||
**Bueschelgroesse haengt am Wachstum.** Der Generator merkt sich `growth_t` am
|
||||
Objekt; der Blattwerk-Operator skaliert die Cards damit (`Setzlings-Anteil`,
|
||||
Standard 45 %). Gemessen ueber vier Stufen: Card-Kante 0.19 → 0.28 → 0.37 → 0.45.
|
||||
@@ -436,10 +459,17 @@ Haken `LOD-Kette erzeugen`: exportiert `<Name>_LODGroup.fbx` mit `_LOD0..3` unte
|
||||
einem `fbx_type=LodGroup`-Empty — dasselbe Schema wie die Fels-Pipeline. **Nur
|
||||
der Stand-Baum**; die Faell-Teile leben Sekunden als Physik-Actor, dort reicht LOD0.
|
||||
|
||||
Die Blatt-Reduktion laeuft ueber die **Dichte**, nicht ueber Decimate: eine Card
|
||||
besteht aus zwei Dreiecken und laesst sich nicht vereinfachen — man muss weniger
|
||||
davon setzen. Damit die Krone dabei nicht ausduennt, werden die verbleibenden
|
||||
Cards groesser. LOD3 ist der Impostor-Ersatz.
|
||||
Die Blatt-Reduktion ist ein **Teilset der LOD0-Cards**, keine Neustreuung. Das
|
||||
ist der Unterschied zwischen „die Krone wird duenner" und „die Krone springt
|
||||
um": beim Neustreuen laegen die Cards an anderen Stellen und die Silhouette
|
||||
wechselt beim LOD-Uebergang sichtbar. Als Teilset bleibt jede verbleibende Card
|
||||
genau dort, wo sie in LOD0 schon war; damit die Krone nicht loechrig wird,
|
||||
werden die Ueberlebenden groesser. Gemessen: **100 % der Cards auf LOD1–3
|
||||
sitzen auf einer LOD0-Position.** LOD3 ist der Impostor-Ersatz.
|
||||
|
||||
Gearbeitet wird auf zusammenhaengenden **Inseln**, nicht auf einzelnen Faces —
|
||||
eine Card besteht aus mehreren Dreiecken und muss ganz bleiben oder ganz
|
||||
verschwinden.
|
||||
|
||||
| Stufe | Dichte | Card-Groesse | Rinde | gemessen (Rinde/Blatt/Tris) |
|
||||
|---|---|---|---|---|
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version = "1.0.0"
|
||||
|
||||
id = "stylized_tree_generator"
|
||||
version = "1.31.0"
|
||||
version = "1.32.0"
|
||||
name = "Stylized Tree Generator"
|
||||
tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen"
|
||||
maintainer = "D4rkst3r"
|
||||
|
||||
Vendored
+3
-3
@@ -50,17 +50,17 @@
|
||||
<td>34.1KB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><tt><a href="./stylized_tree_generator-1.31.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.31.0</a></tt></td>
|
||||
<td><tt><a href="./stylized_tree_generator-1.32.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.32.0</a></tt></td>
|
||||
<td>Stylized Tree Generator</td>
|
||||
<td>Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen</td>
|
||||
<td><a href="https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator">link</a></td>
|
||||
<td>4.2.0 - ~</td>
|
||||
<td>all</td>
|
||||
<td>all</td>
|
||||
<td>36.3KB</td>
|
||||
<td>37.8KB</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<center><p>Built 2026-08-06, 07:20</p></center>
|
||||
<center><p>Built 2026-08-06, 07:44</p></center>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Vendored
+4
-4
@@ -82,7 +82,7 @@
|
||||
"id": "stylized_tree_generator",
|
||||
"name": "Stylized Tree Generator",
|
||||
"tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen",
|
||||
"version": "1.31.0",
|
||||
"version": "1.32.0",
|
||||
"type": "add-on",
|
||||
"maintainer": "D4rkst3r",
|
||||
"license": [
|
||||
@@ -98,9 +98,9 @@
|
||||
"Mesh",
|
||||
"Modeling"
|
||||
],
|
||||
"archive_url": "./stylized_tree_generator-1.31.0.zip",
|
||||
"archive_size": 37196,
|
||||
"archive_hash": "sha256:2afb514c39d4378baa8885b53e117bb460987ac57178a826bc20cc8b1efd71b5"
|
||||
"archive_url": "./stylized_tree_generator-1.32.0.zip",
|
||||
"archive_size": 38712,
|
||||
"archive_hash": "sha256:b92b7caccf18b7690b63cee35b8597484a0a789d47a9df529857d23bbf57be8f"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+168
-45
@@ -1,7 +1,7 @@
|
||||
bl_info = {
|
||||
"name": "Stylized Tree Generator",
|
||||
"author": "D4rkst3r",
|
||||
"version": (1, 31, 0),
|
||||
"version": (1, 32, 0),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "View3D > Sidebar > Tree Gen",
|
||||
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
|
||||
@@ -1195,11 +1195,7 @@ LEAF_GROUP_NAME = "GN_TreeLeaves"
|
||||
# hingehoeren. Mit Bueschel-Atlanten liegen die Groessenordnungen bei
|
||||
# Size ~0.7 und Density ~90.
|
||||
LEAF_DEFAULTS = {
|
||||
# 150 statt der zunaechst gemessenen 170: mit der REALEN Diamond-Card
|
||||
# (6 Tris, nicht 2) landet die Eiche bei 170 auf 4206 Tris und damit ueber
|
||||
# dem 4000er-Budget aus ASSETS.md Teil 6 - 2778 Krone plus 1428 Stamm.
|
||||
# 150 liegt im abgesegneten Sweetspot 130-170 und haelt das Budget (3816).
|
||||
"Density": 150.0, # Punkte pro m2 Zweigoberflaeche
|
||||
"Density": 170.0, # Punkte pro m2 Zweigoberflaeche (S4-Wert)
|
||||
# DER Volumen-Hebel. Bei 0.02 trugen nur die aeussersten Zweigspitzen Laub -
|
||||
# die Krone hatte nur Kontur und war innen nackt. Bei 0.04 wachsen Bueschel
|
||||
# auch am mittleren Geaest und die Krone bekommt Tiefe. Am fertigen Baum in
|
||||
@@ -1385,6 +1381,47 @@ def leaf_socket_ids(ng):
|
||||
if getattr(so, "in_out", "") == 'INPUT'}
|
||||
|
||||
|
||||
# Deckung PRO STUFE statt einer Formel. Die fruehere Rechnung (Dichte durch
|
||||
# faktor^2) war zu grob: sie hielt den Setzling zwar vor der Kahlheit, gab ihm
|
||||
# aber eine Deckung wie einem Altbaum.
|
||||
#
|
||||
# Der Dichte-Anteil bei S1/S2 sieht hoch aus - das taeuscht. "Dichte" ist
|
||||
# Punkte pro m2 ZWEIGFLAECHE, und die waechst ueber die Stufen dramatisch:
|
||||
# gemessen hat der S1-Setzling 0.013 m2 zulaessige Flaeche. Ohne Anhebung
|
||||
# bekaeme er null Cards. Was zaehlt, ist die ANZAHL, und die steigt sauber
|
||||
# von ~8 auf ~65.
|
||||
#
|
||||
# S4 traegt die volle Dichte (170), S2 liegt 15 % darunter, S1 deutlich.
|
||||
# Dichte-Anteil Groessen-Anteil
|
||||
# Boden fuer den Radius-Filter beim Setzling. Nachgemessen: 0.064 (der reine
|
||||
# Stammradius-Faktor) laesst ihn kahl, 0.20 gibt 12 Cards bei 12 % am Stamm.
|
||||
LEAF_RADIUS_YOUNG = 0.20
|
||||
|
||||
# Dichte-Anteil Groessen-Anteil
|
||||
LEAF_STUFEN = (
|
||||
(6.00, 0.45), # S1 - wenige, kleine Bueschel auf winziger Zweigflaeche
|
||||
(1.60, 0.63), # S2 - 15 % unter der vollen Deckung (nach Flaeche gerechnet)
|
||||
(1.00, 0.82), # S3
|
||||
(1.00, 1.00), # S4 - volle Dichte
|
||||
)
|
||||
|
||||
|
||||
def _stufen_anteile(growth_t):
|
||||
"""Dichte- und Groessen-Anteil fuer einen Wachstums-Fortschritt.
|
||||
|
||||
Die Stufen liegen bei t = 0 / 1/3 / 2/3 / 1 (vier Stufen). Zwischen den
|
||||
Stuetzstellen wird linear gemischt, damit auch drei oder fuenf Stufen
|
||||
sinnvolle Werte bekommen.
|
||||
"""
|
||||
n = len(LEAF_STUFEN)
|
||||
x = max(0.0, min(1.0, growth_t)) * (n - 1)
|
||||
i = min(int(x), n - 2)
|
||||
f = x - i
|
||||
d0, g0 = LEAF_STUFEN[i]
|
||||
d1, g1 = LEAF_STUFEN[i + 1]
|
||||
return d0 + (d1 - d0) * f, g0 + (g1 - g0) * f
|
||||
|
||||
|
||||
def _stufen_budget(s, growth_t):
|
||||
"""Tri-Budget fuer einen Baum nach seiner Wachstums-Stufe."""
|
||||
if growth_t < 0.15:
|
||||
@@ -1451,13 +1488,16 @@ def make_leaves(context, tree_obj, card_obj, name=None, card_coll=None, **overri
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# LOD-Stufen fuer den Stand-Baum. Die Blatt-Reduktion laeuft ueber die DICHTE,
|
||||
# nicht ueber Decimate: eine Card ist ein Billboard aus zwei Dreiecken - die
|
||||
# laesst sich nicht sinnvoll vereinfachen, man muss weniger davon setzen. Damit
|
||||
# die Krone dabei nicht ausduennt, werden die verbleibenden Cards groesser.
|
||||
# LOD-Stufen fuer den Stand-Baum.
|
||||
#
|
||||
# Die Blatt-Reduktion ist ein TEILSET der LOD0-Cards, keine Neustreuung: die
|
||||
# verbleibenden Cards liegen genau dort, wo sie in LOD0 schon waren. Wuerde man
|
||||
# je Stufe neu streuen, saessen sie an anderen Stellen und die Silhouette
|
||||
# spraenge beim LOD-Wechsel sichtbar um. Damit die Krone beim Ausduennen nicht
|
||||
# loechrig wird, werden die Ueberlebenden groesser.
|
||||
# LOD3 ist der Impostor-Ersatz: wenige, dafuer sehr grosse Cards.
|
||||
#
|
||||
# Dichte Card-Groesse Rinden-Decimate
|
||||
# Card-Anteil Card-Groesse Rinden-Decimate
|
||||
LOD_STUFEN = (
|
||||
(1.00, 1.00, 1.00), # LOD0 - wie der Stand-Baum
|
||||
(0.60, 1.15, 0.70), # LOD1
|
||||
@@ -1524,6 +1564,91 @@ def _sphere_normals(ob, slot_index, center=None):
|
||||
return center
|
||||
|
||||
|
||||
|
||||
def _cards_ausduennen(ob, slot, anteil, groesse, seed=0):
|
||||
"""Behaelt einen TEILSET der Cards und vergroessert die Ueberlebenden.
|
||||
|
||||
Der Unterschied zum Neustreuen mit geringerer Dichte ist der entscheidende:
|
||||
beim Neustreuen liegen die Cards an ANDEREN Stellen, und beim LOD-Wechsel
|
||||
springt die Silhouette sichtbar um. Ein Teilset laesst die verbleibenden
|
||||
Cards genau dort, wo sie in LOD0 schon waren - die Krone wird duenner, aber
|
||||
ihre Form bleibt.
|
||||
|
||||
Gearbeitet wird auf zusammenhaengenden Inseln, nicht auf einzelnen Faces:
|
||||
eine Card besteht aus mehreren Dreiecken und muss ganz bleiben oder ganz
|
||||
verschwinden. Die Auswahl ist seed-stabil, damit zwei Exporte desselben
|
||||
Baums dieselbe Kette ergeben.
|
||||
"""
|
||||
import random as _rnd
|
||||
|
||||
me = ob.data
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(me)
|
||||
bm.faces.ensure_lookup_table()
|
||||
|
||||
kandidaten = [f for f in bm.faces if f.material_index == slot]
|
||||
if not kandidaten:
|
||||
bm.free()
|
||||
return 0, 0
|
||||
|
||||
# Inseln bilden (zusammenhaengende Flaechen = eine Card)
|
||||
offen = set(kandidaten)
|
||||
inseln = []
|
||||
while offen:
|
||||
start = offen.pop()
|
||||
insel = [start]
|
||||
stapel = [start]
|
||||
while stapel:
|
||||
f = stapel.pop()
|
||||
for e in f.edges:
|
||||
for nf in e.link_faces:
|
||||
if nf in offen:
|
||||
offen.discard(nf)
|
||||
insel.append(nf)
|
||||
stapel.append(nf)
|
||||
inseln.append(insel)
|
||||
|
||||
gesamt = len(inseln)
|
||||
behalten = max(1, int(round(gesamt * max(0.0, min(1.0, anteil)))))
|
||||
|
||||
# Seed-stabile Auswahl: sortiert nach einer festen Kennzahl, damit die
|
||||
# Reihenfolge nicht von der bmesh-Iteration abhaengt.
|
||||
inseln.sort(key=lambda ins: tuple(round(c, 4)
|
||||
for c in ins[0].calc_center_median()))
|
||||
rng = _rnd.Random(seed)
|
||||
reihenfolge = list(range(gesamt))
|
||||
rng.shuffle(reihenfolge)
|
||||
behalte_idx = set(reihenfolge[:behalten])
|
||||
|
||||
weg = []
|
||||
for i, insel in enumerate(inseln):
|
||||
if i not in behalte_idx:
|
||||
weg.extend(insel)
|
||||
if weg:
|
||||
bmesh.ops.delete(bm, geom=weg, context='FACES')
|
||||
|
||||
# Ueberlebende vergroessern - je Card um IHRE EIGENE Mitte, sonst wandert
|
||||
# sie beim Skalieren von der Zweigstelle weg.
|
||||
if groesse != 1.0:
|
||||
for i, insel in enumerate(inseln):
|
||||
if i not in behalte_idx:
|
||||
continue
|
||||
verts = {v for f in insel for v in f.verts if v.is_valid}
|
||||
if not verts:
|
||||
continue
|
||||
mitte = Vector((0.0, 0.0, 0.0))
|
||||
for v in verts:
|
||||
mitte += v.co
|
||||
mitte /= len(verts)
|
||||
for v in verts:
|
||||
v.co = mitte + (v.co - mitte) * groesse
|
||||
|
||||
bm.to_mesh(me)
|
||||
bm.free()
|
||||
me.update()
|
||||
return gesamt, behalten
|
||||
|
||||
|
||||
def _merge_for_export(context, tree_ob, leaf_ob, name, bark_decimate=1.0):
|
||||
"""Baut EINE Export-Kopie aus Stamm + Blattebene mit zwei Material-Slots.
|
||||
|
||||
@@ -1865,7 +1990,7 @@ class TreeGenSettings(PropertyGroup):
|
||||
"damit mischt die Krone 4-6 Varianten statt einer"),
|
||||
)
|
||||
leaf_density: FloatProperty(
|
||||
name="Dichte", default=150.0, min=0.0, max=20000.0,
|
||||
name="Dichte", default=170.0, min=0.0, max=20000.0,
|
||||
description=("Cards pro m2 Zweigoberflaeche. Presets mit viel Geaest (Eiche) "
|
||||
"bekommen dadurch automatisch mehr Blaetter als schlanke. 150 haelt "
|
||||
"auch die Eiche im 2500-Tri-Budget; der Button meldet die Summe"),
|
||||
@@ -1879,7 +2004,7 @@ class TreeGenSettings(PropertyGroup):
|
||||
description="Tri-Budget der zweiten Stufe (ASSETS.md Teil 6)",
|
||||
)
|
||||
leaf_budget: IntProperty(
|
||||
name="Budget S3/S4", default=4000, min=0, max=100000,
|
||||
name="Budget S3/S4", default=4300, min=0, max=100000,
|
||||
description=("Tri-Budget der ausgewachsenen Stufe. Die urspruenglichen "
|
||||
"2500 waren eine konservative Setzung - bei ISM-Instanzen "
|
||||
"mit LOD-Kette sind Roh-Dreiecke nicht der Engpass, "
|
||||
@@ -2118,29 +2243,21 @@ class TREEGEN_OT_export_game(Operator):
|
||||
lod_name = ("%s_LOD%d" % (basis_name, i)) if s.generate_lods \
|
||||
else basis_name
|
||||
|
||||
# Blatt-Ebene je Stufe NEU streuen statt sie zu dezimieren:
|
||||
# eine Card besteht aus zwei Dreiecken, die laesst sich nicht
|
||||
# vereinfachen - man muss weniger davon setzen. Die
|
||||
# verbleibenden werden groesser, damit die Krone nicht
|
||||
# ausduennt.
|
||||
lod_blatt = blatt
|
||||
if blatt is not None and s.generate_lods and i > 0:
|
||||
lod_blatt = make_leaves(
|
||||
context, baum, s.leaf_card,
|
||||
name="%s__leaflod%d" % (basis_name, i),
|
||||
card_coll=s.leaf_cards,
|
||||
Density=s.leaf_density * dichte,
|
||||
Size=s.leaf_size * groesse,
|
||||
**{"Size Var": s.leaf_size_var,
|
||||
"Max Radius": s.leaf_max_radius,
|
||||
"Tilt": s.leaf_tilt, "Seed": s.seed})
|
||||
context.view_layer.update()
|
||||
aufraeumen.append(lod_blatt.name)
|
||||
|
||||
# Alle Stufen gehen von DERSELBEN Blatt-Ebene aus. Die
|
||||
# Reduktion passiert weiter unten als Teilset - neu zu
|
||||
# streuen wuerde die Cards an andere Stellen setzen und die
|
||||
# Silhouette beim LOD-Wechsel sichtbar umspringen lassen.
|
||||
merge, gemacht = _merge_for_export(
|
||||
context, baum, lod_blatt, lod_name, bark_decimate=dec)
|
||||
context, baum, blatt, lod_name, bark_decimate=dec)
|
||||
aufraeumen.extend(gemacht)
|
||||
|
||||
if s.generate_lods and i > 0 and len(merge.data.materials) > 1:
|
||||
vorher, nachher = _cards_ausduennen(
|
||||
merge, 1, dichte, groesse, seed=s.seed)
|
||||
print("[TreeGen] %s: %d von %d Cards behalten, "
|
||||
"%.2fx vergroessert"
|
||||
% (merge.name, nachher, vorher, groesse))
|
||||
|
||||
# Kugel-Normalen auf JEDER Stufe - LOD1..3 haben eine eigene
|
||||
# Card-Verteilung, die Normalen muessen dazu passen.
|
||||
mitte = None
|
||||
@@ -2437,21 +2554,27 @@ class TREEGEN_OT_leaves(Operator):
|
||||
# traegt kleinere Bueschel. Baeume ohne Wachstums-Stufe haben
|
||||
# growth_t = 1.0 und bleiben unveraendert.
|
||||
gt = float(t.get("growth_t", 1.0))
|
||||
faktor = s.leaf_size_growth + (1.0 - s.leaf_size_growth) * gt
|
||||
# Dichte GEGENLAEUFIG mitziehen. "Dichte" ist Punkte pro m2
|
||||
# Zweigoberflaeche, und ein Setzling hat davon fast keine -
|
||||
# gemessen kam bei Stufe 1 exakt NULL Cards heraus, der
|
||||
# Setzling stand kahl da. Da die Bueschel um `faktor` kleiner
|
||||
# sind, deckt jeder nur faktor^2 der Flaeche; die Dichte
|
||||
# entsprechend anzuheben haelt die Blattdeckung ueber alle
|
||||
# Stufen gleich, statt den Setzling auszuduennen.
|
||||
dichte = s.leaf_density / max(faktor * faktor, 1e-3)
|
||||
d_anteil, g_anteil = _stufen_anteile(gt)
|
||||
|
||||
# Der RADIUS-FILTER muss mit dem Baum schrumpfen. Er trennt
|
||||
# duenne Zweige (Laub) von dickem Geaest (kein Laub) - aber ein
|
||||
# Setzling ist KOMPLETT duenner als die Schwelle. Gemessen mit
|
||||
# festem 0.04 m: bei S1 sassen 92 % der Cards am Stamm, bei S2
|
||||
# noch 81 %, das Laub klebte also am Stiel statt in der Krone.
|
||||
# Skaliert wird mit dem Wachstum, aber mit BODEN bei 0.20.
|
||||
# Der reine Stammradius-Faktor (0.064 bei t=0) war zu scharf:
|
||||
# damit fiel die Schwelle unter die duennsten Zweige und der
|
||||
# Setzling stand wieder kahl (gemessen 0 Cards). Bei 0.20
|
||||
# bekommt er 12 Cards, davon nur 12 % am Stamm - vorher waren
|
||||
# es 92 %.
|
||||
r_faktor = LEAF_RADIUS_YOUNG + (1.0 - LEAF_RADIUS_YOUNG) * gt
|
||||
|
||||
lf = make_leaves(context, t, s.leaf_card,
|
||||
card_coll=s.leaf_cards,
|
||||
Density=dichte,
|
||||
Size=s.leaf_size * faktor,
|
||||
Density=s.leaf_density * d_anteil,
|
||||
Size=s.leaf_size * g_anteil,
|
||||
**{"Size Var": s.leaf_size_var,
|
||||
"Max Radius": s.leaf_max_radius,
|
||||
"Max Radius": s.leaf_max_radius * r_faktor,
|
||||
"Tilt": s.leaf_tilt, "Seed": s.seed})
|
||||
except Exception as exc: # noqa: BLE001
|
||||
self.report({'WARNING'}, "%s: %s" % (t.name, exc))
|
||||
|
||||
@@ -186,6 +186,68 @@ if blatt_zahlen and blatt_zahlen[0] > 0:
|
||||
fails.append("LOD3 hat noch %.0f%% der Cards - als Impostor-Ersatz zu "
|
||||
"teuer" % anteil)
|
||||
|
||||
# --- Die Cards muessen ein TEILSET von LOD0 sein -------------------------
|
||||
# Das ist der Test gegen den Silhouetten-Sprung: wuerde je Stufe neu gestreut,
|
||||
# saessen die Cards an anderen Stellen und die Krone spraenge beim LOD-Wechsel
|
||||
# sichtbar um. Als Teilset liegt jede verbleibende Card dort, wo sie in LOD0
|
||||
# schon war.
|
||||
def card_mitten(ob):
|
||||
"""Mittelpunkte der Blatt-Inseln (eine Insel = eine Card)."""
|
||||
m = ob.data
|
||||
faces = [p for p in m.polygons if p.material_index == 1]
|
||||
if not faces:
|
||||
return []
|
||||
# Inseln ueber gemeinsame Vertices finden
|
||||
von_vert = {}
|
||||
for p in faces:
|
||||
for vi in p.vertices:
|
||||
von_vert.setdefault(vi, []).append(p.index)
|
||||
gesehen = set()
|
||||
mitten = []
|
||||
for p in faces:
|
||||
if p.index in gesehen:
|
||||
continue
|
||||
stapel = [p.index]
|
||||
gesehen.add(p.index)
|
||||
insel = []
|
||||
while stapel:
|
||||
fi = stapel.pop()
|
||||
f = m.polygons[fi]
|
||||
insel.append(f)
|
||||
for vi in f.vertices:
|
||||
for nb in von_vert.get(vi, ()):
|
||||
if nb not in gesehen:
|
||||
gesehen.add(nb)
|
||||
stapel.append(nb)
|
||||
mitte = Vector((0.0, 0.0, 0.0))
|
||||
for f in insel:
|
||||
mitte += f.center
|
||||
mitten.append(mitte / len(insel))
|
||||
return mitten
|
||||
|
||||
|
||||
mitten0 = card_mitten(stufen[0])
|
||||
print("")
|
||||
print("LOD0 hat %d Cards" % len(mitten0))
|
||||
for ob in stufen[1:]:
|
||||
mitten = card_mitten(ob)
|
||||
if not mitten0 or not mitten:
|
||||
continue
|
||||
treffer = 0
|
||||
for m1 in mitten:
|
||||
nah = min((m1 - m0).length for m0 in mitten0)
|
||||
# Die Card wurde vergroessert, ihre Mitte bleibt aber - ein paar
|
||||
# Zentimeter Toleranz reichen.
|
||||
if nah < 0.15:
|
||||
treffer += 1
|
||||
quote = 100.0 * treffer / len(mitten)
|
||||
print("%-24s %3d Cards, %.0f%% davon sitzen auf einer LOD0-Card"
|
||||
% (ob.name, len(mitten), quote))
|
||||
if quote < 90.0:
|
||||
fails.append("%s: nur %.0f%% der Cards sitzen auf LOD0-Positionen - "
|
||||
"die Stufe wurde neu gestreut statt ausgeduennt, die "
|
||||
"Silhouette springt beim LOD-Wechsel" % (ob.name, quote))
|
||||
|
||||
# --- Alle Stufen deckungsgleich am Ursprung ------------------------------
|
||||
# Das ist der Test fuer den aus UE gemeldeten Fehler: LOD1-3 sassen bei MINUS
|
||||
# der Standposition, weil ihre Weltmatrix nie ins Mesh gebacken wurde.
|
||||
|
||||
@@ -41,12 +41,22 @@ print("Defaults: Dichte %.0f, Groesse %.2f, Setzlings-Anteil %.2f"
|
||||
if not (0.6 <= s.leaf_size <= 1.1):
|
||||
fails.append("leaf_size ist %.2f - fuer Bueschel-Atlanten werden ~0.85 "
|
||||
"gebraucht, 0.2 waere ein einzelnes Blaettchen" % s.leaf_size)
|
||||
if s.leaf_budget_s1 > 800 or s.leaf_budget_s2 > 1200 or s.leaf_budget > 4000:
|
||||
fails.append("Stufen-Budgets (%d/%d/%d) ueber ASSETS.md Teil 6 (800/1200/4000)"
|
||||
# S3/S4 stehen auf 4300, nicht mehr auf 4000: die Spielseite hebt das Budget
|
||||
# in ASSETS.md an, nachdem der 170er-Export abgenommen wurde. Die Doku folgt dem
|
||||
# Auge, aber erst nach dem Export - deshalb steht die Zahl hier schon.
|
||||
if s.leaf_budget_s1 > 800 or s.leaf_budget_s2 > 1200 or s.leaf_budget > 4300:
|
||||
fails.append("Stufen-Budgets (%d/%d/%d) ueber der Vorgabe (800/1200/4300)"
|
||||
% (s.leaf_budget_s1, s.leaf_budget_s2, s.leaf_budget))
|
||||
if not (130.0 <= s.leaf_density <= 200.0):
|
||||
fails.append("leaf_density ist %.0f - der Sweetspot liegt bei 130-170"
|
||||
% s.leaf_density)
|
||||
# Die Deckung steht als Tabelle pro Stufe, nicht als Formel. Card-ANZAHL muss
|
||||
# ueber die Stufen steigen, Card-Groesse auch.
|
||||
_d0, _g0 = tg._stufen_anteile(0.0)
|
||||
_d1, _g1 = tg._stufen_anteile(1.0)
|
||||
if _g0 >= _g1:
|
||||
fails.append("Groessen-Anteil steigt nicht ueber die Stufen (%.2f -> %.2f)"
|
||||
% (_g0, _g1))
|
||||
# Der Volumen-Hebel: bei 0.02 trugen nur die aeussersten Zweigspitzen Laub und
|
||||
# die Krone war innen nackt.
|
||||
if s.leaf_max_radius < 0.035:
|
||||
|
||||
Reference in New Issue
Block a user