Update outline renderer

This commit is contained in:
Tom 2024-10-12 15:10:10 +01:00
parent d70a8c5b3e
commit 740ca32920
11 changed files with 297 additions and 84 deletions

View File

@ -25,7 +25,7 @@ head: |
</script> </script>
<script src="/assets/js/outline-model-viewer/index.js" type="module"></script> <script src="/assets/js/outline-model-viewer/index.js" type="module"></script>
--- ---
<outline-model-viewer model = "/assets/projects/bike_lights/models/bigger.glb" zoom=37> <outline-model-viewer model = "/assets/projects/bike_lights/models/bigger.glb" camera='{"position":[-7.434,5.128,-6.379],"rotation":[-2.464,-0.7373,-2.646],"zoom":303.06369033128976,"target":[0,0,0]}'>
<img class="outline-model-poster no-wc" src = "/assets/projects/bike_lights/thumbnail.svg"> <img class="outline-model-poster no-wc" src = "/assets/projects/bike_lights/thumbnail.svg">
<p class="has-wc">Loading model...</p> <p class="has-wc">Loading model...</p>
</outline-model-viewer> </outline-model-viewer>

View File

@ -26,7 +26,7 @@ head: |
<script src="/assets/js/outline-model-viewer/index.js" type="module"></script> <script src="/assets/js/outline-model-viewer/index.js" type="module"></script>
--- ---
<outline-model-viewer model = "{{page.models}}/model.glb" zoom=60> <outline-model-viewer model = "{{page.models}}/model.glb" camera='{"position":[6.039,6.456,-6.641],"rotation":[-2.37,0.5778,2.654],"zoom":309.7389923355519,"target":[0,0,0]}'>
<img class="outline-model-poster no-wc" src = "{{page.img.src}}"> <img class="outline-model-poster no-wc" src = "{{page.img.src}}">
<p class="has-wc">Loading model...</p> <p class="has-wc">Loading model...</p>
</outline-model-viewer> </outline-model-viewer>

View File

@ -25,7 +25,7 @@ head: |
</script> </script>
<script src="/assets/js/outline-model-viewer/index.js" type="module"></script> <script src="/assets/js/outline-model-viewer/index.js" type="module"></script>
--- ---
<outline-model-viewer model = "{{page.model}}" camera='{"position":[2.11,4.722,9.765],"rotation":[-0.4425,0.1813,0.08522],"zoom":471.1588632880538,"target":[0.1159,0.06564,-0.06329]}'> <outline-model-viewer model = "{{page.model}}" camera='{"position":[7.699,4.641,6.436],"rotation":[-0.6243,0.7663,0.4633],"zoom":229.77238881409951,"target":[0,0,0]}'>
<img class="outline-model-poster no-wc" src = "{{page.img.src}}"> <img class="outline-model-poster no-wc" src = "{{page.img.src}}">
<p class="has-wc">Loading model...</p> <p class="has-wc">Loading model...</p>
</outline-model-viewer> </outline-model-viewer>

View File

@ -25,7 +25,7 @@ head: |
</script> </script>
<script src="/assets/js/outline-model-viewer/index.js" type="module"></script> <script src="/assets/js/outline-model-viewer/index.js" type="module"></script>
--- ---
<outline-model-viewer model = "{{page.model}}" camera='{"position":[5.148,4.038,8.952],"rotation":[-0.4169,0.4809,0.2021],"zoom":1248.587161014231,"target":[0.03319,0.06938,-0.01135]}'> <outline-model-viewer model = "{{page.model}}" camera='{"position":[7.699,4.641,6.436],"rotation":[-0.6243,0.7663,0.4633],"zoom":229,"target":[0.0,0,0]}'>
<img class="outline-model-poster no-wc" src = "{{page.img.src}}"> <img class="outline-model-poster no-wc" src = "{{page.img.src}}">
<p class="has-wc">Loading model...</p> <p class="has-wc">Loading model...</p>
</outline-model-viewer> </outline-model-viewer>

View File

@ -85,7 +85,7 @@ I've put a 240x240 pixel colour screen on the front to show metrics like total c
## Electronics ## Electronics
Because I am taking this way to far, I wanted to do per port enable/disable and current monitoring. To implement this I'm designing a PCB with 5 channels where each channel consists of this schematic. Because I am taking this way too far, I wanted to do per port enable/disable and current monitoring. To implement this I'm designing a PCB with 5 channels where each channel consists of this schematic.
<figure style="max-width: 350px;"> <figure style="max-width: 350px;">
<img src="{{page.assets}}/channel_sch.png"> <img src="{{page.assets}}/channel_sch.png">
@ -93,27 +93,31 @@ Because I am taking this way to far, I wanted to do per port enable/disable and
There's an INA219 and a shunt resistor for current and voltage monitoring and a chunky MOSFET for enabling and disabling the channel. There's an INA219 and a shunt resistor for current and voltage monitoring and a chunky MOSFET for enabling and disabling the channel.
TODO:
Check the power dissipated in the MOSFET when the gate is driven at 3.3V
Check the inrush current when the MOSFET switches on and off, could potentially limit this by using a larger gate resistor to turn the MOSFET on more slowly.
<figure class="two-wide"> <figure class="two-wide">
<img src="{{page.assets}}/channel_board.png"> <img src="{{page.assets}}/channel_board.png">
<img src="{{page.assets}}/channel_3d.png"> <img src="{{page.assets}}/channel_3d.png">
</figure> </figure>
For now I've broken the functionality for one channel out into a test board that I've sent off to JLCPB for manufacturing with and to be populated with SMT components. This ended up costing about 50 dollars for 5 boards. In future I want to have a go at doing the component placement and reflow myself.
<outline-model-viewer model = "{{page.assets}}/test_board.glb" true-color=true spin=true camera='{"position":[4.016,7.557,6.841],"rotation":[-0.8351,0.3753,0.3848],"zoom":241.86567243589988,"target":[0,0,0]}'>
<img class="outline-model-poster no-wc" src = "{{page.img.src}}">
<p class="has-wc">Loading model...</p>
</outline-model-viewer>
## Software ## Software
In other posts I've described how I made this simulator the test out possible GUIs for this thing. In other posts I've described how I made this simulator the test out possible GUIs for this thing.
TODO: Add some knobs to the simulator so you can test different conditions such as overcurrent, overtemp, sleep, nightmode etc. TODO: Add some knobs to the simulator so you can test different conditions such as overcurrent, overtemp, sleep, nightmode etc.
<usbc-power-supply-simulator disable-console disable-editor code="/assets/blog/micropython/demo.py"></usbc-power-supply-simulator> <!-- <usbc-power-supply-simulator disable-console disable-editor code="/assets/blog/micropython/demo.py"></usbc-power-supply-simulator>
-->
<outline-model-viewer model = "{{page.model}}" true-color=true spin=false camera='{"position":[-6.425,8.003,-3.751],"rotation":[-2.016,-0.6378,-2.246],"zoom":6784.844370099355,"target":[0.1581,-0.01497,0.07167]}'>
<!-- <outline-model-viewer model = "{{page.model}}" true-color=true spin=false camera='{"position":[-6.425,8.003,-3.751],"rotation":[-2.016,-0.6378,-2.246],"zoom":6784.844370099355,"target":[0.1581,-0.01497,0.07167]}'>
<img class="outline-model-poster no-wc" src = "{{page.img.src}}"> <img class="outline-model-poster no-wc" src = "{{page.img.src}}">
<p class="has-wc">Loading model...</p> <p class="has-wc">Loading model...</p>
</outline-model-viewer> </outline-model-viewer> -->
<!-- <kicanvas-embed src="/assets/projects/usbc_power_supply/usb-c_psu.kicad_sch" controls="basic"> </kicanvas-embed> --> <!-- <kicanvas-embed src="/assets/projects/usbc_power_supply/usb-c_psu.kicad_sch" controls="basic"> </kicanvas-embed> -->

View File

@ -24,7 +24,7 @@ head: |
</script> </script>
<script src="/assets/js/outline-model-viewer/index.js" type="module"></script> <script src="/assets/js/outline-model-viewer/index.js" type="module"></script>
--- ---
<outline-model-viewer model = "/assets/blog/vector_magnet/vector_magnet.glb" zoom=500 camera='{"position":[-4.187,2.613,-9.927],"rotation":[-2.895,-0.3904,-3.046],"zoom":715.9863905262143,"target":[0.02078,0.1128,-0.01309]}'> <outline-model-viewer model = "/assets/blog/vector_magnet/vector_magnet.glb" zoom=500 camera='{"position":[3.118,3.203,10.1],"rotation":[-0.3104,0.2858,0.0902],"zoom":428.68750000000136,"target":[0,0,0]}'>
<img class="outline-model-poster no-wc" src = "/assets/projects/bike_lights/thumbnail.svg"> <img class="outline-model-poster no-wc" src = "/assets/projects/bike_lights/thumbnail.svg">
<p class="has-wc">Loading model...</p> <p class="has-wc">Loading model...</p>
</outline-model-viewer> </outline-model-viewer>
@ -35,7 +35,7 @@ Check out a little interactive model of the magnetometer below. The device has t
Here's a cutaway view of the interior. Here's a cutaway view of the interior.
<outline-model-viewer model = "/assets/blog/vector_magnet/vector_magnet_section.glb" zoom=500 spin=false camera='{"position":[-3.078,3.353,10.11],"rotation":[-0.309,-0.2822,-0.08866],"zoom":9794.920097823839,"target":[0.0006876,0.1232,-0.005368]}' true-color=true> <outline-model-viewer model = "/assets/blog/vector_magnet/vector_magnet_section.glb" spin=false camera='{"position":[-3.069,3.172,10.17],"rotation":[-0.3052,-0.2811,-0.08718],"zoom":2860.0091628398345,"target":[0.007077,-0.02863,0.01116]}' true-color=true>
<img class="outline-model-poster no-wc" src = "/assets/projects/bike_lights/thumbnail.svg"> <img class="outline-model-poster no-wc" src = "/assets/projects/bike_lights/thumbnail.svg">
<p class="has-wc">Loading model...</p> <p class="has-wc">Loading model...</p>
</outline-model-viewer> </outline-model-viewer>

View File

@ -95,6 +95,24 @@ class USBCPowerSupplySimulator extends HTMLElement {
run_button.onclick = runPython; run_button.onclick = runPython;
if (editor_disabled) run_button.style.display = "none"; if (editor_disabled) run_button.style.display = "none";
runPython(); runPython();
// Only start simulation when the element is visible
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log(
"Micropython simulator is visible. Starting execution..."
);
runPython();
} else {
console.log(
"Micropython simulator is not visible. Would pause if I could..."
);
}
});
});
observer.observe(this);
} }
constructor() { constructor() {

View File

@ -12,7 +12,7 @@ class FindSurfaces {
constructor() { constructor() {
// This identifier, must be globally unique for each surface // This identifier, must be globally unique for each surface
// across all geometry rendered on screen // across all geometry rendered on screen
this.surfaceId = 0; this.surfaceId = 10;
} }
/* /*
@ -21,7 +21,18 @@ class FindSurfaces {
getSurfaceIdAttribute(mesh) { getSurfaceIdAttribute(mesh) {
const bufferGeometry = mesh.geometry; const bufferGeometry = mesh.geometry;
const numVertices = bufferGeometry.attributes.position.count; const numVertices = bufferGeometry.attributes.position.count;
const vertexIdToSurfaceId = this._generateSurfaceIds(mesh);
// Check if "track" or "pad" is in the name of the mesh
let idOverride = null;
if (
mesh.name.includes("track") ||
mesh.name.includes("pad") ||
mesh.name.includes("zone")
) {
idOverride = 1;
}
const vertexIdToSurfaceId = this._generateSurfaceIds(mesh, idOverride);
const colors = []; const colors = [];
for (let i = 0; i < numVertices; i++) { for (let i = 0; i < numVertices; i++) {
@ -39,7 +50,7 @@ class FindSurfaces {
* Returns a `vertexIdToSurfaceId` map * Returns a `vertexIdToSurfaceId` map
* given a vertex, returns the surfaceId * given a vertex, returns the surfaceId
*/ */
_generateSurfaceIds(mesh) { _generateSurfaceIds(mesh, idOverride = null) {
const bufferGeometry = mesh.geometry; const bufferGeometry = mesh.geometry;
const numVertices = bufferGeometry.attributes.position.count; const numVertices = bufferGeometry.attributes.position.count;
const numIndices = bufferGeometry.index.count; const numIndices = bufferGeometry.index.count;
@ -78,7 +89,7 @@ class FindSurfaces {
// Mark them as explored // Mark them as explored
for (let v of surfaceVertices) { for (let v of surfaceVertices) {
exploredNodes[v] = true; exploredNodes[v] = true;
vertexIdToSurfaceId[v] = this.surfaceId; vertexIdToSurfaceId[v] = idOverride || this.surfaceId;
} }
this.surfaceId += 1; this.surfaceId += 1;

View File

@ -10,22 +10,59 @@ import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js"; import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/addons/shaders/FXAAShader.js"; import { FXAAShader } from "three/addons/shaders/FXAAShader.js";
import GUI from 'lil-gui' import GUI from "lil-gui";
import { CustomOutlinePass } from "./CustomOutlinePass.js"; import { CustomOutlinePass } from "./CustomOutlinePass.js";
import FindSurfaces from "./FindSurfaces.js"; import FindSurfaces from "./FindSurfaces.js";
// Todo: // Todo:
// Swap in the version of this code that has a debug GUI behind a flag // Swap in the version of this code that has a debug GUI behind a flag
// Consider support for transparent objects by rendering them as a wireframe in the color and excluding them from the edge pass. // Consider support for transparent objects by rendering them as a wireframe in the color and excluding them from the edge pass.
// Fix the cetnering and scaling
// Switch to an angled isometric camera to match the style from the main page. // Switch to an angled isometric camera to match the style from the main page.
// Function to lighten or darken a color based on brightness
function adjustColor(color, factor) {
const hsl = color.getHSL({}); // Get the HSL values of the current color
if (hsl.l > 0.7) {
// If the color is light, darken it
hsl.l = Math.max(0, hsl.l - factor);
} else {
// If the color is dark, lighten it
hsl.l = Math.min(1, hsl.l + factor);
}
color.setHSL(hsl.h, hsl.s, hsl.l); // Set the adjusted color
}
function printGLTFScene(scene, maxDepth = 3, depth = 0, indent = 0) {
// Helper function to format the output
if (depth > maxDepth) {
return;
}
const pad = (level) => " ".repeat(level * 2);
// Recursively print scene contents
scene.traverse((object) => {
console.log(
`${pad(indent)}- ${object.type} ${
object.name || "(unnamed)"
} | Position: (${object.position.x.toFixed(
2
)}, ${object.position.y.toFixed(2)}, ${object.position.z.toFixed(2)})`
);
if (object.children && object.children.length > 0) {
console.log(`${pad(indent + 1)}Children:`);
object.children.forEach((child) =>
printGLTFScene(child, maxDepth, depth + 1, indent + 2)
);
}
});
}
const serialiseCamera = (camera, controls) => { const serialiseCamera = (camera, controls) => {
const position = Object.values(camera.position); const position = Object.values(camera.position);
const extractXYZ = ({_x, _y, _z}) => [_x, _y, _z]; const extractXYZ = ({ _x, _y, _z }) => [_x, _y, _z];
const rotation = extractXYZ(camera.rotation); const rotation = extractXYZ(camera.rotation);
const fixed = (l) => l.map( x => parseFloat(x.toPrecision(4))) const fixed = (l) => l.map((x) => parseFloat(x.toPrecision(4)));
return JSON.stringify({ return JSON.stringify({
position: fixed(position), position: fixed(position),
rotation: fixed(rotation), rotation: fixed(rotation),
@ -37,15 +74,31 @@ const serialiseCamera = (camera, controls) => {
class OutlineModelViewer extends HTMLElement { class OutlineModelViewer extends HTMLElement {
constructor() { constructor() {
super(); super();
this.isVisible = true; // Track visibility
let component_rect = this.getBoundingClientRect();
console.log("component_rect", component_rect);
this.shadow = this.attachShadow({ mode: "open" }); this.shadow = this.attachShadow({ mode: "open" });
// Mouse and raycaster
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.intersectedObject = null; // Store currently intersected object
}
// Handle mouse movement and update mouse coordinates
onMouseMove(event, canvas) {
const rect = canvas.getBoundingClientRect();
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}
connectedCallback() {
let component_rect = this.getBoundingClientRect();
this.render(component_rect.height); this.render(component_rect.height);
const model_path = this.getAttribute("model") || "/assets/projects/bike_lights/models/bigger.glb"; const model_path =
const spin = (this.getAttribute("spin") || 'true') === 'true' this.getAttribute("model") ||
"/assets/projects/bike_lights/models/bigger.glb";
const spin = (this.getAttribute("spin") || "true") === "true";
const container = this.shadow.querySelector("div#container"); const container = this.shadow.querySelector("div#container");
const canvas = this.shadow.querySelector("canvas"); const canvas = this.shadow.querySelector("canvas");
@ -61,8 +114,15 @@ class OutlineModelViewer extends HTMLElement {
// // Init scene // // Init scene
// const camera = new THREE.PerspectiveCamera(70, canvas_rect.width / canvas_rect.height, 0.1, 100); // const camera = new THREE.PerspectiveCamera(70, canvas_rect.width / canvas_rect.height, 0.1, 100);
const camera = new THREE.OrthographicCamera( canvas_rect.width / - 2, canvas_rect.width / 2, canvas_rect.height / 2, canvas_rect.height / - 2, 1, 1000 ); const camera = new THREE.OrthographicCamera(
camera.zoom = parseFloat(this.getAttribute("zoom") || "1") canvas_rect.width / -2,
canvas_rect.width / 2,
canvas_rect.height / 2,
canvas_rect.height / -2,
1,
1000
);
camera.zoom = parseFloat(this.getAttribute("zoom") || "1");
camera.position.set(10, 2.5, 4); camera.position.set(10, 2.5, 4);
// create the scene and the camera // create the scene and the camera
@ -87,8 +147,8 @@ class OutlineModelViewer extends HTMLElement {
// See: https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderTarget.depthBuffer // See: https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderTarget.depthBuffer
const depthTexture = new THREE.DepthTexture(); const depthTexture = new THREE.DepthTexture();
const renderTarget = new THREE.WebGLRenderTarget( const renderTarget = new THREE.WebGLRenderTarget(
2*canvas_rect.width, 2 * canvas_rect.width,
2*canvas_rect.height, 2 * canvas_rect.height,
{ {
depthTexture: depthTexture, depthTexture: depthTexture,
depthBuffer: true, depthBuffer: true,
@ -102,18 +162,18 @@ class OutlineModelViewer extends HTMLElement {
// Outline pass. // Outline pass.
const customOutline = new CustomOutlinePass( const customOutline = new CustomOutlinePass(
new THREE.Vector2(2*canvas_rect.width, 2*canvas_rect.height), new THREE.Vector2(2 * canvas_rect.width, 2 * canvas_rect.height),
scene, scene,
camera, camera,
outline_color, outline_color
); );
composer.addPass(customOutline); composer.addPass(customOutline);
// Antialias pass. // Antialias pass.
const effectFXAA = new ShaderPass(FXAAShader); const effectFXAA = new ShaderPass(FXAAShader);
effectFXAA.uniforms["resolution"].value.set( effectFXAA.uniforms["resolution"].value.set(
.5 / canvas_rect.width, 0.5 / canvas_rect.width,
.5 / canvas_rect.height 0.5 / canvas_rect.height
); );
composer.addPass(effectFXAA); composer.addPass(effectFXAA);
@ -121,14 +181,31 @@ class OutlineModelViewer extends HTMLElement {
// Load model // Load model
const loader = new GLTFLoader(); const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader(); const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderConfig({ type: 'js' }); dracoLoader.setDecoderConfig({ type: "js" });
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/'); dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/");
loader.setDRACOLoader( dracoLoader ); loader.setDRACOLoader(dracoLoader);
loader.load(model_path, (gltf) => { loader.load(model_path, (gltf) => {
scene.add(gltf.scene); scene.add(gltf.scene);
surfaceFinder.surfaceId = 0; surfaceFinder.surfaceId = 0;
// Compute bounding box
let box = new THREE.Box3().setFromObject(gltf.scene);
// Scale the model to fit into a unit cube
const size = new THREE.Vector3();
box.getSize(size); // Get the size of the bounding box
const maxDim = Math.max(size.x, size.y, size.z); // Find the largest dimension
const scaleFactor = 1 / maxDim; // Calculate the scaling factor
gltf.scene.scale.set(scaleFactor, scaleFactor, scaleFactor); // Apply the scale uniformly
// Reposition the model so that its center is at the origin
let box2 = new THREE.Box3().setFromObject(gltf.scene);
const center = new THREE.Vector3();
box2.getCenter(center); // Get the center of the bounding box
gltf.scene.position.sub(center); // Subtract the center from the position
// Modify the materials to support surface coloring
scene.traverse((node) => { scene.traverse((node) => {
if (node.type == "Mesh") { if (node.type == "Mesh") {
const colorsTypedArray = surfaceFinder.getSurfaceIdAttribute(node); const colorsTypedArray = surfaceFinder.getSurfaceIdAttribute(node);
@ -137,13 +214,33 @@ class OutlineModelViewer extends HTMLElement {
new THREE.BufferAttribute(colorsTypedArray, 4) new THREE.BufferAttribute(colorsTypedArray, 4)
); );
const material_params = this.getAttribute("true-color") ? {color: node.material.color} : {emissive: model_color}; let material_params = this.getAttribute("true-color")
? { color: node.material.color }
: { emissive: model_color };
if (node.name.includes("track") || node.name.includes("zone")) {
//set to a copper colour
// #c87533
material_params = {
color: new THREE.Color(0x558855),
};
node.position.y += 0.00001;
}
if (node.name.includes("pad")) {
material_params = {
color: new THREE.Color(0xaaaaaa),
};
node.position.y += 0.00002;
}
// override materials // override materials
node.material = new THREE.MeshStandardMaterial(material_params); node.material = new THREE.MeshStandardMaterial(material_params);
} }
}); });
customOutline.updateMaxSurfaceId(surfaceFinder.surfaceId + 1); customOutline.updateMaxSurfaceId(surfaceFinder.surfaceId + 1);
// Print out the scene structure to the console
// printGLTFScene(gltf.scene, 1);
}); });
// Set up orbital camera controls. // Set up orbital camera controls.
@ -151,7 +248,7 @@ class OutlineModelViewer extends HTMLElement {
controls.autoRotate = spin; controls.autoRotate = spin;
controls.update(); controls.update();
if(this.getAttribute("camera")) { if (this.getAttribute("camera")) {
const cameraState = JSON.parse(this.getAttribute("camera")); const cameraState = JSON.parse(this.getAttribute("camera"));
camera.zoom = cameraState.zoom; camera.zoom = cameraState.zoom;
camera.position.set(...cameraState.position); camera.position.set(...cameraState.position);
@ -159,31 +256,102 @@ class OutlineModelViewer extends HTMLElement {
controls.target.set(...cameraState.target); controls.target.set(...cameraState.target);
} }
// Event listener for mouse movement
canvas.addEventListener("mousemove", (event) =>
this.onMouseMove(event, canvas)
);
let intersects = [];
const doRayCast = () => {
// Perform raycasting for hovering
this.raycaster.setFromCamera(this.mouse, camera);
intersects.length = 0;
this.raycaster.intersectObjects(scene.children, true, intersects);
if (intersects.length > 0) {
const object = intersects[0].object;
// If the intersected object has changed
if (this.intersectedObject !== object) {
if (this.intersectedObject) {
// Reset the color of the previously hovered object
this.intersectedObject.material.emissive.setHex(
this.intersectedObject.currentHex
);
}
// Store the current hex color and set highlight color
this.intersectedObject = object;
this.intersectedObject.currentHex =
this.intersectedObject.material.emissive.getHex();
// Adjust the emissive color based on current brightness
const currentColor = new THREE.Color(
this.intersectedObject.material.emissive.getHex()
);
adjustColor(currentColor, 0.2); // Lighten or darken based on brightness
this.intersectedObject.material.emissive.set(currentColor);
// Print the name of the intersected object
params.selectedObject = object.name || "(unnamed object)";
}
} else if (this.intersectedObject) {
// Reset the color if the mouse is no longer hovering over any object
this.intersectedObject.material.emissive.setHex(
this.intersectedObject.currentHex
);
this.intersectedObject = null;
params.selectedObject = "";
}
};
window.addEventListener("click", doRayCast);
// Render loop // Render loop
function update() { const update = () => {
if (this.isVisible) {
requestAnimationFrame(update); requestAnimationFrame(update);
controls.update(); controls.update();
composer.render(); composer.render();
// doRayCast();
} }
};
update(); update();
// Pausing/resuming the render loop when element visibility changes
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("Model Viewer Element is visible. Resuming rendering...");
this.isVisible = true;
update(); // Resume the loop
} else {
console.log(
"Model Viewer Element is not visible. Pausing rendering..."
);
this.isVisible = false; // Pauses rendering
}
});
});
// Observe this element for visibility changes
observer.observe(this);
function onWindowResize() { function onWindowResize() {
canvas_rect = canvas.getBoundingClientRect(); canvas_rect = canvas.getBoundingClientRect();
camera.aspect = canvas_rect.width / canvas_rect.height; camera.aspect = canvas_rect.width / canvas_rect.height;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
renderer.setSize(canvas_rect.width, canvas_rect.height, false); renderer.setSize(canvas_rect.width, canvas_rect.height, false);
composer.setSize(2*canvas_rect.width, 2*canvas_rect.height); composer.setSize(2 * canvas_rect.width, 2 * canvas_rect.height);
effectFXAA.setSize(2*canvas_rect.width, 2*canvas_rect.height); effectFXAA.setSize(2 * canvas_rect.width, 2 * canvas_rect.height);
customOutline.setSize(2*canvas_rect.width, 2*canvas_rect.height); customOutline.setSize(2 * canvas_rect.width, 2 * canvas_rect.height);
effectFXAA.uniforms["resolution"].value.set( effectFXAA.uniforms["resolution"].value.set(
.5 / canvas_rect.width, 0.5 / canvas_rect.width,
.5 / canvas_rect.height 0.5 / canvas_rect.height
); );
} }
window.addEventListener("resize", onWindowResize, false); window.addEventListener("resize", onWindowResize, false);
const gui = new GUI({ const gui = new GUI({
title: "Settings", title: "Settings",
container: container, container: container,
@ -194,6 +362,8 @@ class OutlineModelViewer extends HTMLElement {
const uniforms = customOutline.fsQuad.material.uniforms; const uniforms = customOutline.fsQuad.material.uniforms;
const params = { const params = {
selectedObject: "None",
spin: controls.autoRotate,
mode: { Mode: 0 }, mode: { Mode: 0 },
depthBias: uniforms.multiplierParameters.value.x, depthBias: uniforms.multiplierParameters.value.x,
depthMult: uniforms.multiplierParameters.value.y, depthMult: uniforms.multiplierParameters.value.y,
@ -201,15 +371,19 @@ class OutlineModelViewer extends HTMLElement {
printCamera: () => console.log(serialiseCamera(camera, controls)), printCamera: () => console.log(serialiseCamera(camera, controls)),
}; };
gui.add(params, 'printCamera' ); gui.add(params, "selectedObject").listen();
gui.add(params, "spin").onChange((value) => {
controls.autoRotate = value;
});
gui.add(params, "printCamera");
gui gui
.add(params.mode, "Mode", { .add(params.mode, "Mode", {
"Outlines + Shaded (default)": 0, "Outlines + Shaded (default)": 0,
"Shaded": 2, Shaded: 2,
"Depth buffer": 3, "Depth buffer": 3,
"SurfaceID buffer": 4, "SurfaceID buffer": 4,
"Outlines": 5, Outlines: 5,
}) })
.onChange(function (value) { .onChange(function (value) {
uniforms.debugVisualize.value = value; uniforms.debugVisualize.value = value;
@ -222,12 +396,12 @@ class OutlineModelViewer extends HTMLElement {
uniforms.multiplierParameters.value.y = value; uniforms.multiplierParameters.value.y = value;
}); });
gui.add(params, "FXAA_resolution", 0.0, 2).onChange(value => { gui.add(params, "FXAA_resolution", 0.0, 2).onChange((value) => {
effectFXAA.uniforms["resolution"].value.set( effectFXAA.uniforms["resolution"].value.set(
value / canvas_rect.width, value / canvas_rect.width,
value / canvas_rect.height value / canvas_rect.height
);}) );
});
} }
render(height) { render(height) {
@ -266,6 +440,11 @@ class OutlineModelViewer extends HTMLElement {
border: var(--theme-subtle-outline) 1px solid; border: var(--theme-subtle-outline) 1px solid;
} }
.lil-gui .controller.string input {
background-color: var(--theme-subtle-outline);
color: var(--theme-text-color);
}
canvas { canvas {
position: absolute; position: absolute;
width: 100%; width: 100%;

Binary file not shown.

View File

@ -38,7 +38,8 @@ Welcome to my little home on the web! Below you'll find recent blog posts, proje
<span class="dt-label">Last Modified</span> <span class="dt-label">Last Modified</span>
</section> </section>
<hr class="heading"> <hr class="heading">
{% for post in site.projects limit:5 %} {% assign projects = site.projects | sort_natural: "last_modified_at"%}
{% for post in projects limit:5 %}
{% include project_summary.html %} {% include project_summary.html %}
{% endfor %} {% endfor %}
<a href = "/projects/" class = "highlights-more">More</a> <a href = "/projects/" class = "highlights-more">More</a>