u
This commit is contained in:
23
cube.mtl
23
cube.mtl
@@ -7,17 +7,28 @@ Ka 0.000000 0.000000 0.000000
|
|||||||
Kd 1.000000 1.000000 1.000000
|
Kd 1.000000 1.000000 1.000000
|
||||||
Ks 0.330000 0.330000 0.330000
|
Ks 0.330000 0.330000 0.330000
|
||||||
|
|
||||||
newmtl Glass_Basic_01
|
newmtl BackColor
|
||||||
Ka 0.000000 0.000000 0.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
Kd 0.631373 0.678431 0.729412
|
Kd 0.643137 0.698039 0.733333
|
||||||
Ks 0.330000 0.330000 0.330000
|
Ks 0.330000 0.330000 0.330000
|
||||||
d 0.250000
|
|
||||||
|
|
||||||
newmtl Glass_Mirror_01
|
newmtl Carpet_01_1K
|
||||||
Ka 0.000000 0.000000 0.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
Kd 0.725490 0.725490 0.725490
|
Kd 0.768627 0.764706 0.756863
|
||||||
Ks 0.330000 0.330000 0.330000
|
Ks 0.330000 0.330000 0.330000
|
||||||
map_Kd cube/Glass_Mirror_01.jpg
|
map_Kd cube/Carpet_01_1K.png
|
||||||
|
|
||||||
|
newmtl Marble_01_1K
|
||||||
|
Ka 0.000000 0.000000 0.000000
|
||||||
|
Kd 0.901961 0.866667 0.827451
|
||||||
|
Ks 0.330000 0.330000 0.330000
|
||||||
|
map_Kd cube/Marble_01_1K.png
|
||||||
|
|
||||||
|
newmtl Denim_03_1K
|
||||||
|
Ka 0.000000 0.000000 0.000000
|
||||||
|
Kd 0.364706 0.415686 0.466667
|
||||||
|
Ks 0.330000 0.330000 0.330000
|
||||||
|
map_Kd cube/Denim_03_1K.png
|
||||||
|
|
||||||
newmtl Wood_Veneer_15_1K
|
newmtl Wood_Veneer_15_1K
|
||||||
Ka 0.000000 0.000000 0.000000
|
Ka 0.000000 0.000000 0.000000
|
||||||
|
|||||||
BIN
cube/Carpet_01_1K.png
Normal file
BIN
cube/Carpet_01_1K.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
cube/Denim_03_1K.png
Normal file
BIN
cube/Denim_03_1K.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
BIN
cube/Marble_01_1K.png
Normal file
BIN
cube/Marble_01_1K.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 975 KiB |
633
index.html
633
index.html
@@ -1,633 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>3D Cube Viewer</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
#container {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#info {
|
|
||||||
position: absolute;
|
|
||||||
top: 20px;
|
|
||||||
left: 20px;
|
|
||||||
color: white;
|
|
||||||
z-index: 100;
|
|
||||||
background: rgba(0,0,0,0.5);
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#loading {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
color: white;
|
|
||||||
font-size: 24px;
|
|
||||||
z-index: 200;
|
|
||||||
background: rgba(0,0,0,0.7);
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-spinner {
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
|
||||||
border-radius: 50%;
|
|
||||||
border-top: 3px solid #ffffff;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin: 0 auto 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="container">
|
|
||||||
<div id="loading">
|
|
||||||
<div class="loading-spinner"></div>
|
|
||||||
Loading 3D Model...
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="info">
|
|
||||||
<h2>3D Cube Viewer</h2>
|
|
||||||
<p>🖱️ Left click + drag: Rotate around model</p>
|
|
||||||
<p>🖱️ Right click + drag: Pan</p>
|
|
||||||
<p>⚙️ Mouse wheel: Zoom in/out</p>
|
|
||||||
<p>📱 Touch drag: Rotate around model</p>
|
|
||||||
<p>🤏 Pinch: Zoom in/out</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Three.js Core -->
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
|
||||||
<!-- OBJ Loader -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/OBJLoader.js"></script>
|
|
||||||
<!-- MTL Loader -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/MTLLoader.js"></script>
|
|
||||||
<!-- Orbit Controls -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Global variables
|
|
||||||
let scene, camera, renderer, controls;
|
|
||||||
let cube;
|
|
||||||
|
|
||||||
// Touch controls variables
|
|
||||||
let touchState = {
|
|
||||||
touching: false,
|
|
||||||
touchCount: 0,
|
|
||||||
lastTouches: [],
|
|
||||||
initialDistance: 0,
|
|
||||||
lastDistance: 0,
|
|
||||||
rotationStart: { x: 0, y: 0 },
|
|
||||||
lastRotation: { x: 0, y: 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize the 3D scene
|
|
||||||
function init() {
|
|
||||||
// Create container
|
|
||||||
const container = document.getElementById('container');
|
|
||||||
|
|
||||||
// Create scene
|
|
||||||
scene = new THREE.Scene();
|
|
||||||
scene.background = new THREE.Color(0x2c3e50);
|
|
||||||
|
|
||||||
// Create camera
|
|
||||||
camera = new THREE.PerspectiveCamera(
|
|
||||||
75, // Field of view
|
|
||||||
window.innerWidth / window.innerHeight, // Aspect ratio
|
|
||||||
0.1, // Near clipping plane
|
|
||||||
1000 // Far clipping plane
|
|
||||||
);
|
|
||||||
camera.position.set(20, 20, 20);
|
|
||||||
camera.lookAt(0, 0, 0);
|
|
||||||
|
|
||||||
// Create renderer
|
|
||||||
renderer = new THREE.WebGLRenderer({
|
|
||||||
antialias: true,
|
|
||||||
alpha: true
|
|
||||||
});
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
renderer.shadowMap.enabled = true;
|
|
||||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
||||||
renderer.outputEncoding = THREE.sRGBEncoding;
|
|
||||||
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
||||||
renderer.toneMappingExposure = 1;
|
|
||||||
|
|
||||||
// Enable proper transparency rendering
|
|
||||||
renderer.sortObjects = true;
|
|
||||||
renderer.autoClear = false;
|
|
||||||
|
|
||||||
container.appendChild(renderer.domElement);
|
|
||||||
|
|
||||||
// Add orbit controls
|
|
||||||
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
|
||||||
controls.enableDamping = true;
|
|
||||||
controls.dampingFactor = 0.05;
|
|
||||||
controls.enableZoom = true;
|
|
||||||
controls.enablePan = true;
|
|
||||||
controls.enableRotate = true;
|
|
||||||
|
|
||||||
// Setup touch controls
|
|
||||||
setupTouchControls(renderer.domElement);
|
|
||||||
|
|
||||||
// Add lighting
|
|
||||||
setupLighting();
|
|
||||||
|
|
||||||
// Load the cube model
|
|
||||||
loadCubeModel();
|
|
||||||
|
|
||||||
// Handle window resize
|
|
||||||
window.addEventListener('resize', onWindowResize, false);
|
|
||||||
|
|
||||||
// Start render loop
|
|
||||||
animate();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupLighting() {
|
|
||||||
// Ambient light for overall illumination
|
|
||||||
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
|
|
||||||
scene.add(ambientLight);
|
|
||||||
|
|
||||||
// Directional light (like sunlight)
|
|
||||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
|
||||||
directionalLight.position.set(10, 10, 5);
|
|
||||||
directionalLight.castShadow = true;
|
|
||||||
directionalLight.shadow.mapSize.width = 2048;
|
|
||||||
directionalLight.shadow.mapSize.height = 2048;
|
|
||||||
scene.add(directionalLight);
|
|
||||||
|
|
||||||
// Point light for additional illumination
|
|
||||||
const pointLight = new THREE.PointLight(0xffffff, 0.6, 100);
|
|
||||||
pointLight.position.set(-10, 10, 10);
|
|
||||||
scene.add(pointLight);
|
|
||||||
|
|
||||||
// Interior point lights (will be repositioned when model loads)
|
|
||||||
window.interiorLight = new THREE.PointLight(0xffffff, 0.5, 50);
|
|
||||||
window.interiorLight.position.set(0, 0, 0); // Will be moved to model center
|
|
||||||
scene.add(window.interiorLight);
|
|
||||||
|
|
||||||
// Additional interior light for better coverage
|
|
||||||
window.interiorLight2 = new THREE.PointLight(0xffffff, 0.3, 30);
|
|
||||||
window.interiorLight2.position.set(5, 5, -5); // Will be adjusted relative to model
|
|
||||||
scene.add(window.interiorLight2);
|
|
||||||
|
|
||||||
// Hemisphere light for natural lighting
|
|
||||||
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.5);
|
|
||||||
scene.add(hemiLight);
|
|
||||||
|
|
||||||
// Create environment map for glass reflections
|
|
||||||
setupEnvironmentMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupEnvironmentMap() {
|
|
||||||
// Create a simple cube texture for reflections (makes glass more visible)
|
|
||||||
const cubeTextureLoader = new THREE.CubeTextureLoader();
|
|
||||||
|
|
||||||
// Create a procedural cube map for reflections
|
|
||||||
const envMapRenderTarget = new THREE.WebGLCubeRenderTarget(256, {
|
|
||||||
format: THREE.RGBFormat,
|
|
||||||
generateMipmaps: true,
|
|
||||||
minFilter: THREE.LinearMipmapLinearFilter
|
|
||||||
});
|
|
||||||
|
|
||||||
// Simple gradient environment for reflections
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
canvas.width = 256;
|
|
||||||
canvas.height = 256;
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Create gradient for reflections
|
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, 256);
|
|
||||||
gradient.addColorStop(0, '#87CEEB'); // Sky blue
|
|
||||||
gradient.addColorStop(0.5, '#F0F8FF'); // Alice blue
|
|
||||||
gradient.addColorStop(1, '#4682B4'); // Steel blue
|
|
||||||
|
|
||||||
ctx.fillStyle = gradient;
|
|
||||||
ctx.fillRect(0, 0, 256, 256);
|
|
||||||
|
|
||||||
// Convert to texture
|
|
||||||
const texture = new THREE.CanvasTexture(canvas);
|
|
||||||
texture.mapping = THREE.EquirectangularReflectionMapping;
|
|
||||||
|
|
||||||
// Store for glass materials
|
|
||||||
window.environmentMap = texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupTouchControls(domElement) {
|
|
||||||
// Touch start event
|
|
||||||
domElement.addEventListener('touchstart', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
touchState.touching = true;
|
|
||||||
touchState.touchCount = event.touches.length;
|
|
||||||
touchState.lastTouches = Array.from(event.touches);
|
|
||||||
|
|
||||||
if (event.touches.length === 1) {
|
|
||||||
// Single touch - prepare for rotation
|
|
||||||
const touch = event.touches[0];
|
|
||||||
touchState.rotationStart.x = touch.clientX;
|
|
||||||
touchState.rotationStart.y = touch.clientY;
|
|
||||||
touchState.lastRotation.x = touch.clientX;
|
|
||||||
touchState.lastRotation.y = touch.clientY;
|
|
||||||
|
|
||||||
// Disable OrbitControls to prevent conflicts
|
|
||||||
controls.enabled = false;
|
|
||||||
} else if (event.touches.length === 2) {
|
|
||||||
// Two touches - prepare for pinch zoom
|
|
||||||
const touch1 = event.touches[0];
|
|
||||||
const touch2 = event.touches[1];
|
|
||||||
|
|
||||||
touchState.initialDistance = getTouchDistance(touch1, touch2);
|
|
||||||
touchState.lastDistance = touchState.initialDistance;
|
|
||||||
|
|
||||||
// Disable OrbitControls to prevent conflicts
|
|
||||||
controls.enabled = false;
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Touch move event
|
|
||||||
domElement.addEventListener('touchmove', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (!touchState.touching) return;
|
|
||||||
|
|
||||||
if (event.touches.length === 1 && touchState.touchCount === 1) {
|
|
||||||
// Single touch drag - rotation
|
|
||||||
const touch = event.touches[0];
|
|
||||||
const deltaX = touch.clientX - touchState.lastRotation.x;
|
|
||||||
const deltaY = touch.clientY - touchState.lastRotation.y;
|
|
||||||
|
|
||||||
// Apply rotation to the controls
|
|
||||||
const rotationSpeed = 0.005;
|
|
||||||
controls.enabled = true; // Temporarily enable to apply rotation
|
|
||||||
|
|
||||||
// Simulate mouse movement for OrbitControls
|
|
||||||
const spherical = new THREE.Spherical();
|
|
||||||
spherical.setFromVector3(camera.position.clone().sub(controls.target));
|
|
||||||
|
|
||||||
spherical.theta -= deltaX * rotationSpeed;
|
|
||||||
spherical.phi += deltaY * rotationSpeed;
|
|
||||||
|
|
||||||
// Constrain phi to prevent camera flipping
|
|
||||||
spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi));
|
|
||||||
|
|
||||||
const newPosition = new THREE.Vector3();
|
|
||||||
newPosition.setFromSpherical(spherical).add(controls.target);
|
|
||||||
camera.position.copy(newPosition);
|
|
||||||
camera.lookAt(controls.target);
|
|
||||||
|
|
||||||
controls.enabled = false; // Disable again to prevent conflicts
|
|
||||||
|
|
||||||
touchState.lastRotation.x = touch.clientX;
|
|
||||||
touchState.lastRotation.y = touch.clientY;
|
|
||||||
} else if (event.touches.length === 2) {
|
|
||||||
// Two touches - pinch zoom
|
|
||||||
const touch1 = event.touches[0];
|
|
||||||
const touch2 = event.touches[1];
|
|
||||||
const currentDistance = getTouchDistance(touch1, touch2);
|
|
||||||
|
|
||||||
const scaleFactor = currentDistance / touchState.lastDistance;
|
|
||||||
|
|
||||||
// Apply zoom
|
|
||||||
const zoomSpeed = 0.1;
|
|
||||||
const direction = camera.position.clone().sub(controls.target).normalize();
|
|
||||||
const currentDistance3D = camera.position.distanceTo(controls.target);
|
|
||||||
|
|
||||||
let newDistance;
|
|
||||||
if (scaleFactor > 1) {
|
|
||||||
// Pinching out - zoom in
|
|
||||||
newDistance = Math.max(1, currentDistance3D * (1 - zoomSpeed * (scaleFactor - 1)));
|
|
||||||
} else {
|
|
||||||
// Pinching in - zoom out
|
|
||||||
newDistance = currentDistance3D * (1 + zoomSpeed * (1 - scaleFactor));
|
|
||||||
}
|
|
||||||
|
|
||||||
camera.position.copy(controls.target.clone().add(direction.multiplyScalar(newDistance)));
|
|
||||||
|
|
||||||
touchState.lastDistance = currentDistance;
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Touch end event
|
|
||||||
domElement.addEventListener('touchend', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
touchState.touchCount = event.touches.length;
|
|
||||||
|
|
||||||
if (event.touches.length === 0) {
|
|
||||||
// No more touches - re-enable OrbitControls
|
|
||||||
touchState.touching = false;
|
|
||||||
controls.enabled = true;
|
|
||||||
} else if (event.touches.length === 1 && touchState.touchCount > 1) {
|
|
||||||
// Switched from multi-touch to single touch
|
|
||||||
const touch = event.touches[0];
|
|
||||||
touchState.rotationStart.x = touch.clientX;
|
|
||||||
touchState.rotationStart.y = touch.clientY;
|
|
||||||
touchState.lastRotation.x = touch.clientX;
|
|
||||||
touchState.lastRotation.y = touch.clientY;
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Prevent context menu on long press
|
|
||||||
domElement.addEventListener('contextmenu', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTouchDistance(touch1, touch2) {
|
|
||||||
const dx = touch1.clientX - touch2.clientX;
|
|
||||||
const dy = touch1.clientY - touch2.clientY;
|
|
||||||
return Math.sqrt(dx * dx + dy * dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadCubeModel() {
|
|
||||||
// Create loaders
|
|
||||||
const mtlLoader = new THREE.MTLLoader();
|
|
||||||
const objLoader = new THREE.OBJLoader();
|
|
||||||
|
|
||||||
// Load material file first
|
|
||||||
mtlLoader.load('cube.mtl', function(materials) {
|
|
||||||
materials.preload();
|
|
||||||
|
|
||||||
// Configure materials for double-sided rendering with proper transparency
|
|
||||||
Object.keys(materials.materials).forEach(function(key) {
|
|
||||||
const material = materials.materials[key];
|
|
||||||
material.side = THREE.DoubleSide;
|
|
||||||
|
|
||||||
// Handle glass materials specially
|
|
||||||
if (key.toLowerCase().includes('glass')) {
|
|
||||||
material.transparent = true;
|
|
||||||
|
|
||||||
// Make glass more visible - realistic glass opacity
|
|
||||||
material.opacity = 0.7; // More visible than original MTL setting
|
|
||||||
|
|
||||||
// Enhanced glass properties for better visibility
|
|
||||||
material.shininess = 100;
|
|
||||||
material.reflectivity = 0.8;
|
|
||||||
|
|
||||||
// Glass-like color with slight tint for visibility
|
|
||||||
material.color = new THREE.Color(0.9, 0.95, 1.0);
|
|
||||||
|
|
||||||
// Add specular highlights to make glass more apparent
|
|
||||||
material.specular = new THREE.Color(0.8, 0.9, 1.0);
|
|
||||||
|
|
||||||
// Slight emissive to make glass edges more visible
|
|
||||||
if (material.emissive) {
|
|
||||||
material.emissive.setHex(0x001122); // Very subtle blue glow
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply environment map for reflections
|
|
||||||
if (window.environmentMap) {
|
|
||||||
material.envMap = window.environmentMap;
|
|
||||||
material.envMapIntensity = 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Glass material '${key}' configured with opacity: ${material.opacity}`);
|
|
||||||
} else {
|
|
||||||
// Non-glass materials
|
|
||||||
material.transparent = false;
|
|
||||||
material.opacity = 1.0;
|
|
||||||
|
|
||||||
// Add slight emissive for interior visibility on solid materials
|
|
||||||
if (material.emissive) {
|
|
||||||
material.emissive.setHex(0x111111);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
material.needsUpdate = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set materials to OBJ loader
|
|
||||||
objLoader.setMaterials(materials);
|
|
||||||
|
|
||||||
// Load the OBJ file
|
|
||||||
objLoader.load('cube.obj', function(object) {
|
|
||||||
// Scale and position the model
|
|
||||||
object.scale.set(0.5, 0.5, 0.5);
|
|
||||||
object.position.set(0, 0, 0);
|
|
||||||
|
|
||||||
// Enable shadows and fix materials for interior faces
|
|
||||||
object.traverse(function(child) {
|
|
||||||
if (child instanceof THREE.Mesh) {
|
|
||||||
child.castShadow = true;
|
|
||||||
child.receiveShadow = true;
|
|
||||||
|
|
||||||
// Special handling for glass materials to improve visibility
|
|
||||||
if (child.material && child.material.name && child.material.name.toLowerCase().includes('glass')) {
|
|
||||||
// Glass-specific settings
|
|
||||||
child.material.side = THREE.DoubleSide;
|
|
||||||
child.material.transparent = true;
|
|
||||||
child.material.opacity = 0.7;
|
|
||||||
child.material.alphaTest = 0.1;
|
|
||||||
|
|
||||||
// Enhanced reflective properties
|
|
||||||
child.material.envMapIntensity = 0.9;
|
|
||||||
|
|
||||||
// Apply environment map for reflections
|
|
||||||
if (window.environmentMap) {
|
|
||||||
child.material.envMap = window.environmentMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable shadow casting for glass (more realistic)
|
|
||||||
child.castShadow = false;
|
|
||||||
child.receiveShadow = true;
|
|
||||||
|
|
||||||
console.log('Applied glass-specific settings to mesh');
|
|
||||||
|
|
||||||
// Add subtle wireframe to glass edges for better visibility
|
|
||||||
if (child.geometry) {
|
|
||||||
const wireframe = new THREE.EdgesGeometry(child.geometry);
|
|
||||||
const wireframeMaterial = new THREE.LineBasicMaterial({
|
|
||||||
color: 0x88aaff,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0.3,
|
|
||||||
linewidth: 2
|
|
||||||
});
|
|
||||||
const wireframeLines = new THREE.LineSegments(wireframe, wireframeMaterial);
|
|
||||||
child.add(wireframeLines);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Non-glass materials
|
|
||||||
child.material.side = THREE.DoubleSide;
|
|
||||||
child.material.transparent = false;
|
|
||||||
child.material.opacity = 1.0;
|
|
||||||
|
|
||||||
// Ensure proper lighting on both sides
|
|
||||||
if (child.material.type === 'MeshLambertMaterial' || child.material.type === 'MeshPhongMaterial') {
|
|
||||||
child.material.emissive = new THREE.Color(0x111111); // Slight self-illumination
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
child.material.needsUpdate = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store reference and add to scene
|
|
||||||
cube = object;
|
|
||||||
scene.add(cube);
|
|
||||||
|
|
||||||
// Calculate model center and adjust controls
|
|
||||||
centerModelAndControls(cube);
|
|
||||||
|
|
||||||
// Hide loading indicator
|
|
||||||
document.getElementById('loading').style.display = 'none';
|
|
||||||
|
|
||||||
console.log('Cube loaded successfully!');
|
|
||||||
},
|
|
||||||
function(progress) {
|
|
||||||
console.log('Loading progress:', (progress.loaded / progress.total * 100) + '%');
|
|
||||||
},
|
|
||||||
function(error) {
|
|
||||||
console.error('Error loading OBJ file:', error);
|
|
||||||
document.getElementById('loading').innerHTML =
|
|
||||||
'<div style="color: #ff6b6b;">Error loading model. Please check console for details.</div>';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(progress) {
|
|
||||||
console.log('Loading MTL progress:', (progress.loaded / progress.total * 100) + '%');
|
|
||||||
},
|
|
||||||
function(error) {
|
|
||||||
console.error('Error loading MTL file:', error);
|
|
||||||
// Try to load OBJ without materials
|
|
||||||
loadObjWithoutMaterials();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadObjWithoutMaterials() {
|
|
||||||
const objLoader = new THREE.OBJLoader();
|
|
||||||
|
|
||||||
objLoader.load('cube.obj', function(object) {
|
|
||||||
// Apply default material with better interior visibility
|
|
||||||
const defaultMaterial = new THREE.MeshPhongMaterial({
|
|
||||||
color: 0xffffff,
|
|
||||||
side: THREE.DoubleSide,
|
|
||||||
transparent: false,
|
|
||||||
opacity: 1.0,
|
|
||||||
emissive: new THREE.Color(0x111111), // Slight self-illumination for interior visibility
|
|
||||||
shininess: 30
|
|
||||||
});
|
|
||||||
|
|
||||||
object.traverse(function(child) {
|
|
||||||
if (child instanceof THREE.Mesh) {
|
|
||||||
child.material = defaultMaterial.clone(); // Clone to avoid sharing
|
|
||||||
child.castShadow = true;
|
|
||||||
child.receiveShadow = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Scale and position
|
|
||||||
object.scale.set(0.5, 0.5, 0.5);
|
|
||||||
object.position.set(0, 0, 0);
|
|
||||||
|
|
||||||
cube = object;
|
|
||||||
scene.add(cube);
|
|
||||||
|
|
||||||
// Calculate model center and adjust controls
|
|
||||||
centerModelAndControls(cube);
|
|
||||||
|
|
||||||
document.getElementById('loading').style.display = 'none';
|
|
||||||
console.log('Cube loaded without materials!');
|
|
||||||
},
|
|
||||||
function(progress) {
|
|
||||||
console.log('Loading progress:', (progress.loaded / progress.total * 100) + '%');
|
|
||||||
},
|
|
||||||
function(error) {
|
|
||||||
console.error('Error loading OBJ file:', error);
|
|
||||||
document.getElementById('loading').innerHTML =
|
|
||||||
'<div style="color: #ff6b6b;">Failed to load model. Check if cube.obj exists.</div>';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function centerModelAndControls(object) {
|
|
||||||
// Calculate the bounding box of the model
|
|
||||||
const boundingBox = new THREE.Box3().setFromObject(object);
|
|
||||||
|
|
||||||
// Get the center point of the bounding box
|
|
||||||
const center = boundingBox.getCenter(new THREE.Vector3());
|
|
||||||
|
|
||||||
// Get the size of the bounding box
|
|
||||||
const size = boundingBox.getSize(new THREE.Vector3());
|
|
||||||
|
|
||||||
// Set the orbit controls target to the model's center
|
|
||||||
controls.target.copy(center);
|
|
||||||
|
|
||||||
// Calculate optimal camera distance based on model size
|
|
||||||
const maxDimension = Math.max(size.x, size.y, size.z);
|
|
||||||
const fov = camera.fov * (Math.PI / 180);
|
|
||||||
const distance = Math.abs(maxDimension / Math.sin(fov / 2)) * 1.2; // 1.2 for some padding
|
|
||||||
|
|
||||||
// Position camera at optimal distance from model center
|
|
||||||
const direction = camera.position.clone().sub(center).normalize();
|
|
||||||
camera.position.copy(center).add(direction.multiplyScalar(distance));
|
|
||||||
|
|
||||||
// Update camera to look at the model center
|
|
||||||
camera.lookAt(center);
|
|
||||||
|
|
||||||
// Reposition interior lights relative to model center
|
|
||||||
if (window.interiorLight) {
|
|
||||||
window.interiorLight.position.copy(center);
|
|
||||||
|
|
||||||
// Adjust light range based on model size
|
|
||||||
window.interiorLight.distance = maxDimension * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.interiorLight2) {
|
|
||||||
window.interiorLight2.position.copy(center).add(new THREE.Vector3(
|
|
||||||
size.x * 0.3,
|
|
||||||
size.y * 0.3,
|
|
||||||
-size.z * 0.3
|
|
||||||
));
|
|
||||||
window.interiorLight2.distance = maxDimension * 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update controls
|
|
||||||
controls.update();
|
|
||||||
|
|
||||||
console.log('Model centered at:', center);
|
|
||||||
console.log('Model size:', size);
|
|
||||||
console.log('Camera positioned at:', camera.position);
|
|
||||||
}
|
|
||||||
|
|
||||||
function animate() {
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
|
|
||||||
// Update controls
|
|
||||||
controls.update();
|
|
||||||
|
|
||||||
// Clear and render with proper transparency sorting
|
|
||||||
renderer.clear();
|
|
||||||
renderer.render(scene, camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWindowResize() {
|
|
||||||
camera.aspect = window.innerWidth / window.innerHeight;
|
|
||||||
camera.updateProjectionMatrix();
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize when page loads
|
|
||||||
window.addEventListener('load', init);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Reference in New Issue
Block a user