Compare commits
156 commits
feature/bu
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
313500b988 | ||
|
|
8f01775a92 | ||
|
|
295f79011f | ||
|
|
949d73dfdd | ||
|
|
e8ab954876 | ||
|
|
ff1c0e345e | ||
|
|
91c66a0b98 | ||
|
|
330365e59b | ||
|
|
9589703f7c | ||
|
|
b04a953190 | ||
|
|
48db200d3e | ||
|
|
92e2c688c1 | ||
|
|
f42e133bae | ||
|
|
bb9eb71770 | ||
|
|
8345903ce7 | ||
|
|
902181500a | ||
|
|
bcbd416960 | ||
|
|
5e32c771fc | ||
|
|
961e8c6ff6 | ||
|
|
075735f426 | ||
|
|
fa2feee780 | ||
|
|
3f0d0f3f33 | ||
|
|
ab9a798343 | ||
|
|
491a47d3f1 | ||
|
|
a76c3ecae7 | ||
|
|
fcc7f16f1c | ||
|
|
e3a6b2ad21 | ||
|
|
35a047cc2a | ||
|
|
93cdcb0cd4 | ||
|
|
59c932dc35 | ||
|
|
3094395444 | ||
|
|
3d083648d8 | ||
|
|
8cde37b85c | ||
|
|
26bcbe0cdd | ||
|
|
b1d8f7626d | ||
|
|
a1f386f5e6 | ||
|
|
99a06c6cb8 | ||
|
|
9f2a32df70 | ||
|
|
5a70824dd5 | ||
|
|
7f06e28472 | ||
|
|
4e973795f6 | ||
|
|
4a7b99ee3e | ||
|
|
40ca5ab6ba | ||
|
|
69668eb250 | ||
|
|
66df3bdc1e | ||
|
|
1d8763b881 | ||
|
|
04c6c67ba8 | ||
|
|
f5a4ab54d0 | ||
|
|
8468b89456 | ||
|
|
338e7152a0 | ||
|
|
42a205b6ff | ||
|
|
78d311c75f | ||
|
|
0505463ace | ||
|
|
35a1d80d3e | ||
|
|
65b33a207a | ||
|
|
7a1b5037e7 | ||
|
|
812da1a261 | ||
|
|
6f00737dd3 | ||
|
|
52919b36b4 | ||
|
|
0cdf68622c | ||
|
|
fabbbcce20 | ||
|
|
1f1a413065 | ||
|
|
d637314b82 | ||
|
|
ae551f387a | ||
|
|
f4cfe17594 | ||
|
|
f6c754115a | ||
|
|
0e1406a63e | ||
|
|
835a7f7363 | ||
|
|
5dca5513da | ||
|
|
f16b9c8fb6 | ||
|
|
403895b743 | ||
|
|
60b09bfb74 | ||
|
|
3d7ba993d5 | ||
|
|
a62357dad8 | ||
|
|
e317706df0 | ||
|
|
8c97d00871 | ||
|
|
7c7dc38566 | ||
|
|
4fb883a44c | ||
|
|
ab173ac8aa | ||
|
|
0f86472cfa | ||
|
|
40449932b9 | ||
|
|
85bee00f3d | ||
|
|
8f0e011a07 | ||
|
|
69ffe23a71 | ||
|
|
a5d8f7f79d | ||
|
|
b53e05a145 | ||
|
|
1b943ceb35 | ||
|
|
969e928d74 | ||
|
|
2c5b58f04c | ||
|
|
dca0947a71 | ||
|
|
2c10863759 | ||
|
|
238ca7bb2f | ||
|
|
c15c606311 | ||
|
|
0adb161386 | ||
|
|
437f6bf368 | ||
|
|
d0d4fbd221 | ||
|
|
aa94bde708 | ||
|
|
c5bdb0518e | ||
|
|
967e4dc54a | ||
|
|
370917f96e | ||
|
|
e664d09666 | ||
|
|
9fa8029910 | ||
|
|
c8b9c59fda | ||
|
|
366d482a1a | ||
|
|
c8a41c0794 | ||
|
|
f4748af371 | ||
|
|
ccf1c43f2e | ||
|
|
43636c840f | ||
|
|
91e2b03843 | ||
|
|
2bd1e671e4 | ||
|
|
90c3c7fdc7 | ||
|
|
3e07e91f5a | ||
|
|
785a858ae0 | ||
|
|
c0cbe4bb0c | ||
|
|
e1e07e57b0 | ||
|
|
8f598dc67d | ||
|
|
6572f1e10c | ||
|
|
6f81add8f4 | ||
|
|
6c65b13a8f | ||
|
|
b288e8a1e8 | ||
|
|
c92d35dca2 | ||
|
|
29b1668f57 | ||
|
|
56a808f052 | ||
|
|
c0d426152e | ||
|
|
ebe3dd276b | ||
|
|
0f86182a2c | ||
|
|
310aa695e6 | ||
|
|
76f8143540 | ||
|
|
9dca88e10e | ||
|
|
1cfc4e8c3a | ||
|
|
f6f5ef6c90 | ||
|
|
f99d1e2a28 | ||
|
|
23f7e1d39c | ||
|
|
4c960e2587 | ||
|
|
73847ed1ce | ||
|
|
f2b4f6a824 | ||
|
|
2065ef2252 | ||
|
|
116acd63ed | ||
|
|
2d8b9822f0 | ||
|
|
b85e3bed52 | ||
|
|
d6a0fe2196 | ||
|
|
ffb27645d4 | ||
|
|
e1c737df61 | ||
|
|
db6bcec971 | ||
|
|
d6c472603f | ||
|
|
7521db7f8d | ||
|
|
cda6f48b01 | ||
|
|
afcbda8dd0 | ||
|
|
08a75ac430 | ||
|
|
095af5ff3c | ||
|
|
b20dff2b5d | ||
|
|
e505329a42 | ||
|
|
b7b160d3bc | ||
|
|
adb585d30e | ||
|
|
e2954a38ed | ||
|
|
905edc021a |
8
.gitignore
vendored
|
|
@ -2,5 +2,13 @@
|
||||||
.godot/
|
.godot/
|
||||||
*.tmp
|
*.tmp
|
||||||
|
|
||||||
|
# Mitgelieferte Modelle (Karte t_12cf94fe): werden per tools/sync_models.sh
|
||||||
|
# aus dem Client-Klon gefüllt — NICHT ins Repo (ITHappy-Lizenz erlaubt keine
|
||||||
|
# Weitergabe, siehe README_INSTALL.md §5).
|
||||||
|
models/
|
||||||
|
|
||||||
|
# Export-Builds der eigenständigen App
|
||||||
|
export/
|
||||||
|
|
||||||
# System
|
# System
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
||||||
5
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
[submodule "core"]
|
||||||
|
path = core
|
||||||
|
url = ../scopedvr-map-core.git
|
||||||
|
branch = main
|
||||||
|
ignore = untracked
|
||||||
16
ADDON_LIZENZ.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
addons/modular_builder — "Modular Builder" v0.2.0-alpha von Michel Savadim
|
||||||
|
Quelle: Godot Asset Store (https://store.godotengine.org/asset/michel-savadim/modular-buider/)
|
||||||
|
Lizenz: CC0 1.0 Universal (siehe addons/modular_builder/LICENSE) — gilt für Code,
|
||||||
|
Icons und Dokumentation. Die Demo-Szene/Demo-Texturen des Originals sind NICHT
|
||||||
|
CC0 und wurden beim Import ENTFERNT (addons/modular_builder/demo fehlt bewusst).
|
||||||
|
|
||||||
|
Abgleich 25.10.2026: Der Code der Runtime-Geometrie (modular_piece_geometry.gd,
|
||||||
|
modular_building_detail.gd, modular_piece.gd, modular_pillar_support.gd,
|
||||||
|
modular_roof_support.gd, modular_vertex_simplifier.gd, modular_mesh_baker.gd) ist
|
||||||
|
zeichengleich zu ModularBuilder-English-0.2.0-2.zip (Ordner addons/modular_builder_en,
|
||||||
|
dort CC0 1.0, Copyright (c) 2026 Michel Savadim). Der Bauen-Modus des Map-Editors
|
||||||
|
(src/editor/bau_katalog.gd) nutzt diese Geometrie nur zum Erzeugen der Bauteile
|
||||||
|
(Wandhöhe 3 m, 2-m-Raster); die Teile selbst sind normale Bibliotheks-Objekte
|
||||||
|
(docs/bauen.md). Nicht übernommen: Demo-Szene, Demo-Texturen (nicht CC0).
|
||||||
|
Hinweis der Vorlage: Teile des Codes wurden mit KI-Unterstützung (OpenAI) erstellt —
|
||||||
|
vom Autor geprüft; CC0 gilt unverändert.
|
||||||
115
README.md
|
|
@ -48,17 +48,18 @@ UX-Fundament auf dem App-Skelett (t_5badf371), nach dem Chef-Grundsatz gebaut:
|
||||||
Neue-Map-Dialog mit 4 Pflicht-Spawns pro Team, Orbit-Viewport mit Gitter/
|
Neue-Map-Dialog mit 4 Pflicht-Spawns pro Team, Orbit-Viewport mit Gitter/
|
||||||
Boden/Wetter-Stimmung — siehe Commit 84bc081 und `svmap_io.gd`.)*
|
Boden/Wetter-Stimmung — siehe Commit 84bc081 und `svmap_io.gd`.)*
|
||||||
|
|
||||||
- **Undo/Redo (`src/editor/action_history.gd`):** zentrale ActionHistory über
|
- **Undo/Redo (`core/editor/action_history.gd`):** zentrale ActionHistory über
|
||||||
die svmap-Ober-Blöcke (`objects`/`terrain`/`spawn_zones` — tiefe Kopien vor
|
die svmap-Ober-Blöcke (`objects`/`terrain`/`spawn_zones` — tiefe Kopien vor
|
||||||
und nach jeder Änderung, merge_tags gegen Mausrad-Tick-Fluten, Max-Stufe 100).
|
und nach jeder Änderung, merge_tags gegen Mausrad-Tick-Fluten, Max-Stufe 100).
|
||||||
Strg+Z / Strg+Y UND große Buttons „↩ Rückgängig / ↪ Wiederholen" oben.
|
Strg+Z / Strg+Y UND große Buttons „↩ Rückgängig / ↪ Wiederholen" oben.
|
||||||
Spätere Werkzeuge melden sich mit `snapshot(keys)` vor und
|
Spätere Werkzeuge melden sich mit `snapshot(keys)` vor und
|
||||||
`commit(desc, before, after)` nach der Änderung an — Terrain-Strokes und
|
`commit(desc, before, after)` nach der Änderung an — Terrain-Strokes und
|
||||||
Spawn-Zonen landen damit automatisch in derselben History.
|
Spawn-Zonen landen damit automatisch in derselben History.
|
||||||
- **Objekt-Platzierung mit Ghost-Preview (`src/editor/interaction.gd`):**
|
- **Objekt-Platzierung mit Ghost-Preview (`core/editor/interaction.gd`):**
|
||||||
Bibliothek links (deutsche Namen + Poly-Zahl), Klick startet den
|
Bibliothek links (deutsche Namen + Poly-Zahl), Klick startet den
|
||||||
Platzier-Modus — eine transparente Vorschau schwebt am Cursor auf dem Boden
|
Platzier-Modus — eine transparente Vorschau schwebt am Cursor auf dem Boden
|
||||||
(Höhe kommt aus dem Terrain), Klick setzt, Rechtsklick/ESC bricht ab,
|
(Höhe kommt aus dem Terrain), Klick setzt, ESC bricht ab (Rechte
|
||||||
|
Maustaste = Flug, das Objekt bleibt in der Hand — Karte t_2470de89),
|
||||||
Mausrad dreht die Vorschau VOR dem Setzen (15°-Raster). Modus bleibt an
|
Mausrad dreht die Vorschau VOR dem Setzen (15°-Raster). Modus bleibt an
|
||||||
für schnelles Mehrfachsetzen. Poly-Budget wird VOR dem Platzieren geprüft.
|
für schnelles Mehrfachsetzen. Poly-Budget wird VOR dem Platzieren geprüft.
|
||||||
- **Auswahl-Anfassen:** Klick wählt ein Objekt (orange + Emission), Ziehen
|
- **Auswahl-Anfassen:** Klick wählt ein Objekt (orange + Emission), Ziehen
|
||||||
|
|
@ -93,16 +94,26 @@ synchron ziehen (IDs sind der Vertrag, Entscheidung 20).
|
||||||
|
|
||||||
Auf dem Terrain-Werkzeug (t_9aba04f1) aufgebaut, nach dem Chef-Grundsatz:
|
Auf dem Terrain-Werkzeug (t_9aba04f1) aufgebaut, nach dem Chef-Grundsatz:
|
||||||
|
|
||||||
- **Bibliothek-Browser mit Gruppen (`src/editor/library_browser.gd`):** Die
|
- **Bibliothek-Browser mit Gruppen (`core/editor/library_browser.gd`):**
|
||||||
11 etablierten Objekte aus `library/objects.json`, sortiert in deutsche
|
*(Stand 25.10.2026, Military-Pack-Umstellung)* Die Bibliothek ist das
|
||||||
Klapp-Gruppen (Kisten & Barrieren / Bauwerke / Natur + automatisch
|
komplette Military-Desert-Pack (418 Modelle + 5 Kombis = 423 Objekte aus
|
||||||
„Sonstiges" für neue, noch ungruppierte IDs). Jeder Button zeigt ein
|
`library/objects.json`), sortiert in 19 deutsche Kategorien aus dem
|
||||||
128-px-Thumbnail (`library/thumbs/*.png` — einmalig mit
|
Manifest-Feld `kategorie` (Panzer, Radfahrzeuge, Flugzeuge & Hubschrauber,
|
||||||
`xvfb-run -a godot4 --rendering-driver opengl3 --path . --script
|
Geschütze & Stellungen, Fahrzeugteile, Gebäude & Hangars, Zelte, Türme &
|
||||||
res://tools/gen_thumbs.gd` gerendert und committed; fehlt ein PNG, fällt
|
Masten, Mauern & Wände, Sperren & Zäune, Kisten/Fässer/Container, Technik &
|
||||||
der Button sauber aufs Text-Layout zurück). Deutsche Namen wohnen jetzt in
|
Funk, Ausrüstung, Training, Bäume, Pflanzen/Kakteen/Gras, Felsen & Steine,
|
||||||
`LibraryBrowser` (ohne UI → headless testbar).
|
Boden/Straßen/Dünen, Boden-Decals). Fahrzeuge sind **fertig zusammengebaut**
|
||||||
- **Snap to Ground (`src/editor/object_ops.gd`):** Ein Klick auf
|
(Panzer mit Turm und Kanone, Hummer mit Rädern, Hubschrauber mit Rotoren);
|
||||||
|
die Einzelteile stehen zusätzlich unter „Fahrzeugteile“. Unbekannte
|
||||||
|
Kategorien (z. B. spätere Bauteil-Module) bekommen automatisch eine eigene
|
||||||
|
Gruppe. Jeder Button zeigt ein 128-px-Thumbnail (`core/thumbs/*.png` —
|
||||||
|
erzeugt von `tools/sync_objects.sh`/`tools/gen_thumbs.gd` und committed;
|
||||||
|
fehlt ein PNG, fällt der Button aufs Text-Layout zurück). Deutsche Namen
|
||||||
|
kommen aus `name_de` im Manifest (ohne UI → headless testbar). Kollision:
|
||||||
|
Gebäude/Zelte/Türme exakter Trimesh (betretbar), Fahrzeuge Verbund aus
|
||||||
|
Boxen, Bäume nur Stamm, Dekor ohne Kollision — erzeugt vom Match-Server-Repo
|
||||||
|
(`tools/pack_registry.py`).
|
||||||
|
- **Snap to Ground (`core/editor/object_ops.gd`):** Ein Klick auf
|
||||||
„⚡ Alles auf den Boden setzen" holt ALLE schwebenden Objekte auf die
|
„⚡ Alles auf den Boden setzen" holt ALLE schwebenden Objekte auf die
|
||||||
Terrain-Höhe (z. B. nach dem Terrain-Formen) — EIN Undo-Eintrag für die
|
Terrain-Höhe (z. B. nach dem Terrain-Formen) — EIN Undo-Eintrag für die
|
||||||
ganze Aktion. Objekte, die schon auf dem Boden stehen, bleiben unangetastet.
|
ganze Aktion. Objekte, die schon auf dem Boden stehen, bleiben unangetastet.
|
||||||
|
|
@ -127,14 +138,14 @@ Export-Workflow.
|
||||||
|
|
||||||
Auf der Objekt-Bibliothek (t_3a998ae4) aufgebaut, nach dem Chef-Grundsatz:
|
Auf der Objekt-Bibliothek (t_3a998ae4) aufgebaut, nach dem Chef-Grundsatz:
|
||||||
|
|
||||||
- **Spawn-Zonen malen (`src/editor/spawn_tool.gd`):** Werkzeug
|
- **Spawn-Zonen malen (`core/editor/spawn_tool.gd`):** Werkzeug
|
||||||
„🚩 Spawn-Zonen malen“ — Team Blau 🔵 / Team Rot 🔴 (TDM-Konvention:
|
„🚩 Spawn-Zonen malen“ — Team Blau 🔵 / Team Rot 🔴 (TDM-Konvention:
|
||||||
IDs A…/B…, mapformat § spawn_zones), Klick setzt eine farbige flache
|
IDs A…/B…, mapformat § spawn_zones), Klick setzt eine farbige flache
|
||||||
Kreis-Zone auf dem Terrain (Vorschau-Kreis folgt der Maus), Radius
|
Kreis-Zone auf dem Terrain (Vorschau-Kreis folgt der Maus), Radius
|
||||||
2–30 m per Regler. Zone anklicken → auswählen + ziehen (Terrain-Höhe
|
2–30 m per Regler. Zone anklicken → auswählen + ziehen (Terrain-Höhe
|
||||||
bleibt), Entf löscht, Strg+Z nimmt JEDE Operation zurück (jede Zone =
|
bleibt), Entf löscht, Strg+Z nimmt JEDE Operation zurück (jede Zone =
|
||||||
EIN History-Eintrag, Radius-Slider-Ticks sind merge-gekoppelt).
|
EIN History-Eintrag, Radius-Slider-Ticks sind merge-gekoppelt).
|
||||||
- **Live-Validierung (`src/editor/spawn_ops.gd`):** Block „Spawn-Check“
|
- **Live-Validierung (`core/editor/spawn_ops.gd`):** Block „Spawn-Check“
|
||||||
rechts bleibt STÄNDIG sichtbar und spiegelt exakt die Loader-Regeln
|
rechts bleibt STÄNDIG sichtbar und spiegelt exakt die Loader-Regeln
|
||||||
(V11: ≥ 4 Spawn-Punkte pro Team · V7: Objekt-IDs in der Bibliothek) —
|
(V11: ≥ 4 Spawn-Punkte pro Team · V7: Objekt-IDs in der Bibliothek) —
|
||||||
z. B. „✅ Spawns Team Blau: 4 · ❌ Spawns Team Rot: 2 (mindestens 4
|
z. B. „✅ Spawns Team Blau: 4 · ❌ Spawns Team Rot: 2 (mindestens 4
|
||||||
|
|
@ -170,9 +181,11 @@ aufgebaut, nach dem Chef-Grundsatz (Klick statt Konsole, deutsche UI,
|
||||||
Undo für alles):
|
Undo für alles):
|
||||||
|
|
||||||
- **Duplizieren (`⧉ Duplizieren`-Button + Strg+D):** Die Kopie des
|
- **Duplizieren (`⧉ Duplizieren`-Button + Strg+D):** Die Kopie des
|
||||||
ausgewählten Objekts hängt diagonal 1 m versetzt daneben (mit Raster-Snap
|
ausgewählten Objekts hängt +2 m in X versetzt daneben (Rasterversatz,
|
||||||
eingerastet, wenn an) und ist DIREKT angewählt — sie lässt sich gleich
|
Karte t_c46d31c2; Füßpunkt-Höhe unverändert, mit Raster-Snap eingerastet,
|
||||||
|
wenn an) und ist DIREKT angewählt — sie lässt sich gleich
|
||||||
weiterziehen/drehen/löschen. Drehung, Größe und Höhe werden übernommen.
|
weiterziehen/drehen/löschen. Drehung, Größe und Höhe werden übernommen.
|
||||||
|
Bei Mehrfachauswahl werden ALLE Objekte kopiert (EIN Undo-Schritt).
|
||||||
Poly-Budget und Objekt-Limit werden VOR dem Kopieren geprüft
|
Poly-Budget und Objekt-Limit werden VOR dem Kopieren geprüft
|
||||||
(Klartext-Meldung, was zu tun ist). EIN Undo-Schritt.
|
(Klartext-Meldung, was zu tun ist). EIN Undo-Schritt.
|
||||||
- **Anheben/Absenken (`▲ Anheben / ▼ Absenken` + Bild↑/Bild↓):** Objekte
|
- **Anheben/Absenken (`▲ Anheben / ▼ Absenken` + Bild↑/Bild↓):** Objekte
|
||||||
|
|
@ -183,10 +196,12 @@ Undo für alles):
|
||||||
ist **KEIN Extra-Feld im Dateiformat nötig** und die Loader-Kompatibilität
|
ist **KEIN Extra-Feld im Dateiformat nötig** und die Loader-Kompatibilität
|
||||||
bleibt unangetastet (bewiesen in `tests/test_ux.gd`: Erhöhtes Objekt
|
bleibt unangetastet (bewiesen in `tests/test_ux.gd`: Erhöhtes Objekt
|
||||||
überlebt gespeichern → laden intakt (Höhe bleibt exakt)).
|
überlebt gespeichern → laden intakt (Höhe bleibt exakt)).
|
||||||
- **Absenken stoppt an der Terrain-Höhe** am Objektstandort — nichts wird
|
- **Absenken / VERSENKEN (Karte t_e166e656):** Absenken geht FREI unter die
|
||||||
in Hügel begraben. Steckt ein Objekt im Boden (z. B. nach dem
|
Terrain-Höhe — Steine dürfen halb im Boden stecken (Sly-Wunsch).
|
||||||
Terrain-Formen), bewegt Absenken es NICHT, und die Statusleiste erklärt,
|
Untergrenze ist die Match-Grenze `bounds.min.y`; die Auswahl-Anzeige und
|
||||||
dass `▲ Anheben` oder `⚡ Alles auf den Boden setzen` hilft.
|
die Statusleiste zeigen die Versenk-Tiefe („1,00 m im Boden versenkt“).
|
||||||
|
„Auf Boden setzen“ holt ein versenktes Objekt wieder exakt auf das
|
||||||
|
Terrain.
|
||||||
- **Anheben** hat als Obergrenze die Match-Grenze `bounds.max.y` — höher
|
- **Anheben** hat als Obergrenze die Match-Grenze `bounds.max.y` — höher
|
||||||
passt der Server nicht (V9).
|
passt der Server nicht (V9).
|
||||||
- **Ziehen behält die Höhe:** Ein angehobenes Objekt schwebt beim
|
- **Ziehen behält die Höhe:** Ein angehobenes Objekt schwebt beim
|
||||||
|
|
@ -200,6 +215,11 @@ Undo für alles):
|
||||||
(Herleitung im Quellcode: gespiegelter Richtungs-Vektor = gespiegelter
|
(Herleitung im Quellcode: gespiegelter Richtungs-Vektor = gespiegelter
|
||||||
Winkel; jede Spiegelung ist dadurch selbstinvers — zweimal spiegeln =
|
Winkel; jede Spiegelung ist dadurch selbstinvers — zweimal spiegeln =
|
||||||
Ausgangszustand.) Die Rotation wird mitgespiegelt, wie bestellt.
|
Ausgangszustand.) Die Rotation wird mitgespiegelt, wie bestellt.
|
||||||
|
KIPPEN wird mitgespiegelt (Karte t_e166e656): Achsenspiegelung =
|
||||||
|
Konjugation der Basis `M·B·M` — X-Kippen bleibt bei X-Spiegelung,
|
||||||
|
Z-Kippen dreht das Vorzeichen (und umgekehrt); Punktspiegelung =
|
||||||
|
`Ry(180°)·B`. Für reines Y-Drehen ergeben beide Regeln exakt die alten
|
||||||
|
Zahlenwerte (Doku: docs/map_format.md im Match-Server-Repo).
|
||||||
Bewusst NICHT um die Kartemitte gespiegelt: das würde das Objekt quer
|
Bewusst NICHT um die Kartemitte gespiegelt: das würde das Objekt quer
|
||||||
über die Map springen lassen — überraschend für Einsteiger. Klartext
|
über die Map springen lassen — überraschend für Einsteiger. Klartext
|
||||||
in Buttons + Tooltip.
|
in Buttons + Tooltip.
|
||||||
|
|
@ -218,13 +238,13 @@ Undo für alles):
|
||||||
Mehrfachauswahl + Gruppieren + Gruppen-Verschieben, auf dem UX-Fundament
|
Mehrfachauswahl + Gruppieren + Gruppen-Verschieben, auf dem UX-Fundament
|
||||||
(t_40d855d0) aufgebaut, nach dem Chef-Grundsatz:
|
(t_40d855d0) aufgebaut, nach dem Chef-Grundsatz:
|
||||||
|
|
||||||
- **Mehrfachauswahl (`src/editor/interaction.gd`):** Umschalt+Klick fügt
|
- **Mehrfachauswahl (`core/editor/interaction.gd`):** Umschalt+Klick fügt
|
||||||
ein Objekt hinzu/entfernt es, Rahmen-Aufziehen (Rubberband) über leeren
|
ein Objekt hinzu/entfernt es, Rahmen-Aufziehen (Rubberband) über leeren
|
||||||
Boden wählt ALLE Objekte im Rahmen (Umschalt + Rahmen = zusätzliche zur
|
Boden wählt ALLE Objekte im Rahmen (Umschalt + Rahmen = zusätzliche zur
|
||||||
Auswahl). Loser Klick auf leere Fläche hebt die Auswahl auf. ALLE
|
Auswahl). Loser Klick auf leere Fläche hebt die Auswahl auf. ALLE
|
||||||
markierten Objekte färben sich orange, das Auswahl-Panel rechts listet
|
markierten Objekte färben sich orange, das Auswahl-Panel rechts listet
|
||||||
sie namentlich auf (ab 9: „… und N weitere“). Projektions-Mathe liegt
|
sie namentlich auf (ab 9: „… und N weitere“). Projektions-Mathe liegt
|
||||||
in `src/editor/selection_math.gd` (headless getestet).
|
in `core/editor/selection_math.gd` (headless getestet).
|
||||||
- **Gruppieren:** Buttons „🔗 Gruppieren / ⛓ Gruppe lösen“ im Auswahl-Panel
|
- **Gruppieren:** Buttons „🔗 Gruppieren / ⛓ Gruppe lösen“ im Auswahl-Panel
|
||||||
+ Kürzel Strg+G / Strg+Umschalt+G. Eine Gruppe braucht ≥ 2 Objekte.
|
+ Kürzel Strg+G / Strg+Umschalt+G. Eine Gruppe braucht ≥ 2 Objekte.
|
||||||
Gruppen-Mitglieder ziehen und drehen GEMEINSAM — ein Griff an ein
|
Gruppen-Mitglieder ziehen und drehen GEMEINSAM — ein Griff an ein
|
||||||
|
|
@ -267,7 +287,7 @@ auch ungespeicherte! — sofort als Spiel in einem EIGENEN Fenster. Der Editor
|
||||||
bleibt offen. Im Spiel: WASD laufen, Maus umsehen, Shift sprinten, Leertaste
|
bleibt offen. Im Spiel: WASD laufen, Maus umsehen, Shift sprinten, Leertaste
|
||||||
springen, ESC beendet das Test-Fenster sauber (kein Prozess-Kill nötig).
|
springen, ESC beendet das Test-Fenster sauber (kein Prozess-Kill nötig).
|
||||||
|
|
||||||
- **Temp-Export (`src/editor/test_run.gd`):** Die Map wird nach
|
- **Temp-Export (`core/editor/test_run.gd`):** Die Map wird nach
|
||||||
`user://testrun/<name>.svmap` exportiert (deterministischer Name aus dem
|
`user://testrun/<name>.svmap` exportiert (deterministischer Name aus dem
|
||||||
Map-Namen, erneuter Testlauf überschreibt) — über dieselbe
|
Map-Namen, erneuter Testlauf überschreibt) — über dieselbe
|
||||||
`SvmapIO.save_to_file`-Validierung wie das echte Speichern. Ist die Map
|
`SvmapIO.save_to_file`-Validierung wie das echte Speichern. Ist die Map
|
||||||
|
|
@ -290,3 +310,50 @@ springen, ESC beendet das Test-Fenster sauber (kein Prozess-Kill nötig).
|
||||||
ungültige Map blockiert, Client-Suche inkl. Konfig-Vorrang, Godot-Findung,
|
ungültige Map blockiert, Client-Suche inkl. Konfig-Vorrang, Godot-Findung,
|
||||||
exakte Argumentliste). Parse-Check umfasst `test_run.gd`.
|
exakte Argumentliste). Parse-Check umfasst `test_run.gd`.
|
||||||
|
|
||||||
|
## Status (M2, Karte t_c46d31c2 — Tastatur-Shortcuts 1/3: Navigation + Auswahl + Löschen FERTIG)
|
||||||
|
|
||||||
|
Tastenkürzel-Karte 1 von 3 (Sly-Freigabe 16.10., finale Belegung).
|
||||||
|
**`docs/tasten.md` ist jetzt die deutsche Source of truth** für ALLE
|
||||||
|
Shortcut-Karten — Karten 2/3 ergänzen dort.
|
||||||
|
|
||||||
|
- **Konventionen (alle Karten):** Tastaturcode-basiert — InputMap-Actions mit
|
||||||
|
PHYSISCHEN `KEY_*`-Konstanten (`core/editor/shortcut_keys.gd`, registriert in
|
||||||
|
`main._ready`, idempotent). Physische Codes = deutsche (QWERTZ) und
|
||||||
|
Windows-Layouts identisch, keine OS-Hardcodes. Jede Objekt-Operation =
|
||||||
|
EIN ActionHistory-Eintrag (Ausnahme: reine Kamera-Aktionen).
|
||||||
|
- **Freelook-Flug:** Rechte Maustaste GEDACHT halten = Umsehen + Flug
|
||||||
|
(W/S/A/D + Leertaste hoch/Shift runter, Strg = ×3, Alt = ×0,3, Mausrad =
|
||||||
|
Grundgeschwindigkeit DAUERHAFT). Umschalt+F = Flug dauerhaft togglen.
|
||||||
|
Orbit bleibt Default —
|
||||||
|
beim Flug-Exit werden Ziel/Winkel/Abstand aus Flugposition/Blickrichtung
|
||||||
|
zurückgerechnet (kein Kamera-Sprung, getestet). Mathe pur in
|
||||||
|
`core/editor/fly_math.gd`, Kamera dünn (`core/viewport/orbit_camera.gd`).
|
||||||
|
Konflikt-Auflösung: RMB hat ABSOLUTEN Vorrang für den Flug — in JEDEM
|
||||||
|
Werkzeugzustand (Terrain, Bauen, Spawn, Platzieren; Karte t_2470de89,
|
||||||
|
kein `rmb_filter` mehr), Mausrad im Flug gehört dem Tempo (interaction
|
||||||
|
blockt).
|
||||||
|
- **F** = Auswahl ins Bild zentrieren — Orbit-Ziel wandert auf den Auswahl-
|
||||||
|
Mittelpunkt (Anker + halbe Mesh-Höhe, `SelectionMath.focus_center`); im
|
||||||
|
Flug dreht sich nur die Blickrichtung.
|
||||||
|
- **Esc** = Prioritätskette Werkzeug verlassen → Ghost-Preview abbrechen →
|
||||||
|
Auswahl aufheben, als pure, headless-getestete Funktion
|
||||||
|
(`core/editor/shortcut_policy.gd`).
|
||||||
|
- **Entf (DELETE)** = Auswahl SOFORT löschen ohne Rückfrage (Sly-Yolo) —
|
||||||
|
wirkt auf Szene-Auswahl UND Objektbaum-Markierung (Baum und Szene sind
|
||||||
|
synchron), Strg+Z deckt es ab. War schon mehrfach-fähig, jetzt
|
||||||
|
getestet (EIN Undo-Schritt holt ALLE zurück).
|
||||||
|
- **Strg+D** = Auswahl duplizieren: Kopie(n) +2 m in X (Rasterversatz),
|
||||||
|
Füßpunkt unverändert; bei Mehrfachauswahl ALLE Objekte mit EINEM
|
||||||
|
Undo-Schritt, Budget/Limit gesammelt VORHER geprüft
|
||||||
|
(`ObjectOps.duplicate_objects`).
|
||||||
|
- **Strg+LMB** = Mehrfachauswahl umschalten (Objektbaum: SELECT_MULTI +
|
||||||
|
native Strg+Klick-Logik, spiegelt sich in die Viewport-Auswahl und
|
||||||
|
umgekehrt). Bestandsaktionen (Löschen, Duplizieren, Ziehen, Gruppieren)
|
||||||
|
wirken auf die GANZE Mehrfachauswahl.
|
||||||
|
- **Tests:** `tests/test_shortcuts.gd` NEU (70 Checks: InputMap-Actions
|
||||||
|
inkl. Idempotenz, Freelook-Mathe, Esc-Prioritäten, Duplizieren +2 m X
|
||||||
|
mit Undo-Roundtrip, Entf-Mehrfachauswahl gegen die ECHTE interaction.gd,
|
||||||
|
Focus-Mittelpunkt, Kamera Flug-Roundtrip). Alle 8 Suiten grün
|
||||||
|
(571 Checks), Builder-Export 21/0, Parse-Check aller geänderten Scripts
|
||||||
|
(`tools/parse_check_shortcuts.sh`).
|
||||||
|
|
||||||
|
|
|
||||||
85
README_INSTALL.md
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
# ScopedVR Map Builder auf einem anderen PC installieren
|
||||||
|
|
||||||
|
Diese Anleitung erklärt Schritt für Schritt, wie du den Map Builder auf einem
|
||||||
|
beliebigen PC zum Laufen bringst und dort eine Map testen kannst — **ohne**
|
||||||
|
Godot-Installation, **ohne** ScopedVR-Client, **ohne** Programmierkenntnisse.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Voraussetzungen
|
||||||
|
|
||||||
|
- Ein normaler PC (Linux oder Windows). Kein Godot, keine Kommandozeile nötig,
|
||||||
|
wenn die App einmal exportiert wurde.
|
||||||
|
- Die exportierte App (Ein Ordner oder eine EXE, siehe Schritt 2).
|
||||||
|
- Den Ordner `models/` mit den 3D-Modellen (siehe Schritt 3).
|
||||||
|
|
||||||
|
## 2. Die App bekommen
|
||||||
|
|
||||||
|
Die App ist eine eigenständige Datei (z. B. `MapEditor` unter Linux,
|
||||||
|
`MapEditor.exe` unter Windows). Sie wurde aus dem Map-Editor-Repo exportiert
|
||||||
|
(`export_presets.cfg`, Preset „Linux“). Auf dem Ziel-PC brauchst du sie nur
|
||||||
|
in einen beliebigen Ordner zu kopieren, z. B.:
|
||||||
|
|
||||||
|
~/MapBuilder/ (Linux)
|
||||||
|
C:\MapBuilder\ (Windows)
|
||||||
|
|
||||||
|
Doppelklick drauf — die App startet. Das war's. Keine Installation.
|
||||||
|
|
||||||
|
## 3. Die Modelle danebenlegen (WICHTIG)
|
||||||
|
|
||||||
|
Ohne Modelle zeigt die App nur einfache Platzhalter-Kisten. Damit die echten
|
||||||
|
3D-Modelle erscheinen, muss der Ordner `models/` **direkt neben** der App
|
||||||
|
liegen (genau so wie hier):
|
||||||
|
|
||||||
|
~/MapBuilder/MapEditor ← die App
|
||||||
|
~/MapBuilder/models/ ← die Modelle
|
||||||
|
|
||||||
|
Wie der `models/`-Ordner gefüllt wird: Auf einem PC, auf dem der
|
||||||
|
Client-Klon liegt, im Map-Editor-Repo laufen lassen:
|
||||||
|
|
||||||
|
tools/sync_models.sh
|
||||||
|
|
||||||
|
Das Script kopiert alle Objekte (GLB) und die benötigten Texturen in den
|
||||||
|
`models/`-Ordner — idempotent, du kannst es also gefahrlos mehrmals laufen
|
||||||
|
lassen. Mit `--check` prüft es nur (Exit 0 = alles aktuell, Exit 1 = es fehlt
|
||||||
|
etwas). Die Größe wird am Ende angezeigt.
|
||||||
|
|
||||||
|
Falls die Modelle fehlen, sagt die App beim Start deutlich:
|
||||||
|
> „Für die Anzeige fehlen die Modelle. So bekommst du sie: …“
|
||||||
|
|
||||||
|
Terrain und Bauteile (Wände, Dächer usw.) funktionieren **immer** — auch ganz
|
||||||
|
ohne Modelle (Notfall-Modus).
|
||||||
|
|
||||||
|
## 4. Eine Map testen
|
||||||
|
|
||||||
|
1. App starten.
|
||||||
|
2. Map öffnen: Datei → Map öffnen, dann die gewünschte `.svmap`-Datei wählen.
|
||||||
|
3. Zum Testen: Taste **F5** drücken. Du läufst dann selbst durch die Map
|
||||||
|
(WASD = gehen, Maus = umsehen, Shift = rennen, Leertaste = springen).
|
||||||
|
4. **ESC** zurück in den Editor.
|
||||||
|
|
||||||
|
## 5. Lizenz-Hinweis (bitte wirklich lesen)
|
||||||
|
|
||||||
|
Die 3D-Modelle und Texturen im `models/`-Ordner stammen aus dem
|
||||||
|
**ITHappy-Pack**. Die Lizenz dieses Packs erlaubt es **nicht**, die
|
||||||
|
Roh-Modelle und Texturen weiterzugeben. Das heißt konkret:
|
||||||
|
|
||||||
|
- Die App mit `models/`-Ordner ist für **Slys eigene Rechner** gedacht.
|
||||||
|
- Der Ordner **darf nicht** an andere Leute geschickt oder hochgeladen werden.
|
||||||
|
- Die App selbst (ohne `models/`-Ordner) darf geteilt werden — sie zeigt dann
|
||||||
|
eben nur Platzhalter, bis der Ordner dazukommt.
|
||||||
|
|
||||||
|
## 6. Wenn etwas nicht geht
|
||||||
|
|
||||||
|
| Problem | Lösung |
|
||||||
|
| --- | --- |
|
||||||
|
| Nur Kisten statt echter Modelle | `models/`-Ordner direkt neben die App legen (Schritt 3) |
|
||||||
|
| Meldung „Modelle fehlen“ | `tools/sync_models.sh` ausführen (Schritt 3) |
|
||||||
|
| App startet gar nicht | Auf Linux: Datei per Rechtsklick → „Ausführen“ erlauben, oder im Terminal `chmod +x MapEditor` |
|
||||||
|
| Map lässt sich nicht öffnen | Ist es wirklich eine `.svmap`-Datei? |
|
||||||
|
|
||||||
|
---
|
||||||
|
*Technischer Hintergrund: die Modelle werden zur Laufzeit per GLTFDocument
|
||||||
|
aus dem `models/`-Ordner geladen (siehe `core/viewport/glb_mesh_store.gd`,
|
||||||
|
Quellen-Reihenfolge: Einstellungen → Nachbar-Ordner `../scopedvr-client` →
|
||||||
|
`models/`). Erster Treffer gewinnt.*
|
||||||
|
|
@ -4,9 +4,9 @@ Build modular structures directly in the Godot 4.7 editor. The panel appears at
|
||||||
|
|
||||||
## Install and try the demo
|
## Install and try the demo
|
||||||
|
|
||||||
Open this repository as a Godot project to explore `addons/modular_builder_en/demo/Demo.tscn`. After Godot finishes importing the assets, enable **Modular Builder (English)** in **Project > Project Settings > Plugins**. The demo and its five textures are contained in `addons/modular_builder_en/demo/`.
|
Open this repository as a Godot project to explore `addons/modular_builder/demo/Demo.tscn`. After Godot finishes importing the assets, enable **Modular Builder (English)** in **Project > Project Settings > Plugins**. The demo and its five textures are contained in `addons/modular_builder/demo/`.
|
||||||
|
|
||||||
To use the plugin in an existing project, extract the ZIP at the project root so it creates only `addons/modular_builder_en/`, then enable **Modular Builder (English)** in **Project > Project Settings > Plugins**. Open a 3D scene to use the editor panel. The demo remains inside the addon folder and can be removed if you do not need it.
|
To use the plugin in an existing project, extract the ZIP at the project root so it creates only `addons/modular_builder/`, then enable **Modular Builder (English)** in **Project > Project Settings > Plugins**. Open a 3D scene to use the editor panel. The demo remains inside the addon folder and can be removed if you do not need it.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ Save a selected structure as a reusable `.tscn` module, or insert a saved module
|
||||||
|
|
||||||
## Files and license
|
## Files and license
|
||||||
|
|
||||||
The English plugin lives in `addons/modular_builder_en/`. Editor code is split into UI, scene actions, detail placement, vertex editing, and viewport interaction. Geometry generation, mesh baking, roof/pillar placement, and vertex simplification have separate scripts.
|
The English plugin lives in `addons/modular_builder/`. Editor code is split into UI, scene actions, detail placement, vertex editing, and viewport interaction. Geometry generation, mesh baking, roof/pillar placement, and vertex simplification have separate scripts.
|
||||||
|
|
||||||
The original plugin code, icons, and documentation are dedicated to the public domain under [CC0 1.0](LICENSE). The bundled demo textures are example project assets; the CC0 dedication in this repository's license does not automatically cover them. Check their provenance and permission before redistributing the textures separately.
|
The original plugin code, icons, and documentation are dedicated to the public domain under [CC0 1.0](LICENSE). The bundled demo textures are example project assets; the CC0 dedication in this repository's license does not automatically cover them. Check their provenance and permission before redistributing the textures separately.
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
43
addons/modular_builder/icons/add_building.svg.import
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://kwgsfdxprj4p"
|
||||||
|
path="res://.godot/imported/add_building.svg-02348d81d48c2064f5946a668739ab6e.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/modular_builder/icons/add_building.svg"
|
||||||
|
dest_files=["res://.godot/imported/add_building.svg-02348d81d48c2064f5946a668739ab6e.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
Before Width: | Height: | Size: 195 B After Width: | Height: | Size: 195 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bdfxl3d88c45p"
|
uid="uid://np6bb2n6ohj3"
|
||||||
path="res://.godot/imported/cancel.svg-47b87f3e857516f74642fe118e831480.ctex"
|
path="res://.godot/imported/cancel.svg-48b548656752953c76fdbf1fa691b961.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/cancel.svg"
|
source_file="res://addons/modular_builder/icons/cancel.svg"
|
||||||
dest_files=["res://.godot/imported/cancel.svg-47b87f3e857516f74642fe118e831480.ctex"]
|
dest_files=["res://.godot/imported/cancel.svg-48b548656752953c76fdbf1fa691b961.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://blcmsiuegfhy"
|
uid="uid://cjc2d332bqgfy"
|
||||||
path="res://.godot/imported/collision.svg-e4cf0320ea5b34c663eb051e13179fb1.ctex"
|
path="res://.godot/imported/collision.svg-c665b3652a42642be8f703d82faa449e.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/collision.svg"
|
source_file="res://addons/modular_builder/icons/collision.svg"
|
||||||
dest_files=["res://.godot/imported/collision.svg-e4cf0320ea5b34c663eb051e13179fb1.ctex"]
|
dest_files=["res://.godot/imported/collision.svg-c665b3652a42642be8f703d82faa449e.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 259 B After Width: | Height: | Size: 259 B |
43
addons/modular_builder/icons/create_vertex.svg.import
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://8ivgqetbm82q"
|
||||||
|
path="res://.godot/imported/create_vertex.svg-935b2fd3add0dedfb62239b6f1e7a4db.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/modular_builder/icons/create_vertex.svg"
|
||||||
|
dest_files=["res://.godot/imported/create_vertex.svg-935b2fd3add0dedfb62239b6f1e7a4db.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://shrvgd57wqmy"
|
uid="uid://booy2vg0ci6p7"
|
||||||
path="res://.godot/imported/door.svg-abe4c0258238339b0ce092292a14a613.ctex"
|
path="res://.godot/imported/door.svg-5067a7f9805041b1e0019db73b7f247e.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/door.svg"
|
source_file="res://addons/modular_builder/icons/door.svg"
|
||||||
dest_files=["res://.godot/imported/door.svg-abe4c0258238339b0ce092292a14a613.ctex"]
|
dest_files=["res://.godot/imported/door.svg-5067a7f9805041b1e0019db73b7f247e.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 404 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bmvaiklprw3ob"
|
uid="uid://62ci7tsy4yeq"
|
||||||
path="res://.godot/imported/fence.svg-4eb777531b4c56b60ad89e9e327e738c.ctex"
|
path="res://.godot/imported/fence.svg-b115d5a88a7e382552216868149e7186.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/fence.svg"
|
source_file="res://addons/modular_builder/icons/fence.svg"
|
||||||
dest_files=["res://.godot/imported/fence.svg-4eb777531b4c56b60ad89e9e327e738c.ctex"]
|
dest_files=["res://.godot/imported/fence.svg-b115d5a88a7e382552216868149e7186.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 298 B |
43
addons/modular_builder/icons/floor_square.svg.import
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bey5yf5dc53ms"
|
||||||
|
path="res://.godot/imported/floor_square.svg-c870ce008800866d2fb4f4c19615fe80.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/modular_builder/icons/floor_square.svg"
|
||||||
|
dest_files=["res://.godot/imported/floor_square.svg-c870ce008800866d2fb4f4c19615fe80.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 291 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://b11j8aumv12dr"
|
uid="uid://dghymeocq75is"
|
||||||
path="res://.godot/imported/floor_square.svg-929ec0d7e4700eccd8871b9e7bdc53c8.ctex"
|
path="res://.godot/imported/floor_triangle.svg-cbffdc894d9762c4cb9806dbfa52405c.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/floor_square.svg"
|
source_file="res://addons/modular_builder/icons/floor_triangle.svg"
|
||||||
dest_files=["res://.godot/imported/floor_square.svg-929ec0d7e4700eccd8871b9e7bdc53c8.ctex"]
|
dest_files=["res://.godot/imported/floor_triangle.svg-cbffdc894d9762c4cb9806dbfa52405c.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://blm8vvelfa0ov"
|
uid="uid://3df01la6lxht"
|
||||||
path="res://.godot/imported/floor_triangle.svg-dccf4703c638fbb44617d82da92f981f.ctex"
|
path="res://.godot/imported/foundation_square.svg-23bf2f453c993134ea4ea05145d98488.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/floor_triangle.svg"
|
source_file="res://addons/modular_builder/icons/foundation_square.svg"
|
||||||
dest_files=["res://.godot/imported/floor_triangle.svg-dccf4703c638fbb44617d82da92f981f.ctex"]
|
dest_files=["res://.godot/imported/foundation_square.svg-23bf2f453c993134ea4ea05145d98488.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 285 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://d4iv2qc3e6k8f"
|
uid="uid://ln0jh5ibvhe0"
|
||||||
path="res://.godot/imported/create_vertex.svg-b23af865d7a800b0e078b26b2e99a9f7.ctex"
|
path="res://.godot/imported/foundation_triangle.svg-4b3e87f2198d6b6a8427029009457134.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/create_vertex.svg"
|
source_file="res://addons/modular_builder/icons/foundation_triangle.svg"
|
||||||
dest_files=["res://.godot/imported/create_vertex.svg-b23af865d7a800b0e078b26b2e99a9f7.ctex"]
|
dest_files=["res://.godot/imported/foundation_triangle.svg-4b3e87f2198d6b6a8427029009457134.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://420a0ay7eep0"
|
uid="uid://b16auokjgcjuv"
|
||||||
path="res://.godot/imported/gable_wall.svg-c2d60f6ba4255837e31c628621486d99.ctex"
|
path="res://.godot/imported/gable_wall.svg-1d82415321f6b6ad59f51ba7640a6c53.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/gable_wall.svg"
|
source_file="res://addons/modular_builder/icons/gable_wall.svg"
|
||||||
dest_files=["res://.godot/imported/gable_wall.svg-c2d60f6ba4255837e31c628621486d99.ctex"]
|
dest_files=["res://.godot/imported/gable_wall.svg-1d82415321f6b6ad59f51ba7640a6c53.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 336 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://d3smkmuij3tiu"
|
uid="uid://0jckxct26pqs"
|
||||||
path="res://.godot/imported/remove_vertex.svg-a2650c4a5b88e73778470ad394dd8d24.ctex"
|
path="res://.godot/imported/gable_wall_right.svg-3336022dcfa05da5960f0dc8e3ecc6ff.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/remove_vertex.svg"
|
source_file="res://addons/modular_builder/icons/gable_wall_right.svg"
|
||||||
dest_files=["res://.godot/imported/remove_vertex.svg-a2650c4a5b88e73778470ad394dd8d24.ctex"]
|
dest_files=["res://.godot/imported/gable_wall_right.svg-3336022dcfa05da5960f0dc8e3ecc6ff.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 276 B After Width: | Height: | Size: 276 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bhqb8ky2bus1f"
|
uid="uid://cf2okj2f2eq1h"
|
||||||
path="res://.godot/imported/hatch.svg-0413a819fca720a8bc1e64dc6ad76826.ctex"
|
path="res://.godot/imported/hatch.svg-f0ee2bc7f824245d3082af2deb5cb59a.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/hatch.svg"
|
source_file="res://addons/modular_builder/icons/hatch.svg"
|
||||||
dest_files=["res://.godot/imported/hatch.svg-0413a819fca720a8bc1e64dc6ad76826.ctex"]
|
dest_files=["res://.godot/imported/hatch.svg-f0ee2bc7f824245d3082af2deb5cb59a.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://yesw2vj8yaah"
|
uid="uid://dqhgwj72rp5o1"
|
||||||
path="res://.godot/imported/load_module.svg-63e2b3e2ac8c24bc991fcf4e75ace01b.ctex"
|
path="res://.godot/imported/load_module.svg-308877482b25606c5b7ed43292d640f3.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/load_module.svg"
|
source_file="res://addons/modular_builder/icons/load_module.svg"
|
||||||
dest_files=["res://.godot/imported/load_module.svg-63e2b3e2ac8c24bc991fcf4e75ace01b.ctex"]
|
dest_files=["res://.godot/imported/load_module.svg-308877482b25606c5b7ed43292d640f3.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 460 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://di37e5sjuktph"
|
uid="uid://n7jw3p1fa5eu"
|
||||||
path="res://.godot/imported/save_module.svg-de34a0b11c2e62ba100617a23de20ad2.ctex"
|
path="res://.godot/imported/merge_vertices.svg-d9204aed762a783069511285341aaecc.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/save_module.svg"
|
source_file="res://addons/modular_builder/icons/merge_vertices.svg"
|
||||||
dest_files=["res://.godot/imported/save_module.svg-de34a0b11c2e62ba100617a23de20ad2.ctex"]
|
dest_files=["res://.godot/imported/merge_vertices.svg-d9204aed762a783069511285341aaecc.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://cfh7b0kxdwl6i"
|
uid="uid://buetyw0nroq6k"
|
||||||
path="res://.godot/imported/move_vertex.svg-ceb416f7c4a6671d49b72b5d7058c829.ctex"
|
path="res://.godot/imported/move_vertex.svg-a43c7dd371bb862b05efa6f900fd0556.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/move_vertex.svg"
|
source_file="res://addons/modular_builder/icons/move_vertex.svg"
|
||||||
dest_files=["res://.godot/imported/move_vertex.svg-ceb416f7c4a6671d49b72b5d7058c829.ctex"]
|
dest_files=["res://.godot/imported/move_vertex.svg-a43c7dd371bb862b05efa6f900fd0556.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 275 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://dl55t56w8agod"
|
uid="uid://bkxxyh8ec5oed"
|
||||||
path="res://.godot/imported/parapet.svg-ed0a5acaa465eefbbe527d0d92ac41e6.ctex"
|
path="res://.godot/imported/parapet.svg-f91880fefed49e8dc8e533dcad6bb584.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/parapet.svg"
|
source_file="res://addons/modular_builder/icons/parapet.svg"
|
||||||
dest_files=["res://.godot/imported/parapet.svg-ed0a5acaa465eefbbe527d0d92ac41e6.ctex"]
|
dest_files=["res://.godot/imported/parapet.svg-f91880fefed49e8dc8e533dcad6bb584.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bsvap0ukvvtxl"
|
uid="uid://dukm4mprtq7l"
|
||||||
path="res://.godot/imported/pillar.svg-0acb51ea7c090f4767757e19389c7697.ctex"
|
path="res://.godot/imported/pillar.svg-ccd9ad9da9982abc939a9856fa296417.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/pillar.svg"
|
source_file="res://addons/modular_builder/icons/pillar.svg"
|
||||||
dest_files=["res://.godot/imported/pillar.svg-0acb51ea7c090f4767757e19389c7697.ctex"]
|
dest_files=["res://.godot/imported/pillar.svg-ccd9ad9da9982abc939a9856fa296417.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 250 B After Width: | Height: | Size: 250 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://dkuvxakhpo11m"
|
uid="uid://bjvg4halxhoy5"
|
||||||
path="res://.godot/imported/add_building.svg-c540099545c836d05aaa84aabb9d42a4.ctex"
|
path="res://.godot/imported/remove_vertex.svg-824eacf485182fddca49a06831d65906.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/add_building.svg"
|
source_file="res://addons/modular_builder/icons/remove_vertex.svg"
|
||||||
dest_files=["res://.godot/imported/add_building.svg-c540099545c836d05aaa84aabb9d42a4.ctex"]
|
dest_files=["res://.godot/imported/remove_vertex.svg-824eacf485182fddca49a06831d65906.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 257 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://7mg7tjvygg82"
|
uid="uid://dvtqw0h5dd5yl"
|
||||||
path="res://.godot/imported/roof_gable.svg-19c7e85e7be7616ba9781f8a0b3ca1c3.ctex"
|
path="res://.godot/imported/roof_gable.svg-8b4da12734ae02486f4e916ee6f2b16d.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/roof_gable.svg"
|
source_file="res://addons/modular_builder/icons/roof_gable.svg"
|
||||||
dest_files=["res://.godot/imported/roof_gable.svg-19c7e85e7be7616ba9781f8a0b3ca1c3.ctex"]
|
dest_files=["res://.godot/imported/roof_gable.svg-8b4da12734ae02486f4e916ee6f2b16d.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 306 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://55uqhamplshs"
|
uid="uid://ccew6wsdsj0kn"
|
||||||
path="res://.godot/imported/roof_pyramid.svg-9e1f692744fe6843f12da4ad40b1e7fd.ctex"
|
path="res://.godot/imported/roof_pyramid.svg-97d144eff93367622d2cd47f9133b57d.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/roof_pyramid.svg"
|
source_file="res://addons/modular_builder/icons/roof_pyramid.svg"
|
||||||
dest_files=["res://.godot/imported/roof_pyramid.svg-9e1f692744fe6843f12da4ad40b1e7fd.ctex"]
|
dest_files=["res://.godot/imported/roof_pyramid.svg-97d144eff93367622d2cd47f9133b57d.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 236 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://dl0xf605xhwdl"
|
uid="uid://wyg331tl3rdt"
|
||||||
path="res://.godot/imported/roof_shed.svg-6a66b1c82d6d6e005d37c43a5b9009df.ctex"
|
path="res://.godot/imported/roof_shed.svg-47c9ee22596d5277674eb88f312c9478.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/roof_shed.svg"
|
source_file="res://addons/modular_builder/icons/roof_shed.svg"
|
||||||
dest_files=["res://.godot/imported/roof_shed.svg-6a66b1c82d6d6e005d37c43a5b9009df.ctex"]
|
dest_files=["res://.godot/imported/roof_shed.svg-47c9ee22596d5277674eb88f312c9478.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://dhi55x4bo0dmp"
|
uid="uid://cxcb3kjn18udy"
|
||||||
path="res://.godot/imported/rotate.svg-2f2c1519ec865048747f49f7cbeed3c8.ctex"
|
path="res://.godot/imported/rotate.svg-84e58dd8fc90e4d634e7fbc55f8eda70.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/rotate.svg"
|
source_file="res://addons/modular_builder/icons/rotate.svg"
|
||||||
dest_files=["res://.godot/imported/rotate.svg-2f2c1519ec865048747f49f7cbeed3c8.ctex"]
|
dest_files=["res://.godot/imported/rotate.svg-84e58dd8fc90e4d634e7fbc55f8eda70.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |
43
addons/modular_builder/icons/save_module.svg.import
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://pnq0qqniy5hx"
|
||||||
|
path="res://.godot/imported/save_module.svg-eecd37f83ae3c8dc3d40dabc8e8213e4.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/modular_builder/icons/save_module.svg"
|
||||||
|
dest_files=["res://.godot/imported/save_module.svg-eecd37f83ae3c8dc3d40dabc8e8213e4.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
|
Before Width: | Height: | Size: 355 B After Width: | Height: | Size: 355 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://bl85bimtdaffm"
|
uid="uid://b6ibyaujhybao"
|
||||||
path="res://.godot/imported/stairs.svg-8a83477aca2bfaf1ecd92ddcb3d7ad52.ctex"
|
path="res://.godot/imported/stairs.svg-9b9203b050bb5cf817168a5866b66b2a.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/stairs.svg"
|
source_file="res://addons/modular_builder/icons/stairs.svg"
|
||||||
dest_files=["res://.godot/imported/stairs.svg-8a83477aca2bfaf1ecd92ddcb3d7ad52.ctex"]
|
dest_files=["res://.godot/imported/stairs.svg-9b9203b050bb5cf817168a5866b66b2a.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 267 B After Width: | Height: | Size: 267 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://c61wb4mnf0wls"
|
uid="uid://cmkpq8teurcwt"
|
||||||
path="res://.godot/imported/wall.svg-25deeb903c389412115a2f457cdde263.ctex"
|
path="res://.godot/imported/wall.svg-b57c613ae16e9db4887d98c30036dee9.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/wall.svg"
|
source_file="res://addons/modular_builder/icons/wall.svg"
|
||||||
dest_files=["res://.godot/imported/wall.svg-25deeb903c389412115a2f457cdde263.ctex"]
|
dest_files=["res://.godot/imported/wall.svg-b57c613ae16e9db4887d98c30036dee9.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 222 B |
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
importer="texture"
|
importer="texture"
|
||||||
type="CompressedTexture2D"
|
type="CompressedTexture2D"
|
||||||
uid="uid://np7qscb7nf3s"
|
uid="uid://cu21qr4i7dns1"
|
||||||
path="res://.godot/imported/window.svg-ac8dc7e4787d27b79bd94a1bd7b1f8f8.ctex"
|
path="res://.godot/imported/window.svg-b79f89a5f56cfe779422a43f44ba067f.ctex"
|
||||||
metadata={
|
metadata={
|
||||||
"vram_texture": false
|
"vram_texture": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/window.svg"
|
source_file="res://addons/modular_builder/icons/window.svg"
|
||||||
dest_files=["res://.godot/imported/window.svg-ac8dc7e4787d27b79bd94a1bd7b1f8f8.ctex"]
|
dest_files=["res://.godot/imported/window.svg-b79f89a5f56cfe779422a43f44ba067f.ctex"]
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
@tool
|
@tool
|
||||||
extends "res://addons/modular_builder_en/modular_builder_editor_ui.gd"
|
extends "res://addons/modular_builder/modular_builder_editor_ui.gd"
|
||||||
|
|
||||||
## ScopedVR-Bibliothek-Export (Karte t_82d2a8dd): dritter Export-Modus
|
## ScopedVR-Bibliothek-Export (Karte t_82d2a8dd): dritter Export-Modus
|
||||||
## „In ScopedVR-Bibliothek" — gebackenes Mesh + Trimesh-Kollision durch die
|
## „In ScopedVR-Bibliothek" — gebackenes Mesh + Trimesh-Kollision durch die
|
||||||
## Objekt-Pipeline (tools/objekt im Match-Server-Repo), Name-Dialog inklusive.
|
## Objekt-Pipeline (tools/objekt im Match-Server-Repo), Name-Dialog inklusive.
|
||||||
const SCOPEDVR_EXPORT_UI := preload("res://addons/modular_builder_en/scopedvr_export_ui.gd")
|
const SCOPEDVR_EXPORT_UI := preload("res://addons/modular_builder/scopedvr_export_ui.gd")
|
||||||
var _scopedvr_export: RefCounted = null
|
var _scopedvr_export: RefCounted = null
|
||||||
|
|
||||||
func _add_piece() -> void:
|
func _add_piece() -> void:
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
@tool
|
@tool
|
||||||
extends "res://addons/modular_builder_en/modular_builder_editor_actions.gd"
|
extends "res://addons/modular_builder/modular_builder_editor_actions.gd"
|
||||||
|
|
||||||
const PILLAR_SUPPORT = preload("res://addons/modular_builder_en/modular_pillar_support.gd")
|
const PILLAR_SUPPORT = preload("res://addons/modular_builder/modular_pillar_support.gd")
|
||||||
const VERTEX_SIMPLIFIER = preload("res://addons/modular_builder_en/modular_vertex_simplifier.gd")
|
const VERTEX_SIMPLIFIER = preload("res://addons/modular_builder/modular_vertex_simplifier.gd")
|
||||||
|
|
||||||
func _create_detail_from_click(camera: Camera3D, screen_position: Vector2) -> bool:
|
func _create_detail_from_click(camera: Camera3D, screen_position: Vector2) -> bool:
|
||||||
var placement := _find_detail_placement(camera, screen_position)
|
var placement := _find_detail_placement(camera, screen_position)
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
@tool
|
@tool
|
||||||
extends EditorPlugin
|
extends EditorPlugin
|
||||||
|
|
||||||
const PIECE_SCRIPT = preload("res://addons/modular_builder_en/modular_piece.gd")
|
const PIECE_SCRIPT = preload("res://addons/modular_builder/modular_piece.gd")
|
||||||
const DETAIL_SCRIPT = preload("res://addons/modular_builder_en/modular_building_detail.gd")
|
const DETAIL_SCRIPT = preload("res://core/vendor/modular_builder/modular_building_detail.gd")
|
||||||
const MESH_BAKER = preload("res://addons/modular_builder_en/modular_mesh_baker.gd")
|
const MESH_BAKER = preload("res://addons/modular_builder/modular_mesh_baker.gd")
|
||||||
const TOOL_NONE := ""
|
const TOOL_NONE := ""
|
||||||
const TOOL_FOUNDATION := "foundation"
|
const TOOL_FOUNDATION := "foundation"
|
||||||
const TOOL_FOUNDATION_SQUARE := "foundation_square"
|
const TOOL_FOUNDATION_SQUARE := "foundation_square"
|
||||||
|
|
@ -133,19 +133,19 @@ func _enter_tree() -> void:
|
||||||
size_spin.suffix = " m"
|
size_spin.suffix = " m"
|
||||||
size_spin.custom_minimum_size = Vector2(100, 0)
|
size_spin.custom_minimum_size = Vector2(100, 0)
|
||||||
_creation_row.add_child(size_spin)
|
_creation_row.add_child(size_spin)
|
||||||
var create_button := _make_icon_button("res://addons/modular_builder_en/icons/add_building.svg", "Add ModularBuilderPiece", Color(0.22, 0.30, 0.38))
|
var create_button := _make_icon_button("res://addons/modular_builder/icons/add_building.svg", "Add ModularBuilderPiece", Color(0.22, 0.30, 0.38))
|
||||||
create_button.pressed.connect(Callable(self, "_add_piece"))
|
create_button.pressed.connect(Callable(self, "_add_piece"))
|
||||||
_creation_row.add_child(create_button)
|
_creation_row.add_child(create_button)
|
||||||
var rotate_button := _make_icon_button("res://addons/modular_builder_en/icons/rotate.svg", "Rotate selected piece 90°", Color(0.22, 0.30, 0.38))
|
var rotate_button := _make_icon_button("res://addons/modular_builder/icons/rotate.svg", "Rotate selected piece 90°", Color(0.22, 0.30, 0.38))
|
||||||
rotate_button.pressed.connect(Callable(self, "_rotate_selected_piece"))
|
rotate_button.pressed.connect(Callable(self, "_rotate_selected_piece"))
|
||||||
_creation_row.add_child(rotate_button)
|
_creation_row.add_child(rotate_button)
|
||||||
var collision_button := _make_icon_button("res://addons/modular_builder_en/icons/collision.svg", "Add general collision to selected structure", Color(0.22, 0.30, 0.38))
|
var collision_button := _make_icon_button("res://addons/modular_builder/icons/collision.svg", "Add general collision to selected structure", Color(0.22, 0.30, 0.38))
|
||||||
collision_button.pressed.connect(Callable(self, "_add_general_collision"))
|
collision_button.pressed.connect(Callable(self, "_add_general_collision"))
|
||||||
_creation_row.add_child(collision_button)
|
_creation_row.add_child(collision_button)
|
||||||
var save_module_button := _make_icon_button("res://addons/modular_builder_en/icons/save_module.svg", "Save selected structure as a .tscn module", Color(0.22, 0.30, 0.38))
|
var save_module_button := _make_icon_button("res://addons/modular_builder/icons/save_module.svg", "Save selected structure as a .tscn module", Color(0.22, 0.30, 0.38))
|
||||||
save_module_button.pressed.connect(Callable(self, "_show_save_module_dialog"))
|
save_module_button.pressed.connect(Callable(self, "_show_save_module_dialog"))
|
||||||
_creation_row.add_child(save_module_button)
|
_creation_row.add_child(save_module_button)
|
||||||
var load_module_button := _make_icon_button("res://addons/modular_builder_en/icons/load_module.svg", "Insert saved module into the scene", Color(0.22, 0.30, 0.38))
|
var load_module_button := _make_icon_button("res://addons/modular_builder/icons/load_module.svg", "Insert saved module into the scene", Color(0.22, 0.30, 0.38))
|
||||||
load_module_button.pressed.connect(Callable(self, "_show_load_module_dialog"))
|
load_module_button.pressed.connect(Callable(self, "_show_load_module_dialog"))
|
||||||
_creation_row.add_child(load_module_button)
|
_creation_row.add_child(load_module_button)
|
||||||
_export_row = HFlowContainer.new()
|
_export_row = HFlowContainer.new()
|
||||||
|
|
@ -166,33 +166,33 @@ func _enter_tree() -> void:
|
||||||
dock.add_child(_tool_row)
|
dock.add_child(_tool_row)
|
||||||
_tool_button_group = ButtonGroup.new()
|
_tool_button_group = ButtonGroup.new()
|
||||||
_tool_button_group.allow_unpress = true
|
_tool_button_group.allow_unpress = true
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/foundation_square.svg", "Square foundation", TOOL_FOUNDATION_SQUARE, Color(0.08, 0.48, 0.22))
|
_add_tool_button("res://addons/modular_builder/icons/foundation_square.svg", "Square foundation", TOOL_FOUNDATION_SQUARE, Color(0.08, 0.48, 0.22))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/foundation_triangle.svg", "Triangular foundation", TOOL_FOUNDATION_TRIANGLE, Color(0.08, 0.48, 0.22))
|
_add_tool_button("res://addons/modular_builder/icons/foundation_triangle.svg", "Triangular foundation", TOOL_FOUNDATION_TRIANGLE, Color(0.08, 0.48, 0.22))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/wall.svg", "Wall", TOOL_WALL, Color(0.65, 0.12, 0.12))
|
_add_tool_button("res://addons/modular_builder/icons/wall.svg", "Wall", TOOL_WALL, Color(0.65, 0.12, 0.12))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/gable_wall.svg", "Gable wall", TOOL_GABLE_WALL, Color(0.65, 0.12, 0.12))
|
_add_tool_button("res://addons/modular_builder/icons/gable_wall.svg", "Gable wall", TOOL_GABLE_WALL, Color(0.65, 0.12, 0.12))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/gable_wall_right.svg", "Right triangular gable wall", TOOL_GABLE_WALL_RIGHT, Color(0.65, 0.12, 0.12))
|
_add_tool_button("res://addons/modular_builder/icons/gable_wall_right.svg", "Right triangular gable wall", TOOL_GABLE_WALL_RIGHT, Color(0.65, 0.12, 0.12))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/floor_square.svg", "Square floor", TOOL_FLOOR_SQUARE, Color(0.68, 0.48, 0.06))
|
_add_tool_button("res://addons/modular_builder/icons/floor_square.svg", "Square floor", TOOL_FLOOR_SQUARE, Color(0.68, 0.48, 0.06))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/floor_triangle.svg", "Triangular floor", TOOL_FLOOR_TRIANGLE, Color(0.68, 0.48, 0.06))
|
_add_tool_button("res://addons/modular_builder/icons/floor_triangle.svg", "Triangular floor", TOOL_FLOOR_TRIANGLE, Color(0.68, 0.48, 0.06))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/roof_gable.svg", "Gable roof", TOOL_ROOF_GABLE, Color(0.10, 0.38, 0.62))
|
_add_tool_button("res://addons/modular_builder/icons/roof_gable.svg", "Gable roof", TOOL_ROOF_GABLE, Color(0.10, 0.38, 0.62))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/roof_shed.svg", "Shed roof", TOOL_ROOF_SHED, Color(0.10, 0.38, 0.62))
|
_add_tool_button("res://addons/modular_builder/icons/roof_shed.svg", "Shed roof", TOOL_ROOF_SHED, Color(0.10, 0.38, 0.62))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/roof_pyramid.svg", "Pyramid roof", TOOL_ROOF_PYRAMID, Color(0.10, 0.38, 0.62))
|
_add_tool_button("res://addons/modular_builder/icons/roof_pyramid.svg", "Pyramid roof", TOOL_ROOF_PYRAMID, Color(0.10, 0.38, 0.62))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/stairs.svg", "Stairs", TOOL_STAIRS, Color(0.10, 0.38, 0.62))
|
_add_tool_button("res://addons/modular_builder/icons/stairs.svg", "Stairs", TOOL_STAIRS, Color(0.10, 0.38, 0.62))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/fence.svg", "Decorative fence", TOOL_FENCE, Color(0.88, 0.22, 0.55))
|
_add_tool_button("res://addons/modular_builder/icons/fence.svg", "Decorative fence", TOOL_FENCE, Color(0.88, 0.22, 0.55))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/parapet.svg", "Parapet: single rail and posts", TOOL_PARAPET, Color(0.88, 0.22, 0.55))
|
_add_tool_button("res://addons/modular_builder/icons/parapet.svg", "Parapet: single rail and posts", TOOL_PARAPET, Color(0.88, 0.22, 0.55))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/window.svg", "Detail: window", TOOL_WINDOW, Color(0.88, 0.22, 0.55))
|
_add_tool_button("res://addons/modular_builder/icons/window.svg", "Detail: window", TOOL_WINDOW, Color(0.88, 0.22, 0.55))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/door.svg", "Detail: door", TOOL_DOOR, Color(0.88, 0.22, 0.55))
|
_add_tool_button("res://addons/modular_builder/icons/door.svg", "Detail: door", TOOL_DOOR, Color(0.88, 0.22, 0.55))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/hatch.svg", "Detail: hatch", TOOL_HATCH, Color(0.88, 0.22, 0.55))
|
_add_tool_button("res://addons/modular_builder/icons/hatch.svg", "Detail: hatch", TOOL_HATCH, Color(0.88, 0.22, 0.55))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/pillar.svg", "Plain pillar: two vertices or Alt+click for a vertical pillar", TOOL_PILLAR, Color(0.88, 0.22, 0.55))
|
_add_tool_button("res://addons/modular_builder/icons/pillar.svg", "Plain pillar: two vertices or Alt+click for a vertical pillar", TOOL_PILLAR, Color(0.88, 0.22, 0.55))
|
||||||
var cancel_button := _make_icon_button("res://addons/modular_builder_en/icons/cancel.svg", "Deactivate tool", Color(0.34, 0.20, 0.20))
|
var cancel_button := _make_icon_button("res://addons/modular_builder/icons/cancel.svg", "Deactivate tool", Color(0.34, 0.20, 0.20))
|
||||||
cancel_button.pressed.connect(Callable(self, "_set_tool").bind(TOOL_NONE))
|
cancel_button.pressed.connect(Callable(self, "_set_tool").bind(TOOL_NONE))
|
||||||
_tool_row.add_child(cancel_button)
|
_tool_row.add_child(cancel_button)
|
||||||
_vertex_edit_row = HFlowContainer.new()
|
_vertex_edit_row = HFlowContainer.new()
|
||||||
dock.add_child(_vertex_edit_row)
|
dock.add_child(_vertex_edit_row)
|
||||||
_vertex_edit_row.add_child(_label("Vertices:"))
|
_vertex_edit_row.add_child(_label("Vertices:"))
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/merge_vertices.svg", "Merge vertices to reduce markers", TOOL_MERGE_VERTICES, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
_add_tool_button("res://addons/modular_builder/icons/merge_vertices.svg", "Merge vertices to reduce markers", TOOL_MERGE_VERTICES, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/create_vertex.svg", "Create a connection vertex on the clicked surface", TOOL_CREATE_VERTEX, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
_add_tool_button("res://addons/modular_builder/icons/create_vertex.svg", "Create a connection vertex on the clicked surface", TOOL_CREATE_VERTEX, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/move_vertex.svg", "Move a manual connection vertex", TOOL_MOVE_VERTEX, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
_add_tool_button("res://addons/modular_builder/icons/move_vertex.svg", "Move a manual connection vertex", TOOL_MOVE_VERTEX, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
||||||
_add_tool_button("res://addons/modular_builder_en/icons/remove_vertex.svg", "Remove a manual connection vertex", TOOL_REMOVE_VERTEX, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
_add_tool_button("res://addons/modular_builder/icons/remove_vertex.svg", "Remove a manual connection vertex", TOOL_REMOVE_VERTEX, Color(0.38, 0.32, 0.52), _vertex_edit_row)
|
||||||
var marker_options_row := HFlowContainer.new()
|
var marker_options_row := HFlowContainer.new()
|
||||||
dock.add_child(marker_options_row)
|
dock.add_child(marker_options_row)
|
||||||
_marker_scope_check = CheckBox.new()
|
_marker_scope_check = CheckBox.new()
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
@tool
|
@tool
|
||||||
extends "res://addons/modular_builder_en/modular_builder_editor_details.gd"
|
extends "res://addons/modular_builder/modular_builder_editor_details.gd"
|
||||||
|
|
||||||
const MESH_PICKER = preload("res://addons/modular_builder_en/modular_mesh_picker.gd")
|
const MESH_PICKER = preload("res://addons/modular_builder/modular_mesh_picker.gd")
|
||||||
|
|
||||||
func _sync_vertex_markers() -> void:
|
func _sync_vertex_markers() -> void:
|
||||||
var root := _get_selected_piece()
|
var root := _get_selected_piece()
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
@tool
|
@tool
|
||||||
extends "res://addons/modular_builder_en/modular_builder_editor_vertices.gd"
|
extends "res://addons/modular_builder/modular_builder_editor_vertices.gd"
|
||||||
|
|
||||||
const ROOF_SUPPORT = preload("res://addons/modular_builder_en/modular_roof_support.gd")
|
const ROOF_SUPPORT = preload("res://addons/modular_builder/modular_roof_support.gd")
|
||||||
|
|
||||||
func _forward_3d_gui_input(camera: Camera3D, event: InputEvent) -> int:
|
func _forward_3d_gui_input(camera: Camera3D, event: InputEvent) -> int:
|
||||||
if active_tool != TOOL_NONE and event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
|
if active_tool != TOOL_NONE and event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
@tool
|
@tool
|
||||||
class_name ModularBuilderPiece
|
class_name ModularBuilderPiece
|
||||||
extends "res://addons/modular_builder_en/modular_piece_geometry.gd"
|
extends "res://core/vendor/modular_builder/modular_piece_geometry.gd"
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_rebuild()
|
_rebuild()
|
||||||
|
|
@ -8,7 +8,7 @@ extends RefCounted
|
||||||
## Skript braucht die Editor-Umgebung (Dialoge, OS.execute, Rendering).
|
## Skript braucht die Editor-Umgebung (Dialoge, OS.execute, Rendering).
|
||||||
## Deutsch für Sly: alle Status-/Fehlermeldungen auf Deutsch.
|
## Deutsch für Sly: alle Status-/Fehlermeldungen auf Deutsch.
|
||||||
|
|
||||||
const KERN := preload("res://addons/modular_builder_en/scopedvr_export.gd")
|
const KERN := preload("res://addons/modular_builder/scopedvr_export.gd")
|
||||||
const BUDGET := 20000 # Export-Strukturen (match-server BUILDER_BUDGET)
|
const BUDGET := 20000 # Export-Strukturen (match-server BUILDER_BUDGET)
|
||||||
const THUMB_SIZE := 128
|
const THUMB_SIZE := 128
|
||||||
|
|
||||||
|
|
@ -133,8 +133,8 @@ func _export(oid: String, name_roh: String) -> void:
|
||||||
if not editor_dir.is_empty():
|
if not editor_dir.is_empty():
|
||||||
var img := await _thumbnail_bilden(mesh)
|
var img := await _thumbnail_bilden(mesh)
|
||||||
if img != null:
|
if img != null:
|
||||||
DirAccess.make_dir_recursive_absolute(editor_dir + "/library/thumbs")
|
DirAccess.make_dir_recursive_absolute(editor_dir + "/core/thumbs")
|
||||||
img.save_png(editor_dir + "/library/thumbs/" + oid + ".png")
|
img.save_png(editor_dir + "/core/thumbs/" + oid + ".png")
|
||||||
|
|
||||||
# 6) Editor-Sync (Registry-Kopie; vorhandene Thumbnails bleiben stehen).
|
# 6) Editor-Sync (Registry-Kopie; vorhandene Thumbnails bleiben stehen).
|
||||||
var schritt4 := _lauf(objekt_tool, ["editor", oid])
|
var schritt4 := _lauf(objekt_tool, ["editor", oid])
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://bdg64bvathtkp"
|
|
||||||
path="res://.godot/imported/foundation_square.svg-dc8be81ca55b382cac6eed9723c4ea7c.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/foundation_square.svg"
|
|
||||||
dest_files=["res://.godot/imported/foundation_square.svg-dc8be81ca55b382cac6eed9723c4ea7c.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=0
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=false
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
process/fix_alpha_border=true
|
|
||||||
process/premult_alpha=false
|
|
||||||
process/normal_map_invert_y=false
|
|
||||||
process/hdr_as_srgb=false
|
|
||||||
process/hdr_clamp_exposure=false
|
|
||||||
process/size_limit=0
|
|
||||||
detect_3d/compress_to=1
|
|
||||||
svg/scale=1.0
|
|
||||||
editor/scale_with_editor_scale=false
|
|
||||||
editor/convert_colors_with_editor_theme=false
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://c7sts0wrdier2"
|
|
||||||
path="res://.godot/imported/foundation_triangle.svg-c2747d1fa5be8206a98f02f2c53db66f.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/foundation_triangle.svg"
|
|
||||||
dest_files=["res://.godot/imported/foundation_triangle.svg-c2747d1fa5be8206a98f02f2c53db66f.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=0
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=false
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
process/fix_alpha_border=true
|
|
||||||
process/premult_alpha=false
|
|
||||||
process/normal_map_invert_y=false
|
|
||||||
process/hdr_as_srgb=false
|
|
||||||
process/hdr_clamp_exposure=false
|
|
||||||
process/size_limit=0
|
|
||||||
detect_3d/compress_to=1
|
|
||||||
svg/scale=1.0
|
|
||||||
editor/scale_with_editor_scale=false
|
|
||||||
editor/convert_colors_with_editor_theme=false
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://cbap2sd7o8fwg"
|
|
||||||
path="res://.godot/imported/gable_wall_right.svg-ad644706ef5497d44f89d17f68ec357c.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/gable_wall_right.svg"
|
|
||||||
dest_files=["res://.godot/imported/gable_wall_right.svg-ad644706ef5497d44f89d17f68ec357c.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=0
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=false
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
process/fix_alpha_border=true
|
|
||||||
process/premult_alpha=false
|
|
||||||
process/normal_map_invert_y=false
|
|
||||||
process/hdr_as_srgb=false
|
|
||||||
process/hdr_clamp_exposure=false
|
|
||||||
process/size_limit=0
|
|
||||||
detect_3d/compress_to=1
|
|
||||||
svg/scale=1.0
|
|
||||||
editor/scale_with_editor_scale=false
|
|
||||||
editor/convert_colors_with_editor_theme=false
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://1amy53ump51l"
|
|
||||||
path="res://.godot/imported/merge_vertices.svg-5ece4f02ab55d6738ab8836b942201e1.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://addons/modular_builder_en/icons/merge_vertices.svg"
|
|
||||||
dest_files=["res://.godot/imported/merge_vertices.svg-5ece4f02ab55d6738ab8836b942201e1.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=0
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=false
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
process/fix_alpha_border=true
|
|
||||||
process/premult_alpha=false
|
|
||||||
process/normal_map_invert_y=false
|
|
||||||
process/hdr_as_srgb=false
|
|
||||||
process/hdr_clamp_exposure=false
|
|
||||||
process/size_limit=0
|
|
||||||
detect_3d/compress_to=1
|
|
||||||
svg/scale=1.0
|
|
||||||
editor/scale_with_editor_scale=false
|
|
||||||
editor/convert_colors_with_editor_theme=false
|
|
||||||
|
|
@ -1,284 +0,0 @@
|
||||||
@tool
|
|
||||||
class_name ModularBuilderDetail
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
enum DetailType { WINDOW, DOOR, HATCH, PILLAR, FENCE, PARAPET }
|
|
||||||
enum DetailMode { OPENING, SEPARATE_OBJECT }
|
|
||||||
enum WindowStyle { SINGLE_PANE, CROSS_PANE }
|
|
||||||
enum DoorStyle { FLAT, PANELED }
|
|
||||||
|
|
||||||
@export var detail_type: DetailType = DetailType.WINDOW:
|
|
||||||
set(value):
|
|
||||||
detail_type = value
|
|
||||||
notify_property_list_changed()
|
|
||||||
_rebuild()
|
|
||||||
@export var detail_mode: DetailMode = DetailMode.OPENING:
|
|
||||||
set(value):
|
|
||||||
detail_mode = value
|
|
||||||
notify_property_list_changed()
|
|
||||||
_rebuild()
|
|
||||||
@export var center_align: bool = false:
|
|
||||||
set(value):
|
|
||||||
center_align = value
|
|
||||||
_apply_pillar_alignment()
|
|
||||||
notify_property_list_changed()
|
|
||||||
@export_storage var pillar_direction: Vector3 = Vector3.UP
|
|
||||||
@export_storage var pillar_alignment_basis: Basis = Basis.IDENTITY
|
|
||||||
@export var window_style: WindowStyle = WindowStyle.SINGLE_PANE:
|
|
||||||
set(value):
|
|
||||||
window_style = value
|
|
||||||
_rebuild()
|
|
||||||
@export var door_style: DoorStyle = DoorStyle.FLAT:
|
|
||||||
set(value):
|
|
||||||
door_style = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.01, 5.0, 0.001, "suffix:m") var width: float = 1.0:
|
|
||||||
set(value):
|
|
||||||
width = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.2, 5.0, 0.001, "suffix:m") var height: float = 1.0:
|
|
||||||
set(value):
|
|
||||||
height = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.02, 0.5, 0.001, "suffix:m") var frame_depth: float = 0.08:
|
|
||||||
set(value):
|
|
||||||
frame_depth = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.1, 8.0, 0.001, "suffix:m") var fence_height: float = 0.7:
|
|
||||||
set(value):
|
|
||||||
fence_height = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0, 100, 1, "suffix: posts") var fence_divisions: int = 1:
|
|
||||||
set(value):
|
|
||||||
fence_divisions = value
|
|
||||||
_rebuild()
|
|
||||||
@export_storage var path_points: PackedVector3Array = PackedVector3Array()
|
|
||||||
@export_range(0.01, 0.5, 0.001, "suffix:m") var frame_width: float = 0.08:
|
|
||||||
set(value):
|
|
||||||
frame_width = value
|
|
||||||
_rebuild()
|
|
||||||
@export var follow_host_thickness: bool = true:
|
|
||||||
set(value):
|
|
||||||
follow_host_thickness = value
|
|
||||||
_rebuild()
|
|
||||||
@export var surface_material: Material:
|
|
||||||
set(value):
|
|
||||||
surface_material = value
|
|
||||||
_rebuild()
|
|
||||||
@export var frame_material: Material:
|
|
||||||
set(value):
|
|
||||||
frame_material = value
|
|
||||||
_rebuild()
|
|
||||||
@export var panel_material: Material:
|
|
||||||
set(value):
|
|
||||||
panel_material = value
|
|
||||||
_rebuild()
|
|
||||||
|
|
||||||
func _validate_property(property: Dictionary) -> void:
|
|
||||||
var separate_object := detail_mode == DetailMode.SEPARATE_OBJECT
|
|
||||||
var is_window := detail_type == DetailType.WINDOW
|
|
||||||
var is_door := detail_type == DetailType.DOOR
|
|
||||||
var has_frame_and_panel := separate_object and detail_type != DetailType.PILLAR and detail_type != DetailType.FENCE and detail_type != DetailType.PARAPET
|
|
||||||
var show_property := true
|
|
||||||
match StringName(property.name):
|
|
||||||
&"detail_mode":
|
|
||||||
show_property = detail_type != DetailType.PILLAR and detail_type != DetailType.FENCE and detail_type != DetailType.PARAPET
|
|
||||||
&"center_align":
|
|
||||||
show_property = detail_type == DetailType.PILLAR
|
|
||||||
&"fence_height", &"fence_divisions":
|
|
||||||
show_property = detail_type == DetailType.FENCE or detail_type == DetailType.PARAPET
|
|
||||||
&"path_points":
|
|
||||||
show_property = false
|
|
||||||
&"width":
|
|
||||||
show_property = detail_type != DetailType.FENCE and detail_type != DetailType.PARAPET
|
|
||||||
if detail_type == DetailType.PILLAR:
|
|
||||||
property.hint = PROPERTY_HINT_RANGE
|
|
||||||
property.hint_string = "0.01,5.0,0.001,suffix:m"
|
|
||||||
&"height":
|
|
||||||
show_property = detail_type != DetailType.FENCE and detail_type != DetailType.PARAPET
|
|
||||||
&"window_style":
|
|
||||||
show_property = is_window and separate_object
|
|
||||||
&"door_style":
|
|
||||||
show_property = is_door and separate_object
|
|
||||||
&"frame_width", &"frame_material", &"panel_material":
|
|
||||||
show_property = has_frame_and_panel
|
|
||||||
&"follow_host_thickness":
|
|
||||||
show_property = has_frame_and_panel
|
|
||||||
&"frame_depth":
|
|
||||||
show_property = has_frame_and_panel and not follow_host_thickness
|
|
||||||
&"surface_material":
|
|
||||||
show_property = separate_object
|
|
||||||
if not show_property:
|
|
||||||
property.usage = PROPERTY_USAGE_NO_EDITOR
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
set_notify_transform(true)
|
|
||||||
_rebuild()
|
|
||||||
|
|
||||||
func _notification(what: int) -> void:
|
|
||||||
if what == NOTIFICATION_TRANSFORM_CHANGED and is_inside_tree():
|
|
||||||
var host := get_parent()
|
|
||||||
if host != null and host.has_method("_rebuild"):
|
|
||||||
host.call("_rebuild")
|
|
||||||
|
|
||||||
func _on_host_thickness_changed() -> void:
|
|
||||||
_rebuild(false)
|
|
||||||
|
|
||||||
func _apply_pillar_alignment() -> void:
|
|
||||||
if detail_type != DetailType.PILLAR:
|
|
||||||
return
|
|
||||||
var direction := pillar_direction.normalized()
|
|
||||||
if direction.is_zero_approx():
|
|
||||||
direction = Vector3.UP
|
|
||||||
if center_align:
|
|
||||||
var object_axes := pillar_alignment_basis.orthonormalized()
|
|
||||||
var best_axis := direction
|
|
||||||
var best_dot := -1.0
|
|
||||||
for axis in [object_axes.x, object_axes.y, object_axes.z]:
|
|
||||||
var normalized_axis: Vector3 = axis.normalized()
|
|
||||||
var alignment := absf(direction.dot(normalized_axis))
|
|
||||||
if alignment > best_dot:
|
|
||||||
best_dot = alignment
|
|
||||||
best_axis = normalized_axis if direction.dot(normalized_axis) >= 0.0 else -normalized_axis
|
|
||||||
direction = best_axis
|
|
||||||
var reference_axis := Vector3.FORWARD if absf(direction.dot(Vector3.UP)) > 0.99 else Vector3.UP
|
|
||||||
var x_axis := reference_axis.cross(direction).normalized()
|
|
||||||
var z_axis := x_axis.cross(direction).normalized()
|
|
||||||
transform.basis = Basis(x_axis, direction, z_axis).orthonormalized()
|
|
||||||
if center_align and pillar_direction.length() > 0.0:
|
|
||||||
height = pillar_direction.length()
|
|
||||||
|
|
||||||
func _rebuild(notify_host: bool = true) -> void:
|
|
||||||
if not is_inside_tree():
|
|
||||||
return
|
|
||||||
for child in get_children():
|
|
||||||
if child.name == "DetailGeometry":
|
|
||||||
remove_child(child)
|
|
||||||
child.queue_free()
|
|
||||||
var host := get_parent()
|
|
||||||
if notify_host and host != null and host.has_method("_rebuild"):
|
|
||||||
host.call("_rebuild")
|
|
||||||
if detail_mode == DetailMode.OPENING and detail_type != DetailType.FENCE and detail_type != DetailType.PARAPET:
|
|
||||||
return
|
|
||||||
var holder := Node3D.new()
|
|
||||||
holder.name = "DetailGeometry"
|
|
||||||
add_child(holder)
|
|
||||||
var scene_root: Node = null
|
|
||||||
if Engine.is_editor_hint() and not has_meta("modular_builder_preview"):
|
|
||||||
scene_root = get_tree().edited_scene_root
|
|
||||||
if scene_root:
|
|
||||||
holder.owner = scene_root
|
|
||||||
var effective_depth := _effective_frame_depth()
|
|
||||||
var frame := minf(maxf(frame_width, 0.01), minf(width, height) * 0.45)
|
|
||||||
if detail_type == DetailType.FENCE or detail_type == DetailType.PARAPET:
|
|
||||||
_build_fence_geometry(holder)
|
|
||||||
return
|
|
||||||
if detail_type == DetailType.PILLAR:
|
|
||||||
_add_box(holder, "PillarBody", Vector3(width, height, width), Vector3.ZERO, surface_material)
|
|
||||||
return
|
|
||||||
if detail_type == DetailType.HATCH:
|
|
||||||
_add_box(holder, "LeftFrame", Vector3(frame, effective_depth, height), Vector3(-width * 0.5 + frame * 0.5, 0.0, 0.0), _frame_material())
|
|
||||||
_add_box(holder, "RightFrame", Vector3(frame, effective_depth, height), Vector3(width * 0.5 - frame * 0.5, 0.0, 0.0), _frame_material())
|
|
||||||
_add_box(holder, "NearFrame", Vector3(width, effective_depth, frame), Vector3(0.0, 0.0, -height * 0.5 + frame * 0.5), _frame_material())
|
|
||||||
_add_box(holder, "FarFrame", Vector3(width, effective_depth, frame), Vector3(0.0, 0.0, height * 0.5 - frame * 0.5), _frame_material())
|
|
||||||
_add_box(holder, "HatchPanel", Vector3(width - frame * 2.0, effective_depth * 0.35, height - frame * 2.0), Vector3(0.0, effective_depth * 0.4, 0.0), _panel_material())
|
|
||||||
return
|
|
||||||
_add_box(holder, "LeftFrame", Vector3(frame, height, effective_depth), Vector3(-width * 0.5 + frame * 0.5, 0.0, 0.0), _frame_material())
|
|
||||||
_add_box(holder, "RightFrame", Vector3(frame, height, effective_depth), Vector3(width * 0.5 - frame * 0.5, 0.0, 0.0), _frame_material())
|
|
||||||
_add_box(holder, "TopFrame", Vector3(width, frame, effective_depth), Vector3(0.0, height * 0.5 - frame * 0.5, 0.0), _frame_material())
|
|
||||||
if detail_type != DetailType.DOOR:
|
|
||||||
_add_box(holder, "BottomFrame", Vector3(width, frame, effective_depth), Vector3(0.0, -height * 0.5 + frame * 0.5, 0.0), _frame_material())
|
|
||||||
if detail_type == DetailType.WINDOW:
|
|
||||||
_add_box(holder, "WindowPanel", Vector3(maxf(width - frame * 2.0, 0.001), maxf(height - frame * 2.0, 0.001), effective_depth * 0.35), Vector3.ZERO, _panel_material())
|
|
||||||
if window_style == WindowStyle.CROSS_PANE:
|
|
||||||
_add_box(holder, "VerticalMullion", Vector3(frame * 0.55, height - frame * 2.0, effective_depth * 0.4), Vector3.ZERO, _frame_material())
|
|
||||||
_add_box(holder, "HorizontalMullion", Vector3(width - frame * 2.0, frame * 0.55, effective_depth * 0.4), Vector3.ZERO, _frame_material())
|
|
||||||
elif detail_type == DetailType.DOOR:
|
|
||||||
_add_box(holder, "DoorPanel", Vector3(width - frame * 2.0, height - frame, effective_depth * 0.45), Vector3(0.0, -frame * 0.5, 0.0), _panel_material())
|
|
||||||
if door_style == DoorStyle.PANELED:
|
|
||||||
var inset_depth := maxf(effective_depth * 0.04, 0.005)
|
|
||||||
var panel_width := (width - frame * 2.0) * 0.82
|
|
||||||
var panel_height := maxf((height - frame) * 0.28, 0.05)
|
|
||||||
for panel_index in range(2):
|
|
||||||
var panel_y := -frame * 0.5 + (panel_index - 0.5) * (panel_height + frame * 0.25)
|
|
||||||
_add_box(holder, "DoorInset_%d" % panel_index, Vector3(panel_width, panel_height, inset_depth), Vector3(0.0, panel_y, effective_depth * 0.24), _frame_material())
|
|
||||||
|
|
||||||
func _effective_frame_depth() -> float:
|
|
||||||
if follow_host_thickness:
|
|
||||||
var host := get_parent()
|
|
||||||
if host != null:
|
|
||||||
var host_thickness = host.get("thickness")
|
|
||||||
if host_thickness is float or host_thickness is int:
|
|
||||||
var depth := float(host_thickness)
|
|
||||||
if detail_type == DetailType.WINDOW or detail_type == DetailType.DOOR:
|
|
||||||
depth *= 1.25
|
|
||||||
return maxf(depth, 0.02)
|
|
||||||
return frame_depth
|
|
||||||
|
|
||||||
func _build_fence_geometry(holder: Node3D) -> void:
|
|
||||||
if path_points.size() < 2 or fence_height <= 0.0:
|
|
||||||
return
|
|
||||||
var start := path_points[0]
|
|
||||||
var finish := path_points[1]
|
|
||||||
var path := finish - start
|
|
||||||
var path_length := path.length()
|
|
||||||
if path_length < 0.05:
|
|
||||||
return
|
|
||||||
var path_axis := path / path_length
|
|
||||||
var up_axis := Vector3.UP - path_axis * path_axis.dot(Vector3.UP)
|
|
||||||
if up_axis.length_squared() < 0.001:
|
|
||||||
up_axis = Vector3.FORWARD - path_axis * path_axis.dot(Vector3.FORWARD)
|
|
||||||
up_axis = up_axis.normalized()
|
|
||||||
var side_axis := path_axis.cross(up_axis).normalized()
|
|
||||||
var post_width := 0.16 if detail_type == DetailType.PARAPET else 0.08
|
|
||||||
var post_count := fence_divisions + 2
|
|
||||||
var posts: Array[Vector3] = []
|
|
||||||
for i in range(post_count):
|
|
||||||
var base := start.lerp(finish, float(i) / float(post_count - 1))
|
|
||||||
posts.append(base)
|
|
||||||
_add_oriented_box(holder, "Post_%d" % i, base + up_axis * fence_height * 0.5, path_axis, up_axis, side_axis, Vector3(post_width, fence_height, post_width))
|
|
||||||
for i in range(posts.size() - 1):
|
|
||||||
var a := posts[i]
|
|
||||||
var b := posts[i + 1]
|
|
||||||
var segment := b - a
|
|
||||||
var segment_length := segment.length()
|
|
||||||
if detail_type == DetailType.PARAPET:
|
|
||||||
_add_oriented_box(holder, "Rail_%d" % i, (a + b) * 0.5 + up_axis * fence_height * 0.78, segment.normalized(), up_axis, side_axis, Vector3(segment_length, post_width * 0.8, post_width * 0.8))
|
|
||||||
else:
|
|
||||||
for height_ratio in [0.28, 0.78]:
|
|
||||||
_add_oriented_box(holder, "Rail_%d_%d" % [i, int(height_ratio * 100.0)], (a + b) * 0.5 + up_axis * fence_height * height_ratio, segment.normalized(), up_axis, side_axis, Vector3(segment_length, post_width, post_width))
|
|
||||||
|
|
||||||
func _add_oriented_box(holder: Node3D, node_name: String, center: Vector3, axis_x: Vector3, axis_y: Vector3, axis_z: Vector3, dimensions: Vector3) -> void:
|
|
||||||
var mesh_instance := MeshInstance3D.new()
|
|
||||||
mesh_instance.name = node_name
|
|
||||||
var box := BoxMesh.new()
|
|
||||||
box.size = dimensions
|
|
||||||
mesh_instance.mesh = box
|
|
||||||
var basis := Basis(axis_x.normalized(), axis_y.normalized(), axis_z.normalized()).orthonormalized()
|
|
||||||
mesh_instance.transform = Transform3D(basis, center)
|
|
||||||
mesh_instance.material_override = surface_material
|
|
||||||
holder.add_child(mesh_instance)
|
|
||||||
if Engine.is_editor_hint() and not has_meta("modular_builder_preview"):
|
|
||||||
var scene_root := get_tree().edited_scene_root
|
|
||||||
if scene_root:
|
|
||||||
mesh_instance.owner = scene_root
|
|
||||||
|
|
||||||
func _frame_material() -> Material:
|
|
||||||
return frame_material if frame_material != null else surface_material
|
|
||||||
|
|
||||||
func _panel_material() -> Material:
|
|
||||||
return panel_material if panel_material != null else surface_material
|
|
||||||
|
|
||||||
func _add_box(holder: Node3D, node_name: String, box_size: Vector3, box_position: Vector3, material: Material = null) -> void:
|
|
||||||
var mesh_instance := MeshInstance3D.new()
|
|
||||||
mesh_instance.name = node_name
|
|
||||||
var box := BoxMesh.new()
|
|
||||||
box.size = box_size
|
|
||||||
mesh_instance.mesh = box
|
|
||||||
mesh_instance.position = box_position
|
|
||||||
mesh_instance.material_override = material if material != null else surface_material
|
|
||||||
holder.add_child(mesh_instance)
|
|
||||||
if Engine.is_editor_hint() and not has_meta("modular_builder_preview"):
|
|
||||||
var scene_root := get_tree().edited_scene_root
|
|
||||||
if scene_root:
|
|
||||||
mesh_instance.owner = scene_root
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://dsttda167kuhi
|
|
||||||
|
|
@ -1,748 +0,0 @@
|
||||||
@tool
|
|
||||||
class_name ModularBuilderPieceGeometry
|
|
||||||
extends Node3D
|
|
||||||
|
|
||||||
const DETAIL_SCRIPT_PATH := "res://addons/modular_builder_en/modular_building_detail.gd"
|
|
||||||
const DETAIL_TYPE_HATCH := 2
|
|
||||||
const DETAIL_TYPE_PILLAR := 3
|
|
||||||
|
|
||||||
enum PieceType { FOUNDATION_SQUARE, FOUNDATION_TRIANGLE, FLOOR, WALL, BUILDING, GABLE_WALL, ROOF_GABLE, ROOF_SHED, GABLE_WALL_RIGHT, ROOF_PYRAMID, STAIRS, FENCE }
|
|
||||||
|
|
||||||
@export var piece_type: PieceType = PieceType.FOUNDATION_SQUARE:
|
|
||||||
set(value):
|
|
||||||
piece_type = value
|
|
||||||
notify_property_list_changed()
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.5, 8.0, 0.001, "suffix:m") var unit_size: float = 2.0:
|
|
||||||
set(value):
|
|
||||||
unit_size = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.01, 1.0, 0.001, "suffix:m") var thickness: float = 0.2:
|
|
||||||
set(value):
|
|
||||||
thickness = value
|
|
||||||
_rebuild()
|
|
||||||
for child in get_children():
|
|
||||||
if child.has_method("_on_host_thickness_changed"):
|
|
||||||
child.call("_on_host_thickness_changed")
|
|
||||||
@export_range(0.1, 8.0, 0.001, "suffix:m") var rise: float = 1.0:
|
|
||||||
set(value):
|
|
||||||
rise = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.01, 32.0, 0.001, "suffix:m") var roof_width: float = 2.0:
|
|
||||||
set(value):
|
|
||||||
roof_width = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(1, 100, 1, "suffix: steps") var stair_count: int = 10:
|
|
||||||
set(value):
|
|
||||||
stair_count = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.01, 8.0, 0.001, "suffix:m") var stair_width: float = 1.0:
|
|
||||||
set(value):
|
|
||||||
stair_width = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0, 100, 1, "suffix: posts") var fence_divisions: int = 1:
|
|
||||||
set(value):
|
|
||||||
fence_divisions = value
|
|
||||||
_rebuild()
|
|
||||||
@export_range(0.1, 8.0, 0.001, "suffix:m") var fence_height: float = 1.0:
|
|
||||||
set(value):
|
|
||||||
fence_height = value
|
|
||||||
_rebuild()
|
|
||||||
@export var symmetric_dimensions: bool = true:
|
|
||||||
set(value):
|
|
||||||
if symmetric_dimensions == value:
|
|
||||||
return
|
|
||||||
if not value:
|
|
||||||
_capture_mesh_dimensions()
|
|
||||||
symmetric_dimensions = value
|
|
||||||
notify_property_list_changed()
|
|
||||||
if value:
|
|
||||||
scale = Vector3.ONE
|
|
||||||
else:
|
|
||||||
_apply_dimension_scale()
|
|
||||||
@export_range(0.01, 100.0, 0.001, "suffix:m") var dimension_width: float = 2.0:
|
|
||||||
set(value):
|
|
||||||
dimension_width = value
|
|
||||||
_apply_dimension_scale()
|
|
||||||
@export_range(0.01, 100.0, 0.001, "suffix:m") var dimension_height: float = 2.0:
|
|
||||||
set(value):
|
|
||||||
dimension_height = value
|
|
||||||
_apply_dimension_scale()
|
|
||||||
@export_range(0.01, 100.0, 0.001, "suffix:m") var dimension_depth: float = 2.0:
|
|
||||||
set(value):
|
|
||||||
dimension_depth = value
|
|
||||||
_apply_dimension_scale()
|
|
||||||
@export_storage var _dimension_reference: Vector3 = Vector3.ZERO
|
|
||||||
@export var center_align: bool = false:
|
|
||||||
set(value):
|
|
||||||
center_align = value
|
|
||||||
_rebuild()
|
|
||||||
@export var surface_material: Material:
|
|
||||||
set(value):
|
|
||||||
surface_material = value
|
|
||||||
_rebuild()
|
|
||||||
@export var custom_points: PackedVector3Array = PackedVector3Array():
|
|
||||||
set(value):
|
|
||||||
custom_points = value
|
|
||||||
_rebuild()
|
|
||||||
@export var centered_origin: bool = false:
|
|
||||||
set(value):
|
|
||||||
centered_origin = value
|
|
||||||
_rebuild()
|
|
||||||
|
|
||||||
func _rebuild() -> void:
|
|
||||||
pass
|
|
||||||
|
|
||||||
func _capture_mesh_dimensions() -> void:
|
|
||||||
var geometry := get_node_or_null("Geometry") as MeshInstance3D
|
|
||||||
if geometry == null or geometry.mesh == null:
|
|
||||||
return
|
|
||||||
var dimensions := geometry.mesh.get_aabb().size
|
|
||||||
if dimensions.x > 0.001:
|
|
||||||
dimension_width = dimensions.x
|
|
||||||
if dimensions.y > 0.001:
|
|
||||||
dimension_height = dimensions.y
|
|
||||||
if dimensions.z > 0.001:
|
|
||||||
dimension_depth = dimensions.z
|
|
||||||
_dimension_reference = dimensions
|
|
||||||
|
|
||||||
func _apply_dimension_scale() -> void:
|
|
||||||
if symmetric_dimensions or _dimension_reference.x <= 0.001 or _dimension_reference.y <= 0.001 or _dimension_reference.z <= 0.001:
|
|
||||||
return
|
|
||||||
var target := Vector3(dimension_width, dimension_height, dimension_depth)
|
|
||||||
if piece_type == PieceType.FOUNDATION_SQUARE or piece_type == PieceType.FOUNDATION_TRIANGLE or piece_type == PieceType.FLOOR:
|
|
||||||
target.y = _dimension_reference.y
|
|
||||||
if piece_type == PieceType.WALL:
|
|
||||||
target.z = _dimension_reference.z
|
|
||||||
scale = Vector3(target.x / _dimension_reference.x, target.y / _dimension_reference.y, target.z / _dimension_reference.z)
|
|
||||||
|
|
||||||
func _make_mesh() -> ArrayMesh:
|
|
||||||
if piece_type == PieceType.GABLE_WALL and custom_points.size() >= 2:
|
|
||||||
return _make_gable_wall_mesh()
|
|
||||||
if piece_type == PieceType.GABLE_WALL_RIGHT and custom_points.size() >= 2:
|
|
||||||
return _make_right_gable_wall_mesh()
|
|
||||||
if (piece_type == PieceType.ROOF_GABLE or piece_type == PieceType.ROOF_SHED or piece_type == PieceType.ROOF_PYRAMID) and custom_points.size() >= 2:
|
|
||||||
return _make_roof_mesh()
|
|
||||||
if piece_type == PieceType.STAIRS and custom_points.size() >= 2:
|
|
||||||
return _make_stair_mesh()
|
|
||||||
if piece_type == PieceType.FENCE and custom_points.size() >= 2:
|
|
||||||
return _make_fence_mesh()
|
|
||||||
if custom_points.size() >= 2 and piece_type == PieceType.WALL:
|
|
||||||
return _make_custom_wall_mesh()
|
|
||||||
if custom_points.size() >= 3 and piece_type == PieceType.FLOOR:
|
|
||||||
return _make_custom_floor_mesh()
|
|
||||||
if custom_points.size() >= 3 and (piece_type == PieceType.FOUNDATION_SQUARE or piece_type == PieceType.FOUNDATION_TRIANGLE):
|
|
||||||
return _make_custom_foundation_mesh()
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var s := unit_size
|
|
||||||
var polygon: Array[Vector2]
|
|
||||||
match piece_type:
|
|
||||||
PieceType.FOUNDATION_TRIANGLE:
|
|
||||||
polygon = [Vector2(-s * 0.5, -s * 0.5), Vector2(s * 0.5, -s * 0.5), Vector2(-s * 0.5, s * 0.5)]
|
|
||||||
_:
|
|
||||||
polygon = [Vector2(-s * 0.5, -s * 0.5), Vector2(s * 0.5, -s * 0.5), Vector2(s * 0.5, s * 0.5), Vector2(-s * 0.5, s * 0.5)]
|
|
||||||
var y0 := -thickness * 0.5 if centered_origin else 0.0
|
|
||||||
var y1 := thickness * 0.5 if centered_origin else thickness
|
|
||||||
if piece_type == PieceType.WALL:
|
|
||||||
polygon = [Vector2(-s * 0.5, -thickness * 0.5), Vector2(s * 0.5, -thickness * 0.5), Vector2(s * 0.5, thickness * 0.5), Vector2(-s * 0.5, thickness * 0.5)]
|
|
||||||
y0 = -s * 0.5 if centered_origin else 0.0
|
|
||||||
y1 = s * 0.5 if centered_origin else s
|
|
||||||
if piece_type == PieceType.FOUNDATION_SQUARE or piece_type == PieceType.FOUNDATION_TRIANGLE:
|
|
||||||
var hatch_mesh := _make_hatch_surface_mesh(polygon, y1, y0)
|
|
||||||
if hatch_mesh != null:
|
|
||||||
return hatch_mesh
|
|
||||||
for i in range(1, polygon.size() - 1):
|
|
||||||
_add_tri(vertices, normals, Vector3(polygon[0].x, y1, polygon[0].y), Vector3(polygon[i + 1].x, y1, polygon[i + 1].y), Vector3(polygon[i].x, y1, polygon[i].y))
|
|
||||||
_add_tri(vertices, normals, Vector3(polygon[0].x, y0, polygon[0].y), Vector3(polygon[i].x, y0, polygon[i].y), Vector3(polygon[i + 1].x, y0, polygon[i + 1].y))
|
|
||||||
for i in range(polygon.size()):
|
|
||||||
var j := (i + 1) % polygon.size()
|
|
||||||
var a := polygon[i]
|
|
||||||
var b := polygon[j]
|
|
||||||
_add_tri(vertices, normals, Vector3(a.x, y0, a.y), Vector3(b.x, y0, b.y), Vector3(b.x, y1, b.y))
|
|
||||||
_add_tri(vertices, normals, Vector3(a.x, y0, a.y), Vector3(b.x, y1, b.y), Vector3(a.x, y1, a.y))
|
|
||||||
var arrays: Array = []
|
|
||||||
arrays.resize(Mesh.ARRAY_MAX)
|
|
||||||
arrays[Mesh.ARRAY_VERTEX] = vertices
|
|
||||||
arrays[Mesh.ARRAY_NORMAL] = normals
|
|
||||||
var result := ArrayMesh.new()
|
|
||||||
result.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
|
||||||
return result
|
|
||||||
|
|
||||||
func _make_gable_wall_mesh() -> ArrayMesh:
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var base_points := _get_centered_gable_base() if center_align else PackedVector3Array([custom_points[0], custom_points[1]])
|
|
||||||
var a: Vector3 = base_points[0]
|
|
||||||
var b: Vector3 = base_points[1]
|
|
||||||
var direction := (b - a).normalized()
|
|
||||||
var side := Vector3(-direction.z, 0.0, direction.x).normalized() * thickness * 0.5
|
|
||||||
var peak := (a + b) * 0.5 + Vector3.UP * rise
|
|
||||||
var front_a := a + side
|
|
||||||
var front_b := b + side
|
|
||||||
var front_peak := peak + side
|
|
||||||
var back_a := a - side
|
|
||||||
var back_b := b - side
|
|
||||||
var back_peak := peak - side
|
|
||||||
_add_tri(vertices, normals, front_a, front_b, front_peak)
|
|
||||||
_add_tri(vertices, normals, back_b, back_a, back_peak)
|
|
||||||
_add_quad(vertices, normals, front_a, back_a, back_b, front_b)
|
|
||||||
_add_quad(vertices, normals, front_b, back_b, back_peak, front_peak)
|
|
||||||
_add_quad(vertices, normals, front_peak, back_peak, back_a, front_a)
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _make_right_gable_wall_mesh() -> ArrayMesh:
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var base_points := _get_centered_gable_base() if center_align else PackedVector3Array([custom_points[0], custom_points[1]])
|
|
||||||
var a: Vector3 = base_points[0]
|
|
||||||
var b: Vector3 = base_points[1]
|
|
||||||
var direction := (b - a).normalized()
|
|
||||||
var side := Vector3(-direction.z, 0.0, direction.x).normalized() * thickness * 0.5
|
|
||||||
var peak := b + Vector3.UP * rise
|
|
||||||
var front_a := a + side
|
|
||||||
var front_b := b + side
|
|
||||||
var front_peak := peak + side
|
|
||||||
var back_a := a - side
|
|
||||||
var back_b := b - side
|
|
||||||
var back_peak := peak - side
|
|
||||||
_add_tri(vertices, normals, front_a, front_b, front_peak)
|
|
||||||
_add_tri(vertices, normals, back_peak, back_b, back_a)
|
|
||||||
_add_quad(vertices, normals, front_a, back_a, back_b, front_b)
|
|
||||||
_add_quad(vertices, normals, front_b, back_b, back_peak, front_peak)
|
|
||||||
_add_quad(vertices, normals, front_peak, back_peak, back_a, front_a)
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _make_roof_mesh() -> ArrayMesh:
|
|
||||||
if piece_type == PieceType.ROOF_SHED and custom_points.size() == 2:
|
|
||||||
return _make_shed_roof_mesh()
|
|
||||||
if custom_points.size() >= 4:
|
|
||||||
return _make_oriented_roof_mesh()
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var first: Vector3 = custom_points[0]
|
|
||||||
var second: Vector3 = custom_points[1]
|
|
||||||
var min_x := minf(first.x, second.x)
|
|
||||||
var max_x := maxf(first.x, second.x)
|
|
||||||
var min_z := minf(first.z, second.z)
|
|
||||||
var max_z := maxf(first.z, second.z)
|
|
||||||
var span_x := max_x - min_x
|
|
||||||
var span_z := max_z - min_z
|
|
||||||
if span_x < 0.02 or span_z < 0.02:
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
var panels: Array = []
|
|
||||||
if piece_type == PieceType.ROOF_GABLE:
|
|
||||||
if span_x <= span_z:
|
|
||||||
var ridge_x := (min_x + max_x) * 0.5
|
|
||||||
panels.append([Vector3(min_x, 0.0, min_z), Vector3(min_x, 0.0, max_z), Vector3(ridge_x, rise, max_z), Vector3(ridge_x, rise, min_z)])
|
|
||||||
panels.append([Vector3(ridge_x, rise, min_z), Vector3(ridge_x, rise, max_z), Vector3(max_x, 0.0, max_z), Vector3(max_x, 0.0, min_z)])
|
|
||||||
else:
|
|
||||||
var ridge_z := (min_z + max_z) * 0.5
|
|
||||||
panels.append([Vector3(min_x, 0.0, min_z), Vector3(max_x, 0.0, min_z), Vector3(max_x, rise, ridge_z), Vector3(min_x, rise, ridge_z)])
|
|
||||||
panels.append([Vector3(min_x, rise, ridge_z), Vector3(max_x, rise, ridge_z), Vector3(max_x, 0.0, max_z), Vector3(min_x, 0.0, max_z)])
|
|
||||||
else:
|
|
||||||
if span_x <= span_z:
|
|
||||||
var low_x := first.x
|
|
||||||
var high_x := second.x
|
|
||||||
panels.append([Vector3(low_x, 0.0, min_z), Vector3(low_x, 0.0, max_z), Vector3(high_x, rise, max_z), Vector3(high_x, rise, min_z)])
|
|
||||||
else:
|
|
||||||
var low_z := first.z
|
|
||||||
var high_z := second.z
|
|
||||||
panels.append([Vector3(min_x, 0.0, low_z), Vector3(max_x, 0.0, low_z), Vector3(max_x, rise, high_z), Vector3(min_x, rise, high_z)])
|
|
||||||
for panel in panels:
|
|
||||||
_append_solid_panel(vertices, normals, panel)
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _make_shed_roof_mesh() -> ArrayMesh:
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var low: Vector3 = custom_points[0]
|
|
||||||
var high: Vector3 = custom_points[1]
|
|
||||||
var slope := high - low
|
|
||||||
var horizontal := Vector3(slope.x, 0.0, slope.z)
|
|
||||||
if horizontal.length_squared() < 0.0001 or roof_width <= 0.0:
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
var across := Vector3(horizontal.z, 0.0, -horizontal.x).normalized() * roof_width * 0.5
|
|
||||||
var panel := [low - across, low + across, high + across, high - across]
|
|
||||||
_append_solid_panel(vertices, normals, panel)
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _make_oriented_roof_mesh() -> ArrayMesh:
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var p0: Vector3 = custom_points[0]
|
|
||||||
var p1: Vector3 = custom_points[1]
|
|
||||||
var p2: Vector3 = custom_points[2]
|
|
||||||
var p3: Vector3 = custom_points[3]
|
|
||||||
var edge_01 := Vector2(p1.x - p0.x, p1.z - p0.z).length()
|
|
||||||
var edge_12 := Vector2(p2.x - p1.x, p2.z - p1.z).length()
|
|
||||||
var panels: Array = []
|
|
||||||
if piece_type == PieceType.ROOF_GABLE:
|
|
||||||
if edge_01 <= edge_12:
|
|
||||||
var ridge_01 := (p0 + p1) * 0.5 + Vector3.UP * rise
|
|
||||||
var ridge_23 := (p2 + p3) * 0.5 + Vector3.UP * rise
|
|
||||||
panels.append([p0, p3, ridge_23, ridge_01])
|
|
||||||
panels.append([ridge_01, ridge_23, p2, p1])
|
|
||||||
else:
|
|
||||||
var ridge_12 := (p1 + p2) * 0.5 + Vector3.UP * rise
|
|
||||||
var ridge_30 := (p3 + p0) * 0.5 + Vector3.UP * rise
|
|
||||||
panels.append([p0, p1, ridge_12, ridge_30])
|
|
||||||
panels.append([ridge_30, ridge_12, p2, p3])
|
|
||||||
elif piece_type == PieceType.ROOF_PYRAMID:
|
|
||||||
var peak := (p0 + p1 + p2 + p3) * 0.25 + Vector3.UP * rise
|
|
||||||
panels.append([p0, p1, peak])
|
|
||||||
panels.append([p1, p2, peak])
|
|
||||||
panels.append([p2, p3, peak])
|
|
||||||
panels.append([p3, p0, peak])
|
|
||||||
else:
|
|
||||||
# The four points define the low and high edges. Keep their centers
|
|
||||||
# fixed when adjusting both edge widths in the Inspector.
|
|
||||||
var base_width := Vector2(p1.x - p0.x, p1.z - p0.z).length()
|
|
||||||
if base_width > 0.001 and roof_width > 0.0:
|
|
||||||
var direction := Vector3(p1.x - p0.x, 0.0, p1.z - p0.z).normalized()
|
|
||||||
var low_center := (p0 + p1) * 0.5
|
|
||||||
var high_center := (p2 + p3) * 0.5
|
|
||||||
var half_width := roof_width * 0.5
|
|
||||||
panels.append([
|
|
||||||
low_center - direction * half_width,
|
|
||||||
low_center + direction * half_width,
|
|
||||||
high_center + direction * half_width,
|
|
||||||
high_center - direction * half_width,
|
|
||||||
])
|
|
||||||
else:
|
|
||||||
panels.append([p0, p1, p2, p3])
|
|
||||||
for panel in panels:
|
|
||||||
_append_solid_panel(vertices, normals, panel)
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _make_stair_mesh() -> ArrayMesh:
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var start: Vector3 = custom_points[0]
|
|
||||||
var finish: Vector3 = custom_points[1]
|
|
||||||
var horizontal := Vector3(finish.x - start.x, 0.0, finish.z - start.z)
|
|
||||||
var run := horizontal.length()
|
|
||||||
var rise_amount := finish.y - start.y
|
|
||||||
if run < 0.05 or rise_amount <= 0.05 or stair_count < 1 or stair_width <= 0.0:
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
var forward := horizontal / run
|
|
||||||
var width_axis := Vector3(-forward.z, 0.0, forward.x) * stair_width * 0.5
|
|
||||||
var profile := PackedVector2Array([Vector2(0.0, 0.0), Vector2(run, 0.0), Vector2(run, rise_amount)])
|
|
||||||
for step_index in range(stair_count - 1, -1, -1):
|
|
||||||
var tread_start := run * float(step_index) / stair_count
|
|
||||||
var tread_end := run * float(step_index + 1) / stair_count
|
|
||||||
var tread_height := rise_amount * float(step_index + 1) / stair_count
|
|
||||||
profile.append(Vector2(tread_start, tread_height))
|
|
||||||
if step_index > 0:
|
|
||||||
profile.append(Vector2(tread_start, rise_amount * float(step_index) / stair_count))
|
|
||||||
var triangulation := Geometry2D.triangulate_polygon(profile)
|
|
||||||
for side_sign in [-1.0, 1.0]:
|
|
||||||
for triangle_index in range(0, triangulation.size(), 3):
|
|
||||||
var a := profile[triangulation[triangle_index]]
|
|
||||||
var b := profile[triangulation[triangle_index + 1]]
|
|
||||||
var c := profile[triangulation[triangle_index + 2]]
|
|
||||||
var point_a: Vector3 = start + forward * a.x + Vector3.UP * a.y + width_axis * side_sign
|
|
||||||
var point_b: Vector3 = start + forward * b.x + Vector3.UP * b.y + width_axis * side_sign
|
|
||||||
var point_c: Vector3 = start + forward * c.x + Vector3.UP * c.y + width_axis * side_sign
|
|
||||||
if side_sign > 0.0:
|
|
||||||
_add_tri(vertices, normals, point_a, point_b, point_c)
|
|
||||||
else:
|
|
||||||
_add_tri(vertices, normals, point_c, point_b, point_a)
|
|
||||||
for profile_index in range(profile.size()):
|
|
||||||
var next_index := (profile_index + 1) % profile.size()
|
|
||||||
var a := profile[profile_index]
|
|
||||||
var b := profile[next_index]
|
|
||||||
var point_a_left := start + forward * a.x + Vector3.UP * a.y - width_axis
|
|
||||||
var point_b_left := start + forward * b.x + Vector3.UP * b.y - width_axis
|
|
||||||
var point_b_right := start + forward * b.x + Vector3.UP * b.y + width_axis
|
|
||||||
var point_a_right := start + forward * a.x + Vector3.UP * a.y + width_axis
|
|
||||||
_add_quad(vertices, normals, point_a_left, point_b_left, point_b_right, point_a_right)
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _make_fence_mesh() -> ArrayMesh:
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var start: Vector3 = custom_points[0]
|
|
||||||
var finish: Vector3 = custom_points[1]
|
|
||||||
var path := finish - start
|
|
||||||
var path_length := path.length()
|
|
||||||
if path_length < 0.05 or fence_height <= 0.0:
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
var path_axis := path / path_length
|
|
||||||
var post_count := fence_divisions + 2
|
|
||||||
var post_positions: Array[Vector3] = []
|
|
||||||
for post_index in range(post_count):
|
|
||||||
var base_position := start.lerp(finish, float(post_index) / float(post_count - 1))
|
|
||||||
post_positions.append(base_position)
|
|
||||||
var side_axis := path_axis.cross(Vector3.UP).normalized()
|
|
||||||
if side_axis.length_squared() < 0.001:
|
|
||||||
side_axis = Vector3.FORWARD
|
|
||||||
_append_oriented_box(vertices, normals, base_position + Vector3.UP * fence_height * 0.5, path_axis, Vector3.UP, side_axis, Vector3(thickness, fence_height, thickness))
|
|
||||||
for section_index in range(post_positions.size() - 1):
|
|
||||||
var section_start := post_positions[section_index]
|
|
||||||
var section_end := post_positions[section_index + 1]
|
|
||||||
var section_axis := (section_end - section_start).normalized()
|
|
||||||
var rail_up := Vector3.UP - section_axis * section_axis.dot(Vector3.UP)
|
|
||||||
if rail_up.length_squared() < 0.001:
|
|
||||||
rail_up = Vector3.FORWARD - section_axis * section_axis.dot(Vector3.FORWARD)
|
|
||||||
rail_up = rail_up.normalized()
|
|
||||||
var rail_side := section_axis.cross(rail_up).normalized()
|
|
||||||
var section_length := section_start.distance_to(section_end)
|
|
||||||
for height_ratio in [0.28, 0.78]:
|
|
||||||
var rail_center: Vector3 = (section_start + section_end) * 0.5 + Vector3.UP * fence_height * height_ratio
|
|
||||||
_append_oriented_box(vertices, normals, rail_center, section_axis, rail_up, rail_side, Vector3(section_length, thickness, thickness))
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _append_oriented_box(vertices: PackedVector3Array, normals: PackedVector3Array, center: Vector3, axis_x: Vector3, axis_y: Vector3, axis_z: Vector3, dimensions: Vector3) -> void:
|
|
||||||
var x := axis_x.normalized() * dimensions.x * 0.5
|
|
||||||
var y := axis_y.normalized() * dimensions.y * 0.5
|
|
||||||
var z := axis_z.normalized() * dimensions.z * 0.5
|
|
||||||
var nnn := center - x - y - z
|
|
||||||
var pnn := center + x - y - z
|
|
||||||
var ppn := center + x + y - z
|
|
||||||
var npn := center - x + y - z
|
|
||||||
var nnp := center - x - y + z
|
|
||||||
var pnp := center + x - y + z
|
|
||||||
var ppp := center + x + y + z
|
|
||||||
var npp := center - x + y + z
|
|
||||||
_add_quad(vertices, normals, nnp, pnp, ppp, npp)
|
|
||||||
_add_quad(vertices, normals, pnn, nnn, npn, ppn)
|
|
||||||
_add_quad(vertices, normals, npp, ppp, ppn, npn)
|
|
||||||
_add_quad(vertices, normals, nnn, pnn, pnp, nnp)
|
|
||||||
_add_quad(vertices, normals, nnn, nnp, npp, npn)
|
|
||||||
_add_quad(vertices, normals, pnp, pnn, ppn, ppp)
|
|
||||||
|
|
||||||
func _get_centered_gable_base() -> PackedVector3Array:
|
|
||||||
var selected_a := to_global(custom_points[0])
|
|
||||||
var selected_b := to_global(custom_points[1])
|
|
||||||
var search_root: Node = get_parent()
|
|
||||||
while search_root != null and not search_root is ModularBuilderPieceGeometry:
|
|
||||||
search_root = search_root.get_parent()
|
|
||||||
if search_root == null:
|
|
||||||
return PackedVector3Array([custom_points[0], custom_points[1]])
|
|
||||||
var walls: Array[ModularBuilderPieceGeometry] = []
|
|
||||||
_collect_centering_walls(search_root, walls)
|
|
||||||
var centered_a := selected_a
|
|
||||||
var centered_b := selected_b
|
|
||||||
var nearest_a := INF
|
|
||||||
var nearest_b := INF
|
|
||||||
for wall in walls:
|
|
||||||
if wall.custom_points.size() < 2:
|
|
||||||
continue
|
|
||||||
var wall_a := wall.to_global(wall.custom_points[0] + Vector3.UP * wall.unit_size)
|
|
||||||
var wall_b := wall.to_global(wall.custom_points[1] + Vector3.UP * wall.unit_size)
|
|
||||||
var tolerance: float = wall.thickness + 0.04
|
|
||||||
for endpoint in [wall_a, wall_b]:
|
|
||||||
var distance_a := selected_a.distance_to(endpoint)
|
|
||||||
if distance_a < nearest_a and distance_a <= tolerance:
|
|
||||||
nearest_a = distance_a
|
|
||||||
centered_a = endpoint
|
|
||||||
var distance_b := selected_b.distance_to(endpoint)
|
|
||||||
if distance_b < nearest_b and distance_b <= tolerance:
|
|
||||||
nearest_b = distance_b
|
|
||||||
centered_b = endpoint
|
|
||||||
if nearest_a < INF and nearest_b < INF and centered_a.distance_to(centered_b) >= 0.02:
|
|
||||||
return PackedVector3Array([to_local(centered_a), to_local(centered_b)])
|
|
||||||
return PackedVector3Array([custom_points[0], custom_points[1]])
|
|
||||||
|
|
||||||
func _collect_centering_walls(node: Node, walls: Array[ModularBuilderPieceGeometry]) -> void:
|
|
||||||
if node is ModularBuilderPieceGeometry and node.piece_type == PieceType.WALL:
|
|
||||||
walls.append(node)
|
|
||||||
for child in node.get_children():
|
|
||||||
_collect_centering_walls(child, walls)
|
|
||||||
|
|
||||||
func _append_solid_panel(vertices: PackedVector3Array, normals: PackedVector3Array, panel: Array) -> void:
|
|
||||||
if panel.size() < 3:
|
|
||||||
return
|
|
||||||
var p0: Vector3 = panel[0]
|
|
||||||
var p1: Vector3 = panel[1]
|
|
||||||
var p2: Vector3 = panel[2]
|
|
||||||
var normal: Vector3 = (p1 - p0).cross(p2 - p0).normalized()
|
|
||||||
if normal.is_zero_approx():
|
|
||||||
return
|
|
||||||
var half_depth: Vector3 = normal * thickness * 0.5
|
|
||||||
var front: Array[Vector3] = []
|
|
||||||
var back: Array[Vector3] = []
|
|
||||||
for point in panel:
|
|
||||||
front.append(point + half_depth)
|
|
||||||
back.append(point - half_depth)
|
|
||||||
for i in range(1, panel.size() - 1):
|
|
||||||
_add_tri(vertices, normals, front[0], front[i], front[i + 1])
|
|
||||||
_add_tri(vertices, normals, back[i + 1], back[i], back[0])
|
|
||||||
for i in range(panel.size()):
|
|
||||||
var next := (i + 1) % panel.size()
|
|
||||||
_add_quad(vertices, normals, front[i], back[i], back[next], front[next])
|
|
||||||
|
|
||||||
func _make_custom_wall_mesh() -> ArrayMesh:
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var wall_points: PackedVector3Array = _get_centered_wall_points() if center_align else custom_points
|
|
||||||
var a: Vector3 = wall_points[0]
|
|
||||||
var b: Vector3 = wall_points[1]
|
|
||||||
var direction := (b - a).normalized()
|
|
||||||
var length := a.distance_to(b)
|
|
||||||
var side := Vector3(-direction.z, 0.0, direction.x).normalized() * thickness * 0.5
|
|
||||||
var height := Vector3.UP * unit_size
|
|
||||||
var openings: Array[PackedVector2Array] = []
|
|
||||||
for child in get_children():
|
|
||||||
if _is_detail_node(child) and int(child.get("detail_type")) != DETAIL_TYPE_HATCH and int(child.get("detail_type")) != DETAIL_TYPE_PILLAR:
|
|
||||||
var detail := child as Node3D
|
|
||||||
var center := Vector2((detail.position - a).dot(direction), detail.position.y - a.y)
|
|
||||||
var axis_x := Vector2(detail.transform.basis.x.dot(direction), detail.transform.basis.x.y).normalized()
|
|
||||||
var axis_y := Vector2(detail.transform.basis.y.dot(direction), detail.transform.basis.y.y).normalized()
|
|
||||||
var opening := PackedVector2Array([
|
|
||||||
center - axis_x * float(detail.get("width")) * 0.5 - axis_y * float(detail.get("height")) * 0.5,
|
|
||||||
center + axis_x * float(detail.get("width")) * 0.5 - axis_y * float(detail.get("height")) * 0.5,
|
|
||||||
center + axis_x * float(detail.get("width")) * 0.5 + axis_y * float(detail.get("height")) * 0.5,
|
|
||||||
center - axis_x * float(detail.get("width")) * 0.5 + axis_y * float(detail.get("height")) * 0.5
|
|
||||||
])
|
|
||||||
var wall_rect := PackedVector2Array([Vector2(0.0, 0.0), Vector2(length, 0.0), Vector2(length, unit_size), Vector2(0.0, unit_size)])
|
|
||||||
for clipped_opening in Geometry2D.intersect_polygons(opening, wall_rect):
|
|
||||||
if clipped_opening.size() >= 3:
|
|
||||||
openings.append(clipped_opening)
|
|
||||||
var xs: Array[float] = [0.0, length]
|
|
||||||
var ys: Array[float] = [0.0, unit_size]
|
|
||||||
for opening in openings:
|
|
||||||
for point in opening:
|
|
||||||
xs.append(clampf(point.x, 0.0, length))
|
|
||||||
ys.append(clampf(point.y, 0.0, unit_size))
|
|
||||||
xs.sort()
|
|
||||||
ys.sort()
|
|
||||||
for xi in range(xs.size() - 1):
|
|
||||||
for yi in range(ys.size() - 1):
|
|
||||||
var u0: float = xs[xi]
|
|
||||||
var u1: float = xs[xi + 1]
|
|
||||||
var v0: float = ys[yi]
|
|
||||||
var v1: float = ys[yi + 1]
|
|
||||||
var mid := Vector2((u0 + u1) * 0.5, (v0 + v1) * 0.5)
|
|
||||||
var cell := PackedVector2Array([Vector2(u0, v0), Vector2(u1, v0), Vector2(u1, v1), Vector2(u0, v1)])
|
|
||||||
var remaining: Array[PackedVector2Array] = [cell]
|
|
||||||
for opening in openings:
|
|
||||||
var next_remaining: Array[PackedVector2Array] = []
|
|
||||||
for polygon in remaining:
|
|
||||||
for clipped in Geometry2D.clip_polygons(polygon, opening):
|
|
||||||
if clipped.size() >= 3:
|
|
||||||
next_remaining.append(clipped)
|
|
||||||
remaining = next_remaining
|
|
||||||
for polygon in remaining:
|
|
||||||
var indices := Geometry2D.triangulate_polygon(polygon)
|
|
||||||
for ti in range(0, indices.size(), 3):
|
|
||||||
var p0: Vector2 = polygon[indices[ti]]
|
|
||||||
var p1: Vector2 = polygon[indices[ti + 1]]
|
|
||||||
var p2: Vector2 = polygon[indices[ti + 2]]
|
|
||||||
var f0 := a + direction * p0.x + Vector3.UP * p0.y
|
|
||||||
var f1 := a + direction * p1.x + Vector3.UP * p1.y
|
|
||||||
var f2 := a + direction * p2.x + Vector3.UP * p2.y
|
|
||||||
_add_tri(vertices, normals, f0 + side, f1 + side, f2 + side)
|
|
||||||
_add_tri(vertices, normals, f2 - side, f1 - side, f0 - side)
|
|
||||||
# Close the wall body at its ends, top, and base.
|
|
||||||
_add_quad(vertices, normals, a - side, a + side, a + side + height, a - side + height)
|
|
||||||
_add_quad(vertices, normals, b + side, b - side, b - side + height, b + side + height)
|
|
||||||
_add_quad(vertices, normals, a + side + height, b + side + height, b - side + height, a - side + height)
|
|
||||||
_add_quad(vertices, normals, a - side, b - side, b + side, a + side)
|
|
||||||
for opening in openings:
|
|
||||||
for edge_index in range(opening.size()):
|
|
||||||
var p0 := opening[edge_index]
|
|
||||||
var p1 := opening[(edge_index + 1) % opening.size()]
|
|
||||||
var a0 := a + direction * p0.x + Vector3.UP * p0.y
|
|
||||||
var a1 := a + direction * p1.x + Vector3.UP * p1.y
|
|
||||||
_add_quad(vertices, normals, a0 + side, a0 - side, a1 - side, a1 + side)
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _get_centered_wall_points() -> PackedVector3Array:
|
|
||||||
if custom_points.size() < 2:
|
|
||||||
return custom_points
|
|
||||||
var selected_a := to_global(custom_points[0])
|
|
||||||
var selected_b := to_global(custom_points[1])
|
|
||||||
var search_root: Node = get_parent()
|
|
||||||
while search_root != null and not search_root is ModularBuilderPieceGeometry:
|
|
||||||
search_root = search_root.get_parent()
|
|
||||||
if search_root == null:
|
|
||||||
return custom_points
|
|
||||||
var walls: Array[ModularBuilderPieceGeometry] = []
|
|
||||||
_collect_centering_walls(search_root, walls)
|
|
||||||
var centered_a := selected_a
|
|
||||||
var centered_b := selected_b
|
|
||||||
var nearest_a := INF
|
|
||||||
var nearest_b := INF
|
|
||||||
for wall in walls:
|
|
||||||
if wall == self or wall.custom_points.size() < 2:
|
|
||||||
continue
|
|
||||||
var direction := (wall.custom_points[1] - wall.custom_points[0]).normalized()
|
|
||||||
var side := Vector3(-direction.z, 0.0, direction.x).normalized() * wall.thickness * 0.5
|
|
||||||
for endpoint in wall.custom_points:
|
|
||||||
var center := wall.to_global(endpoint + Vector3.UP * wall.unit_size)
|
|
||||||
for surface_corner in [center + side, center - side]:
|
|
||||||
var distance_a := selected_a.distance_to(surface_corner)
|
|
||||||
if distance_a < nearest_a and distance_a <= wall.thickness + 0.04:
|
|
||||||
nearest_a = distance_a
|
|
||||||
centered_a = center
|
|
||||||
var distance_b := selected_b.distance_to(surface_corner)
|
|
||||||
if distance_b < nearest_b and distance_b <= wall.thickness + 0.04:
|
|
||||||
nearest_b = distance_b
|
|
||||||
centered_b = center
|
|
||||||
if nearest_a < INF and nearest_b < INF and centered_a.distance_to(centered_b) >= 0.02:
|
|
||||||
return PackedVector3Array([to_local(centered_a), to_local(centered_b)])
|
|
||||||
return custom_points
|
|
||||||
|
|
||||||
func _make_custom_floor_mesh() -> ArrayMesh:
|
|
||||||
var polygon: Array[Vector2] = []
|
|
||||||
for point in custom_points:
|
|
||||||
polygon.append(Vector2(point.x, point.z))
|
|
||||||
var hatch_mesh := _make_hatch_surface_mesh(polygon, custom_points[0].y, custom_points[0].y - thickness)
|
|
||||||
if hatch_mesh != null:
|
|
||||||
return hatch_mesh
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var points := custom_points
|
|
||||||
var bottom: Array[Vector3] = []
|
|
||||||
for point in points:
|
|
||||||
bottom.append(point - Vector3.UP * thickness)
|
|
||||||
for i in range(1, points.size() - 1):
|
|
||||||
_add_tri(vertices, normals, points[0], points[i + 1], points[i])
|
|
||||||
_add_tri(vertices, normals, bottom[0], bottom[i], bottom[i + 1])
|
|
||||||
for i in range(points.size()):
|
|
||||||
var j := (i + 1) % points.size()
|
|
||||||
_add_quad(vertices, normals, bottom[i], bottom[j], points[j], points[i])
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _make_hatch_surface_mesh(polygon: Array[Vector2], top_y: float, bottom_y: float) -> ArrayMesh:
|
|
||||||
var openings: Array[PackedVector2Array] = []
|
|
||||||
for child in get_children():
|
|
||||||
if _is_detail_node(child) and int(child.get("detail_type")) == DETAIL_TYPE_HATCH:
|
|
||||||
var detail := child as Node3D
|
|
||||||
var center := Vector2(detail.position.x, detail.position.z)
|
|
||||||
var axis_x := Vector2(detail.transform.basis.x.x, detail.transform.basis.x.z).normalized()
|
|
||||||
var axis_y := Vector2(detail.transform.basis.z.x, detail.transform.basis.z.z).normalized()
|
|
||||||
var opening := PackedVector2Array([
|
|
||||||
center - axis_x * float(detail.get("width")) * 0.5 - axis_y * float(detail.get("height")) * 0.5,
|
|
||||||
center + axis_x * float(detail.get("width")) * 0.5 - axis_y * float(detail.get("height")) * 0.5,
|
|
||||||
center + axis_x * float(detail.get("width")) * 0.5 + axis_y * float(detail.get("height")) * 0.5,
|
|
||||||
center - axis_x * float(detail.get("width")) * 0.5 + axis_y * float(detail.get("height")) * 0.5
|
|
||||||
])
|
|
||||||
openings.append(opening)
|
|
||||||
if openings.is_empty() or polygon.size() < 3:
|
|
||||||
return null
|
|
||||||
var min_x := polygon[0].x
|
|
||||||
var max_x := polygon[0].x
|
|
||||||
var min_z := polygon[0].y
|
|
||||||
var max_z := polygon[0].y
|
|
||||||
var xs: Array[float] = []
|
|
||||||
var zs: Array[float] = []
|
|
||||||
for point in polygon:
|
|
||||||
min_x = minf(min_x, point.x)
|
|
||||||
max_x = maxf(max_x, point.x)
|
|
||||||
min_z = minf(min_z, point.y)
|
|
||||||
max_z = maxf(max_z, point.y)
|
|
||||||
xs.append(point.x)
|
|
||||||
zs.append(point.y)
|
|
||||||
for opening in openings:
|
|
||||||
for point in opening:
|
|
||||||
xs.append(clampf(point.x, min_x, max_x))
|
|
||||||
zs.append(clampf(point.y, min_z, max_z))
|
|
||||||
xs.sort()
|
|
||||||
zs.sort()
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
var source_polygon := PackedVector2Array()
|
|
||||||
for point in polygon:
|
|
||||||
source_polygon.append(point)
|
|
||||||
for xi in range(xs.size() - 1):
|
|
||||||
for zi in range(zs.size() - 1):
|
|
||||||
var x0: float = xs[xi]
|
|
||||||
var x1: float = xs[xi + 1]
|
|
||||||
var z0: float = zs[zi]
|
|
||||||
var z1: float = zs[zi + 1]
|
|
||||||
if x1 - x0 < 0.0001 or z1 - z0 < 0.0001:
|
|
||||||
continue
|
|
||||||
var cell := PackedVector2Array([Vector2(x0, z0), Vector2(x1, z0), Vector2(x1, z1), Vector2(x0, z1)])
|
|
||||||
var remaining: Array[PackedVector2Array] = []
|
|
||||||
for clipped_surface in Geometry2D.intersect_polygons(source_polygon, cell):
|
|
||||||
remaining.append(clipped_surface)
|
|
||||||
for opening in openings:
|
|
||||||
var next_remaining: Array[PackedVector2Array] = []
|
|
||||||
for surface_polygon in remaining:
|
|
||||||
for clipped_surface in Geometry2D.clip_polygons(surface_polygon, opening):
|
|
||||||
if clipped_surface.size() >= 3:
|
|
||||||
next_remaining.append(clipped_surface)
|
|
||||||
remaining = next_remaining
|
|
||||||
for clipped_surface in remaining:
|
|
||||||
var triangulated := Geometry2D.triangulate_polygon(clipped_surface)
|
|
||||||
for ti in range(0, triangulated.size(), 3):
|
|
||||||
var a: Vector2 = clipped_surface[triangulated[ti]]
|
|
||||||
var b: Vector2 = clipped_surface[triangulated[ti + 1]]
|
|
||||||
var c: Vector2 = clipped_surface[triangulated[ti + 2]]
|
|
||||||
_add_tri(vertices, normals, Vector3(a.x, top_y, a.y), Vector3(c.x, top_y, c.y), Vector3(b.x, top_y, b.y))
|
|
||||||
_add_tri(vertices, normals, Vector3(a.x, bottom_y, a.y), Vector3(b.x, bottom_y, b.y), Vector3(c.x, bottom_y, c.y))
|
|
||||||
for i in range(polygon.size()):
|
|
||||||
var j := (i + 1) % polygon.size()
|
|
||||||
var a := polygon[i]
|
|
||||||
var b := polygon[j]
|
|
||||||
_add_quad(vertices, normals, Vector3(a.x, bottom_y, a.y), Vector3(b.x, bottom_y, b.y), Vector3(b.x, top_y, b.y), Vector3(a.x, top_y, a.y))
|
|
||||||
for opening in openings:
|
|
||||||
var visible_edges := Geometry2D.intersect_polygons(opening, source_polygon)
|
|
||||||
for visible_opening in visible_edges:
|
|
||||||
for edge_index in range(visible_opening.size()):
|
|
||||||
var p0 := visible_opening[edge_index]
|
|
||||||
var p1 := visible_opening[(edge_index + 1) % visible_opening.size()]
|
|
||||||
_add_quad(vertices, normals, Vector3(p0.x, bottom_y, p0.y), Vector3(p0.x, top_y, p0.y), Vector3(p1.x, top_y, p1.y), Vector3(p1.x, bottom_y, p1.y))
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _is_detail_node(node: Node) -> bool:
|
|
||||||
if not node is Node3D or node.get_script() == null:
|
|
||||||
return false
|
|
||||||
return node.get_script().resource_path == DETAIL_SCRIPT_PATH
|
|
||||||
|
|
||||||
func _make_custom_foundation_mesh() -> ArrayMesh:
|
|
||||||
var polygon: Array[Vector2] = []
|
|
||||||
for point in custom_points:
|
|
||||||
polygon.append(Vector2(point.x, point.z))
|
|
||||||
var bottom_y := -thickness * 0.5 if centered_origin else 0.0
|
|
||||||
var top_y := thickness * 0.5 if centered_origin else thickness
|
|
||||||
var hatch_mesh := _make_hatch_surface_mesh(polygon, top_y, bottom_y)
|
|
||||||
if hatch_mesh != null:
|
|
||||||
return hatch_mesh
|
|
||||||
var vertices := PackedVector3Array()
|
|
||||||
var normals := PackedVector3Array()
|
|
||||||
for i in range(1, custom_points.size() - 1):
|
|
||||||
var a := custom_points[0]
|
|
||||||
var b := custom_points[i]
|
|
||||||
var c := custom_points[i + 1]
|
|
||||||
_add_tri(vertices, normals, Vector3(a.x, top_y, a.z), Vector3(c.x, top_y, c.z), Vector3(b.x, top_y, b.z))
|
|
||||||
_add_tri(vertices, normals, Vector3(a.x, bottom_y, a.z), Vector3(b.x, bottom_y, b.z), Vector3(c.x, bottom_y, c.z))
|
|
||||||
for i in range(custom_points.size()):
|
|
||||||
var j := (i + 1) % custom_points.size()
|
|
||||||
var a := custom_points[i]
|
|
||||||
var b := custom_points[j]
|
|
||||||
_add_quad(vertices, normals, Vector3(a.x, bottom_y, a.z), Vector3(b.x, bottom_y, b.z), Vector3(b.x, top_y, b.z), Vector3(a.x, top_y, a.z))
|
|
||||||
return _array_mesh(vertices, normals)
|
|
||||||
|
|
||||||
func _add_quad(v: PackedVector3Array, n: PackedVector3Array, a: Vector3, b: Vector3, c: Vector3, d: Vector3) -> void:
|
|
||||||
_add_tri(v, n, a, b, c)
|
|
||||||
_add_tri(v, n, a, c, d)
|
|
||||||
|
|
||||||
func _array_mesh(v: PackedVector3Array, n: PackedVector3Array) -> ArrayMesh:
|
|
||||||
var arrays: Array = []
|
|
||||||
arrays.resize(Mesh.ARRAY_MAX)
|
|
||||||
arrays[Mesh.ARRAY_VERTEX] = v
|
|
||||||
arrays[Mesh.ARRAY_NORMAL] = n
|
|
||||||
var result := ArrayMesh.new()
|
|
||||||
result.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
|
||||||
return result
|
|
||||||
|
|
||||||
func _add_tri(v: PackedVector3Array, n: PackedVector3Array, a: Vector3, b: Vector3, c: Vector3) -> void:
|
|
||||||
var normal := (b - a).cross(c - a).normalized()
|
|
||||||
v.append(a)
|
|
||||||
v.append(b)
|
|
||||||
v.append(c)
|
|
||||||
n.append(normal)
|
|
||||||
n.append(normal)
|
|
||||||
n.append(normal)
|
|
||||||
# Also generate reversed faces to show both sides.
|
|
||||||
# This keeps floors and foundations visible from below with any material.
|
|
||||||
v.append(c)
|
|
||||||
v.append(b)
|
|
||||||
v.append(a)
|
|
||||||
n.append(-normal)
|
|
||||||
n.append(-normal)
|
|
||||||
n.append(-normal)
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://dp1333xlylabf
|
|
||||||
0
assets/textures/terrain/.gdignore
Normal file
BIN
assets/textures/terrain/terrain_arrays.res
Normal file
1
core
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5e98535a0afc83af4e4017806686a76af4913afd
|
||||||