← BásicoObjeto 3Dfogon/mortero
Mortero
Arrastrá para girarlo// «Mortero» · gpt-6-sol (medium)
// Mortar («Mortero»). Be faithful to the reference: keep thin parts thin (1–2 blocks: strings, necks, handles, legs, rods), keep its exact proportions and silhouette, and every visible detail (books on shelves, sound holes, tuning pegs, valves). Don't make it chunkier than the reference.
// Olive-green mortar with a hollow muzzle, mounting hardware and two long braces.
const dark = "#303a2e", shadow = "#414c38", olive = "#566347";
const light = "#748065", edge = "#899078", metal = "#a0a493";
function mottled(base, brighter, chance = 0.12) {
return rand() < chance ? brighter : base;
}
// Low rectangular base plate, with clipped corners and stepped edges.
for (let z = 2; z <= 5; z++) {
const inset = z === 5 ? 2 : z === 4 ? 1 : 0;
for (let x = 10 + inset; x <= 31 - inset; x++)
for (let y = 17 + inset; y <= 35 - inset; y++) {
if ((x < 13 + inset || x > 28 - inset) &&
(y < 20 + inset || y > 32 - inset)) continue;
const c = z === 5 ? mottled(olive, light) :
z === 4 ? olive : z === 2 ? dark : shadow;
set(x, y, z, c);
}
}
for (let x = 13; x <= 28; x++) {
set(x, 19, 5, light);
set(x, 32, 5, shadow);
}
for (let y = 21; y <= 31; y++) set(12, y, 5, light);
// Raised fittings and scattered bolt heads on the plate.
box(16, 22, 6, 26, 30, 7, shadow);
box(18, 23, 8, 25, 29, 9, olive);
for (const [x, y] of [[14,21],[17,20],[27,20],[29,23],[13,29],[17,32],[28,31]]) {
box(x, y, 6, x + 1, y + 1, 6, dark);
set(x, y, 7, light);
}
// Two narrow splayed legs, with flat, weighted shoes.
line(21, 23, 17, 17, 8, 3, 0.85, shadow);
line(22, 24, 13, 17, 8, 3, 0.55, light);
line(25, 26, 21, 39, 14, 3, 0.85, shadow);
line(26, 26, 19, 39, 14, 3, 0.55, olive);
for (const [cx, cy] of [[17, 7], [39, 14]]) {
box(cx-3, cy-2, 0, cx+3, cy+2, 1, dark);
box(cx-2, cy-2, 2, cx+2, cy+2, 2, olive);
box(cx-2, cy-2, 3, cx+2, cy-2, 3, light);
box(cx-1, cy-1, 3, cx+1, cy+1, 4, shadow);
}
// Slender diagonal stays and the lower cradle.
line(16, 25, 7, 21, 25, 19, 0.75, olive);
line(28, 27, 7, 25, 28, 20, 0.75, shadow);
line(17, 28, 8, 22, 27, 17, 0.55, light);
box(19, 23, 9, 25, 28, 12, dark);
box(20, 23, 12, 25, 28, 13, olive);
// The barrel rises at an angle toward the rear.
function barrelCenter(z) {
return [22 + 0.19 * (z - 11), 24 + 0.23 * (z - 11)];
}
for (let z = 11; z <= 39; z++) {
const [cx, cy] = barrelCenter(z);
const r = z < 17 ? 2.15 : z < 35 ? 3.05 : 3.65;
for (let x = Math.floor(cx-r); x <= Math.ceil(cx+r); x++)
for (let y = Math.floor(cy-r); y <= Math.ceil(cy+r); y++) {
const d = Math.hypot(x-cx, y-cy);
if (d > r) continue;
let c = x < cx-1 || y < cy-1 ? light :
x > cx+1 || y > cy+1 ? shadow : olive;
if (rand() < 0.11) c = rand() < 0.5 ? shadow : edge;
if (z >= 36 && d > 2.15) c = x < cx || y < cy ? edge : olive;
set(x, y, z, c);
}
}
// Distinct dark bore and thick, pale, slightly uneven muzzle lip.
for (let z = 37; z <= 40; z++) {
const [cx, cy] = barrelCenter(z);
for (let x = Math.floor(cx-4); x <= Math.ceil(cx+4); x++)
for (let y = Math.floor(cy-4); y <= Math.ceil(cy+4); y++) {
const d = Math.hypot(x-cx, y-cy);
if (d <= 1.85) {
set(x, y, z, z < 39 ? "#171f1c" : "#222b26");
} else if (d <= 3.8 && z >= 39) {
set(x, y, z, x < cx || y < cy ? metal : light);
}
}
}
const [mx, my] = barrelCenter(40);
for (let x = Math.floor(mx-2); x <= Math.ceil(mx+2); x++)
for (let y = Math.floor(my-2); y <= Math.ceil(my+2); y++)
if (Math.hypot(x-mx,y-my) < 1.8) set(x,y,40,"#111b19");
// Bands and small stepped highlights along the barrel.
for (const z of [18, 25, 33]) {
const [cx, cy] = barrelCenter(z);
line(cx-3, cy-1, z, cx-2, cy+2, z, 0, shadow);
set(cx-3, cy, z+1, light);
}
// Side-mounted elevation housings: compact boxes and visible pale axle ends.
box(16, 26, 21, 19, 30, 25, dark);
box(16, 26, 23, 19, 29, 26, olive);
box(16, 26, 26, 19, 29, 26, light);
box(15, 27, 22, 15, 28, 24, shadow);
set(15, 27, 24, metal);
box(29, 28, 22, 31, 31, 25, shadow);
box(29, 28, 25, 31, 30, 25, light);
set(32, 29, 23, metal);
set(32, 29, 24, edge);
// Small adjustment rods and pins around the cradle.
line(17, 28, 20, 20, 27, 15, 0, dark);
line(30, 29, 22, 27, 28, 15, 0, dark);
box(18, 20, 8, 19, 21, 11, shadow);
set(18, 20, 11, light);
box(27, 22, 7, 28, 24, 10, olive);
set(28, 22, 10, edge);import { draw } from "./assets/voxel";
import { mortero } from "./assets/mortero";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, mortero, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: lo que mide en px su lado más largo
// angle: giro sobre el piso, en radianes
// t: segundos; si se mueve, se anima solo con eso# En una terminal, en la carpeta del juego
fogon assets add mortero
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["mortero"] })
# Queda en client/assets/mortero.ts
# https://fogon.page/object/mortero