Files
chorus-services/brand-assets/brand-style-guide-site/public/logo-glb-simple.js
tony d83dbdce46 feat: Implement proper Three.js logo system with custom environment mapping
- 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>
2025-08-24 14:13:29 +10:00

288 lines
8.5 KiB
JavaScript

// Simple GLB Logo loader using ES modules via dynamic import
console.log('Loading simple GLB logo script...');
// Logo instances storage
const logoInstances = new Map();
let darkMaterial, lightMaterial;
async function loadThreeJSAndGLTF() {
try {
console.log('Loading Three.js modules dynamically...');
// Dynamically import Three.js modules
const THREE = await import('https://unpkg.com/three@0.160.0/build/three.module.js');
const { GLTFLoader } = await import('https://unpkg.com/three@0.160.0/examples/jsm/loaders/GLTFLoader.js');
const { DRACOLoader } = await import('https://unpkg.com/three@0.160.0/examples/jsm/loaders/DRACOLoader.js');
console.log('Three.js modules loaded successfully');
// Make THREE available globally
window.THREE = THREE;
window.GLTFLoader = GLTFLoader;
window.DRACOLoader = DRACOLoader;
return { THREE, GLTFLoader, DRACOLoader };
} catch (error) {
console.error('Failed to load Three.js modules:', error);
throw error;
}
}
function createMaterials(THREE) {
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");
}
async function initGLBLogo(canvas, THREE, GLTFLoader, DRACOLoader) {
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(THREE);
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 GLTFLoader();
// Set up DRACOLoader if available
try {
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
loader.setDRACOLoader(dracoLoader);
console.log('DRACOLoader configured');
} catch (error) {
console.log('DRACOLoader not available, continuing without it');
}
console.log('GLTFLoader configured, loading GLB file...');
try {
const gltf = await new Promise((resolve, reject) => {
loader.load(
"/logos/mobius-ring.glb", // Ensure absolute path
resolve,
(progress) => {
if (progress.total > 0) {
console.log("GLB loading progress:", Math.round(progress.loaded / progress.total * 100) + '%');
}
},
reject
);
});
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);
} catch (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
async function initAllGLBLogos() {
console.log('initAllGLBLogos called - looking for canvas.chorus-logo elements');
try {
// Load Three.js and modules first
const { THREE, GLTFLoader, DRACOLoader } = await loadThreeJSAndGLTF();
console.log('All Three.js modules loaded successfully');
const canvases = document.querySelectorAll("canvas.chorus-logo");
console.log(`Found ${canvases.length} canvas elements with chorus-logo class`);
// Initialize all canvases
for (let i = 0; i < canvases.length; i++) {
const canvas = canvases[i];
console.log(`Initializing GLB logo ${i} for canvas:`, canvas);
await initGLBLogo(canvas, THREE, GLTFLoader, DRACOLoader);
}
console.log('All GLB logos initialized successfully');
} catch (error) {
console.error('Failed to initialize GLB logos:', 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.initAllGLBLogosSimple = initAllGLBLogos;
console.log('Simple GLB logo script loaded, initAllGLBLogosSimple available');