ThorAILabs commited on
Commit
04fb0ab
·
verified ·
1 Parent(s): acf2d73

refactor... yet again

Browse files
Files changed (1) hide show
  1. index.html +240 -226
index.html CHANGED
@@ -1,238 +1,252 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>3D Scene Editor with Raytracing & Import/Export</title>
7
-
8
- <!-- Tailwind CSS -->
9
  <script src="https://cdn.tailwindcss.com"></script>
10
-
11
- <!-- Font Awesome -->
12
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13
-
14
- <!-- Three.js -->
15
- <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
16
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
17
-
18
- <!-- Loaders & Exporters -->
19
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/STLLoader.js"></script>
20
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/OBJLoader.js"></script>
21
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/exporters/STLExporter.js"></script>
22
- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/exporters/OBJExporter.js"></script>
23
- </head>
24
- <body class="bg-gray-900 h-screen overflow-hidden">
25
- <div class="flex h-screen">
26
- <!-- Sidebar -->
27
- <div class="w-64 bg-gray-800 p-4 text-white flex flex-col gap-4">
28
- <h2 class="text-xl font-bold mb-4"><i class="fas fa-cube mr-2"></i>Scene Controls</h2>
29
-
30
- <div class="flex flex-col gap-2">
31
- <button onclick="addCube()" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded">
32
- <i class="fas fa-cube mr-2"></i>Add Cube
33
- </button>
34
- <button onclick="addSphere()" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded">
35
- <i class="fas fa-globe mr-2"></i>Add Sphere
36
- </button>
37
-
38
- <!-- Import Section -->
39
- <div class="mt-4 pt-4 border-t border-gray-700">
40
- <button onclick="document.getElementById('stlInput').click()" class="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded w-full">
41
- <i class="fas fa-file-import mr-2"></i>Import STL
42
- </button>
43
- <input type="file" id="stlInput" accept=".stl" hidden>
44
-
45
- <button onclick="document.getElementById('objInput').click()" class="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded w-full mt-2">
46
- <i class="fas fa-file-import mr-2"></i>Import OBJ
47
- </button>
48
- <input type="file" id="objInput" accept=".obj" hidden>
49
- </div>
50
-
51
- <!-- Export Section -->
52
- <div class="mt-4 pt-4 border-t border-gray-700">
53
- <button onclick="exportSTL()" class="bg-pink-600 hover:bg-pink-700 px-4 py-2 rounded w-full">
54
- <i class="fas fa-file-export mr-2"></i>Export STL
55
- </button>
56
- <button onclick="exportOBJ()" class="bg-pink-600 hover:bg-pink-700 px-4 py-2 rounded w-full mt-2">
57
- <i class="fas fa-file-export mr-2"></i>Export OBJ
58
- </button>
59
- </div>
60
-
61
- <button onclick="raytrace()" class="bg-indigo-600 hover:bg-indigo-700 px-4 py-2 rounded mt-4">
62
- <i class="fas fa-raygun mr-2"></i>Raytrace Scene
63
- </button>
64
- </div>
65
  </div>
66
-
67
- <!-- 3D Viewport -->
68
- <div id="viewport" class="flex-1"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  </div>
70
 
71
- <script>
72
- let scene, camera, renderer, controls;
73
- let raytraceMode = false;
74
-
75
- function init() {
76
- // Main scene setup
77
- scene = new THREE.Scene();
78
- camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
79
- renderer = new THREE.WebGLRenderer({ antialias: true });
80
- renderer.setSize(window.innerWidth - 256, window.innerHeight);
81
- renderer.setPixelRatio(window.devicePixelRatio);
82
- document.getElementById('viewport').appendChild(renderer.domElement);
83
-
84
- // Lighting
85
- const ambientLight = new THREE.AmbientLight(0x404040);
86
- scene.add(ambientLight);
87
- const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
88
- directionalLight.position.set(5, 5, 5);
89
- scene.add(directionalLight);
90
-
91
- // Controls
92
- controls = new THREE.OrbitControls(camera, renderer.domElement);
93
- camera.position.z = 5;
94
-
95
- // Ground plane
96
- const planeGeometry = new THREE.PlaneGeometry(10, 10);
97
- const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x444444 });
98
- const plane = new THREE.Mesh(planeGeometry, planeMaterial);
99
- plane.rotation.x = -Math.PI / 2;
100
- scene.add(plane);
101
-
102
- animate();
103
- }
104
-
105
- function animate() {
106
- requestAnimationFrame(animate);
107
- controls.update();
108
- renderer.render(scene, camera);
109
- }
110
-
111
- function addCube() {
112
- const geometry = new THREE.BoxGeometry();
113
- const material = new THREE.MeshStandardMaterial({
114
- color: new THREE.Color(Math.random(), Math.random(), Math.random()),
115
- metalness: 0.3,
116
- roughness: 0.8
117
- });
118
- const cube = new THREE.Mesh(geometry, material);
119
- cube.position.set(Math.random() * 5 - 2.5, 0.5, Math.random() * 5 - 2.5);
120
- scene.add(cube);
121
- }
122
-
123
- function addSphere() {
124
- const geometry = new THREE.SphereGeometry(0.5, 32, 32);
125
- const material = new THREE.MeshStandardMaterial({
126
- color: new THREE.Color(Math.random(), Math.random(), Math.random()),
127
- metalness: 0.8,
128
- roughness: 0.2
129
- });
130
- const sphere = new THREE.Mesh(geometry, material);
131
- sphere.position.set(Math.random() * 5 - 2.5, 0.5, Math.random() * 5 - 2.5);
132
- scene.add(sphere);
133
- }
134
 
135
- function handleSTLUpload(event) {
136
- const file = event.target.files[0];
137
- if (!file) return;
138
-
139
- const reader = new FileReader();
140
- reader.onload = function(e) {
141
- const loader = new THREE.STLLoader();
142
- const geometry = loader.parse(e.target.result);
143
- const material = new THREE.MeshStandardMaterial({
144
- color: new THREE.Color(Math.random(), Math.random(), Math.random()),
145
- metalness: 0.3,
146
- roughness: 0.8
147
- });
148
- const mesh = new THREE.Mesh(geometry, material);
149
- mesh.position.set(0, 0, 0);
150
- scene.add(mesh);
151
- };
152
- reader.readAsArrayBuffer(file);
153
- }
154
-
155
- function handleOBJUpload(event) {
156
- const file = event.target.files[0];
157
- if (!file) return;
158
-
159
- const reader = new FileReader();
160
- reader.onload = function(e) {
161
- const loader = new THREE.OBJLoader();
162
- const obj = loader.parse(e.target.result);
163
-
164
- obj.traverse(child => {
165
- if (child.isMesh) {
166
- child.material = new THREE.MeshStandardMaterial({
167
- color: new THREE.Color(Math.random(), Math.random(), Math.random()),
168
- metalness: 0.3,
169
- roughness: 0.8
170
- });
171
- }
172
- });
173
-
174
- scene.add(obj);
175
- };
176
- reader.readAsText(file);
177
- }
178
-
179
- function exportSTL() {
180
- const exporter = new THREE.STLExporter();
181
- const result = exporter.parse(scene);
182
-
183
- const blob = new Blob([result], { type: 'text/plain' });
184
- const link = document.createElement('a');
185
- link.href = URL.createObjectURL(blob);
186
- link.download = 'scene.stl';
187
- link.click();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }
189
-
190
- function exportOBJ() {
191
- const exporter = new THREE.OBJExporter();
192
- const result = exporter.parse(scene);
193
-
194
- const blob = new Blob([result], { type: 'text/plain' });
195
- const link = document.createElement('a');
196
- link.href = URL.createObjectURL(blob);
197
- link.download = 'scene.obj';
198
- link.click();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
-
201
- function raytrace() {
202
- const raytracer = new THREE.WebGLRenderer();
203
- const rtCanvas = raytracer.domElement;
204
-
205
- const clonedScene = scene.clone();
206
- const clonedCamera = camera.clone();
207
-
208
- clonedScene.traverse(child => {
209
- if (child.isMesh) {
210
- child.material = new THREE.MeshBasicMaterial({
211
- color: child.material.color,
212
- wireframe: false
213
- });
214
- }
215
- });
216
-
217
- raytracer.setSize(window.innerWidth - 256, window.innerHeight);
218
- raytracer.render(clonedScene, clonedCamera);
219
-
220
- document.getElementById('viewport').replaceChild(rtCanvas, renderer.domElement);
221
- renderer = raytracer;
222
  }
223
-
224
- // Initialize on load
225
- window.onload = function() {
226
- init();
227
- document.getElementById('stlInput').addEventListener('change', handleSTLUpload);
228
- document.getElementById('objInput').addEventListener('change', handleOBJUpload);
229
- };
230
-
231
- window.addEventListener('resize', () => {
232
- camera.aspect = (window.innerWidth - 256) / window.innerHeight;
233
- camera.updateProjectionMatrix();
234
- renderer.setSize(window.innerWidth - 256, window.innerHeight);
235
- });
 
 
 
 
 
 
236
  </script>
237
- </body>
238
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>3D Scene Editor with Raytracing</title>
7
+ <!-- Tailwind CSS CDN -->
 
8
  <script src="https://cdn.tailwindcss.com"></script>
9
+ <!-- FontAwesome CDN -->
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-yHzo+M3GtT8OqysHzf3R+KwV/B+8/7ICiwAdgZrmEMXn1x5slIYi9PNRbE2LkoOmnJ5cZtL7B5ZpCyVQV3K8kw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
11
+ <!-- Three.js and Helpers -->
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r146/three.min.js"></script>
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r146/examples/js/controls/OrbitControls.js"></script>
14
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r146/examples/js/loaders/STLLoader.js"></script>
15
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r146/examples/js/loaders/OBJLoader.js"></script>
16
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r146/examples/js/exporters/STLExporter.js"></script>
17
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r146/examples/js/exporters/OBJExporter.js"></script>
18
+ <style>
19
+ /* Remove margin and set canvas to full screen */
20
+ body, html {
21
+ margin: 0;
22
+ padding: 0;
23
+ height: 100%;
24
+ overflow: hidden;
25
+ }
26
+ #editorCanvas {
27
+ display: block;
28
+ }
29
+ </style>
30
+ </head>
31
+ <body class="bg-gray-900">
32
+ <!-- Toolbar -->
33
+ <div class="absolute top-4 left-4 z-10 space-y-4">
34
+ <div class="bg-gray-800 bg-opacity-80 rounded p-4 shadow-lg">
35
+ <h1 class="text-white text-lg font-bold mb-2"><i class="fas fa-cube"></i> 3D Scene Editor</h1>
36
+ <div class="space-x-2">
37
+ <!-- Toggle Raytracing -->
38
+ <button id="toggleRaytrace" class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded shadow">
39
+ <i class="fas fa-lightbulb"></i> Toggle Raytrace
40
+ </button>
41
+ <!-- Add Light -->
42
+ <select id="lightType" class="bg-gray-700 text-white rounded px-2 py-1">
43
+ <option value="ambient">Ambient Light</option>
44
+ <option value="point">Point Light</option>
45
+ <option value="directional">Directional Light</option>
46
+ </select>
47
+ <button id="addLight" class="bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded shadow">
48
+ <i class="fas fa-plus"></i> Add Light
49
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  </div>
51
+ </div>
52
+ <!-- File Operations -->
53
+ <div class="bg-gray-800 bg-opacity-80 rounded p-4 shadow-lg">
54
+ <h2 class="text-white text-md font-semibold mb-2"><i class="fas fa-file"></i> Load / Export Files</h2>
55
+ <div class="mb-2">
56
+ <input type="file" id="fileInput" class="bg-gray-700 text-white rounded px-2 py-1" accept=".stl,.obj" />
57
+ </div>
58
+ <div class="space-x-2">
59
+ <select id="exportType" class="bg-gray-700 text-white rounded px-2 py-1">
60
+ <option value="stl">Export as STL</option>
61
+ <option value="obj">Export as OBJ</option>
62
+ </select>
63
+ <button id="exportScene" class="bg-purple-500 hover:bg-purple-600 text-white px-3 py-1 rounded shadow">
64
+ <i class="fas fa-download"></i> Export
65
+ </button>
66
+ </div>
67
+ </div>
68
  </div>
69
 
70
+ <!-- Three.js Canvas -->
71
+ <canvas id="editorCanvas"></canvas>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
+ <!-- Script -->
74
+ <script>
75
+ let scene, camera, renderer, controls;
76
+ let raytraceOn = true; // state for raytrace mode
77
+
78
+ // Store original materials to toggle between modes
79
+ const originalMaterials = new Map();
80
+
81
+ // Initialize scene, camera, renderer
82
+ function init() {
83
+ const canvas = document.getElementById('editorCanvas');
84
+ renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
85
+ renderer.setSize(window.innerWidth, window.innerHeight);
86
+ renderer.setPixelRatio(window.devicePixelRatio);
87
+ renderer.setClearColor(0x222222);
88
+
89
+ scene = new THREE.Scene();
90
+
91
+ camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
92
+ camera.position.set(3, 3, 3);
93
+ scene.add(camera);
94
+
95
+ // Add a default ambient light
96
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
97
+ scene.add(ambientLight);
98
+
99
+ // Add grid helper to orient the scene
100
+ const gridHelper = new THREE.GridHelper(10, 10);
101
+ scene.add(gridHelper);
102
+
103
+ // Add OrbitControls
104
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
105
+
106
+ // Start render loop
107
+ animate();
108
+
109
+ // Window resize listener
110
+ window.addEventListener('resize', onWindowResize, false);
111
+ }
112
+
113
+ function onWindowResize() {
114
+ camera.aspect = window.innerWidth / window.innerHeight;
115
+ camera.updateProjectionMatrix();
116
+ renderer.setSize(window.innerWidth, window.innerHeight);
117
+ }
118
+
119
+ // Render loop
120
+ function animate() {
121
+ requestAnimationFrame(animate);
122
+ controls.update();
123
+ renderer.render(scene, camera);
124
+ }
125
+
126
+ // Toggle raytracing mode by switching materials.
127
+ function toggleRaytrace() {
128
+ raytraceOn = !raytraceOn;
129
+ scene.traverse((child) => {
130
+ if (child.isMesh) {
131
+ if (raytraceOn) {
132
+ // Store original material if not stored yet
133
+ if (!originalMaterials.has(child)) {
134
+ originalMaterials.set(child, child.material);
135
+ }
136
+ // Use a physical material to simulate raytraced appearance
137
+ child.material = new THREE.MeshPhysicalMaterial({
138
+ color: child.material.color ? child.material.color.getHex() : 0xffffff,
139
+ metalness: 0.5,
140
+ roughness: 0.1,
141
+ clearcoat: 1.0,
142
+ clearcoatRoughness: 0.1,
143
+ });
144
+ } else {
145
+ // revert to original material
146
+ if (originalMaterials.has(child)) {
147
+ child.material = originalMaterials.get(child);
148
+ }
149
+ }
150
+ }
151
+ });
152
+ }
153
+
154
+ // Add light to scene from the select dropdown
155
+ function addLight() {
156
+ const type = document.getElementById('lightType').value;
157
+ let light;
158
+ switch (type) {
159
+ case "ambient":
160
+ light = new THREE.AmbientLight(0xffffff, 0.5);
161
+ break;
162
+ case "point":
163
+ light = new THREE.PointLight(0xffffff, 1);
164
+ light.position.set(2, 2, 2);
165
+ break;
166
+ case "directional":
167
+ light = new THREE.DirectionalLight(0xffffff, 1);
168
+ light.position.set(5, 5, 5);
169
+ break;
170
+ default:
171
+ light = new THREE.AmbientLight(0xffffff, 0.5);
172
  }
173
+ scene.add(light);
174
+ }
175
+
176
+ // Handle file loading for STL and OBJ files
177
+ function handleFile(files) {
178
+ if (files.length === 0) return;
179
+ const file = files[0];
180
+ const reader = new FileReader();
181
+ reader.addEventListener("load", function (event) {
182
+ const contents = event.target.result;
183
+ let loader;
184
+ const extension = file.name.split('.').pop().toLowerCase();
185
+ if (extension === 'stl') {
186
+ loader = new THREE.STLLoader();
187
+ const geometry = loader.parse(contents);
188
+ const material = new THREE.MeshStandardMaterial({ color: 0x606060 });
189
+ const mesh = new THREE.Mesh(geometry, material);
190
+ mesh.name = file.name;
191
+ mesh.position.set(0, 0, 0);
192
+ scene.add(mesh);
193
+ } else if (extension === 'obj') {
194
+ loader = new THREE.OBJLoader();
195
+ const object = loader.parse(contents);
196
+ object.name = file.name;
197
+ scene.add(object);
198
+ } else {
199
+ alert("Unsupported file type. Please load an STL or OBJ file.");
200
+ }
201
+ }, false);
202
+
203
+ // Read file as appropriate (for STL use arraybuffer; for OBJ use text)
204
+ if (file.name.endsWith(".stl")) {
205
+ reader.readAsArrayBuffer(file);
206
+ } else {
207
+ reader.readAsText(file);
208
  }
209
+ }
210
+
211
+ // Export scene objects as STL or OBJ
212
+ function exportScene() {
213
+ const exportType = document.getElementById("exportType").value;
214
+ let exportedData;
215
+ if (exportType === "stl") {
216
+ const exporter = new THREE.STLExporter();
217
+ // Export only mesh objects (ignoring helpers)
218
+ const meshes = [];
219
+ scene.traverse((child) => {
220
+ if (child.isMesh) {
221
+ meshes.push(child);
222
+ }
223
+ });
224
+ exportedData = exporter.parse(meshes);
225
+ downloadFile(exportedData, "scene.stl", "application/sla");
226
+ } else if (exportType === "obj") {
227
+ const exporter = new THREE.OBJExporter();
228
+ exportedData = exporter.parse(scene);
229
+ downloadFile(exportedData, "scene.obj", "text/plain");
 
230
  }
231
+ }
232
+
233
+ // Utility to force download
234
+ function downloadFile(data, filename, type) {
235
+ const blob = new Blob([data], { type: type });
236
+ const link = document.createElement("a");
237
+ link.href = window.URL.createObjectURL(blob);
238
+ link.download = filename;
239
+ link.click();
240
+ }
241
+
242
+ // Event listeners
243
+ document.getElementById('toggleRaytrace').addEventListener('click', toggleRaytrace);
244
+ document.getElementById('addLight').addEventListener('click', addLight);
245
+ document.getElementById('fileInput').addEventListener('change', (e) => handleFile(e.target.files));
246
+ document.getElementById('exportScene').addEventListener('click', exportScene);
247
+
248
+ // Initialize the scene
249
+ init();
250
  </script>
251
+ </body>
252
  </html>