← Nature3D assetfogon/planta-tropical
Planta tropical
Drag to turn it// «Planta tropical» · gpt-6-sol (medium)
// A big tropical plant with very large broad glossy green leaves (monstera style with splits), fanning out from the ground.
// A ground-level crown of glossy, split tropical leaves.
const C = {
soil: "#805533",
soilLight: "#a77342",
soilDark: "#65432e",
stem: "#318b39",
stemLight: "#57b94a",
underside: "#247b35",
shade: "#29953b",
green: "#39aa42",
bright: "#4fc651",
sunlit: "#69d55a",
rim: "#81df68",
vein: "#a6e874"
};
cylinder(24, 24, 0, 5, 2, C.soil);
ellipsoid(24, 24, 2, 4, 4, 2, C.soilLight);
for (let i = 0; i < 35; i++) {
const a = rand() * Math.PI * 2;
const r = 2 + rand() * 3;
set(24 + Math.cos(a) * r, 24 + Math.sin(a) * r, 1,
rand() < 0.6 ? C.soilDark : "#679c3d");
}
cylinder(24, 24, 2, 2, 9, C.stem);
cylinder(24, 24, 7, 1, 7, C.stemLight);
function growLeaf(angle, reach, baseZ, tipZ, width, phase) {
const dx = Math.cos(angle), dy = Math.sin(angle);
const sx = -dy, sy = dx;
const rootR = 4.2;
function point(t, u) {
const r = rootR + (reach - rootR) * t;
const drift = 1.1 * Math.sin(Math.PI * t + phase) * Math.sin(Math.PI * t);
const x = 24 + dx * r + sx * (u + drift);
const y = 24 + dy * r + sy * (u + drift);
const z = baseZ + (tipZ - baseZ) * t
+ 2.3 * Math.sin(Math.PI * t)
- 0.065 * Math.abs(u) ** 1.45
+ 0.55 * Math.sin(5 * Math.PI * t + phase) * Math.abs(u) / width;
return [Math.round(x), Math.round(y), Math.round(z)];
}
// Curved, sturdy petiole underneath the blade.
const jointX = 24 + dx * 1.5, jointY = 24 + dy * 1.5;
const midX = 24 + dx * 3.1, midY = 24 + dy * 3.1;
const rootX = 24 + dx * rootR, rootY = 24 + dy * rootR;
line(24, 24, 4, jointX, jointY, 8, 1, C.stem);
line(jointX, jointY, 8, midX, midY, baseZ - 4, 1, C.stemLight);
line(midX, midY, baseZ - 4, rootX, rootY, baseZ, 1, C.stem);
function halfWidth(t) {
return Math.max(0.18, width * Math.pow(Math.sin(Math.PI * t), 0.72));
}
function isCut(t, fraction, side) {
// Three deep, tapered cuts from each edge.
for (let i = 0; i < 3; i++) {
const center = [0.34, 0.57, 0.78][i]
+ (side < 0 ? 0.035 : 0);
const depth = Math.max(0, (fraction - 0.46) / 0.54);
if (Math.abs(t - center) < 0.078 * Math.pow(depth, 0.8))
return true;
}
// Open oval fenestrations inside the leaf.
const holes = [
[0.27, 0.51, 0.057, 0.18],
[0.46, -0.50, 0.068, 0.18],
[0.66, 0.49, 0.065, 0.17]
];
for (const [ht, hu, tr, ur] of holes) {
if (((t - ht) / tr) ** 2 + ((side * fraction - hu) / ur) ** 2 < 1)
return true;
}
return false;
}
// Sample densely so the sloping voxel leaf stays continuous.
for (let t = 0; t <= 1.001; t += 0.013) {
const w = halfWidth(t);
for (let u = -w; u <= w + 0.01; u += 0.29) {
const f = Math.abs(u) / w;
if (f > 1 || isCut(t, f, Math.sign(u) || 1)) continue;
const [x, y, z] = point(t, u);
set(x, y, z - 1, C.underside);
let color = C.green;
if (f > 0.8) color = C.bright;
if (f < 0.28 || y < 24) color = C.bright;
if (f < 0.17 && rand() < 0.55) color = C.sunlit;
if (f > 0.82 && rand() < 0.35) color = C.rim;
if (rand() < 0.055) color = C.shade;
set(x, y, z, color);
}
}
// Raised central rib, with short diagonal veins that stop before the cuts.
for (let t = 0.06; t < 0.94; t += 0.022) {
const [x, y, z] = point(t, 0);
set(x, y, z + 1, t < 0.75 ? C.vein : C.sunlit);
}
for (const t0 of [0.18, 0.37, 0.55, 0.72]) {
for (const side of [-1, 1]) {
for (let k = 0; k <= 11; k++) {
const f = 0.09 + k * 0.055;
const t = t0 + k * 0.006;
if (isCut(t, f, side)) continue;
const [x, y, z] = point(t, side * halfWidth(t) * f);
if (get(x, y, z)) set(x, y, z + 1, C.sunlit);
}
}
}
}
// Back leaves first, then the broad leaves fanning toward the viewer.
growLeaf( 2.38, 20, 15, 38, 8.0, 0.2);
growLeaf( 1.53, 19, 14, 36, 7.8, 1.1);
growLeaf( 0.76, 20, 15, 39, 8.5, 2.0);
growLeaf( Math.PI, 19, 13, 31, 8.2, 0.7);
growLeaf( 0.03, 19, 13, 32, 8.2, 1.7);
growLeaf(-2.34, 20, 14, 36, 8.7, 0.5);
growLeaf(-0.78, 20, 14, 37, 8.7, 1.5);
growLeaf(-Math.PI / 2, 21, 12, 31, 9.0, 2.3);
// A fresh, narrower leaf emerging through the middle of the crown.
line(24, 24, 7, 25, 23, 27, 1, C.stemLight);
ellipsoid(25, 23, 29, 2, 2, 8, C.bright);
line(25, 22, 23, 25, 22, 36, 0, C.vein);
// Small leaves hide the junction of the stalks at ground level.
for (let i = 0; i < 7; i++) {
const a = i * Math.PI * 2 / 7;
const x = 24 + Math.cos(a) * 4;
const y = 24 + Math.sin(a) * 4;
line(24, 24, 3, x, y, 5, 1, C.stem);
ellipsoid(x, y, 5, 2, 2, 2, i % 2 ? C.bright : C.green);
set(x, y, 7, C.sunlit);
}import { draw } from "./assets/voxel";
import { plantaTropical } from "./assets/planta-tropical";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, plantaTropical, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add planta-tropical
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["planta-tropical"] })
# It lands in client/assets/planta-tropical.ts
# https://fogon.page/object/planta-tropical