21.07.2026, 07:49
(Dieser Beitrag wurde zuletzt bearbeitet: 21.07.2026, 08:28 von Manfred Aabye.)
Auto Plant 2.27
Automatisches erstellen von Regionen mit Objekten und Texturen.
Automatische Größenerkennung.
Es setzt 4 Terrain PBR Materialien.
Raut die Region leicht auf.
Setzt Bäume, Büsche, Farne, Todholz, Grass und Steine.
Fazit: Es wird ein Zufallswald erstellt und ist noch nicht ganz fertig, zur Demo ist das aber schon gut genug.
Automatisches erstellen von Regionen mit Objekten und Texturen.
Automatische Größenerkennung.
Es setzt 4 Terrain PBR Materialien.
Raut die Region leicht auf.
Setzt Bäume, Büsche, Farne, Todholz, Grass und Steine.
PHP-Code:
// ============================================================
// OPENSIM WALDPFLANZER SKRIPT MIT DIALOG-MENÜ
// ============================================================
// ===== GLOBALE VARIABLEN =====
integer gIntX;
integer gIntY;
integer gIntZ;
integer gIntStartX;
integer gIntStartY;
integer gIntEndX;
integer gIntEndY;
float gFltPlantProbability;
float gFltRandMax;
// ===== START/STOP-FUNKTION =====
integer gIntRunning = FALSE;
key gKeyToucher;
integer gIntCurrentX;
integer gIntCurrentY;
// ===== PFLANZPARAMETER =====
integer PFLANZABSTAND = 6;
// ===== KOMBINIERTE LISTE FÜR ALLE OBJEKTE =====
// Format: [Name, Höhe, Priorität, X-Rotation, Y-Rotation, Z-Rotation, Z-Offset]
// Je höher die Priorität, desto wahrscheinlicher wird das Objekt ausgewählt
list ALLE_OBJEKTE = [
// Bäume (Priorität 30)
"European-Beech-tree-A", 10.0, 30, 0.0, 0.0, 0.0, 0.0,
"European-Beech-tree-B", 10.0, 30, 0.0, 0.0, 0.0, 0.0,
"European-Oak-tree", 12.0, 30, 0.0, 0.0, 0.0, 0.0,
// Büsche (Priorität 25)
"Buesche-1", 3.0, 25, 90.0, 0.0, 0.0, -2.00,
"Buesche-2", 3.0, 25, 90.0, 0.0, 0.0, -2.00,
"Buesche-3", 3.0, 25, 90.0, 0.0, 0.0, -2.00,
"Buesche-4", 3.0, 25, 90.0, 0.0, 0.0, -2.00,
// Farne (Priorität 20)
"Fern-1", 1.2, 20, 90.0, 0.0, 0.0, -0.70,
"Fern-2", 1.2, 20, 90.0, 0.0, 0.0, -0.70,
"Fern-3", 1.2, 20, 90.0, 0.0, 0.0, -0.70,
"Fern-4", 1.2, 20, 90.0, 0.0, 0.0, -0.70,
// Todholz (Priorität 15)
"Todholz-1", 1.5, 15, 0.0, 0.0, 0.0, -1.30,
"Todholz-2", 1.5, 15, 0.0, 0.0, 0.0, -1.30,
"Todholz-3", 1.5, 15, 0.0, 0.0, 0.0, -1.30,
"Todholz-4", 1.5, 15, 0.0, 0.0, 0.0, -1.30,
// Gräser (Priorität 15)
"Grass-1", 0.5, 15, 0.0, 0.0, 0.0, -0.10,
"Grass-2", 0.5, 15, 0.0, 0.0, 0.0, -0.10,
"Grass-3", 0.5, 15, 0.0, 0.0, 0.0, -0.10,
"Grass-4", 0.5, 15, 0.0, 0.0, 0.0, -0.10,
// Steine (Priorität 10)
"Stone-1", 0.8, 10, 0.0, 0.0, 0.0, -0.40,
"Stone-2", 0.8, 10, 0.0, 0.0, 0.0, -0.40,
"Stone-3", 0.8, 10, 0.0, 0.0, 0.0, -0.40,
"Stone-4", 0.8, 10, 0.0, 0.0, 0.0, -0.40
];
// ===== TERRAIN-TEXTUREN =====
float TextureLow = 10.0;
float TextureHigh = 30.0;
key Terrain_Dirt = "1e78c37b-90a1-40d3-a913-31c965309679";
key Terrain_Grass = "a9b4d0d5-5679-4fd5-b9e3-ac4367ea9b95";
key Terrain_Mountain = "795204d3-bd28-467c-b526-08b95090841e";
key Terrain_Rock = "4b012b05-a357-46af-8e6e-e8a9aa501a8c";
// key Terrain_Dirt = "9dc31f61-e30b-44bf-91dc-e4f6a8735407";
// key Terrain_Grass = "16620c30-5efd-451a-ab10-6fb3e69ad59a";
// key Terrain_Mountain = "5abbc6ef-7cb9-4340-9950-6dc61b21c99c";
// key Terrain_Rock = "429726cd-34e4-4bc9-a669-a94f2f69f11e";
// ===== DIALOG-MENÜ VARIABLEN =====
integer MENU_CHANNEL;
integer listenHandle;
string MENU_TEXT = "\nWähle eine Aktion:";
list MENU_BUTTONS = ["? Pflanzen", "? Terrain", "❌ Schließen"];
// ===== HILFSFUNKTIONEN =====
float getObjectHeight(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 1);
return 4.0; // Fallback-Höhe
}
float getObjectRotationX(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 3);
return 0.0; // Fallback-Rotation
}
float getObjectRotationY(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 4);
return 0.0; // Fallback-Rotation
}
float getObjectRotationZ(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 5);
return 0.0; // Fallback-Rotation
}
float getObjectZOffset(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 6);
return 0.0; // Fallback-Z-Offset
}
rotation getRotation(float gradX, float gradY, float gradZ)
{
return llEuler2Rot(<gradX * DEG_TO_RAD, gradY * DEG_TO_RAD, gradZ * DEG_TO_RAD>);
}
// ===== TERRAIN-FUNKTIONEN =====
setTerrain()
{
list textures = [Terrain_Dirt, Terrain_Grass, Terrain_Mountain, Terrain_Rock];
integer types = 2;
osSetTerrainTextures(textures, types);
osSetTerrainTexture(0, Terrain_Dirt);
osSetTerrainTextureHeight(0, TextureLow, TextureHigh);
osSetTerrainTexture(1, Terrain_Grass);
osSetTerrainTextureHeight(1, TextureLow, TextureHigh);
osSetTerrainTexture(2, Terrain_Mountain);
osSetTerrainTextureHeight(2, TextureLow, TextureHigh);
osSetTerrainTexture(3, Terrain_Rock);
osSetTerrainTextureHeight(3, TextureLow, TextureHigh);
llModifyLand(LAND_NOISE, 0);
osTerrainFlush();
llOwnerSay("✅ Terrain-Texturen wurden gesetzt und aufgeraut!");
}
// ===== PFLANZEN-FUNKTIONEN =====
plantOneTree()
{
if (!gIntRunning) return;
if (gIntCurrentX >= gIntEndX || gIntCurrentY >= gIntEndY)
{
gIntRunning = FALSE;
llOwnerSay("✅ Pflanzvorgang abgeschlossen!");
llSetText("Fertig - Klicke für neues Menü", <0,1,0>, 1.0);
llSetTimerEvent(0.0);
return;
}
gIntX = gIntCurrentX;
gIntY = gIntCurrentY;
llSetRegionPos(<gIntX, gIntY, gIntZ>);
llSetText("X: " + (string)gIntX + " Y: " + (string)gIntY, <1,1,1>, 1.0);
// 1. Zufällig entscheiden, ob überhaupt etwas gepflanzt wird
float fltDoIRez = llFrand(1.0);
if (fltDoIRez >= gFltPlantProbability)
{
gIntCurrentY += PFLANZABSTAND;
if (gIntCurrentY >= gIntEndY)
{
gIntCurrentY = gIntStartY + (PFLANZABSTAND / 2);
gIntCurrentX += PFLANZABSTAND;
}
return;
}
// ===== GEWICHTETE AUSWAHL MIT ÜBERSPRINGEN FEHLENDER OBJEKTE =====
// 1. Liste der verfügbaren Objekte erstellen (nur die, die im Inventar sind)
list verfuegbareObjekte = [];
integer listLength = llGetListLength(ALLE_OBJEKTE) / 7;
integer i;
for (i = 0; i < listLength; i++)
{
string objName = llList2String(ALLE_OBJEKTE, i * 7);
// Prüfen, ob Objekt im Inventar existiert
if (llGetInventoryType(objName) == INVENTORY_OBJECT)
{
float objHeight = llList2Float(ALLE_OBJEKTE, (i * 7) + 1);
integer objPriority = llList2Integer(ALLE_OBJEKTE, (i * 7) + 2);
float objRotationX = llList2Float(ALLE_OBJEKTE, (i * 7) + 3);
float objRotationY = llList2Float(ALLE_OBJEKTE, (i * 7) + 4);
float objRotationZ = llList2Float(ALLE_OBJEKTE, (i * 7) + 5);
float objZOffset = llList2Float(ALLE_OBJEKTE, (i * 7) + 6);
verfuegbareObjekte += [objName, objHeight, objPriority, objRotationX, objRotationY, objRotationZ, objZOffset];
}
// Wenn nicht im Inventar: einfach überspringen
}
// Falls keine Objekte verfügbar sind
if (llGetListLength(verfuegbareObjekte) == 0)
{
llOwnerSay("⚠️ Keine Objekte im Inventar gefunden!");
gIntRunning = FALSE;
llSetTimerEvent(0.0);
return;
}
// 2. Gesamtgewicht der verfügbaren Objekte berechnen
integer availableCount = llGetListLength(verfuegbareObjekte) / 7;
integer totalWeight = 0;
for (i = 0; i < availableCount; i++)
{
totalWeight += llList2Integer(verfuegbareObjekte, (i * 7) + 2);
}
// 3. Zufällige Auswahl basierend auf Gewicht
integer randomWeight = (integer)llFrand(totalWeight);
integer selectedIndex = 0;
integer cumulativeWeight = 0;
for (i = 0; i < availableCount; i++)
{
integer priority = llList2Integer(verfuegbareObjekte, (i * 7) + 2);
cumulativeWeight += priority;
if (randomWeight < cumulativeWeight)
{
selectedIndex = i;
i = availableCount; // Schleife manuell beenden (statt break)
}
}
// 4. Objekt auswählen und pflanzen
string gStrObjectName = llList2String(verfuegbareObjekte, selectedIndex * 7);
float objectHeight = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 1);
float rotationX = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 3);
float rotationY = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 4);
float rotationZ = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 5);
float zOffset = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 6);
// Position und Rotation setzen
vector vecNowPos = llGetPos();
float fltLandHeight = osGetTerrainHeight((integer)vecNowPos.x, (integer)vecNowPos.y);
integer intNewX = (integer)vecNowPos.x;
integer intNewY = (integer)vecNowPos.y;
integer intNewZ = (integer)fltLandHeight;
vecNowPos = <intNewX, intNewY, intNewZ>;
llSetRegionPos(vecNowPos);
// Rotation basierend auf Objekt-Einstellungen
rotation objectRotation = getRotation(rotationX, rotationY, rotationZ);
// Objekt rezzieren mit Höhenanpassung
llRezObject(gStrObjectName,
llGetPos() + <0.0, 0.0, objectHeight + zOffset>,
<0.0, 0.0, 0.0>,
objectRotation,
0);
llModifyLand(LAND_NOISE, 0); // Dabei direkt das Terrain aufrauhen.
llSleep(0.1);
// Debug-Ausgabe (optional - auskommentieren wenn nicht benötigt)
// llOwnerSay("? " + gStrObjectName + " | X-Rot: " + (string)rotationX + "° | Y-Rot: " + (string)rotationY + "° | Z-Rot: " + (string)rotationZ + "° | Z-Off: " + (string)zOffset);
// Zur nächsten Position
gIntCurrentY += PFLANZABSTAND;
if (gIntCurrentY >= gIntEndY)
{
gIntCurrentY = gIntStartY + (PFLANZABSTAND / 2);
gIntCurrentX += PFLANZABSTAND;
}
}
startPlanting()
{
vector regionSize = osGetRegionSize();
gIntStartX = 0;
gIntStartY = 0;
gIntEndX = (integer)regionSize.x;
gIntEndY = (integer)regionSize.y;
gFltPlantProbability = .7; // 70% Wahrscheinlichkeit für Bewuchs
gIntZ = 0;
gIntRunning = TRUE;
gIntCurrentX = gIntStartX + (PFLANZABSTAND / 2);
gIntCurrentY = gIntStartY + (PFLANZABSTAND / 2);
// Anzahl der verfügbaren Objekte zählen
integer availableCount = 0;
integer listLength = llGetListLength(ALLE_OBJEKTE) / 7;
integer i;
for (i = 0; i < listLength; i++)
{
string objName = llList2String(ALLE_OBJEKTE, i * 7);
if (llGetInventoryType(objName) == INVENTORY_OBJECT)
availableCount++;
}
llOwnerSay("? Starte Pflanzvorgang mit " + (string)availableCount + " verfügbaren Objekten...");
llSetText("? Pflanze... Klicke zum Stoppen", <1,1,0>, 1.0);
llSetTimerEvent(2.0);
}
// ===== HAUPTTEIL DES SKRIPTS =====
default
{
state_entry()
{
MENU_CHANNEL = -1 - (integer)("0x" + llGetSubString((string)llGetKey(), -7, -1));
llOwnerSay("? Waldpflanzer bereit. Klicke für Menü.");
llSetText("Bereit - Klicke für Menü", <0,1,0>, 1.0);
llSetTimerEvent(0.0);
}
touch_start(integer num_detected)
{
if (gIntRunning)
{
gIntRunning = FALSE;
llSetTimerEvent(0.0);
llListenRemove(listenHandle);
llOwnerSay("⏹️ Pflanzvorgang wurde gestoppt!");
llSetText("Gestoppt - Klicke für neues Menü", <1,0,0>, 1.0);
return;
}
key toucher = llDetectedKey(0);
llListenRemove(listenHandle);
listenHandle = llListen(MENU_CHANNEL, "", toucher, "");
llDialog(toucher, MENU_TEXT, MENU_BUTTONS, MENU_CHANNEL);
llSetTimerEvent(60.0);
}
listen(integer channel, string name, key id, string message)
{
llSetTimerEvent(0.0);
llListenRemove(listenHandle);
if (message == "? Pflanzen")
{
llOwnerSay("? Starte Pflanzvorgang...");
startPlanting();
}
else if (message == "? Terrain")
{
setTerrain();
llDialog(id, "✅ Terrain-Texturen wurden aktualisiert.", ["OK"], MENU_CHANNEL);
}
else if (message == "❌ Schließen")
{
llOwnerSay("Menü geschlossen.");
}
else if (message == "OK")
{
llOwnerSay("✅ OK.");
}
}
timer()
{
if (gIntRunning)
{
plantOneTree();
}
else
{
llSetTimerEvent(0.0);
}
}
}
Fazit: Es wird ein Zufallswald erstellt und ist noch nicht ganz fertig, zur Demo ist das aber schon gut genug.
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.


![[-]](https://www.gridtalk.de/images/collapse.png)