Discusión

VISOR DE CONCEPTOS 3D SANDWORM MK-X

De Wikiprompt, la enciclopedia libre de prompts

AYi
Contribuido porAYiXFuente

29 sept 2026

VISOR DE CONCEPTOS 3D SANDWORM MK-X Un aviso completo para generar una aplicación HTML de un solo archivo y autocontenida usando Three.js, que presente un visor interactivo de diseño conceptual 3D de un gusano de arena mecánico con especificaciones detalladas de visuales, entorno, HUD y controles.

Contenido del PromptGuardar

🌐
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SANDWORM MK-X · Estudio de diseño</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #EDE6D8; font-family: 'Courier New', Courier, monospace; overflow: hidden; } #info { position: absolute; top: 10px; left: 10px; z-index: 100; background: rgba(237, 230, 216, 0.85); padding: 8px 12px; border: 1px solid #2a2a2a; font-size: 12px; letter-spacing: 1px; color: #2a2a2a; pointer-events: none; backdrop-filter: blur(2px); } #hud { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 50; display: flex; flex-direction: column; justify-content: space-between; padding: 20px 30px; } .panel { background: rgba(237, 230, 216, 0.7); border: 1px solid #3a3a3a; padding: 12px 15px; font-size: 11px; color: #1a1a1a; box-shadow: 2px 2px 0 rgba(0,0,0,0.05); backdrop-filter: blur(2px); letter-spacing: 0.5px; } .panel-title { font-weight: bold; font-size: 12px; border-bottom: 1px solid #3a3a3a; padding-bottom: 4px; margin-bottom: 8px; display: flex; justify-content: space-between; } .grid-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; } .label { color: #5a5a5a; } .value { font-weight: bold; color: #1a1a1a; } .accent-orange { color: #C45A2C; } .accent-cyan { color: #1B7F8A; } #top-left { position: absolute; top: 20px; left: 30px; width: 260px; } #top-right { position: absolute; top: 20px; right: 30px; width: 240px; } #bottom-left { position: absolute; bottom: 20px; left: 30px; width: 260px; } #bottom-center { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); width: 500px; display: flex; gap: 10px; } #bottom-right { position: absolute; bottom: 20px; right: 30px; width: 220px; } #controls { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 200; display: flex; gap: 10px; background: rgba(237,230,216,0.8); padding: 8px 15px; border: 1px solid #3a3a3a; backdrop-filter: blur(4px); pointer-events: auto; } .btn { background: transparent; border: 1px solid #3a3a3a; color: #1a1a1a; padding: 4px 10px; font-family: inherit; font-size: 11px; cursor: pointer; letter-spacing: 1px; transition: all 0.2s; } .btn:hover { background: #d4c9b2; } .btn.active { background: #C45A2C; color: #EDE6D8; border-color: #C45A2C; } #status-line { position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); font-size: 10px; color: #3a3a3a; letter-spacing: 2px; background: rgba(237,230,216,0.6); padding: 2px 12px; border: 1px solid #aaa; white-space: nowrap; } #xray-toggle, #pause-toggle, #pose-toggle { pointer-events: auto; } .legend { display: flex; gap: 15px; margin-top: 8px; font-size: 10px; } .legend-item { display: flex; align-items: center; gap: 4px; } .legend-color { width: 12px; height: 12px; border: 1px solid #333; } .svg-line { stroke: #2a2a2a; stroke-width: 0.5; fill: none; } .annotation { position: absolute; font-size: 10px; color: #1a1a1a; background: rgba(237,230,216,0.5); padding: 2px 6px; border-left: 2px solid #C45A2C; pointer-events: none; } #annotations { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; } .coord-ruler { position: absolute; bottom: 30px; right: 30px; width: 200px; height: 20px; border-bottom: 1px solid #2a2a2a; display: flex; justify-content: space-between; font-size: 9px; color: #2a2a2a; padding-left: 5px; padding-right: 5px; } .coord-ruler span { transform: translateY(12px); } </style> </head> <body> <div id="info">SANDWORM MK-X · SUBTERRANEAN TERRAIN ADAPTIVE EXCAVATION UNIT</div> <div id="controls"> <button class="btn" data-view="front">FRONT</button> <button class="btn" data-view="side">SIDE</button> <button class="btn" data-view="aerial">AERIAL</button> <button class="btn" data-view="chase">CHASE</button> <button class="btn" data-view="outpost">OUTPOST</button> <button class="btn" data-view="orbit">ORBIT</button> <span style="width:10px;"></span> <button class="btn" id="xray-toggle">X-RAY</button> <button class="btn" id="pause-toggle">PAUSE</button> <button class="btn" id="pose-toggle">REFERENCE POSE</button> </div> <div id="status-line">FRONT VIEW · UNDER WAY · SURFACE PLOUGH</div> <div id="hud"> <div id="top-left" class="panel"> <div class="panel-title">PARAMETERS <span style="font-weight:normal;">MK-X</span></div> <div class="grid-2col"> <span class="label">CLASS</span><span class="value">EXCAVATION</span> <span class="label">LENGTH</span><span class="value">1,312 m</span> <span class="label">DIAMETER</span><span class="value">187 m</span> <span class="label">MASS</span><span class="value">~2.4 Mt</span> <span class="label">PROPULSION</span><span class="value">Peristaltic</span> <span class="label">POWER PLANT</span><span class="value">Fusion core</span> <span class="label">DEPTH CAP.</span><span class="value">1,200 m</span> <span class="label">CREW</span><span class="value">NONE (AUTO)</span> </div> <div style="margin-top:10px; border:1px solid #aaa; height:60px; display:flex; align-items:center; justify-content:center; font-size:10px; color:#555;"> ▣ DEPLOYMENT MAP (MINI) </div> </div> <div id="top-right" class="panel"> <div class="panel-title">HEAD SECTION · CROSS VIEW</div> <svg width="100%" height="80" viewBox="0 0 100 80"> <circle cx="50" cy="40" r="30" fill="none" stroke="#2a2a2a" stroke-width="0.8"/> <circle cx="50" cy="40" r="20" fill="none" stroke="#2a2a2a" stroke-width="0.5"/> <circle cx="50" cy="40" r="10" fill="none" stroke="#2a2a2a" stroke-width="0.5"/> <line x1="50" y1="10" x2="50" y2="70" stroke="#C45A2C" stroke-width="0.5"/> <line x1="20" y1="40" x2="80" y2="40" stroke="#C45A2C" stroke-width="0.5"/> <text x="50" y="8" font-size="6" text-anchor="middle" fill="#1a1a1a">ROTARY RING</text> <text x="50" y="76" font-size="6" text-anchor="middle" fill="#1a1a1a">SCALE 1:500</text> </svg> <div style="font-size:10px; margin-top:6px;"> <div>· CONCENTRIC DRILL RINGS</div> <div>· RADIAL TEETH (TUNGSTEN)</div> <div>· MODULAR JAW ASSEMBLY</div> <div>· TERRAIN SENSOR SUITE</div> </div> </div> <div id="bottom-left" class="panel"> <div class="panel-title">TERRAIN & ROUTE</div> <svg width="100%" height="70" viewBox="0 0 120 70"> <path d="M10,60 Q30,30 50,50 T90,20 T115,40" fill="none" stroke="#2a2a2a" stroke-width="0.8" stroke-dasharray="3,2"/> <circle cx="10" cy="60" r="2" fill="#C45A2C"/> <circle cx="115" cy="40" r="2" fill="#C45A2C"/> <text x="10" y="68" font-size="6" fill="#1a1a1a">START</text> <text x="100" y="35" font-size="6" fill="#1a1a1a">END</text> <path d="M20,10 L25,15 M30,20 L35,25" stroke="#aaa" stroke-width="0.5"/> </svg> <div style="display:flex; justify-content:space-between; font-size:9px; margin-top:4px;"> <span>CONTOUR 5m</span><span>TRACK LIVE</span> </div> </div> <div id="bottom-center"> <div class="panel" style="flex:1;"> <div class="panel-title">BODY PROFILE · SIDE</div> <svg width="100%" height="50" viewBox="0 0 200 50"> <rect x="10" y="20" width="180" height="10" fill="none" stroke="#2a2a2a" stroke-width="0.8"/> <line x1="30" y1="20" x2="30" y2="30" stroke="#2a2a2a" stroke-width="0.5"/> <line x1="80" y1="20" x2="80" y2="30" stroke="#2a2a2a" stroke-width="0.5"/> <line x1="130" y1="20" x2="130" y2="30" stroke="#2a2a2a" stroke-width="0.5"/> <line x1="170" y1="20" x2="170" y2="30" stroke="#2a2a2a" stroke-width="0.5"/> <text x="20" y="15" font-size="6">TAIL</text> <text x="70" y="15" font-size="6">POWER</text> <text x="120" y="15" font-size="6">ACTUATION</text> <text x="160" y="15" font-size="6">HEAD</text> <text x="10" y="45" font-size="6">0m</text> <text x="190" y="45" font-size="6">1312m</text> </svg> </div> <div class="panel" style="flex:1;"> <div class="panel-title">SEGMENT DETAIL · TYPICAL RING</div> <svg width="100%" height="50" viewBox="0 0 100 50"> <circle cx="30" cy="25" r="12" fill="none" stroke="#2a2a2a" stroke-width="0.8"/> <circle cx="30" cy="25" r="8" fill="none" stroke="#2a2a2a" stroke-width="0.5"/> <circle cx="30" cy="25" r="4" fill="none" stroke="#2a2a2a" stroke-width="0.5"/> <line x1="50" y1="15" x2="70" y2="10" stroke="#C45A2C" stroke-width="0.5"/> <text x="72" y="10" font-size="6">1</text> <line x1="50" y1="25" x2="70" y2="25" stroke="#C45A2C" stroke-width="0.5"/> <text x="72" y="25" font-size="6">2</text> <line x1="50" y1="35" x2="70" y2="40" stroke="#C45A2C" stroke-width="0.5"/> <text x="72" y="40" font-size="6">3</text> </svg> </div> </div> <div id="bottom-right" class="panel"> <div class="panel-title">LOCOMOTION CYCLE</div> <svg width="100%" height="40" viewBox="0 0 100 40"> <path d="M10,20 Q20,10 30,20 T50,20 T70,20 T90,20" fill="none" stroke="#2a2a2a" stroke-width="0.8"/> <circle cx="20" cy="15" r="2" fill="#C45A2C"/> <circle cx="50" cy="20" r="2" fill="#C45A2C"/> <circle cx="80" cy="15" r="2" fill="#C45A2C"/> <text x="10" y="35" font-size="6">PHASE 1</text> <text x="40" y="35" font-size="6">PHASE 2</text> <text x="70" y="35" font-size="6">PHASE 3</text> </svg> </div> </div> <div id="annotations"> <div class="annotation" style="top:30%; left:5%;">SEGMENTED TORSO RINGS</div> <div class="annotation" style="top:40%; left:5%;">DORSAL MAINTENANCE HATCHES</div> <div class="annotation" style="top:50%; left:5%;">SAND-FLOW INTAKE VENTS</div> <div class="annotation" style="top:60%; left:5%;">THERMAL EXHAUST PORTS</div> <div class="annotation" style="top:30%; right:5%;">MODULAR JAW ASSEMBLY</div> <div class="annotation" style="top:40%; right:5%;">PRIMARY EXCAVATION RING</div> <div class="annotation" style="top:50%; right:5%;">TERRAIN SENSOR SUITE</div> </div> <div class="coord-ruler"> <span>0</span><span>500m</span><span>1000m</span><span>1312m</span> </div> <!-- Three.js will be injected here --> <div id="canvas-container" style="position: absolute; top:0; left:0; width:100%; height:100%; z-index:1;"></div> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.128.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.128.0/examples/jsm/" } } </script> <script type="module"> import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; // --- setup scene --- const scene = new THREE.Scene(); scene.background = new THREE.Color(0xEDE6D8); scene.fog = new THREE.Fog(0xEDE6D8, 30, 80); const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200); camera.position.set(20, 12, 25); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; document.getElementById('canvas-container').appendChild(renderer.domElement); const labelRenderer = new CSS2DRenderer(); labelRenderer.setSize(window.innerWidth, window.innerHeight); labelRenderer.domElement.style.position = 'absolute'; labelRenderer.domElement.style.top = '0px'; labelRenderer.domElement.style.left = '0px'; labelRenderer.domElement.style.pointerEvents = 'none'; document.getElementById('canvas-container').appendChild(labelRenderer.domElement); const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; controls.maxPolarAngle = Math.PI / 2.2; controls.minDistance = 5; controls.maxDistance = 60; // --- lights --- const sunLight = new THREE.DirectionalLight(0xfff5e6, 1.2); sunLight.position.set(20, 30, 10); sunLight.castShadow = true; sunLight.shadow.mapSize.width = 1024; sunLight.shadow.mapSize.height = 1024; scene.add(sunLight); const hemiLight = new THREE.HemisphereLight(0xEDE6D8, 0x8a7a5a, 0.6); scene.add(hemiLight); // --- procedural sand dunes --- const groundGeo = new THREE.PlaneGeometry(80, 80, 128, 128); const groundMat = new THREE.MeshStandardMaterial({ color: 0xCBBFA8, roughness: 0.9, metalness: 0.0 }); const ground = new THREE.Mesh(groundGeo, groundMat); ground.rotation.x = -Math.PI / 2; ground.position.y = -0.5; ground.receiveShadow = true; scene.add(ground); // modify vertices for dunes const pos = groundGeo.attributes.position; for (let i = 0; i < pos.count; i++) { const x = pos.getX(i); const y = pos.getY(i); let z = Math.sin(x * 0.3) * 1.5 + Math.cos(y * 0.4) * 1.2 + Math.sin(x * 0.8 + y * 0.6) * 0.8; pos.setZ(i, z); } groundGeo.computeVertexNormals(); // --- sandworm body (procedural rings) --- const wormGroup = new THREE.Group(); scene.add(wormGroup); const ringCount = 48; const rings = []; const curvePoints = []; for (let i = 0; i < ringCount; i++) { const t = i / (ringCount - 1); // path: sine wave in x-z plane, y variation const x = (t - 0.5) * 20; const y = Math.sin(t * Math.PI * 4) * 1.2 + 0.5; const z = Math.sin(t * Math.PI * 2) * 2.0; curvePoints.push(new THREE.Vector3(x, y, z)); } const curve = new THREE.CatmullRomCurve3(curvePoints); // create ring meshes const ringMaterial = new THREE.MeshStandardMaterial({ color: 0xD8CFC0, roughness: 0.7, metalness: 0.1 }); const darkMaterial = new THREE.MeshStandardMaterial({ color: 0x3a3a3a, roughness: 0.9 }); for (let i = 0; i < ringCount; i++) { const t = i / (ringCount - 1); const point = curve.getPoint(t); const tangent = curve.getTangent(t).normalize(); // ring geometry const radius = 1.2 - t * 0.5; // taper tail const ringGeo = new THREE.CylinderGeometry(radius, radius, 0.5, 12); const ring = new THREE.Mesh(ringGeo, ringMaterial); ring.position.copy(point); ring.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), tangent); ring.castShadow = true; ring.receiveShadow = true; wormGroup.add(ring); rings.push(ring); // dark gap between rings const gapGeo = new THREE.CylinderGeometry(radius * 0.95, radius * 0.95, 0.08, 8); const gap = new THREE.Mesh(gapGeo, darkMaterial); const offset = tangent.clone().multiplyScalar(0.3); gap.position.copy(point).add(offset); gap.quaternion.copy(ring.quaternion); wormGroup.add(gap); } // head section (excavation front) const headGroup = new THREE.Group(); const headPos = curve.getPoint(0); const headTangent = curve.getTangent(0).normalize(); headGroup.position.copy(headPos); headGroup.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), headTangent); wormGroup.add(headGroup); // main excavation ring const headRing = new THREE.Mesh(new THREE.TorusGeometry(1.4, 0.15, 8, 20), ringMaterial); headRing.rotation.x = Math.PI / 2; headRing.position.z = 0.8; headGroup.add(headRing); // inner rings for (let j = 0; j < 3; j++) { const innerRing = new THREE.Mesh(new THREE.TorusGeometry(1.0 - j*0.25, 0.08, 6, 16), ringMaterial); innerRing.rotation.x = Math.PI / 2; innerRing.position.z = 0.8 + j * 0.25; headGroup.add(innerRing); } // teeth / blades for (let k = 0; k < 8; k++) { const angle = (k / 8) * Math.PI * 2; const tooth = new THREE.Mesh(new THREE.ConeGeometry(0.15, 0.6, 4), darkMaterial); tooth.position.set(Math.cos(angle) * 1.2, Math.sin(angle) * 1.2, 1.2); tooth.rotation.z = angle; headGroup.add(tooth); } // tail fin const tailPos = curve.getPoint(1); const tailTangent = curve.getTangent(1).normalize(); const tailFin = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.8, 0.2), ringMaterial); tailFin.position.copy(tailPos).add(tailTangent.clone().multiplyScalar(0.5)); tailFin.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), tailTangent); wormGroup.add(tailFin); // --- outpost buildings (scale props) --- const buildingMat = new THREE.MeshStandardMaterial({ color: 0xB0A58A, roughness: 0.8 }); for (let i = 0; i < 4; i++) { const building = new THREE.Mesh(new THREE.BoxGeometry(0.8, 1.5, 0.8), buildingMat); building.position.set(-6 + i * 1.8, 0.2, 6 + Math.sin(i)*1.5); building.castShadow = true; building.receiveShadow = true; scene.add(building); } // seismic station const station = new THREE.Mesh(new THREE.CylinderGeometry(0.3, 0.5, 0.4, 6), darkMaterial); station.position.set(3, 0.2, -4); scene.add(station); // rocks const rockMat = new THREE.MeshStandardMaterial({ color: 0x8a7a5a, roughness: 0.9 }); for (let r = 0; r < 10; r++) { const rock = new THREE.Mesh(new THREE.DodecahedronGeometry(0.2 + Math.random()*0.3), rockMat); rock.position.set(Math.random()*12-6, 0.1, Math.random()*12-6); rock.rotation.set(Math.random(), Math.random(), Math.random()); scene.add(rock); } // --- ornithopter drones (simple) --- const droneMat = new THREE.MeshStandardMaterial({ color: 0xC45A2C, emissive: 0x332200 }); for (let d = 0; d < 3; d++) { const drone = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.1, 0.6), droneMat); drone.position.set(-2 + d*2, 4 + Math.sin(d)*0.5, 2); drone.castShadow = true; scene.add(drone); } // --- animation state --- let isPaused = false; let isXRay = false; let isReferencePose = false; let time = 0; const clock = new THREE.Clock(); // --- UI events --- document.querySelectorAll('.btn[data-view]').forEach(btn => { btn.addEventListener('click', () => { const view = btn.dataset.view; let targetPos, targetLook; switch(view) { case 'front': targetPos = new THREE.Vector3(0, 2, 15); targetLook = new THREE.Vector3(0, 1, 0); break; case 'side': targetPos = new THREE.Vector3(15, 2, 0); targetLook = new THREE.Vector3(0, 1, 0); break; case 'aerial': targetPos = new THREE.Vector3(0, 18, 0.1); targetLook = new THREE.Vector3(0, 0, 0); break; case 'chase': targetPos = new THREE.Vector3(0, 3, 8); targetLook = new THREE.Vector3(0, 1, 0); break; case 'outpost': targetPos = new THREE.Vector3(-6, 1.5, 8); targetLook = new THREE.Vector3(0, 1, 0); break; case 'orbit': targetPos = new THREE.Vector3(10, 6, 10); targetLook = new THREE.Vector3(0, 1, 0); break; } // simple lerp animation const startPos = camera.position.clone(); const startTarget = controls.target.clone(); const duration = 1000; const startTime = performance.now(); function animateCam() { const elapsed = performance.now() - startTime; const t = Math.min(elapsed / duration, 1); const ease = t * t * (3 - 2 * t); camera.position.lerpVectors(startPos, targetPos, ease); controls.target.lerpVectors(startTarget, targetLook, ease); if (t < 1) requestAnimationFrame(animateCam); } animateCam(); document.getElementById('status-line').textContent = view.toUpperCase() + ' VIEW · UNDER WAY · SURFACE PLOUGH'; }); }); document.getElementById('xray-toggle').addEventListener('click', () => { isXRay = !isXRay; document.getElementById('xray-toggle').classList.toggle('active', isXRay); rings.forEach(ring => { if (isXRay) { ring.material.color.setHex(0x1B7F8A); ring.material.transparent = true; ring.material.opacity = 0.4; } else { ring.material.color.setHex(0xD8CFC0); ring.material.transparent = false; ring.material.opacity = 1.0; } }); }); document.getElementById('pause-toggle').addEventListener('click', () => { isPaused = !isPaused; document.getElementById('pause-toggle').classList.toggle('active', isPaused); }); document.getElementById('pose-toggle').addEventListener('click', () => { isReferencePose = !isReferencePose; document.getElementById('pose-toggle').classList.toggle('active', isReferencePose); // straighten worm if (isReferencePose) { rings.forEach((ring, idx) => { const t = idx / (ringCount - 1); ring.position.set((t - 0.5) * 20, 1.0, 0); ring.quaternion.identity(); }); headGroup.position.set(-10, 1.0, 0); headGroup.quaternion.identity(); } else { // restore curve rings.forEach((ring, idx) => { const t = idx / (ringCount - 1); const p = curve.getPoint(t); const tan = curve.getTangent(t).normalize(); ring.position.copy(p); ring.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0), tan); }); const hp = curve.getPoint(0); const ht = curve.getTangent(0).normalize(); headGroup.position.copy(hp); headGroup.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0), ht); } }); // --- animation loop --- function animate() { requestAnimationFrame(animate); if (!isPaused) { time += clock.getDelta(); // peristaltic wave rings.forEach((ring, idx) => { const t = idx / (ringCount - 1); const offset = Math.sin(time * 3 + t * Math.PI * 6) * 0.2; if (!isReferencePose) { const p = curve.getPoint(t); ring.position.y = p.y + offset; } }); } controls.update(); renderer.render(scene, camera); labelRenderer.render(scene, camera); } animate(); // --- resize --- window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); labelRenderer.setSize(window.innerWidth, window.innerHeight); }); // initial status document.getElementById('status-line').textContent = 'FRONT VIEW · UNDER WAY · SURFACE PLOUGH'; </script> </body> </html>

Iniciá sesión para ver el prompt completo

Continuar con:

Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad

Uso

Este prompt está diseñado para usarse con creative. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.

Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.

Referencias

Categorías:creative| twitter| three-js| 3d-modeling

Discusión