-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathshortened description of timecube code.txt
312 lines (272 loc) · 11.6 KB
/
shortened description of timecube code.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
Okay, now let's go for a slightly more complicated code setup. I have the following index.html:
"""
<!DOCTYPE html>
<html>
<head>
<title>3D Video Timecube</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
#upload {
display: none;
}
#overlay {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5); /* semi-transparent black */
z-index: 9990; /* make sure it appears on top */
}
#loading-text {
color: white;
font-size: 2em;
}
</style>
</head>
<body>
<div id="upload">
<input type="file" id="plyFile" accept=".ply">
</div>
<div id="overlay">
<div id="loading-text">Loading...</div>
</div>
<div id="canvasContainer" style="position: absolute; top: 0; left: 0; pointer-events: none;"></div>
<script src="timecube_slicer.js"></script>
</body>
</html>
"""
and the corresponding "timecube_slicer.js" script [note that when I use "//...", it means there's a chunk of code in the actual file that I'm skipping over for brevity]:
"""
import * as THREE from 'three';
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import * as dat from 'dat.gui';
import { createGrid, findNearestNeighbor } from './nearestNeighbor.js'; //import custom code
import Stats from 'stats.js' //check framerate
//Declaring (most) global variables here
//...
let displayWidth = 100;
let displayWidthOriginal = displayWidth; //in order to reset display to original once we change it
let displayHeight = 100;
let displayHeightOriginal = displayHeight; //in order to reset display to original once we change it
let lowResWidth = displayWidth / 2;
let lowResHeight = displayHeight / 2;
let planeWidth = displayWidth;
let planeHeight = displayHeight;
//...
// Set up material variables here, so we can have fun messing with 'em :)
//...
// Scene, Camera, Renderer
//...
//create GUI
const gui = new dat.GUI();
//...
//instantiating fps counter if debugging mode is on
//...
//function for lowering resolution while plane is being moved
function doWhileMoving() {
planeIsMoving = true;
displayWidth = lowResWidth;
displayHeight = lowResHeight;
updateAfterMoving = true;
displayColors = new Array(lowResHeight).fill(0).map(() => new Array(lowResWidth).fill([0, 0, 0]));
}
// Let user upload their own .ply timecube files
function userPlyUploadOption() {
//...
// Load the PLY from the generated URL
loadPly(url);
});
// Let user upload .ply file of their choice
var params = {
loadFile : function() {
document.getElementById('plyFile').click();
}
};
// Add .ply loader to GUI
timecubeFolder.add(params, 'loadFile').name('Load Custom PLY File');
}
// Call the function
userPlyUploadOption();
// Set background color
//...
//add red cube in center for debugging + troubleshooting
//...
// create a 2D array to store the color values for each pixel in the GUI display:
let displayColors = new Array(displayHeight).fill(0).map(() => new Array(displayWidth).fill([0, 0, 0])); // Initialize to black
// Create a 2D canvas for the GUI
canvas = document.createElement('canvas');
canvas.width = displayWidth;
canvas.height = displayHeight;
document.body.appendChild(canvas);
ctx = canvas.getContext('2d');
//append the canvas to a <div> in the html instead of to the body
const canvasContainer = document.getElementById('canvasContainer');
canvasContainer.appendChild(canvas);
// Create a second canvas for buffering
let bufferCanvas = document.createElement('canvas');
bufferCanvas.width = displayWidth;
bufferCanvas.height = displayHeight;
let bufferCtx = bufferCanvas.getContext('2d');
// Create the texture here, after the canvas is created
planeTexture = new THREE.Texture(canvas);
planeTexture.minFilter = THREE.NearestFilter; // Disable minification filtering with THREE.NearestFilter
planeTexture.magFilter = THREE.NearestFilter; // Disable magnification filtering with THREE.NearestFilter
// Set up material setting for plane so it shows projection of the canvas
// (thereby showing the nearest neighbor pixels of point cloud).
//const planeMaterial = new THREE.MeshBasicMaterial({color: 'white', side: THREE.DoubleSide});
//const planeMaterial = new THREE.MeshBasicMaterial({ map: planeTexture, side: THREE.DoubleSide });
// Modify plane material to correct gamma miscalibration problem and display the same thing canvas does
const planeMaterial = new THREE.ShaderMaterial({
//...
});
//add intersecting plane to the scene
let planeGeometry = new THREE.PlaneGeometry(planeWidth, planeHeight); //width and height of plane
plane = new THREE.Mesh(planeGeometry, planeMaterial);
//have plane be child of planeContainer (an invisible point in the center of world)
const planeContainer = new THREE.Object3D(); //this is what we are rotating around
planeContainer.add(plane);
scene.add(planeContainer);
// Allow user to manipulate the location and visibility of the plane
//...
// Set up different material properties
//...
// Initialize material, should be basicMaterial normally
material = basicMaterial;
//.ply loader
function loadPly(url) {
// Remove the old Points object from the scene, if it exists
if (points) {
scene.remove(points);
points = null;
}
//load new ply file
const loader = new PLYLoader();
loader.load(url, function (geometry) {
// your original code here, minus the loader.load call
geometry.rotateZ(Math.PI);
geometry.center();
// Compute the bounding box
geometry.computeBoundingBox();
// Get dimensions
let bbox = geometry.boundingBox;
let width = bbox.max.x - bbox.min.x;
let height = bbox.max.y - bbox.min.y;
// [Add code here which makes the GUI and plane the new width and height of the bounding box]
plane.geometry.dispose();
plane.geometry = new THREE.PlaneGeometry(planeWidth, planeHeight);
plane = new THREE.Mesh(planeGeometry, planeMaterial);
displayColors = new Array(displayHeight).fill(0).map(() => new Array(displayWidth).fill([0, 0, 0]));
material.needsUpdate = true;
points = new THREE.Points(geometry, material);
scene.add(points);
// And make the scene update
doWhileMoving();
// Create the grid after the points have been added to the scene
grid = createGrid(points, cellSize);
}, undefined, function (error) {
console.error(error);
});
}
// Load a default PLY file from a URL when the script runs
loadPly(defaultPlyFile);
// This function updates the canvas
function updateCanvas() {
if (updateAfterMoving && !planeIsMoving) {
forceRefreshDisplay = true;
displayWidth = displayWidthOriginal;
displayHeight = displayHeightOriginal;
displayColors = new Array(displayHeight).fill(0).map(() => new Array(displayWidth).fill([0, 0, 0]));
updateAfterMoving = false;
}
if (points && (planeIsMoving || displayWidth !== displayWidthOriginal || displayHeight !== displayHeightOriginal || forceRefreshDisplay === true)) {
// Update displayColors at the current resolution
for (let y = 0; y < displayHeight; y++) {
for (let x = 0; x < displayWidth; x++) {
// Map the pixel coordinates to the corresponding coordinates on the plane in 3D space.
let localPos = new THREE.Vector3(
x / displayWidth * planeGeometry.parameters.width - planeGeometry.parameters.width / 2,
// Subtract y from displayHeight to flip the y-axis
(displayHeight - y) / displayHeight * planeGeometry.parameters.height - planeGeometry.parameters.height / 2,
//y / displayHeight * planeGeometry.parameters.height - planeGeometry.parameters.height / 2,
0
);
// Transform the local position to world space according to the plane's world matrix
let worldPos = localPos.applyMatrix4(plane.matrixWorld);
// //Find the vertex in the point cloud which is closest to the given coordinate
let closestVertexIndex = findNearestNeighbor(grid, worldPos, cellSize);
if (closestVertexIndex !== null) {
// Proceed with getting the color and updating displayColors
// Sample the RGB color value of the given vertex, to be stored in `color`
let color = new THREE.Color();
color.fromBufferAttribute(points.geometry.attributes.color, closestVertexIndex);
// Store the color in the displayColors array
displayColors[y][x] = [color.r, color.g, color.b];
} else {
//displayColors[y][x] = [1, 1, 1]; //Original, ChatGPT says I'm wrong tho lol
displayColors[y][x] = [0, 0, 0];
}
}
}
forceRefreshDisplay = false;
areArraysReady = true;
} else {areArraysReady = true;}
}
// This function draws the new frame
function drawFrame() {
if (areArraysReady) {
// Calculate scaling factors
let scaleX = canvas.width / displayWidth;
let scaleY = canvas.height / displayHeight;
// Code for drawing the new frame
// Draw the displayColors onto the canvas
if (areArraysReady) {
for (let y = 0; y < displayHeight; y++) {
for (let x = 0; x < displayWidth; x++) {
let [r, g, b] = displayColors[y][x];
// Apply gamma correction, becuase we need to do that for some reason
gammaPowerAmount = 1 / uniforms.gammaCorrection.value;
r = Math.pow(r, gammaPowerAmount);
g = Math.pow(g, gammaPowerAmount);
b = Math.pow(b, gammaPowerAmount);
//bufferCtx.fillStyle = `rgb(${Math.round(r*255)}, ${Math.round(g*255)}, ${Math.round(b*255)})`; //Original code
bufferCtx.fillStyle = `rgb(${r*255}, ${g*255}, ${b*255})`;
bufferCtx.fillRect(x * scaleX, y * scaleY, scaleX, scaleY); // Draw a rectangle for each pixel
}
}
// Only when you're done drawing, copy the content of the bufferCanvas onto the visible canvas
ctx.drawImage(bufferCanvas, 0, 0);
areArraysReady = false;
// Update the texture
planeTexture.needsUpdate = true;
}
}
}
// Animation loop
function animate() {
fpsCounter.begin()
//check if loading is finished or not
if (points){
document.getElementById('overlay').style.display = 'none'; // hide loading overlay
} else {
document.getElementById('overlay').style.display = 'flex';
}
//update and draw canvas on plane and GUI
updateCanvas();
drawFrame();
// Render the scene
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
// reset flag
if (planeIsMoving) {
planeIsMoving = false;
}
fpsCounter.end()
}
animate();