Органайзер для кошачьего корма: 3D-модель, STL для печати, просмотрщик

This commit is contained in:
apuc committed 2026-09-29 12:43:43 +03:00
commit bd44338cbd
19 files changed
+61107

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Органайзер — оригинал</title>
<style>
body { margin:0; overflow:hidden; font-family:'Segoe UI',Arial,sans-serif; background:#1a1d24; color:#e8e8e8; }
#info { position:absolute; top:12px; left:12px; z-index:10; background:rgba(20,24,32,.85);
padding:10px 14px; border-radius:8px; font-size:13px; line-height:1.5; max-width:300px;
border:1px solid rgba(255,255,255,.12); backdrop-filter:blur(4px); }
#info b { color:#8ecae6; }
#hint { position:absolute; bottom:12px; left:50%; transform:translateX(-50%); z-index:10;
font-size:12px; color:#9aa3b2; background:rgba(20,24,32,.7); padding:6px 12px; border-radius:20px; }
canvas { display:block; }
</style>
</head>
<body>
<div id="info"><b>Органайзер — оригинал</b><br>Коробка: 131.5 × 156.2 × 200 мм<br>Крышка: верх с ушками + низ с защёлками<br>С боковой конструкцией (как в файле)</div>
<div id="hint">ЛКМ — вращение · колесо — масштаб · ПКМ — сдвиг</div>
<script type="importmap">
{ "imports": {
"three": "./vendor/three.module.js",
"three/addons/": "./vendor/examples/jsm/"
}}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1d24);
const camera = new THREE.PerspectiveCamera(45, innerWidth/innerHeight, 1, 3000);
camera.position.set(320, 260, 380);
const renderer = new THREE.WebGLRenderer({ antialias:true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 110, 0);
controls.enableDamping = true;
scene.add(new THREE.HemisphereLight(0xffffff, 0x444455, 0.9));
const sun = new THREE.DirectionalLight(0xffffff, 1.8);
sun.castShadow = true;
scene.add(sun);
scene.add(sun.target);
const fill = new THREE.DirectionalLight(0x88aaff, 0.35);
fill.position.set(-250, 150, -200); scene.add(fill);
const grid = new THREE.GridHelper(400, 20, 0x3a4150, 0x2a2f3a);
grid.position.y = -1; scene.add(grid);
const b64 = "Z2xURgIAAACw+QMAXAkAAEpTT057InNjZW5lIjowLCJzY2VuZXMiOlt7Im5vZGVzIjpbMCwxLDJdfV0sImFzc2V0Ijp7InZlcnNpb24iOiIyLjAiLCJnZW5lcmF0b3IiOiJodHRwczovL2dpdGh1Yi5jb20vbWlrZWRoL3RyaW1lc2gifSwiYWNjZXNzb3JzIjpbeyJjb21wb25lbnRUeXBlIjo1MTI1LCJ0eXBlIjoiU0NBTEFSIiwiYnVmZmVyVmlldyI6MCwiY291bnQiOjI1NTY2LCJtYXgiOls0MjYwXSwibWluIjpbMF19LHsiY29tcG9uZW50VHlwZSI6NTEyNiwidHlwZSI6IlZFQzMiLCJieXRlT2Zmc2V0IjowLCJidWZmZXJWaWV3IjoxLCJjb3VudCI6NDI2MSwibWF4IjpbNjUuNzY2MDA2NDY5NzI2NTYsNzguMDgwNTUxMTQ3NDYwOTQsMjAwLjBdLCJtaW4iOlstNjUuNzY2MDA2NDY5NzI2NTYsLTc4LjA4MDU1MTE0NzQ2MDk0LDAuMF19LHsiY29tcG9uZW50VHlwZSI6NTEyMSwibm9ybWFsaXplZCI6dHJ1ZSwidHlwZSI6IlZFQzQiLCJieXRlT2Zmc2V0IjowLCJidWZmZXJWaWV3IjoyLCJjb3VudCI6NDI2MSwibWF4IjpbMTQyLDIwMiwyMzAsMjU1XSwibWluIjpbMTQyLDIwMiwyMzAsMjU1XX0seyJjb21wb25lbnRUeXBlIjo1MTI1LCJ0eXBlIjoiU0NBTEFSIiwiYnVmZmVyVmlldyI6MywiY291bnQiOjE3MTYsIm1heCI6WzI4N10sIm1pbiI6WzBdfSx7ImNvbXBvbmVudFR5cGUiOjUxMjYsInR5cGUiOiJWRUMzIiwiYnl0ZU9mZnNldCI6MCwiYnVmZmVyVmlldyI6NCwiY291bnQiOjI4OCwibWF4IjpbNTAuMTAwMDA2MTAzNTE1NjI1LDc1LjA5OTk5ODQ3NDEyMTEsMjA5LjBdLCJtaW4iOlstNTAuMTAwMDA2MTAzNTE1NjI1LC03NS4wOTk5OTg0NzQxMjExLDIwMC4wXX0seyJjb21wb25lbnRUeXBlIjo1MTIxLCJub3JtYWxpemVkIjp0cnVlLCJ0eXBlIjoiVkVDNCIsImJ5dGVPZmZzZXQiOjAsImJ1ZmZlclZpZXciOjUsImNvdW50IjoyODgsIm1heCI6WzE2OCwyMTgsMjIwLDI1NV0sIm1pbiI6WzE2OCwyMTgsMjIwLDI1NV19LHsiY29tcG9uZW50VHlwZSI6NTEyNSwidHlwZSI6IlNDQUxBUiIsImJ1ZmZlclZpZXciOjYsImNvdW50IjoxMTQyNCwibWF4IjpbMTkwNV0sIm1pbiI6WzBdfSx7ImNvbXBvbmVudFR5cGUiOjUxMjYsInR5cGUiOiJWRUMzIiwiYnl0ZU9mZnNldCI6MCwiYnVmZmVyVmlldyI6NywiY291bnQiOjE5MDYsIm1heCI6WzUyLjUsNzcuNSwyMzMuMF0sIm1pbiI6Wy01Mi41LC03Ny41LDIwOS4wXX0seyJjb21wb25lbnRUeXBlIjo1MTIxLCJub3JtYWxpemVkIjp0cnVlLCJ0eXBlIjoiVkVDNCIsImJ5dGVPZmZzZXQiOjAsImJ1ZmZlclZpZXciOjgsImNvdW50IjoxOTA2LCJtYXgiOlsyNTUsMTgzLDMsMjU1XSwibWluIjpbMjU1LDE4MywzLDI1NV19XSwibWVzaGVzIjpbeyJuYW1lIjoiZ2VvbWV0cnlfMCIsImV4dHJhcyI6eyJ1bml0cyI6Im1pbGxpbWV0ZXIiLCJwcm9jZXNzZWQiOnRydWV9LCJwcmltaXRpdmVzIjpbeyJhdHRyaWJ1dGVzIjp7IlBPU0lUSU9OIjoxLCJDT0xPUl8wIjoyfSwiaW5kaWNlcyI6MCwibW9kZSI6NH1dfSx7Im5hbWUiOiJnZW9tZXRyeV8xIiwiZXh0cmFzIjp7InVuaXRzIjoibWlsbGltZXRlciIsInByb2Nlc3NlZCI6dHJ1ZX0sInByaW1pdGl2ZXMiOlt7ImF0dHJpYnV0ZXMiOnsiUE9TSVRJT04iOjQsIkNPTE9SXzAiOjV9LCJpbmRpY2VzIjozLCJtb2RlIjo0fV19LHsibmFtZSI6Imdlb21ldHJ5XzIiLCJleHRyYXMiOnsidW5pdHMiOiJtaWxsaW1ldGVyIiwicHJvY2Vzc2VkIjp0cnVlfSwicHJpbWl0aXZlcyI6W3siYXR0cmlidXRlcyI6eyJQT1NJVElPTiI6NywiQ09MT1JfMCI6OH0sImluZGljZXMiOjYsIm1vZGUiOjR9XX1dLCJub2RlcyI6W3sibmFtZSI6Imdlb21ldHJ5XzAiLCJtZXNoIjowfSx7Im5hbWUiOiJnZW9tZXRyeV8xIiwibWVzaCI6MX0seyJuYW1lIjoiZ2VvbWV0cnlfMiIsIm1lc2giOjJ9XSwiYnVmZmVycyI6W3siYnl0ZUxlbmd0aCI6MjU4MTA0fV0sImJ1ZmZlclZpZXdzIjpbeyJidWZmZXIiOjAsImJ5dGVPZmZzZXQiOjAsImJ5dGVMZW5ndGgiOjEwMjI2NH0seyJidWZmZXIiOjAsImJ5dGVPZmZzZXQiOjEwMjI2NCwiYnl0ZUxlbmd0aCI6NTExMzJ9LHsiYnVmZmVyIjowLCJieXRlT2Zmc2V0IjoxNTMzOTYsImJ5dGVMZW5ndGgiOjE3MDQ0fSx7ImJ1ZmZlciI6MCwiYnl0ZU9mZnNldCI6MTcwNDQwLCJieXRlTGVuZ3RoIjo2ODY0fSx7ImJ1ZmZlciI6MCwiYnl0ZU9mZnNldCI6MTc3MzA0LCJieXRlTGVuZ3RoIjozNDU2fSx7ImJ1ZmZlciI6MCwiYnl0ZU9mZnNldCI6MTgwNzYwLCJieXRlTGVuZ3RoIjoxMTUyfSx7ImJ1ZmZlciI6MCwiYnl0ZU9mZnNldCI6MTgxOTEyLCJieXRlTGVuZ3RoIjo0NTY5Nn0seyJidWZmZXIiOjAsImJ5dGVPZmZzZXQiOjIyNzYwOCwiYnl0ZUxlbmd0aCI6MjI4NzJ9LHsiYnVmZmVyIjowLCJieXRlT2Zmc2V0IjoyNTA0ODAsImJ5dGVMZW5ndGgiOjc2MjR9XX0gIDjwAwBCSU4AAAAAAAEAAAACAAAAAgAAAAEAAAADAAAAAgAAAAMAAAAEAAAABAAAAAMAAAAFAAAABAAAAAUAAAAGAAAAAAAAAAcAAAABAAAAAQAAAAcAAAAIAAAACAAAAAcAAAAJAAAACQAAAAcAAAAKAAAACQAAAAoAAAALAAAACQAAAAsAAAAMAAAADAAAAAsAAAANAAAADAAAAA0AAAAOAAAADgAAAA0AAAAPAAAADgAAAA8AAAAQAAAAEAAAAA8AAAARAAAAEAAAABEAAAASAAAAEgAAABEAAAATAAAAEgAAABMAAAAUAAAAFAAAABMAAAAVAAAAFAAAABUAAAAWAAAAFgAAABUAAAAXAAAAFgAAABcAAAAYAAAAGAAAABcAAAAZAAAAGAAAABkAAAAaAAAAGgAAABkAAAAbAAAAGgAAABsAAAAcAAAAHAAAABsAAAAdAAAAHAAAAB0AAAAeAAAAHgAAAB0AAAAfAAAAHgAAAB8AAAAgAAAAIAAAAB8AAAAhAAAAIAAAACEAAAAiAAAAIgAAACEAAAAjAAAAIgAAACMAAAAkAAAAJAAAACMAAAAlAAAAJAAAACUAAAAmAAAAJgAAACUAAAAnAAAAJwAAACUAAAAoAAAAJwAAACgAAAApAAAAKgAAACsAAAApAAAAKQAAACsAAAAsAAAAKQAAACwAAAAnAAAAKwAAACoAAAAtAAAALQAAACoAAAAuAAAALQAAAC4AAAAvAAAAMAAAADEAAAAvAAAALwAAADEAAAAyAAAALwAAADIAAAAtAAAAMAAAADMAAAAxAAAAMQAAADMAAAA0AAAANAAAADMAAAA1AAAANAAAADUAAAA2AAAANgAAADUAAAA3AAAANgAAADcAAAA4AAAAOAAAADcAAAA5AAAAOAAAADkAAAA6AAAAOgAAADkAAAA7AAAAOgAAADsAAAA8AAAAPAAAADsAAAA9AAAAPAAAAD0AAAA+AAAAPgAAAD0AAAA/AAAAPgAAAD8AAABAAAAAQAAAAD8AAABBAAAAQAAAAEEAAABCAAAAQgAAAEEAAABDAAAAQgAAAEMAAABEAAAARAAAAEMAAABFAAAARAAAAEUAAABGAAAARgAAAEUAAABHAAAARgAAAEcAAABIAAAASAAAAEcAAABJAAAASAAAAEkAAABKAAAASgAAAEkAAABLAAAASgAAAEsAAABMAAAATAAAAEsAAABNAAAATAAAAE0AAABOAAAATgAAAE0AAAAGAAAABgAAAE0AAABPAAAABgAAAE8AAAAEAAAAAAAAAAIAAABJAAAASQAAAAIAAAAEAAAASQAAAAQAAABPAAAATwAAAE0AAABJAAAASQAAAE0AAABLAAAARwAAABMAAABJAAAASQAAABMAAAARAAAASQAAABEAAAAPAAAAEwAAAEcAAAAVAAAAFQAAAEcAAABFAAAAFQAAAEUAAAAXAAAAFwAAAEUAAABDAAAAFwAAAEMAAAAZAAAAGQAAAEMAAABBAAAAGQAAAEEAAAAbAAAAGwAAAEEAAAA/AAAAGwAAAD8AAAAdAAAAHQAAAD8AAAA9AAAAHQAAAD0AAAAfAAAAHwAAAD0AAAA7AAAAHwAAADsAAAAhAAAAIQAAADsAAAA5AAAAIQAAADkAAAAjAAAAIwAAADkAAAA3AAAAIwAAADcAAAAlAAAAJQAAADcAAAAoAAAAKAAAADcAAAA1AAAAKAAAADUAAAApAAAAKQAAADUAAAAqAAAAKgAAADUAAALine truncated
const bin = atob(b64);
const arr = new Uint8Array(bin.length);
for (let i=0;i<bin.length;i++) arr[i] = bin.charCodeAt(i);
new GLTFLoader().parse(arr.buffer, '', (gltf) => {
const model = gltf.scene;
model.traverse(o => { if (o.isMesh) { o.castShadow = true; o.receiveShadow = true; } });
scene.add(model);
const box = new THREE.Box3().setFromObject(model);
const c = box.getCenter(new THREE.Vector3());
controls.target.copy(c);
camera.position.copy(c).add(new THREE.Vector3(320, 260, 380));
}, (e) => console.error(e));
window.addEventListener('resize', () => {
camera.aspect = innerWidth/innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
function animate(){ requestAnimationFrame(animate); controls.update();
sun.position.copy(camera.position).add(new THREE.Vector3(60, 80, 40));
sun.target.position.copy(controls.target);
renderer.render(scene, camera); }
animate();
</script>
</body>
</html>