mirror of
https://github.com/TomHodson/tomhodson.github.com.git
synced 2025-06-26 10:01:18 +02:00
Update outline renderer
This commit is contained in:
parent
d70a8c5b3e
commit
740ca32920
@ -25,7 +25,7 @@ head: |
|
||||
</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">
|
||||
<p class="has-wc">Loading model...</p>
|
||||
</outline-model-viewer>
|
||||
|
@ -26,7 +26,7 @@ head: |
|
||||
<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}}">
|
||||
<p class="has-wc">Loading model...</p>
|
||||
</outline-model-viewer>
|
||||
|
@ -25,7 +25,7 @@ head: |
|
||||
</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}}">
|
||||
<p class="has-wc">Loading model...</p>
|
||||
</outline-model-viewer>
|
@ -25,7 +25,7 @@ head: |
|
||||
</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}}">
|
||||
<p class="has-wc">Loading model...</p>
|
||||
</outline-model-viewer>
|
@ -85,35 +85,39 @@ I've put a 240x240 pixel colour screen on the front to show metrics like total c
|
||||
|
||||
## 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;">
|
||||
<img src="{{page.assets}}/channel_sch.png">
|
||||
</figure>
|
||||
|
||||
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.
|
||||
There's an INA219 and a shunt resistor for current and voltage monitoring and a chunky MOSFET for enabling and disabling the channel.
|
||||
|
||||
<figure class="two-wide">
|
||||
<img src="{{page.assets}}/channel_board.png">
|
||||
<img src="{{page.assets}}/channel_3d.png">
|
||||
</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
|
||||
|
||||
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.
|
||||
|
||||
<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}}">
|
||||
<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> -->
|
||||
|
@ -24,7 +24,7 @@ head: |
|
||||
</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">
|
||||
<p class="has-wc">Loading model...</p>
|
||||
</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.
|
||||
|
||||
<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">
|
||||
<p class="has-wc">Loading model...</p>
|
||||
</outline-model-viewer>
|
||||
|
@ -95,6 +95,24 @@ class USBCPowerSupplySimulator extends HTMLElement {
|
||||
run_button.onclick = runPython;
|
||||
if (editor_disabled) run_button.style.display = "none";
|
||||
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() {
|
||||
|
@ -12,7 +12,7 @@ class FindSurfaces {
|
||||
constructor() {
|
||||
// This identifier, must be globally unique for each surface
|
||||
// across all geometry rendered on screen
|
||||
this.surfaceId = 0;
|
||||
this.surfaceId = 10;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -21,7 +21,18 @@ class FindSurfaces {
|
||||
getSurfaceIdAttribute(mesh) {
|
||||
const bufferGeometry = mesh.geometry;
|
||||
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 = [];
|
||||
for (let i = 0; i < numVertices; i++) {
|
||||
@ -39,7 +50,7 @@ class FindSurfaces {
|
||||
* Returns a `vertexIdToSurfaceId` map
|
||||
* given a vertex, returns the surfaceId
|
||||
*/
|
||||
_generateSurfaceIds(mesh) {
|
||||
_generateSurfaceIds(mesh, idOverride = null) {
|
||||
const bufferGeometry = mesh.geometry;
|
||||
const numVertices = bufferGeometry.attributes.position.count;
|
||||
const numIndices = bufferGeometry.index.count;
|
||||
@ -78,7 +89,7 @@ class FindSurfaces {
|
||||
// Mark them as explored
|
||||
for (let v of surfaceVertices) {
|
||||
exploredNodes[v] = true;
|
||||
vertexIdToSurfaceId[v] = this.surfaceId;
|
||||
vertexIdToSurfaceId[v] = idOverride || this.surfaceId;
|
||||
}
|
||||
|
||||
this.surfaceId += 1;
|
||||
@ -166,4 +177,4 @@ function getFragmentShader() {
|
||||
gl_FragColor = vec4(surfaceId, 0.0, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -10,42 +10,95 @@ import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
|
||||
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.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 FindSurfaces from "./FindSurfaces.js";
|
||||
|
||||
|
||||
// Todo:
|
||||
// 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.
|
||||
// Fix the cetnering and scaling
|
||||
// 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 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 fixed = (l) => l.map( x => parseFloat(x.toPrecision(4)))
|
||||
const fixed = (l) => l.map((x) => parseFloat(x.toPrecision(4)));
|
||||
return JSON.stringify({
|
||||
position: fixed(position),
|
||||
rotation: fixed(rotation),
|
||||
zoom: camera.zoom,
|
||||
target: fixed(Object.values(controls.target)),
|
||||
});
|
||||
position: fixed(position),
|
||||
rotation: fixed(rotation),
|
||||
zoom: camera.zoom,
|
||||
target: fixed(Object.values(controls.target)),
|
||||
});
|
||||
};
|
||||
|
||||
class OutlineModelViewer extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
let component_rect = this.getBoundingClientRect();
|
||||
console.log("component_rect", component_rect);
|
||||
|
||||
this.isVisible = true; // Track visibility
|
||||
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);
|
||||
|
||||
const model_path = this.getAttribute("model") || "/assets/projects/bike_lights/models/bigger.glb";
|
||||
const spin = (this.getAttribute("spin") || 'true') === 'true'
|
||||
const model_path =
|
||||
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 canvas = this.shadow.querySelector("canvas");
|
||||
@ -53,7 +106,7 @@ class OutlineModelViewer extends HTMLElement {
|
||||
let canvas_rect = canvas.getBoundingClientRect();
|
||||
console.log(canvas_rect);
|
||||
|
||||
// determine the outline and bg colors
|
||||
// determine the outline and bg colors
|
||||
const body = document.getElementsByTagName("body")[0];
|
||||
const style = window.getComputedStyle(body);
|
||||
const outline_color = style.getPropertyValue("--theme-model-line-color");
|
||||
@ -61,8 +114,15 @@ class OutlineModelViewer extends HTMLElement {
|
||||
|
||||
// // Init scene
|
||||
// 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 );
|
||||
camera.zoom = parseFloat(this.getAttribute("zoom") || "1")
|
||||
const camera = new THREE.OrthographicCamera(
|
||||
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);
|
||||
|
||||
// create the scene and the camera
|
||||
@ -75,7 +135,7 @@ class OutlineModelViewer extends HTMLElement {
|
||||
|
||||
// renderer.setPixelRatio( window.devicePixelRatio );
|
||||
renderer.setSize(canvas_rect.width, canvas_rect.height, false);
|
||||
|
||||
|
||||
const light = new THREE.DirectionalLight(0xffffff, 2);
|
||||
scene.add(light);
|
||||
light.position.set(1.7, 1, -1);
|
||||
@ -87,8 +147,8 @@ class OutlineModelViewer extends HTMLElement {
|
||||
// See: https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderTarget.depthBuffer
|
||||
const depthTexture = new THREE.DepthTexture();
|
||||
const renderTarget = new THREE.WebGLRenderTarget(
|
||||
2*canvas_rect.width,
|
||||
2*canvas_rect.height,
|
||||
2 * canvas_rect.width,
|
||||
2 * canvas_rect.height,
|
||||
{
|
||||
depthTexture: depthTexture,
|
||||
depthBuffer: true,
|
||||
@ -102,18 +162,18 @@ class OutlineModelViewer extends HTMLElement {
|
||||
|
||||
// Outline pass.
|
||||
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,
|
||||
camera,
|
||||
outline_color,
|
||||
outline_color
|
||||
);
|
||||
composer.addPass(customOutline);
|
||||
|
||||
// Antialias pass.
|
||||
const effectFXAA = new ShaderPass(FXAAShader);
|
||||
effectFXAA.uniforms["resolution"].value.set(
|
||||
.5 / canvas_rect.width,
|
||||
.5 / canvas_rect.height
|
||||
0.5 / canvas_rect.width,
|
||||
0.5 / canvas_rect.height
|
||||
);
|
||||
composer.addPass(effectFXAA);
|
||||
|
||||
@ -121,14 +181,31 @@ class OutlineModelViewer extends HTMLElement {
|
||||
// Load model
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
dracoLoader.setDecoderConfig({ type: 'js' });
|
||||
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
|
||||
loader.setDRACOLoader( dracoLoader );
|
||||
dracoLoader.setDecoderConfig({ type: "js" });
|
||||
dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/");
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
loader.load(model_path, (gltf) => {
|
||||
scene.add(gltf.scene);
|
||||
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) => {
|
||||
if (node.type == "Mesh") {
|
||||
const colorsTypedArray = surfaceFinder.getSurfaceIdAttribute(node);
|
||||
@ -136,14 +213,34 @@ class OutlineModelViewer extends HTMLElement {
|
||||
"color",
|
||||
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
|
||||
node.material = new THREE.MeshStandardMaterial(material_params);
|
||||
}
|
||||
});
|
||||
|
||||
customOutline.updateMaxSurfaceId(surfaceFinder.surfaceId + 1);
|
||||
|
||||
// Print out the scene structure to the console
|
||||
// printGLTFScene(gltf.scene, 1);
|
||||
});
|
||||
|
||||
// Set up orbital camera controls.
|
||||
@ -151,7 +248,7 @@ class OutlineModelViewer extends HTMLElement {
|
||||
controls.autoRotate = spin;
|
||||
controls.update();
|
||||
|
||||
if(this.getAttribute("camera")) {
|
||||
if (this.getAttribute("camera")) {
|
||||
const cameraState = JSON.parse(this.getAttribute("camera"));
|
||||
camera.zoom = cameraState.zoom;
|
||||
camera.position.set(...cameraState.position);
|
||||
@ -159,31 +256,102 @@ class OutlineModelViewer extends HTMLElement {
|
||||
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
|
||||
function update() {
|
||||
requestAnimationFrame(update);
|
||||
controls.update();
|
||||
composer.render();
|
||||
}
|
||||
const update = () => {
|
||||
if (this.isVisible) {
|
||||
requestAnimationFrame(update);
|
||||
controls.update();
|
||||
composer.render();
|
||||
// doRayCast();
|
||||
}
|
||||
};
|
||||
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() {
|
||||
canvas_rect = canvas.getBoundingClientRect();
|
||||
camera.aspect = canvas_rect.width / canvas_rect.height;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize(canvas_rect.width, canvas_rect.height, false);
|
||||
composer.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);
|
||||
composer.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);
|
||||
effectFXAA.uniforms["resolution"].value.set(
|
||||
.5 / canvas_rect.width,
|
||||
.5 / canvas_rect.height
|
||||
0.5 / canvas_rect.width,
|
||||
0.5 / canvas_rect.height
|
||||
);
|
||||
}
|
||||
window.addEventListener("resize", onWindowResize, false);
|
||||
|
||||
|
||||
const gui = new GUI({
|
||||
title: "Settings",
|
||||
container: container,
|
||||
@ -191,43 +359,49 @@ class OutlineModelViewer extends HTMLElement {
|
||||
closeFolders: true,
|
||||
});
|
||||
gui.close();
|
||||
|
||||
|
||||
const uniforms = customOutline.fsQuad.material.uniforms;
|
||||
const params = {
|
||||
selectedObject: "None",
|
||||
spin: controls.autoRotate,
|
||||
mode: { Mode: 0 },
|
||||
depthBias: uniforms.multiplierParameters.value.x,
|
||||
depthMult: uniforms.multiplierParameters.value.y,
|
||||
FXAA_resolution: 0.5,
|
||||
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
|
||||
.add(params.mode, "Mode", {
|
||||
"Outlines + Shaded (default)": 0,
|
||||
"Shaded": 2,
|
||||
Shaded: 2,
|
||||
"Depth buffer": 3,
|
||||
"SurfaceID buffer": 4,
|
||||
"Outlines": 5,
|
||||
Outlines: 5,
|
||||
})
|
||||
.onChange(function (value) {
|
||||
uniforms.debugVisualize.value = value;
|
||||
});
|
||||
|
||||
gui.add(params, "depthBias", 0.0, 5).onChange(function (value) {
|
||||
uniforms.multiplierParameters.value.x = value;
|
||||
});
|
||||
gui.add(params, "depthMult", 0.0, 20).onChange(function (value) {
|
||||
uniforms.multiplierParameters.value.y = value;
|
||||
});
|
||||
|
||||
gui.add(params, "FXAA_resolution", 0.0, 2).onChange(value => {
|
||||
effectFXAA.uniforms["resolution"].value.set(
|
||||
|
||||
gui.add(params, "depthBias", 0.0, 5).onChange(function (value) {
|
||||
uniforms.multiplierParameters.value.x = value;
|
||||
});
|
||||
gui.add(params, "depthMult", 0.0, 20).onChange(function (value) {
|
||||
uniforms.multiplierParameters.value.y = value;
|
||||
});
|
||||
|
||||
gui.add(params, "FXAA_resolution", 0.0, 2).onChange((value) => {
|
||||
effectFXAA.uniforms["resolution"].value.set(
|
||||
value / canvas_rect.width,
|
||||
value / canvas_rect.height
|
||||
);})
|
||||
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render(height) {
|
||||
@ -266,6 +440,11 @@ class OutlineModelViewer extends HTMLElement {
|
||||
border: var(--theme-subtle-outline) 1px solid;
|
||||
}
|
||||
|
||||
.lil-gui .controller.string input {
|
||||
background-color: var(--theme-subtle-outline);
|
||||
color: var(--theme-text-color);
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@ -277,4 +456,4 @@ class OutlineModelViewer extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("outline-model-viewer", OutlineModelViewer);
|
||||
customElements.define("outline-model-viewer", OutlineModelViewer);
|
||||
|
BIN
assets/projects/usbc_power_supply/test_board.glb
Normal file
BIN
assets/projects/usbc_power_supply/test_board.glb
Normal file
Binary file not shown.
@ -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>
|
||||
</section>
|
||||
<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 %}
|
||||
{% endfor %}
|
||||
<a href = "/projects/" class = "highlights-more">More</a>
|
||||
|
Loading…
x
Reference in New Issue
Block a user