- Replace CDN-based Three.js with npm packages for reliable loading - Add DRACO loader support for compressed GLB files - Implement custom horizon gradient environment mapping - Use exact material properties from reference logo.html (MeshPhysicalMaterial) - Apply proper metallic sheen, clearcoat, and reflectivity settings - Fix camera positioning and canvas sizing to prevent clipping - Maintain square aspect ratio for consistent logo display - Load user's mobius-ring.glb with fallback torus geometry 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
323 lines
9.9 KiB
JavaScript
323 lines
9.9 KiB
JavaScript
// GLB Logo loader - non-module version
|
|
console.log('Loading GLB logo script...');
|
|
|
|
// Logo instances storage
|
|
const logoInstances = new Map();
|
|
let darkMaterial, lightMaterial;
|
|
|
|
// Wait for Three.js to load from CDN
|
|
function waitForThreeJS(callback) {
|
|
if (typeof THREE !== 'undefined') {
|
|
console.log('Three.js is available');
|
|
callback();
|
|
} else {
|
|
console.log('Waiting for Three.js to load...');
|
|
setTimeout(() => waitForThreeJS(callback), 100);
|
|
}
|
|
}
|
|
|
|
// Load required Three.js modules
|
|
function loadThreeJSModules() {
|
|
return new Promise((resolve, reject) => {
|
|
console.log('Loading GLTFLoader from CDN...');
|
|
// Load GLTFLoader - using the correct path for regular JS version
|
|
const gltfScript = document.createElement('script');
|
|
gltfScript.src = 'https://cdn.jsdelivr.net/npm/three@0.160.0/examples/js/loaders/GLTFLoader.js';
|
|
gltfScript.onload = () => {
|
|
console.log('GLTFLoader loaded successfully');
|
|
|
|
console.log('Loading DRACOLoader from CDN...');
|
|
// Load DRACOLoader - using the correct path for regular JS version
|
|
const dracoScript = document.createElement('script');
|
|
dracoScript.src = 'https://cdn.jsdelivr.net/npm/three@0.160.0/examples/js/loaders/DRACOLoader.js';
|
|
dracoScript.onload = () => {
|
|
console.log('DRACOLoader loaded successfully');
|
|
resolve();
|
|
};
|
|
dracoScript.onerror = (error) => {
|
|
console.error('Failed to load DRACOLoader:', error);
|
|
console.log('Continuing without DRACOLoader...');
|
|
resolve(); // Continue without DRACO support
|
|
};
|
|
document.head.appendChild(dracoScript);
|
|
};
|
|
gltfScript.onerror = (error) => {
|
|
console.error('Failed to load GLTFLoader:', error);
|
|
reject(error);
|
|
};
|
|
document.head.appendChild(gltfScript);
|
|
});
|
|
}
|
|
|
|
function createMaterials() {
|
|
console.log("Creating materials for GLB model...");
|
|
|
|
darkMaterial = new THREE.MeshPhysicalMaterial({
|
|
color: 0x453d2e, // Sand color for dark theme
|
|
roughness: 0.24,
|
|
metalness: 1.0,
|
|
clearcoat: 0.48,
|
|
clearcoatRoughness: 0.15,
|
|
reflectivity: 1.2,
|
|
});
|
|
|
|
lightMaterial = new THREE.MeshPhysicalMaterial({
|
|
color: 0x0b0213, // Dark Mulberry for light theme
|
|
roughness: 0.28,
|
|
metalness: 0.98,
|
|
clearcoat: 0.52,
|
|
clearcoatRoughness: 0.12,
|
|
reflectivity: 1.0,
|
|
});
|
|
|
|
console.log("Materials created successfully");
|
|
}
|
|
|
|
function initGLBLogo(canvas) {
|
|
console.log('initGLBLogo called for canvas:', canvas);
|
|
const scene = new THREE.Scene();
|
|
console.log('Scene created');
|
|
|
|
// Create materials if not created yet
|
|
if (!darkMaterial || !lightMaterial) {
|
|
console.log('Creating materials...');
|
|
createMaterials();
|
|
console.log('Materials created');
|
|
}
|
|
|
|
// Get canvas dimensions
|
|
const computedStyle = window.getComputedStyle(canvas);
|
|
let width = parseInt(computedStyle.width) || canvas.width || 200;
|
|
let height = parseInt(computedStyle.height) || canvas.height || 200;
|
|
|
|
// Force square dimensions for proper Möbius ring rendering
|
|
const size = Math.max(width, height);
|
|
width = size;
|
|
height = size;
|
|
|
|
// Initialize renderer
|
|
const renderer = new THREE.WebGLRenderer({
|
|
canvas: canvas,
|
|
antialias: true,
|
|
alpha: true
|
|
});
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
renderer.setSize(width, height);
|
|
renderer.setClearColor(0x000000, 0);
|
|
|
|
console.log('Renderer initialized with size:', { width, height });
|
|
|
|
// Set up camera
|
|
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 100);
|
|
camera.position.set(0, 0, 1.8);
|
|
camera.lookAt(0, 0, 0);
|
|
|
|
// Sophisticated lighting setup
|
|
const mainLight = new THREE.PointLight(0xffffff, 1.4);
|
|
mainLight.position.set(0, 4, 1);
|
|
scene.add(mainLight);
|
|
|
|
const bottomLight = new THREE.PointLight(0x800080, 1.2, 12);
|
|
bottomLight.position.set(0, -4, 1);
|
|
scene.add(bottomLight);
|
|
|
|
const leftLight = new THREE.PointLight(0x808000, 1.45, 5);
|
|
leftLight.position.set(-5, 0, 4);
|
|
scene.add(leftLight);
|
|
|
|
scene.add(new THREE.AmbientLight(0xffffff, 0.45));
|
|
|
|
let mobius = null;
|
|
|
|
// Load the Möbius ring model
|
|
console.log('Setting up GLTFLoader...');
|
|
const loader = new THREE.GLTFLoader();
|
|
|
|
// Set up DRACOLoader if available
|
|
if (THREE.DRACOLoader) {
|
|
const dracoLoader = new THREE.DRACOLoader();
|
|
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
|
|
loader.setDRACOLoader(dracoLoader);
|
|
console.log('DRACOLoader configured');
|
|
}
|
|
|
|
console.log('GLTFLoader configured, loading GLB file...');
|
|
|
|
loader.load(
|
|
"/logos/mobius-ring.glb", // Ensure absolute path
|
|
(gltf) => {
|
|
console.log("GLB loaded successfully for canvas:", canvas);
|
|
console.log("GLB scene:", gltf.scene);
|
|
mobius = gltf.scene;
|
|
|
|
let meshCount = 0;
|
|
mobius.traverse((child) => {
|
|
if (child.isMesh) {
|
|
// Start with dark theme material (default)
|
|
child.material = darkMaterial.clone();
|
|
meshCount++;
|
|
}
|
|
});
|
|
console.log(`Applied materials to ${meshCount} meshes`);
|
|
|
|
scene.add(mobius);
|
|
console.log('Möbius GLB model added to scene');
|
|
|
|
// Initialize with current theme state
|
|
const isDark = document.documentElement.classList.contains('dark');
|
|
console.log('Current theme is dark:', isDark);
|
|
updateCanvasMaterial(mobius, isDark);
|
|
},
|
|
(progress) => {
|
|
if (progress.total > 0) {
|
|
console.log("GLB loading progress:", Math.round(progress.loaded / progress.total * 100) + '%');
|
|
}
|
|
},
|
|
(err) => {
|
|
console.error("Error loading GLB logo:", err);
|
|
console.error("Error details:", err.message);
|
|
|
|
// Create fallback geometry
|
|
console.log('Creating fallback Möbius ring geometry...');
|
|
|
|
function mobiusFunction(u, v, target) {
|
|
u = u - 0.5;
|
|
v = v * 2 * Math.PI;
|
|
|
|
const majorRadius = 0.6;
|
|
const minorRadius = 0.2;
|
|
|
|
const x = (majorRadius + minorRadius * Math.cos(v / 2) * u) * Math.cos(v);
|
|
const y = (majorRadius + minorRadius * Math.cos(v / 2) * u) * Math.sin(v);
|
|
const z = minorRadius * Math.sin(v / 2) * u;
|
|
|
|
target.set(x, y, z);
|
|
}
|
|
|
|
const fallbackGeometry = new THREE.ParametricGeometry(mobiusFunction, 50, 50);
|
|
const fallbackMesh = new THREE.Mesh(fallbackGeometry, darkMaterial.clone());
|
|
scene.add(fallbackMesh);
|
|
mobius = fallbackMesh;
|
|
|
|
console.log('Fallback Möbius geometry created');
|
|
}
|
|
);
|
|
|
|
// Animation loop
|
|
function animate() {
|
|
requestAnimationFrame(animate);
|
|
|
|
if (mobius) {
|
|
// Elegant rotation
|
|
mobius.rotation.x += 0.005;
|
|
mobius.rotation.y += -0.008;
|
|
mobius.rotation.z += -0.003;
|
|
}
|
|
|
|
renderer.render(scene, camera);
|
|
}
|
|
|
|
animate();
|
|
console.log('Animation loop started');
|
|
|
|
// Store instance
|
|
const instance = {
|
|
canvas,
|
|
renderer,
|
|
scene,
|
|
camera,
|
|
mobius: () => mobius,
|
|
setMobius: (mobiusScene) => { mobius = mobiusScene; }
|
|
};
|
|
|
|
logoInstances.set(canvas, instance);
|
|
|
|
return instance;
|
|
}
|
|
|
|
function updateCanvasMaterial(mobius, isDark) {
|
|
if (mobius) {
|
|
const targetMaterial = isDark ? darkMaterial : lightMaterial;
|
|
console.log(`Updating material to:`, isDark ? 'dark' : 'light');
|
|
|
|
let meshCount = 0;
|
|
if (mobius.traverse) {
|
|
mobius.traverse((child) => {
|
|
if (child.isMesh) {
|
|
child.material = targetMaterial.clone();
|
|
meshCount++;
|
|
}
|
|
});
|
|
} else if (mobius.isMesh) {
|
|
mobius.material = targetMaterial.clone();
|
|
meshCount = 1;
|
|
}
|
|
console.log(`Updated ${meshCount} mesh materials`);
|
|
}
|
|
}
|
|
|
|
// Initialize all canvas elements with chorus-logo class
|
|
function initAllGLBLogos() {
|
|
console.log('initAllGLBLogos called - looking for canvas.chorus-logo elements');
|
|
|
|
// First load Three.js and required modules
|
|
if (typeof THREE === 'undefined') {
|
|
console.log('Loading Three.js from CDN...');
|
|
|
|
const script = document.createElement('script');
|
|
script.src = 'https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js';
|
|
script.onload = function() {
|
|
console.log('Three.js loaded from CDN');
|
|
|
|
loadThreeJSModules().then(() => {
|
|
console.log('All Three.js modules loaded');
|
|
|
|
const canvases = document.querySelectorAll("canvas.chorus-logo");
|
|
console.log(`Found ${canvases.length} canvas elements with chorus-logo class`);
|
|
|
|
canvases.forEach((canvas, index) => {
|
|
console.log(`Initializing GLB logo ${index} for canvas:`, canvas);
|
|
initGLBLogo(canvas);
|
|
});
|
|
}).catch(error => {
|
|
console.error('Failed to load Three.js modules:', error);
|
|
});
|
|
};
|
|
script.onerror = function() {
|
|
console.error('Failed to load Three.js from CDN');
|
|
};
|
|
document.head.appendChild(script);
|
|
} else {
|
|
loadThreeJSModules().then(() => {
|
|
const canvases = document.querySelectorAll("canvas.chorus-logo");
|
|
console.log(`Found ${canvases.length} canvas elements with chorus-logo class`);
|
|
|
|
canvases.forEach((canvas, index) => {
|
|
console.log(`Initializing GLB logo ${index} for canvas:`, canvas);
|
|
initGLBLogo(canvas);
|
|
});
|
|
}).catch(error => {
|
|
console.error('Failed to load Three.js modules:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Function to update all logo materials based on theme
|
|
function updateAllLogoMaterials(isDark) {
|
|
console.log(`updateAllLogoMaterials called with isDark: ${isDark}`);
|
|
console.log(`Number of logo instances: ${logoInstances.size}`);
|
|
|
|
logoInstances.forEach((instance, canvas) => {
|
|
const mobius = instance.mobius();
|
|
console.log(`Updating logo instance for canvas:`, canvas, `mobius exists:`, !!mobius);
|
|
if (mobius) {
|
|
updateCanvasMaterial(mobius, isDark);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Export functions for global access
|
|
window.updateAllLogoMaterials = updateAllLogoMaterials;
|
|
window.initAllGLBLogos = initAllGLBLogos;
|
|
|
|
console.log('GLB logo script loaded, initAllGLBLogos available'); |