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>
This commit is contained in:
307
brand-assets/brand-style-guide-site/public/logo-local.js
Normal file
307
brand-assets/brand-style-guide-site/public/logo-local.js
Normal file
@@ -0,0 +1,307 @@
|
||||
// Local GLB Logo loader - using local Three.js files
|
||||
console.log('Loading local GLB logo script...');
|
||||
|
||||
// Logo instances storage
|
||||
const logoInstances = new Map();
|
||||
let darkMaterial, lightMaterial;
|
||||
|
||||
// Simple script loader helper
|
||||
function loadScript(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
script.onload = () => {
|
||||
console.log(`Successfully loaded: ${src}`);
|
||||
resolve();
|
||||
};
|
||||
script.onerror = (error) => {
|
||||
console.error(`Failed to load: ${src}`, error);
|
||||
reject(error);
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadThreeJS() {
|
||||
try {
|
||||
console.log('Loading Three.js from local files...');
|
||||
await loadScript('/js/three/three.min.js');
|
||||
console.log('Three.js loaded successfully');
|
||||
|
||||
console.log('Loading GLTFLoader from local files...');
|
||||
await loadScript('/js/three/GLTFLoader.js');
|
||||
console.log('GLTFLoader loaded successfully');
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to load Three.js from local files:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function createMaterials() {
|
||||
console.log("Creating materials for GLB model...");
|
||||
|
||||
darkMaterial = new THREE.MeshPhongMaterial({
|
||||
color: 0x453d2e, // Sand color for dark theme
|
||||
shininess: 100,
|
||||
specular: 0x333333
|
||||
});
|
||||
|
||||
lightMaterial = new THREE.MeshPhongMaterial({
|
||||
color: 0x0b0213, // Dark Mulberry for light theme
|
||||
shininess: 80,
|
||||
specular: 0x222222
|
||||
});
|
||||
|
||||
console.log("Materials created successfully");
|
||||
}
|
||||
|
||||
async function initLocalLogo(canvas) {
|
||||
console.log('initLocalLogo 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;
|
||||
|
||||
// Try to load the GLB file
|
||||
console.log('Attempting to load your GLB file...');
|
||||
|
||||
try {
|
||||
if (THREE.GLTFLoader) {
|
||||
console.log('GLTFLoader is available, loading your mobius-ring.glb...');
|
||||
const loader = new THREE.GLTFLoader();
|
||||
|
||||
const gltf = await new Promise((resolve, reject) => {
|
||||
loader.load(
|
||||
"/logos/mobius-ring.glb",
|
||||
(gltf) => {
|
||||
console.log("SUCCESS: Your GLB loaded!", gltf);
|
||||
resolve(gltf);
|
||||
},
|
||||
(progress) => {
|
||||
if (progress.total > 0) {
|
||||
const percent = Math.round(progress.loaded / progress.total * 100);
|
||||
console.log(`GLB loading progress: ${percent}% (${progress.loaded}/${progress.total} bytes)`);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.error("GLB loading error:", error);
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
console.log("🎉 Your GLB loaded successfully for canvas:", canvas);
|
||||
console.log("GLB scene contains:", gltf.scene);
|
||||
console.log("GLB animations:", gltf.animations);
|
||||
console.log("GLB scene children:", gltf.scene.children);
|
||||
|
||||
mobius = gltf.scene;
|
||||
|
||||
let meshCount = 0;
|
||||
mobius.traverse((child) => {
|
||||
console.log("GLB child:", child.type, child.name || 'unnamed');
|
||||
if (child.isMesh) {
|
||||
console.log("Found mesh:", child.name || 'unnamed mesh', child.geometry, child.material);
|
||||
// Start with dark theme material (default)
|
||||
child.material = darkMaterial.clone();
|
||||
meshCount++;
|
||||
}
|
||||
});
|
||||
console.log(`✅ Applied materials to ${meshCount} meshes in your GLB`);
|
||||
|
||||
scene.add(mobius);
|
||||
console.log('🎯 Your 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);
|
||||
|
||||
} else {
|
||||
throw new Error('GLTFLoader not available - this should not happen');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error("❌ Error loading your GLB logo:", err);
|
||||
console.error("Error details:", err.message);
|
||||
console.error("Error stack:", err.stack);
|
||||
console.log('Creating fallback Möbius ring geometry...');
|
||||
|
||||
// Create fallback procedural Möbius ring
|
||||
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 (procedural)');
|
||||
}
|
||||
|
||||
// Animation loop
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
if (mobius) {
|
||||
// Elegant rotation to show the topology
|
||||
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 initAllLocalLogos() {
|
||||
console.log('🚀 initAllLocalLogos called - looking for canvas.chorus-logo elements');
|
||||
|
||||
// First load Three.js from local files
|
||||
const loaded = await loadThreeJS();
|
||||
if (!loaded) {
|
||||
console.error('❌ Failed to load Three.js from local files, cannot initialize logos');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Three.js loaded successfully from local files');
|
||||
|
||||
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 local logo ${i} for canvas:`, canvas);
|
||||
try {
|
||||
await initLocalLogo(canvas);
|
||||
console.log(`✅ Logo ${i} initialized successfully`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to initialize logo ${i}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🎉 All local logos processing complete');
|
||||
}
|
||||
|
||||
// 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.initAllLocalLogos = initAllLocalLogos;
|
||||
|
||||
console.log('✅ Local GLB logo script loaded, initAllLocalLogos available');
|
||||
Reference in New Issue
Block a user