mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-22 02:18:10 +02:00
395 lines
14 KiB
HTML
395 lines
14 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>BVH Visualization</title>
|
||
|
|
</head>
|
||
|
|
<style>
|
||
|
|
body {
|
||
|
|
font-family: Arial, sans-serif;
|
||
|
|
background-color: #f0f0f0;
|
||
|
|
margin: 0;
|
||
|
|
padding: 20px;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: center;
|
||
|
|
height: 100vh;
|
||
|
|
}
|
||
|
|
|
||
|
|
#container {
|
||
|
|
width: 100%;
|
||
|
|
height: 50%;
|
||
|
|
border: 2px solid #ccc;
|
||
|
|
background-color: #fff;
|
||
|
|
padding: 10px;
|
||
|
|
border-radius: 8px;
|
||
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.node circle {
|
||
|
|
fill: #999;
|
||
|
|
stroke: steelblue;
|
||
|
|
stroke-width: 1.5px;
|
||
|
|
cursor: pointer;
|
||
|
|
}
|
||
|
|
|
||
|
|
.node text {
|
||
|
|
font: 12px sans-serif;
|
||
|
|
}
|
||
|
|
|
||
|
|
.link {
|
||
|
|
fill: none;
|
||
|
|
stroke: #555;
|
||
|
|
stroke-opacity: 0.4;
|
||
|
|
stroke-width: 1.5px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.tooltip {
|
||
|
|
position: absolute;
|
||
|
|
text-align: left;
|
||
|
|
width: 300px;
|
||
|
|
height: auto;
|
||
|
|
padding: 10px;
|
||
|
|
font: 12px sans-serif;
|
||
|
|
background: lightsteelblue;
|
||
|
|
border: 1px solid #333;
|
||
|
|
border-radius: 8px;
|
||
|
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
|
||
|
|
overflow-y: auto;
|
||
|
|
max-height: 400px;
|
||
|
|
visibility: hidden;
|
||
|
|
}
|
||
|
|
|
||
|
|
.tooltip h3 {
|
||
|
|
margin: 0;
|
||
|
|
font-size: 14px;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
.tooltip p {
|
||
|
|
margin: 5px 0;
|
||
|
|
font-size: 12px;
|
||
|
|
word-wrap: break-word;
|
||
|
|
}
|
||
|
|
|
||
|
|
.tooltip .indent {
|
||
|
|
margin-left: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
#header {
|
||
|
|
margin-bottom: 20px;
|
||
|
|
padding: 10px;
|
||
|
|
background-color: #e0e0e0;
|
||
|
|
border-radius: 8px;
|
||
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
#header h2 {
|
||
|
|
margin-top: 0;
|
||
|
|
color: #333;
|
||
|
|
}
|
||
|
|
|
||
|
|
#header p {
|
||
|
|
margin: 5px 0;
|
||
|
|
font-size: 14px;
|
||
|
|
line-height: 1.5;
|
||
|
|
}
|
||
|
|
|
||
|
|
#threeContainer {
|
||
|
|
width: 75%;
|
||
|
|
height: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
#toggleControls {
|
||
|
|
width: 25%;
|
||
|
|
height: 100%;
|
||
|
|
overflow-y: auto;
|
||
|
|
margin-left: 20px;
|
||
|
|
border: 2px solid #ccc;
|
||
|
|
border-radius: 8px;
|
||
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||
|
|
padding: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle-container {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
margin-bottom: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle-container label {
|
||
|
|
font-size: 14px;
|
||
|
|
margin-left: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
#mainContainer {
|
||
|
|
display: flex;
|
||
|
|
width: 100%;
|
||
|
|
height: 50%;
|
||
|
|
justify-content: space-between;
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
<body>
|
||
|
|
<div id="header"></div>
|
||
|
|
<div id="container">
|
||
|
|
<div class="tooltip"></div>
|
||
|
|
<svg id="canvas"></svg>
|
||
|
|
</div>
|
||
|
|
<br></br>
|
||
|
|
<br></br>
|
||
|
|
<br></br>
|
||
|
|
<div id="mainContainer">
|
||
|
|
<div id="threeContainer"></div>
|
||
|
|
<div id="toggleControls"></div>
|
||
|
|
</div>
|
||
|
|
<br></br>
|
||
|
|
<br></br>
|
||
|
|
<br></br>
|
||
|
|
<script src="https://d3js.org/d3.v6.min.js"></script>
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/three@0.130.1/build/three.min.js"></script>
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/three@0.130.1/examples/js/controls/OrbitControls.js"></script>
|
||
|
|
<script>
|
||
|
|
function formatProperties(properties, depth = 0, maxDepth = 2, index = '') {
|
||
|
|
if (depth > maxDepth) {
|
||
|
|
return '<p><strong>...</strong></p>';
|
||
|
|
}
|
||
|
|
|
||
|
|
let html = '';
|
||
|
|
for (const [key, value] of Object.entries(properties)) {
|
||
|
|
const currentIndex = index ? `${index}.${key}` : key;
|
||
|
|
if (typeof value === 'object' && value !== null) {
|
||
|
|
html += `<p><strong>${currentIndex}:</strong><div class="indent">${formatProperties(value, depth + 1, maxDepth, currentIndex)}</div></p>`;
|
||
|
|
} else {
|
||
|
|
let displayValue = value;
|
||
|
|
if (typeof value === 'string' && value.length > 100) {
|
||
|
|
displayValue = value.substring(0, 100) + '...';
|
||
|
|
}
|
||
|
|
html += `<p><strong>${currentIndex}:</strong> ${displayValue}</p>`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return html;
|
||
|
|
}
|
||
|
|
|
||
|
|
d3.json('bvh_dump.json').then(function(treeData) {
|
||
|
|
document.getElementById('header').innerHTML = `<h2>Header Information</h2>${formatProperties(treeData.header)}`;
|
||
|
|
|
||
|
|
var width = 1400,
|
||
|
|
height = 400; // Adjusted for half the height
|
||
|
|
|
||
|
|
var svg = d3.select("#canvas")
|
||
|
|
.attr("width", width)
|
||
|
|
.attr("height", height)
|
||
|
|
.call(d3.zoom().on("zoom", function (event) {
|
||
|
|
g.attr("transform", event.transform);
|
||
|
|
}))
|
||
|
|
.append("g");
|
||
|
|
|
||
|
|
var g = svg.append("g");
|
||
|
|
|
||
|
|
var root = d3.hierarchy(treeData.nodes.find(n => n.id === 0), function(d) {
|
||
|
|
return treeData.relationships[d.id].map(id => treeData.nodes.find(n => n.id === id));
|
||
|
|
});
|
||
|
|
|
||
|
|
var treeLayout = d3.tree().size([height, width - 250]);
|
||
|
|
|
||
|
|
treeLayout(root);
|
||
|
|
|
||
|
|
var link = g.selectAll(".link")
|
||
|
|
.data(root.links())
|
||
|
|
.enter().append("path")
|
||
|
|
.attr("class", "link")
|
||
|
|
.attr("d", d3.linkHorizontal()
|
||
|
|
.x(d => d.y)
|
||
|
|
.y(d => d.x));
|
||
|
|
|
||
|
|
var node = g.selectAll(".node")
|
||
|
|
.data(root.descendants())
|
||
|
|
.enter().append("g")
|
||
|
|
.attr("class", "node")
|
||
|
|
.attr("transform", d => `translate(${d.y},${d.x})`);
|
||
|
|
|
||
|
|
node.append("circle")
|
||
|
|
.attr("r", 10)
|
||
|
|
.on("click", function(event, d) {
|
||
|
|
const propertiesHtml = formatProperties(d.data.properties);
|
||
|
|
|
||
|
|
d3.select(".tooltip")
|
||
|
|
.html(`<h3>ID: ${d.data.id}</h3>${propertiesHtml}`)
|
||
|
|
.style("visibility", "visible")
|
||
|
|
.style("left", (event.pageX + 20) + "px")
|
||
|
|
.style("top", (event.pageY - 20) + "px");
|
||
|
|
d3.selectAll("circle").attr("r", 10);
|
||
|
|
d3.select(this).attr("r", 15);
|
||
|
|
});
|
||
|
|
|
||
|
|
node.append("text")
|
||
|
|
.attr("dy", 3)
|
||
|
|
.attr("x", d => d.children ? -12 : 12)
|
||
|
|
.style("text-anchor", d => d.children ? "end" : "start")
|
||
|
|
.text(d => d.data.type);
|
||
|
|
|
||
|
|
d3.select("body").on("click", function(event) {
|
||
|
|
if (!event.target.closest(".node")) {
|
||
|
|
d3.select(".tooltip").style("visibility", "hidden");
|
||
|
|
d3.selectAll("circle").attr("r", 10);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
initThreeJs(treeData);
|
||
|
|
});
|
||
|
|
|
||
|
|
function initThreeJs(treeData) {
|
||
|
|
const scene = new THREE.Scene();
|
||
|
|
const camera = new THREE.PerspectiveCamera(75, window.innerWidth * 0.7 / 800, 0.1, 1000); // Adjusted for the new layout
|
||
|
|
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
||
|
|
renderer.setSize(window.innerWidth * 0.7, 800); // Adjusted for half the height
|
||
|
|
document.getElementById('threeContainer').appendChild(renderer.domElement);
|
||
|
|
|
||
|
|
// Add orbit controls for zoom and rotate
|
||
|
|
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
||
|
|
controls.enableDamping = true;
|
||
|
|
controls.dampingFactor = 0.25;
|
||
|
|
controls.screenSpacePanning = false;
|
||
|
|
controls.maxPolarAngle = Math.PI / 2;
|
||
|
|
|
||
|
|
// Add grid helper and axis helper for reference
|
||
|
|
const gridHelper = new THREE.GridHelper(10, 10);
|
||
|
|
scene.add(gridHelper);
|
||
|
|
|
||
|
|
const axesHelper = new THREE.AxesHelper(5);
|
||
|
|
scene.add(axesHelper);
|
||
|
|
|
||
|
|
const geometries = [];
|
||
|
|
|
||
|
|
function createBox(coord, color, id) {
|
||
|
|
const geometry = new THREE.BoxGeometry(
|
||
|
|
coord.x_upper - coord.x_lower,
|
||
|
|
coord.y_upper - coord.y_lower,
|
||
|
|
coord.z_upper - coord.z_lower
|
||
|
|
);
|
||
|
|
const edges = new THREE.EdgesGeometry(geometry);
|
||
|
|
const material = new THREE.LineBasicMaterial({ color: color });
|
||
|
|
const box = new THREE.LineSegments(edges, material);
|
||
|
|
box.position.set(
|
||
|
|
(coord.x_upper + coord.x_lower) / 2,
|
||
|
|
(coord.y_upper + coord.y_lower) / 2,
|
||
|
|
(coord.z_upper + coord.z_lower) / 2
|
||
|
|
);
|
||
|
|
|
||
|
|
scene.add(box);
|
||
|
|
geometries.push(box);
|
||
|
|
|
||
|
|
addToggleControl(box, id);
|
||
|
|
}
|
||
|
|
|
||
|
|
function createTriangle(vertices, leafColor, id) {
|
||
|
|
const geometry = new THREE.BufferGeometry();
|
||
|
|
const verticesArray = new Float32Array(vertices.flat());
|
||
|
|
geometry.setAttribute('position', new THREE.BufferAttribute(verticesArray, 3));
|
||
|
|
const material = new THREE.MeshBasicMaterial({ color: leafColor, side: THREE.DoubleSide, wireframe: true });
|
||
|
|
const triangle = new THREE.Mesh(geometry, material);
|
||
|
|
scene.add(triangle);
|
||
|
|
geometries.push(triangle);
|
||
|
|
|
||
|
|
addToggleControl(triangle, id);
|
||
|
|
}
|
||
|
|
|
||
|
|
function createInstance(instanceProperties, color, id) {
|
||
|
|
const geometry = new THREE.SphereGeometry(0.1, 32, 32); // Create a sphere geometry
|
||
|
|
const material = new THREE.MeshBasicMaterial({ color: color, wireframe: false }); // Create a material with wireframe
|
||
|
|
const sphere = new THREE.Mesh(geometry, material); // Create a mesh
|
||
|
|
|
||
|
|
const { obj2world_p } = instanceProperties.part0;
|
||
|
|
sphere.position.set(obj2world_p[0], obj2world_p[1], obj2world_p[2]);
|
||
|
|
|
||
|
|
scene.add(sphere);
|
||
|
|
geometries.push(sphere);
|
||
|
|
|
||
|
|
addToggleControl(sphere, id);
|
||
|
|
}
|
||
|
|
function addToggleControl(geometry, id) {
|
||
|
|
const toggleContainer = document.createElement('div');
|
||
|
|
toggleContainer.className = 'toggle-container';
|
||
|
|
|
||
|
|
const checkbox = document.createElement('input');
|
||
|
|
checkbox.type = 'checkbox';
|
||
|
|
checkbox.checked = true;
|
||
|
|
checkbox.addEventListener('change', () => {
|
||
|
|
geometry.visible = checkbox.checked;
|
||
|
|
});
|
||
|
|
|
||
|
|
const labelElement = document.createElement('label');
|
||
|
|
labelElement.textContent = `${id}`;
|
||
|
|
|
||
|
|
toggleContainer.appendChild(checkbox);
|
||
|
|
toggleContainer.appendChild(labelElement);
|
||
|
|
document.getElementById('toggleControls').appendChild(toggleContainer);
|
||
|
|
}
|
||
|
|
|
||
|
|
function handleNode(node, parentType) {
|
||
|
|
const leafColor = 0xffa500;
|
||
|
|
const internalColor = 0x00ff00;
|
||
|
|
if (node.type === 'AnvInternalNode') {
|
||
|
|
// Check if the internal node is a fatLeaf
|
||
|
|
const isFatProceduralLeaf = node.properties.node_type.nodeType === 0x3;
|
||
|
|
const isFatInstanceLeaf = node.properties.node_type.nodeType === 0x1;
|
||
|
|
node.properties.child_data.forEach((child, index) => {
|
||
|
|
if (child.blockIncr !== 1 && child.blockIncr !== 2) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const childIsProcedural = child.startPrim === 0x3 || isFatProceduralLeaf;
|
||
|
|
const childIsInstance = child.startPrim === 0x1 || isFatInstanceLeaf;
|
||
|
|
const color = (childIsProcedural || childIsInstance) ? leafColor : internalColor;
|
||
|
|
let label = node.id + "'s child box";
|
||
|
|
label += (childIsProcedural) ? " also a procedural leaf" : "";
|
||
|
|
label += (childIsInstance) ? " also a instance leaf" : "";
|
||
|
|
createBox(node.properties.actual_coords[index], color, label);
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
switch (node.type) {
|
||
|
|
case 'AnvQuadLeafNode':
|
||
|
|
createTriangle(node.properties.v, leafColor, `Triangle. NodeID=${node.id}`);
|
||
|
|
break;
|
||
|
|
case 'AnvInstanceLeaf':
|
||
|
|
// Skip. Already drawn by parents
|
||
|
|
break;
|
||
|
|
case 'AnvAabbLeafNode':
|
||
|
|
// Skip. Already drawn by parents
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Draw AABB from header
|
||
|
|
const headerAABB = treeData.header.aabb;
|
||
|
|
createBox({
|
||
|
|
x_lower: headerAABB.min_x,
|
||
|
|
x_upper: headerAABB.max_x,
|
||
|
|
y_lower: headerAABB.min_y,
|
||
|
|
y_upper: headerAABB.max_y,
|
||
|
|
z_lower: headerAABB.min_z,
|
||
|
|
z_upper: headerAABB.max_z
|
||
|
|
}, 0xff00ff, 'Root AABB');
|
||
|
|
|
||
|
|
// Draw nodes
|
||
|
|
treeData.nodes.forEach(node => {
|
||
|
|
handleNode(node, node.properties.node_type);
|
||
|
|
});
|
||
|
|
|
||
|
|
camera.position.z = 5;
|
||
|
|
|
||
|
|
function animate() {
|
||
|
|
requestAnimationFrame(animate);
|
||
|
|
controls.update();
|
||
|
|
renderer.render(scene, camera);
|
||
|
|
}
|
||
|
|
animate();
|
||
|
|
}
|
||
|
|
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
|