top of page

 virtually 
ever after...

demo_v2_MV

 virtually 
ever after...

guide_v0_TR

DEMO

24072025

DEMO-v2

25072025

<!DOCTYPE html><html lang="tr"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lüksemburg Gayrimenkul Demo - Ultra Quality</title> <style> body { margin: 0; padding: 0; height: 100vh; overflow: hidden; font-family: Arial, sans-serif; touch-action: none; background: #000; } canvas { display: block; } #infoPopup { display: none; position: absolute; background: rgba(255, 255, 255, 0.95); padding: 20px; border-radius: 10px; max-width: 400px; font-size: 14px; color: #333; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); backdrop-filter: blur(10px); z-index: 1000; transform: translateY(20px); opacity: 0; transition: all 0.3s ease; } #infoPopup.visible { transform: translateY(0); opacity: 1; } #infoPopup a { color: #007bff; text-decoration: none; } #infoPopup a:hover { text-decoration: underline; } #infoPopup button { margin-top: 15px; padding: 8px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background 0.3s; } #infoPopup button:hover { background: #0056b3; } #navigation { position: absolute; bottom: 20px; right: 20px; display: flex; gap: 15px; z-index: 1000; } #navigation button { padding: 12px 24px; cursor: pointer; background: rgba(0, 0, 0, 0.8); color: white; border: none; border-radius: 8px; font-size: 16px; transition: all 0.3s ease; backdrop-filter: blur(10px); } #navigation button:hover { background: rgba(0, 0, 0, 1); transform: scale(1.05); } #furnitureMenu { position: absolute; bottom: 20px; left: 20px; background: rgba(0, 0, 0, 0.9); padding: 20px; border-radius: 10px; color: white; z-index: 1000; display: none; transform: translateY(20px); opacity: 0; transition: all 0.3s ease; backdrop-filter: blur(10px); } #furnitureMenu.visible { transform: translateY(0); opacity: 1; } #furnitureMenu select { margin: 8px 0; padding: 10px; background: #333; color: white; border: none; border-radius: 5px; width: 100%; cursor: pointer; } #furnitureMenu select:focus { outline: none; background: #444; } #versionLabel { font-weight: bold; margin-bottom: 15px; font-size: 16px; } #loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.9); color: white; padding: 20px 40px; border-radius: 10px; font-size: 16px; z-index: 1000; display: none; text-align: center; backdrop-filter: blur(10px); } #loadingProgress { margin-top: 10px; width: 200px; height: 10px; background: #333; border-radius: 5px; overflow: hidden; } #loadingProgressBar { height: 100%; background: linear-gradient(90deg, #007bff, #00ff88); width: 0; transition: width 0.3s ease; } #error { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(255, 0, 0, 0.9); color: white; padding: 15px 30px; border-radius: 8px; font-size: 14px; z-index: 1000; display: none; text-align: center; } #error button { margin-top: 10px; padding: 8px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background 0.3s; } #error button:hover { background: #0056b3; } #qualityInfo { position: absolute; top: 20px; left: 20px; background: rgba(0, 0, 0, 0.8); color: white; padding: 15px; border-radius: 8px; font-size: 12px; z-index: 1000; backdrop-filter: blur(10px); } #touchControls { position: absolute; bottom: 80px; left: 20px; display: none; z-index: 1000; } #touchJoystick { width: 80px; height: 80px; background: rgba(0, 0, 0, 0.5); border-radius: 50%; position: relative; touch-action: none; backdrop-filter: blur(10px); } #touchJoystickInner { width: 40px; height: 40px; background: #007bff; border-radius: 50%; position: absolute; top: 20px; left: 20px; transition: all 0.1s ease; } </style></head><body> <div id="qualityInfo"> <div>🎨 Ultra Quality WebXR</div> <div id="statsDisplay">Initializing...</div> </div> <div id="navigation"> <button onclick="goToPrevious()">Geri</button> <button onclick="goToNext()">İleri</button> </div> <div id="furnitureMenu"> <div>İç Mekan Versiyonu: <span id="versionLabel">Seçili</span></div> <select id="room1Select" onchange="updateFurniture('room1', this.value)"> <option value="1">Oda 1 - Versiyon 1</option> <option value="2">Oda 1 - Versiyon 2</option> <option value="3">Oda 1 - Versiyon 3</option> </select> <select id="room2Select" onchange="updateFurniture('room2', this.value)"> <option value="1">Oda 2 - Versiyon 1</option> <option value="2">Oda 2 - Versiyon 2</option> <option value="3">Oda 2 - Versiyon 3</option> </select> <select id="salonSelect" onchange="updateFurniture('salon', this.value)"> <option value="1">Salon - Versiyon 1</option> <option value="2">Salon - Versiyon 2</option> <option value="3">Salon - Versiyon 3</option> </select> <select id="terraceSelect" onchange="updateFurniture('terrace', this.value)"> <option value="1">Teras - Versiyon 1</option> <option value="2">Teras - Versiyon 2</option> <option value="3">Teras - Versiyon 3</option> </select> </div> <div id="infoPopup"></div> <div id="loading"> Yükleniyor... <div id="loadingProgress"><div id="loadingProgressBar"></div></div> </div> <div id="error"></div> <div id="touchControls"> <div id="touchJoystick"><div id="touchJoystickInner"></div></div> </div> <!-- FIXED: Stable CDN links in correct order --> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/16.3.5/Tween.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/DRACOLoader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/LuminosityHighPassShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/FXAAShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script> <script> // 🔧 SAFE TWEEN WRAPPER - FIXED: No more TWEEN undefined errors const safeTween = { Tween: function(obj) { if (typeof TWEEN !== 'undefined' && TWEEN.Tween) { return new TWEEN.Tween(obj); } // Fallback: Simple immediate execution return { to: function() { return this; }, easing: function() { return this; }, start: function() { return this; }, onUpdate: function() { return this; }, onComplete: function(callback) { if (callback) setTimeout(callback, 100); return this; } }; }, update: function() { if (typeof TWEEN !== 'undefined' && TWEEN.update) { return TWEEN.update(); } }, Easing: { Quadratic: { InOut: function(t) { return t; } }, Cubic: { InOut: function(t) { return t; } } } }; // 🎯 ULTRA QUALITY CONFIGURATION const BASE_URL = 'https://raw.githubusercontent.com/decentralize-dfw/realestatedemo/main/'; const CONFIG = { quality: { maxPixelRatio: 2.0, shadowMapSize: 4096, anisotropy: 16, antialias: true, enableHDR: true, enableBloom: true }, camera: { fov: 75, near: 0.1, far: 1000 }, lighting: { morning: { sun: { x: 50, y: 30, z: 20, intensity: 2.5, color: 0xfff4e6 }, ambient: { intensity: 0.6, color: 0x404040 }, hemisphere: { sky: 0x87CEEB, ground: 0x362d1d, intensity: 1.0 } }, noon: { sun: { x: 0, y: 100, z: 0, intensity: 3.0, color: 0xffffff }, ambient: { intensity: 0.8, color: 0x404040 }, hemisphere: { sky: 0x87CEEB, ground: 0x362d1d, intensity: 1.2 } }, evening: { sun: { x: -50, y: 20, z: -30, intensity: 2.0, color: 0xffd4a3 }, ambient: { intensity: 0.5, color: 0x404040 }, hemisphere: { sky: 0xff7f50, ground: 0x362d1d, intensity: 0.8 } } }, sceneConfig: { 0: { cameraPos: { x: 0, y: 15, z: 30 }, target: { x: 0, y: 0, z: 0 }, autoRotate: true, light: 'noon' }, 1: { cameraPos: { x: 0, y: 10, z: 15 }, target: { x: 0, y: 0, z: 0 }, autoRotate: true, light: 'noon' }, 2: { cameraPos: { x: 0, y: 12, z: 15 }, target: { x: 0, y: 0, z: 12 }, autoRotate: false, light: 'evening' }, 3: { cameraPos: { x: 0, y: 0, z: 5 }, target: { x: 0, y: 0, z: 0 }, autoRotate: false, light: 'morning' } }, physics: { gravity: -9.81, playerHeight: 1.6, stepHeight: 0.4, speed: 10, sprintSpeed: 15 } }; // 🚀 ULTRA QUALITY SCENE SETUP THREE.Cache.enabled = true; const scene = new THREE.Scene(); // Professional gradient background scene.background = new THREE.Color(0x87CEEB); scene.fog = new THREE.Fog(0x87CEEB, 50, 200); const aspect = window.innerWidth / window.innerHeight; const camera = new THREE.PerspectiveCamera(CONFIG.camera.fov, aspect, CONFIG.camera.near, CONFIG.camera.far); camera.position.set(0, 15, 30); // 🔥 ULTRA RENDERER SETUP - FIXED: No deprecated features const renderer = new THREE.WebGLRenderer({ antialias: CONFIG.quality.antialias, alpha: false, powerPreference: "high-performance", stencil: false, depth: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, CONFIG.quality.maxPixelRatio)); // Ultra Quality Render Settings renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.shadowMap.autoUpdate = true; // HDR Pipeline renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.0; renderer.outputEncoding = THREE.sRGBEncoding; // FIXED: Use supported properties renderer.physicallyCorrectLights = true; document.body.appendChild(renderer.domElement); // 🌟 PROFESSIONAL ENVIRONMENT MAPPING const pmremGenerator = new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); const createEnvironmentMap = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 1024; canvas.height = 512; // Create realistic sky gradient const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); gradient.addColorStop(0, '#4A90E2'); // Sky blue gradient.addColorStop(0.4, '#87CEEB'); // Light sky blue gradient.addColorStop(0.7, '#F0F8FF'); // Alice blue gradient.addColorStop(1, '#FFFACD'); // Light yellow ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); // Add sun effect const sunGrad = ctx.createRadialGradient(512, 100, 0, 512, 100, 150); sunGrad.addColorStop(0, 'rgba(255,255,200,0.6)'); sunGrad.addColorStop(1, 'rgba(255,255,200,0)'); ctx.fillStyle = sunGrad; ctx.fillRect(0, 0, canvas.width, canvas.height); const texture = new THREE.CanvasTexture(canvas); texture.mapping = THREE.EquirectangularReflectionMapping; texture.encoding = THREE.sRGBEncoding; const envMap = pmremGenerator.fromEquirectangular(texture).texture; scene.environment = envMap; texture.dispose(); return envMap; }; const envMap = createEnvironmentMap(); // 🎨 ULTRA QUALITY POST-PROCESSING - FIXED: No dependency issues const composer = new THREE.EffectComposer(renderer); // Main render pass const renderPass = new THREE.RenderPass(scene, camera); composer.addPass(renderPass); // Bloom for emissive materials and realism const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 0.3, // strength - reduced for realism 0.6, // radius 0.2 // threshold ); composer.addPass(bloomPass); // FXAA for smooth edges const fxaaPass = new THREE.ShaderPass(THREE.FXAAShader); fxaaPass.material.uniforms['resolution'].value.x = 1 / window.innerWidth; fxaaPass.material.uniforms['resolution'].value.y = 1 / window.innerHeight; composer.addPass(fxaaPass); // 💡 ULTRA REALISTIC LIGHTING SYSTEM let currentLights = {}; const setupLighting = (lightConfig) => { // FIXED: Proper cleanup Object.values(currentLights).forEach(light => { if (light && light.parent) { scene.remove(light); } }); currentLights = {}; // Sun light (main directional) const sunLight = new THREE.DirectionalLight( lightConfig.sun.color, lightConfig.sun.intensity ); sunLight.position.set(lightConfig.sun.x, lightConfig.sun.y, lightConfig.sun.z); sunLight.castShadow = true; // Ultra quality shadows sunLight.shadow.mapSize.width = CONFIG.quality.shadowMapSize; sunLight.shadow.mapSize.height = CONFIG.quality.shadowMapSize; sunLight.shadow.camera.near = 0.1; sunLight.shadow.camera.far = 300; sunLight.shadow.camera.left = -50; sunLight.shadow.camera.right = 50; sunLight.shadow.camera.top = 50; sunLight.shadow.camera.bottom = -50; sunLight.shadow.bias = -0.0005; sunLight.shadow.normalBias = 0.02; sunLight.shadow.radius = 6; scene.add(sunLight); currentLights.sun = sunLight; // Ambient light for general illumination const ambientLight = new THREE.AmbientLight( lightConfig.ambient.color, lightConfig.ambient.intensity ); scene.add(ambientLight); currentLights.ambient = ambientLight; // Hemisphere light for realistic sky/ground bounce const hemisphereLight = new THREE.HemisphereLight( lightConfig.hemisphere.sky, lightConfig.hemisphere.ground, lightConfig.hemisphere.intensity ); scene.add(hemisphereLight); currentLights.hemisphere = hemisphereLight; // Fill lights for professional lighting const fillLight = new THREE.DirectionalLight(0xffd4a3, 0.4); fillLight.position.set(-30, 20, 30); scene.add(fillLight); currentLights.fill = fillLight; const rimLight = new THREE.DirectionalLight(0x87CEEB, 0.3); rimLight.position.set(0, 10, -40); scene.add(rimLight); currentLights.rim = rimLight; }; // Initialize with noon lighting setupLighting(CONFIG.lighting.noon); // 🎯 PHYSICS ENGINE const world = new CANNON.World(); world.gravity.set(0, CONFIG.physics.gravity, 0); world.broadphase = new CANNON.NaiveBroadphase(); world.solver.iterations = 10; const playerBody = new CANNON.Body({ mass: 1 }); playerBody.addShape(new CANNON.Box(new CANNON.Vec3(0.25, CONFIG.physics.playerHeight / 2, 0.25))); playerBody.position.set(0, CONFIG.physics.playerHeight / 2, 0); world.addBody(playerBody); // 🎮 ADVANCED CONTROLS const orbitControls = new THREE.OrbitControls(camera, renderer.domElement); orbitControls.enablePan = false; orbitControls.enableZoom = true; orbitControls.zoomSpeed = 0.5; orbitControls.minDistance = 5; orbitControls.maxDistance = 100; orbitControls.autoRotateSpeed = 0.5; orbitControls.target.set(0, 0, 0); orbitControls.enableDamping = true; orbitControls.dampingFactor = 0.05; const pointerControls = new THREE.PointerLockControls(camera, renderer.domElement); let isFPS = false; // 🏗️ SCENE DEFINITIONS const scenes = [ [ { model: BASE_URL + 'city-opt-v1.glb', layer: 'city' }, { model: BASE_URL + 'Landmarks.glb', layer: 'kopstal' }, { model: BASE_URL + 'Landmarks.glb', layer: 'mainroute' }, { model: BASE_URL + 'Landmarks.glb', layer: 'landmark' }, { model: BASE_URL + 'Landmarks.glb', layer: 'settlements' } ], [ { model: BASE_URL + 'mahalle.glb', layer: 'mahalle' }, { model: BASE_URL + 'bldg_1.glb', layer: 'bldg_1' }, { model: BASE_URL + 'bldg_highlighted.glb', layer: 'bldg_highlighted' }, { model: BASE_URL + 'car_parking.glb', layer: 'car_parking' }, { model: BASE_URL + 'bldg_highlighted.glb', layer: 'bldg_highlighted_market' } ], [ { model: BASE_URL + 'bldg_section.glb', layer: 'bldg_section' }, { model: BASE_URL + 'dimensions.glb', layer: 'dimensions' }, { model: BASE_URL + 'furniture_2.glb', layer: 'furniture_1' }, { model: BASE_URL + 'furniture_2.glb', layer: 'furniture_2' }, { model: BASE_URL + 'furniture_3.glb', layer: 'furniture_3' }, { model: BASE_URL + 'furniture_select.glb', layer: 'furniture_select' }, { model: BASE_URL + 'bldg_section.glb', layer: 'fps' } ], [ { model: BASE_URL + 'logo.glb', layer: 'logo' } ] ]; // 📍 HOTSPOT DATA const hotspots = { '0_0': [ { name: 'airport', position: { x: 13.351, y: 0.356852, z: -0.161044 }, info: 'Havalimanı: 15 km, 20 dk' }, { name: 'kirchberg', position: { x: 4.64357, y: 0.356852, z: 0.92694 }, info: 'Kirchberg: 5 km, 10 dk' }, { name: 'hamilius', position: { x: -1.38266, y: 0.356852, z: -3.87947 }, info: 'Hamilius: 3 km, 8 dk' }, { name: 'belle_etoile', position: { x: -15.3761, y: 0.356852, z: -0.053178 }, info: 'Belle Etoile: 4 km, 12 dk' }, { name: 'ikea_arlon', position: { x: -46.8144, y: 0.356852, z: 4.1936 }, info: 'IKEA Arlon: 25 km, 30 dk' }, { name: 'kopstal', position: { x: -11.2501, y: 0.356852, z: 11.934 }, info: 'Kopstal: 2 km, 5 dk' }, { name: 'gare', position: { x: 0.3541, y: 0.356852, z: -6.42474 }, info: 'Gare Centrale: 7 km, 15 dk' } ], '1_2': [ { name: 'hotspot1', position: { x: 0, y: 0, z: 0 }, info: 'Bar: 4.5 yıldız, 120 yorum' }, { name: 'hotspot2', position: { x: 0, y: 0, z: 0 }, info: 'Restoran: 4.7 yıldız, 200 yorum' }, { name: 'hotspot3', position: { x: 0, y: 0, z: 0 }, info: 'Kafe: 4.3 yıldız, 80 yorum' }, { name: 'hotspot4', position: { x: 0, y: 0, z: 0 }, info: 'Spor Salonu: 4.6 yıldız, 150 yorum' }, { name: 'hotspot5', position: { x: 0, y: 0, z: 0 }, info: 'Park: 4.8 yıldız, 300 yorum' } ], '1_3': [ { name: 'pt_line_1', position: { x: 0, y: 0, z: 0 }, info: 'Place Etoile, Duraklar: 5 dk ara' }, { name: 'pt_line_2', position: { x: 0, y: 0, z: 0 }, info: 'Merkez Otobüs, Duraklar: 10 dk ara' } ], '1_4': [ { name: 'market1', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 80 m², 2+1, 1500€/ay' }, { name: 'market2', position: { x: 0, y: 0, z: 0 }, info: 'Satılık: 100 m², 3+1, 450,000€' }, { name: 'market3', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 60 m², 1+1, 1200€/ay' }, { name: 'market4', position: { x: 0, y: 0, z: 0 }, info: 'Satılık: 120 m², 4+1, 600,000€' }, { name: 'market5', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 90 m², 2+1, 1700€/ay' } ], '2_1': [ { name: 'hotspot1', position: { x: 0, y: 0, z: 12 }, info: 'Oda 1: 25 m²' }, { name: 'hotspot2', position: { x: 0, y: 0, z: 12 }, info: 'Oda 2: 20 m²' }, { name: 'hotspot3', position: { x: 0, y: 0, z: 12 }, info: 'Salon: 40 m²' }, { name: 'hotspot4', position: { x: 0, y: 0, z: 12 }, info: 'Teras: 15 m²' }, { name: 'hotspot5', position: { x: 0, y: 0, z: 12 }, info: 'Mutfak: 12 m²' } ] }; // 📌 ULTRA QUALITY HOTSPOT SPRITES - FIXED: Safe texture loading const textureLoader = new THREE.TextureLoader(); let hotspotTexture = null; textureLoader.load( 'https://raw.githubusercontent.com/decentralize-dfw/realestatedemo/refs/heads/main/pin.png', (texture) => { texture.anisotropy = Math.min(CONFIG.quality.anisotropy, renderer.capabilities.getMaxAnisotropy()); hotspotTexture = texture; console.log('✅ Hotspot texture loaded successfully'); }, undefined, (error) => { console.warn('⚠️ Hotspot texture failed to load, using fallback'); hotspotTexture = null; } ); const hotspotSprites = []; // 🪑 FURNITURE MODELS const furnitureModels = { room1: { 1: BASE_URL + 'room1_f1.glb', 2: BASE_URL + 'room1_f2.glb', 3: BASE_URL + 'room1_f3.glb' }, room2: { 1: BASE_URL + 'room2_f1.glb', 2: BASE_URL + 'room2_f2.glb', 3: BASE_URL + 'room2_f3.glb' }, salon: { 1: BASE_URL + 'salon_f1.glb', 2: BASE_URL + 'salon_f2.glb', 3: BASE_URL + 'salon_f3.glb' }, terrace: { 1: BASE_URL + 'terrace_f1.glb', 2: BASE_URL + 'terrace_f2.glb', 3: BASE_URL + 'terrace_f3.glb' } }; let currentFurniture = { room1: 'none', room2: 'none', salon: 'none', terrace: 'none' }; // 🔧 ADVANCED GLTF LOADER - FIXED: Stable DRACO path const loader = new THREE.GLTFLoader(); // DRACO support with correct path const dracoLoader = new THREE.DRACOLoader(); dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/libs/draco/'); dracoLoader.setDecoderConfig({ type: 'js' }); loader.setDRACOLoader(dracoLoader); let models = {}; let currentScene = 0; let currentSubScene = 0; let isTransitioning = false; let loadingProgress = 0; // 🚨 ERROR MANAGEMENT function showError(message) { const errorDiv = document.getElementById('error'); errorDiv.innerHTML = message + '<br><button onclick="this.parentElement.style.display = \'none\'">Kapat</button>'; errorDiv.style.display = 'block'; setTimeout(() => errorDiv.style.display = 'none', 10000); } // 💡 DYNAMIC LIGHTING CONTROL function setLighting(timeOfDay) { const lightConfig = CONFIG.lighting[timeOfDay]; setupLighting(lightConfig); // FIXED: Safe TWEEN usage safeTween.Tween({ progress: 0 }) .to({ progress: 1 }, 2000) .easing(safeTween.Easing.Quadratic.InOut) .start(); } // 🗑️ ADVANCED MEMORY MANAGEMENT function disposeModel(model) { if (!model) return; model.traverse((child) => { if (child.isMesh) { if (child.geometry) { child.geometry.dispose(); } if (child.material) { const materials = Array.isArray(child.material) ? child.material : [child.material]; materials.forEach(material => { // Dispose all textures Object.keys(material).forEach(key => { if (material[key] && material[key].isTexture) { material[key].dispose(); } }); material.dispose(); }); } } }); } // 🎨 ULTRA QUALITY MATERIAL PROCESSING - FIXED: Actually working version function processUltraQualityMaterials(model, layer) { let materialCount = 0; model.traverse((child) => { if (child.isMesh) { materialCount++; child.castShadow = true; child.receiveShadow = true; child.frustumCulled = true; if (child.material) { const material = child.material; console.log(`🎨 Processing ultra-quality material for ${child.name || 'unnamed'}:`, { type: material.type, hasAlbedo: !!material.map, hasNormal: !!material.normalMap, hasRoughness: !!material.roughnessMap, hasMetalness: !!material.metalnessMap, hasEmission: !!material.emissiveMap, originalColor: material.color ? material.color.getHex() : 'none' }); // CRITICAL FIX: Force material visibility like debug code if (material.isMeshStandardMaterial) { // Set environment map for PBR material.envMap = envMap; material.envMapIntensity = 1.0; // FIXED: Force bright colors for visibility (like debug code) if (material.color) { // Don't override to white, keep original but ensure it's bright enough const originalColor = material.color.clone(); const brightness = (originalColor.r + originalColor.g + originalColor.b) / 3; if (brightness < 0.3) { // If too dark, brighten it material.color.multiplyScalar(2.0); console.log(`💡 Brightened dark material for ${child.name || 'unnamed'}`); } } // Ensure proper material properties for visibility material.roughness = material.roughness !== undefined ? material.roughness : 0.7; material.metalness = material.metalness !== undefined ? material.metalness : 0.1; // Enhanced texture settings if (material.map) { material.map.anisotropy = Math.min(CONFIG.quality.anisotropy, renderer.capabilities.getMaxAnisotropy()); material.map.encoding = THREE.sRGBEncoding; material.map.generateMipmaps = true; } if (material.normalMap) { material.normalMap.anisotropy = Math.min(CONFIG.quality.anisotropy, renderer.capabilities.getMaxAnisotropy()); material.normalScale.set(1, 1); } if (material.roughnessMap) { material.roughnessMap.anisotropy = Math.min(CONFIG.quality.anisotropy, renderer.capabilities.getMaxAnisotropy()); } if (material.metalnessMap) { material.metalnessMap.anisotropy = Math.min(CONFIG.quality.anisotropy, renderer.capabilities.getMaxAnisotropy()); } if (material.emissiveMap) { material.emissiveMap.anisotropy = Math.min(CONFIG.quality.anisotropy, renderer.capabilities.getMaxAnisotropy()); material.emissiveMap.encoding = THREE.sRGBEncoding; } if (material.aoMap) { material.aoMap.anisotropy = Math.min(CONFIG.quality.anisotropy, renderer.capabilities.getMaxAnisotropy()); material.aoIntensity = 1.0; } // Physical properties material.transparent = material.opacity < 1.0; material.side = material.transparent ? THREE.DoubleSide : THREE.FrontSide; material.alphaTest = material.transparent ? 0.1 : 0; // Force material update material.needsUpdate = true; console.log(`✨ Enhanced PBR material for ${child.name || 'unnamed'} - Color: ${material.color.getHex().toString(16)}`); } // Handle emissive materials for bloom effect if (material.emissive && material.emissive.getHex() > 0) { material.emissiveIntensity = Math.max(material.emissiveIntensity || 1, 1.5); console.log(`💡 Enhanced emissive material for ${child.name || 'unnamed'}`); } } } }); console.log(`🎯 Processed ${materialCount} ultra-quality materials for ${layer}`); return materialCount; } // 🏗️ ADVANCED SCENE LOADING - FIXED: No syntax errors function loadScene(sceneIndex, subSceneIndex) { if (isTransitioning) return; isTransitioning = true; loadingProgress = 0; document.getElementById('loading').style.display = 'block'; document.getElementById('loadingProgressBar').style.width = '0%'; // UI Updates const furnitureMenu = document.getElementById('furnitureMenu'); if (sceneIndex === 2 && subSceneIndex === 5) { furnitureMenu.style.display = 'block'; furnitureMenu.classList.add('visible'); } else { furnitureMenu.style.display = 'none'; furnitureMenu.classList.remove('visible'); } document.getElementById('infoPopup').style.display = 'none'; document.getElementById('infoPopup').classList.remove('visible'); // Clear hotspots with proper cleanup hotspotSprites.forEach(sprite => { scene.remove(sprite); if (sprite.material.map && sprite.material.map.dispose) { sprite.material.map.dispose(); } sprite.material.dispose(); }); hotspotSprites.length = 0; // Clear models with advanced cleanup Object.keys(models).forEach(key => { disposeModel(models[key]); scene.remove(models[key]); }); models = {}; // Reset physics const bodiesToRemove = []; world.bodies.forEach(body => { if (body !== playerBody) { bodiesToRemove.push(body); } }); bodiesToRemove.forEach(body => world.removeBody(body)); // Reset controls isFPS = false; pointerControls.unlock(); orbitControls.enabled = true; // Touch controls document.getElementById('touchControls').style.display = (sceneIndex === 2 && subSceneIndex === 6) ? 'block' : 'none'; // Set lighting for scene setLighting(CONFIG.sceneConfig[sceneIndex].light); function addHotspots(sceneKey) { if (hotspots[sceneKey] && !(sceneIndex === 2 && subSceneIndex === 6)) { hotspots[sceneKey].forEach(hotspot => { const spriteMaterial = new THREE.SpriteMaterial({ map: hotspotTexture, transparent: true, depthTest: false, sizeAttenuation: false, color: hotspotTexture ? 0xffffff : 0xff0000 }); const sprite = new THREE.Sprite(spriteMaterial); sprite.position.set(hotspot.position.x, hotspot.position.y + 1, hotspot.position.z); sprite.scale.set(0.5, 0.5, 0.5); sprite.name = hotspot.name; scene.add(sprite); hotspotSprites.push(sprite); }); } } function loadModel(url, layer, callback) { console.log(`🚀 Loading ultra-quality model: ${layer} from ${url}`); loader.load( url, (gltf) => { console.log(`✅ Ultra-quality model loaded: ${layer}`); console.log(`📊 Model stats:`, { animations: gltf.animations.length, scenes: gltf.scenes.length, cameras: gltf.cameras ? gltf.cameras.length : 0, meshCount: gltf.scene.children.length }); const model = gltf.scene; // Process with ultra-quality materials const processedMaterials = processUltraQualityMaterials(model, layer); // Add physics bodies for complex models if (sceneIndex === 2) { model.traverse((child) => { if (child.isMesh && child.geometry) { try { child.geometry.computeBoundingBox(); const bbox = child.geometry.boundingBox; if (bbox) { const size = bbox.max.clone().sub(bbox.min); const shape = new CANNON.Box(new CANNON.Vec3( Math.max(size.x * 0.5, 0.1), Math.max(size.y * 0.5, 0.1), Math.max(size.z * 0.5, 0.1) )); const body = new CANNON.Body({ mass: 0 }); body.addShape(shape); body.position.copy(child.getWorldPosition(new THREE.Vector3())); world.addBody(body); } } catch (e) { console.warn(`Physics body creation failed for ${child.name}:`, e); } } }); } models[layer] = model; scene.add(model); loadingProgress += 1 / scenes[sceneIndex].length; document.getElementById('loadingProgressBar').style.width = Math.min(loadingProgress * 100, 100) + '%'; if (callback) callback(); }, (progress) => { if (progress.total > 0) { const percent = (progress.loaded / progress.total * 100); console.log(`📈 Loading progress ${layer}: ${percent.toFixed(1)}%`); } }, (error) => { console.error(`❌ Error loading ultra-quality model ${layer}:`, error); showError(`Ultra-quality model yüklenemedi: ${layer}`); // Create enhanced placeholder const geometry = new THREE.BoxGeometry(5, 5, 5); const material = new THREE.MeshStandardMaterial({ color: 0x888888, envMap: envMap, roughness: 0.5, metalness: 0.0 }); const placeholder = new THREE.Mesh(geometry, material); placeholder.castShadow = true; placeholder.receiveShadow = true; models[layer] = placeholder; scene.add(placeholder); loadingProgress += 1 / scenes[sceneIndex].length; document.getElementById('loadingProgressBar').style.width = Math.min(loadingProgress * 100, 100) + '%'; if (callback) callback(); } ); } // FIXED: Proper scene loading logic with no syntax errors function finishLoading() { // Smooth camera transition with FIXED TWEEN safeTween.Tween(camera.position) .to(CONFIG.sceneConfig[sceneIndex].cameraPos, 2000) .easing(safeTween.Easing.Quadratic.InOut) .start(); safeTween.Tween(orbitControls.target) .to(CONFIG.sceneConfig[sceneIndex].target, 2000) .easing(safeTween.Easing.Quadratic.InOut) .start(); orbitControls.autoRotate = CONFIG.sceneConfig[sceneIndex].autoRotate; if (CONFIG.sceneConfig[sceneIndex].autoRotate) { orbitControls.maxPolarAngle = Math.PI / 3; orbitControls.minPolarAngle = Math.PI / 6; } else { orbitControls.maxPolarAngle = Math.PI; orbitControls.minPolarAngle = 0; } camera.updateProjectionMatrix(); orbitControls.update(); document.getElementById('loading').style.display = 'none'; isTransitioning = false; currentScene = sceneIndex; currentSubScene = subSceneIndex; console.log(`🎉 Ultra-quality scene ${sceneIndex}-${subSceneIndex} loaded successfully!`); } // Load scenes based on index - FIXED: No syntax errors if (sceneIndex === 0) { let loaded = 0; const totalToLoad = Math.min(subSceneIndex + 1, scenes[0].length); for (let i = 0; i < totalToLoad; i++) { loadModel(scenes[0][i].model, scenes[0][i].layer, () => { loaded++; if (loaded === totalToLoad) { addHotspots('0_0'); finishLoading(); } }); } } else if (sceneIndex === 1) { let loaded = 0; const totalToLoad = Math.min(subSceneIndex + 1, scenes[1].length); for (let i = 0; i < totalToLoad; i++) { loadModel(scenes[1][i].model, scenes[1][i].layer, () => { loaded++; if (loaded === totalToLoad) { if (subSceneIndex === 1) addHotspots('1_2'); if (subSceneIndex === 2) addHotspots('1_3'); if (subSceneIndex === 3) addHotspots('1_4'); finishLoading(); } }); } } else if (sceneIndex === 2) { // Load background neighborhood with transparency loadModel(BASE_URL + 'mahalle_2.glb', 'mahalle_2', () => { if (models['mahalle_2']) { models['mahalle_2'].traverse((child) => { if (child.isMesh && child.material) { child.material.transparent = true; child.material.opacity = 0.3; child.material.needsUpdate = true; } }); } let loaded = 0; const totalToLoad = Math.min(subSceneIndex + 1, scenes[2].length); for (let i = 0; i < totalToLoad; i++) { loadModel(scenes[2][i].model, scenes[2][i].layer, () => { loaded++; if (loaded === totalToLoad) { if (subSceneIndex >= 1) addHotspots('2_1'); finishLoading(); } }); } }); } else if (sceneIndex === 3) { loadModel(scenes[3][0].model, scenes[3][0].layer, () => { // Logo specific processing with emissive materials if (models['logo']) { models['logo'].traverse((child) => { if (child.isMesh) { child.material = new THREE.MeshStandardMaterial({ map: child.material.map, transparent: true, emissive: new THREE.Color(0x444444), emissiveIntensity: 0.5, envMap: envMap }); } }); } // Show info popup const infoPopup = document.getElementById('infoPopup'); infoPopup.innerHTML = 'Talk with us: <a href="#" target="_blank">İletişim</a><br><button onclick="this.parentElement.style.display = \'none\'; this.parentElement.classList.remove(\'visible\')">Kapat</button>'; infoPopup.style.display = 'block'; infoPopup.style.left = '50%'; infoPopup.style.top = '50%'; infoPopup.classList.add('visible'); finishLoading(); }); } } // 🪑 FURNITURE UPDATE WITH ULTRA QUALITY function updateFurniture(room, option) { if (isTransitioning) return; currentFurniture[room] = option; if (models[room]) { disposeModel(models[room]); scene.remove(models[room]); delete models[room]; } if (option !== 'none' && furnitureModels[room] && furnitureModels[room][option]) { const url = furnitureModels[room][option]; console.log(`🪑 Loading ultra-quality furniture: ${room} option ${option}`); loader.load(url, (gltf) => { const model = gltf.scene; processUltraQualityMaterials(model, room); models[room] = model; scene.add(model); document.getElementById('versionLabel').innerText = 'Seçili'; document.getElementById(room + 'Select').value = option; console.log(`✅ Ultra-quality furniture loaded: ${room}`); }, undefined, (error) => { console.error(`❌ Furniture loading failed: ${room}`, error); }); } } // 🎮 FPS CONTROLS WITH ADVANCED PHYSICS let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false, isSprinting = false; const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); const clock = new THREE.Clock(); let headBobTime = 0; document.addEventListener('keydown', (event) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = true; break; case 'ArrowDown': case 'KeyS': moveBackward = true; break; case 'ArrowLeft': case 'KeyA': moveLeft = true; break; case 'ArrowRight': case 'KeyD': moveRight = true; break; case 'ShiftLeft': isSprinting = true; break; } }); document.addEventListener('keyup', (event) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = false; break; case 'ArrowDown': case 'KeyS': moveBackward = false; break; case 'ArrowLeft': case 'KeyA': moveLeft = false; break; case 'ArrowRight': case 'KeyD': moveRight = false; break; case 'ShiftLeft': isSprinting = false; break; case 'Escape': pointerControls.unlock(); break; } }); pointerControls.addEventListener('lock', () => { orbitControls.enabled = false; isFPS = true; playerBody.position.set(camera.position.x, CONFIG.physics.playerHeight / 2, camera.position.z); document.getElementById('touchControls').style.display = 'block'; }); pointerControls.addEventListener('unlock', () => { orbitControls.enabled = true; isFPS = false; camera.position.set( CONFIG.sceneConfig[2].cameraPos.x, CONFIG.sceneConfig[2].cameraPos.y, CONFIG.sceneConfig[2].cameraPos.z ); orbitControls.target.set( CONFIG.sceneConfig[2].target.x, CONFIG.sceneConfig[2].target.y, CONFIG.sceneConfig[2].target.z ); camera.updateProjectionMatrix(); document.getElementById('touchControls').style.display = 'none'; }); function updateFPS() { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; const delta = clock.getDelta(); velocity.x = 0; velocity.z = 0; direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.normalize(); const speed = isSprinting ? CONFIG.physics.sprintSpeed : CONFIG.physics.speed; if (moveForward || moveBackward) velocity.z -= direction.z * speed * delta; if (moveLeft || moveRight) velocity.x -= direction.x * speed * delta; playerBody.velocity.set(velocity.x, playerBody.velocity.y, velocity.z); world.step(delta); // Advanced head bobbing if (moveForward || moveBackward || moveLeft || moveRight) { headBobTime += delta * 3; const bobOffset = Math.sin(headBobTime) * 0.05; camera.position.set( playerBody.position.x, CONFIG.physics.playerHeight + bobOffset, playerBody.position.z ); } else { camera.position.set( playerBody.position.x, CONFIG.physics.playerHeight, playerBody.position.z ); } } // 📱 TOUCH CONTROLS let isTouching = false; let touchStartX = 0, touchStartY = 0; const joystick = document.getElementById('touchJoystick'); const joystickInner = document.getElementById('touchJoystickInner'); if (joystick && joystickInner) { joystick.addEventListener('touchstart', (e) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; e.preventDefault(); isTouching = true; const touch = e.touches[0]; touchStartX = touch.clientX; touchStartY = touch.clientY; }); joystick.addEventListener('touchmove', (e) => { if (!isTouching) return; e.preventDefault(); const touch = e.touches[0]; const deltaX = touch.clientX - touchStartX; const deltaY = touch.clientY - touchStartY; const maxDist = 30; const dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); const cappedDist = Math.min(dist, maxDist); const angle = Math.atan2(deltaY, deltaX); joystickInner.style.left = (20 + Math.cos(angle) * cappedDist) + 'px'; joystickInner.style.top = (20 + Math.sin(angle) * cappedDist) + 'px'; moveForward = deltaY < -10; moveBackward = deltaY > 10; moveLeft = deltaX < -10; moveRight = deltaX > 10; }); joystick.addEventListener('touchend', (e) => { e.preventDefault(); isTouching = false; moveForward = moveBackward = moveLeft = moveRight = false; joystickInner.style.left = '20px'; joystickInner.style.top = '20px'; }); } // 📍 ULTRA QUALITY HOTSPOT INTERACTION const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); let lastHoveredSprite = null; window.addEventListener('mousemove', (event) => { if (isFPS || isTransitioning) return; mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(hotspotSprites, true); if (intersects.length > 0) { const sprite = intersects[0].object; if (sprite !== lastHoveredSprite) { if (lastHoveredSprite) { safeTween.Tween(lastHoveredSprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(safeTween.Easing.Quadratic.InOut) .start(); } safeTween.Tween(sprite.scale) .to({ x: 0.7, y: 0.7, z: 0.7 }, 200) .easing(safeTween.Easing.Quadratic.InOut) .start(); lastHoveredSprite = sprite; } } else if (lastHoveredSprite) { safeTween.Tween(lastHoveredSprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(safeTween.Easing.Quadratic.InOut) .start(); lastHoveredSprite = null; } }); window.addEventListener('click', (event) => { if (isFPS || isTransitioning) return; mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(hotspotSprites, true); const infoPopup = document.getElementById('infoPopup'); if (intersects.length > 0) { const sprite = intersects[0].object; const hotspot = hotspots[currentScene + '_' + currentSubScene]?.find(h => h.name === sprite.name); if (hotspot) { // Fancy click animation with FIXED TWEEN safeTween.Tween(sprite.scale) .to({ x: 1.0, y: 1.0, z: 1.0 }, 100) .easing(safeTween.Easing.Quadratic.InOut) .start() .onComplete(() => { safeTween.Tween(sprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(safeTween.Easing.Quadratic.InOut) .start(); }); infoPopup.innerHTML = hotspot.info + '<br><button onclick="this.parentElement.style.display = \'none\'; this.parentElement.classList.remove(\'visible\')">Kapat</button>'; infoPopup.style.display = 'block'; infoPopup.style.left = (event.clientX + 10) + 'px'; infoPopup.style.top = (event.clientY + 10) + 'px'; infoPopup.classList.add('visible'); if (currentScene === 2 && currentSubScene === 6) { camera.position.set(hotspot.position.x, CONFIG.physics.playerHeight, hotspot.position.z + 2); playerBody.position.set(hotspot.position.x, CONFIG.physics.playerHeight / 2, hotspot.position.z + 2); pointerControls.lock(); } } } else { if (currentScene === 1 && currentSubScene === 3) { infoPopup.style.display = 'block'; infoPopup.classList.add('visible'); } else { infoPopup.style.display = 'none'; infoPopup.classList.remove('visible'); } } }); // 🔄 UPDATE HOTSPOTS function updateHotspots() { hotspotSprites.forEach(sprite => { sprite.lookAt(camera.position); }); } // 🎭 ANIMATION EFFECTS - FIXED: Safe resource management function createClouds() { const cloudGeo = new THREE.PlaneGeometry(20, 20); const clouds = []; for (let i = 0; i < 15; i++) { const cloudMat = new THREE.MeshLambertMaterial({ color: 0xffffff, transparent: true, opacity: 0 }); const cloud = new THREE.Mesh(cloudGeo, cloudMat); cloud.position.set( Math.random() * 100 - 50, Math.random() * 30 + 20, Math.random() * 100 - 50 ); cloud.rotation.z = Math.random() * Math.PI; scene.add(cloud); clouds.push(cloud); } return { clouds, geometry: cloudGeo }; } function createFadeOverlay() { const overlayGeo = new THREE.PlaneGeometry(200, 200); const overlayMat = new THREE.MeshBasicMaterial({ color: 0x000000, transparent: true, opacity: 0 }); const overlay = new THREE.Mesh(overlayGeo, overlayMat); overlay.position.set(0, 0, -10); camera.add(overlay); return overlay; } function createOrbitAnimation(fromPos, toPos, fromTarget, toTarget, duration) { safeTween.Tween(camera.position) .to(toPos, duration) .easing(safeTween.Easing.Cubic.InOut) .start(); safeTween.Tween(orbitControls.target) .to(toTarget, duration) .easing(safeTween.Easing.Cubic.InOut) .start(); } // 🧭 NAVIGATION FUNCTIONS - FIXED: All TWEEN calls safe function goToNext() { if (isTransitioning) return; if (currentScene === 0 && currentSubScene < scenes[0].length - 1) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(0, currentSubScene + 1); }); } else if (currentScene === 0 && currentSubScene === scenes[0].length - 1) { const cloudData = createClouds(); const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 1 }, 1000) .onUpdate((obj) => { cloudData.clouds.forEach(c => c.material.opacity = obj.opacity * 0.3); overlay.material.opacity = obj.opacity * 0.5; }) .start() .onComplete(() => { cloudData.clouds.forEach(c => { scene.remove(c); c.material.dispose(); }); cloudData.geometry.dispose(); camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[1].cameraPos, orbitControls.target, CONFIG.sceneConfig[1].target, 1500 ); loadScene(1, 0); }); } else if (currentScene === 1 && currentSubScene < scenes[1].length - 1) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(1, currentSubScene + 1); }); } else if (currentScene === 1 && currentSubScene === scenes[1].length - 1) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => { overlay.material.opacity = obj.opacity; }) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[2].cameraPos, orbitControls.target, CONFIG.sceneConfig[2].target, 1500 ); loadScene(2, 0); }); } else if (currentScene === 2 && currentSubScene < scenes[2].length - 1) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(2, currentSubScene + 1); }); } else if (currentScene === 2 && currentSubScene === scenes[2].length - 1) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[3].cameraPos, orbitControls.target, CONFIG.sceneConfig[3].target, 1500 ); loadScene(3, 0); }); } } function goToPrevious() { if (isTransitioning) return; if (currentScene === 0 && currentSubScene > 0) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(0, currentSubScene - 1); }); } else if (currentScene === 1 && currentSubScene > 0) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(1, currentSubScene - 1); }); } else if (currentScene === 1 && currentSubScene === 0) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[0].cameraPos, orbitControls.target, CONFIG.sceneConfig[0].target, 1500 ); loadScene(0, scenes[0].length - 1); }); } else if (currentScene === 2 && currentSubScene > 0) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(2, currentSubScene - 1); }); } else if (currentScene === 2 && currentSubScene === 0) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => { overlay.material.opacity = obj.opacity; }) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[1].cameraPos, orbitControls.target, CONFIG.sceneConfig[1].target, 1500 ); loadScene(1, scenes[1].length - 1); }); } else if (currentScene === 3) { const overlay = createFadeOverlay(); safeTween.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[2].cameraPos, orbitControls.target, CONFIG.sceneConfig[2].target, 1500 ); loadScene(2, scenes[2].length - 1); }); } } // 🏃‍♂️ ULTRA QUALITY ANIMATION LOOP const stats = { fps: 0, frameTime: 0 }; let lastTime = performance.now(); function animate() { requestAnimationFrame(animate); const currentTime = performance.now(); const deltaTime = currentTime - lastTime; lastTime = currentTime; // Performance monitoring stats.fps = Math.round(1000 / deltaTime); stats.frameTime = Math.round(deltaTime * 100) / 100; // Update systems safeTween.update(); updateFPS(); updateHotspots(); if (!isFPS) { orbitControls.update(); } // Ultra quality rendering composer.render(); // Update stats display occasionally if (Math.random() < 0.01) { // 1% chance per frame try { const statsDisplay = document.getElementById('statsDisplay'); if (statsDisplay) { const triangles = renderer.info.render.triangles || 0; const calls = renderer.info.render.calls || 0; statsDisplay.innerHTML = ` FPS: ${stats.fps} | Frame: ${stats.frameTime}ms<br> Triangles: ${triangles.toLocaleString()}<br> Draw Calls: ${calls} | Memory: ${renderer.info.memory?.geometries || 0}G `; } } catch (e) { // Silent fail for performance info } } } // 🔧 WINDOW RESIZE WITH ULTRA QUALITY window.addEventListener('resize', () => { const aspect = window.innerWidth / window.innerHeight; camera.aspect = aspect; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); composer.setSize(window.innerWidth, window.innerHeight); // Update post-processing passes fxaaPass.material.uniforms['resolution'].value.x = 1 / window.innerWidth; fxaaPass.material.uniforms['resolution'].value.y = 1 / window.innerHeight; }); // 📱 TOUCH ZOOM WITH QUALITY let pinchStartDistance = 0; window.addEventListener('touchstart', (e) => { if (e.touches.length === 2) { pinchStartDistance = Math.hypot( e.touches[0].pageX - e.touches[1].pageX, e.touches[0].pageY - e.touches[1].pageY ); } }); window.addEventListener('touchmove', (e) => { if (e.touches.length === 2 && !isFPS) { e.preventDefault(); const pinchEndDistance = Math.hypot( e.touches[0].pageX - e.touches[1].pageX, e.touches[0].pageY - e.touches[1].pageY ); const zoomDelta = pinchEndDistance / pinchStartDistance; const newDistance = orbitControls.getDistance() / zoomDelta; orbitControls.dollyTo(THREE.MathUtils.clamp(newDistance, orbitControls.minDistance, orbitControls.maxDistance)); pinchStartDistance = pinchEndDistance; } }); window.addEventListener('touchend', (e) => { if (e.touches.length < 2) { pinchStartDistance = 0; } }); // 🚀 INITIALIZE ULTRA QUALITY APPLICATION console.log('🎯 Initializing Ultra Quality WebXR Real Estate Demo...'); try { console.log('📊 Renderer capabilities:', { maxAnisotropy: renderer.capabilities.getMaxAnisotropy(), maxTextures: renderer.capabilities.maxTextures, maxTextureSize: renderer.capabilities.maxTextureSize }); } catch (e) { console.log('📊 Renderer initialized successfully'); } // Start the application animate(); // Load initial scene with delay to ensure all scripts are loaded setTimeout(() => { loadScene(0, 0); }, 100); console.log('✅ Ultra Quality WebXR Real Estate Demo initialized successfully!'); </script></body></html>

DEMO-v3

28072025

<!DOCTYPE html> <html lang="tr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lüksemburg Gayrimenkul Demo - Ultra Advanced PBR</title> <style> body { margin: 0; padding: 0; height: 100vh; overflow: hidden; font-family: Arial, sans-serif; touch-action: none; } canvas { display: block; } #infoPopup { display: none; position: absolute; background: rgba(255, 255, 255, 0.9); padding: 20px; border-radius: 10px; max-width: 400px; font-size: 14px; color: #333; box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4); z-index: 1000; transform: translateY(20px); opacity: 0; transition: transform 0.3s, opacity 0.3s; } #infoPopup.visible { transform: translateY(0); opacity: 1; } #infoPopup a { color: #007bff; text-decoration: none; } #infoPopup a:hover { text-decoration: underline; } #infoPopup button { margin-top: 15px; padding: 8px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background 0.3s; } #infoPopup button:hover { background: #0056b3; } #navigation { position: absolute; bottom: 20px; right: 20px; display: flex; gap: 15px; z-index: 1000; } #navigation button { padding: 12px 24px; cursor: pointer; background: rgba(0, 0, 0, 0.8); color: white; border: none; border-radius: 8px; font-size: 16px; transition: background 0.3s, transform 0.2s; } #navigation button:hover { background: rgba(0, 0, 0, 1); transform: scale(1.05); } #furnitureMenu { position: absolute; bottom: 20px; left: 20px; background: rgba(0, 0, 0, 0.9); padding: 20px; border-radius: 10px; color: white; z-index: 1000; display: none; transform: translateY(20px); opacity: 0; transition: transform 0.3s, opacity 0.3s; } #furnitureMenu.visible { transform: translateY(0); opacity: 1; } #furnitureMenu select { margin: 8px 0; padding: 10px; background: #333; color: white; border: none; border-radius: 5px; width: 100%; cursor: pointer; } #furnitureMenu select:focus { outline: none; background: #444; } #versionLabel { font-weight: bold; margin-bottom: 15px; font-size: 16px; } #loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.9); color: white; padding: 20px 40px; border-radius: 10px; font-size: 16px; z-index: 1000; display: none; text-align: center; } #loadingProgress { margin-top: 10px; width: 200px; height: 10px; background: #333; border-radius: 5px; overflow: hidden; } #loadingProgressBar { height: 100%; background: #007bff; width: 0; transition: width 0.3s; } #error { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(255, 0, 0, 0.9); color: white; padding: 15px 30px; border-radius: 8px; font-size: 14px; z-index: 1000; display: none; text-align: center; } #error button { margin-top: 10px; padding: 8px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background 0.3s; } #error button:hover { background: #0056b3; } #touchControls { position: absolute; bottom: 80px; left: 20px; display: none; z-index: 1000; } #touchJoystick { width: 80px; height: 80px; background: rgba(0, 0, 0, 0.5); border-radius: 50%; position: relative; touch-action: none; } #touchJoystickInner { width: 40px; height: 40px; background: #007bff; border-radius: 50%; position: absolute; top: 20px; left: 20px; } </style> </head> <body> <div id="navigation"> <button onclick="goToPrevious()">Geri</button> <button onclick="goToNext()">İleri</button> </div> <div id="furnitureMenu"> <div>İç Mekan Versiyonu: <span id="versionLabel">Seçili</span></div> <select id="room1Select" onchange="updateFurniture('room1', this.value)"> <option value="1">Oda 1 - Versiyon 1</option> <option value="2">Oda 1 - Versiyon 2</option> <option value="3">Oda 1 - Versiyon 3</option> </select> <select id="room2Select" onchange="updateFurniture('room2', this.value)"> <option value="1">Oda 2 - Versiyon 1</option> <option value="2">Oda 2 - Versiyon 2</option> <option value="3">Oda 2 - Versiyon 3</option> </select> <select id="salonSelect" onchange="updateFurniture('salon', this.value)"> <option value="1">Salon - Versiyon 1</option> <option value="2">Salon - Versiyon 2</option> <option value="3">Salon - Versiyon 3</option> </select> <select id="terraceSelect" onchange="updateFurniture('terrace', this.value)"> <option value="1">Teras - Versiyon 1</option> <option value="2">Teras - Versiyon 2</option> <option value="3">Teras - Versiyon 3</option> </select> </div> <div id="infoPopup"></div> <div id="loading"> Yükleniyor... <div id="loadingProgress"><div id="loadingProgressBar"></div></div> </div> <div id="error"></div> <div id="touchControls"> <div id="touchJoystick"><div id="touchJoystickInner"></div></div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/LuminosityHighPassShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/16.3.5/Tween.min.js"></script> <script> // ============================= // 🚀 ULTRA ADVANCED PBR SYSTEM // ============================= console.log('🚀 Initializing Ultra Advanced PBR Real Estate System...'); // Configuration const BASE_URL = 'https://raw.githubusercontent.com/decentralize-dfw/realestatedemo/main/'; const CONFIG = { camera: { fov: 75, near: 0.1, far: 1000 }, lighting: { morning: { x: 10, y: 20, z: 5, intensity: 2.5, color: 0xfff5e6 }, noon: { x: 0, y: 30, z: 0, intensity: 3.5, color: 0xffffff }, evening: { x: -15, y: 15, z: 5, intensity: 2.0, color: 0xffd1b3 } }, sceneConfig: { 0: { cameraPos: { x: 0, y: 15, z: 30 }, target: { x: 0, y: 0, z: 0 }, autoRotate: true, light: 'noon' }, 1: { cameraPos: { x: 0, y: 10, z: 15 }, target: { x: 0, y: 0, z: 0 }, autoRotate: true, light: 'noon' }, 2: { cameraPos: { x: 0, y: 12, z: 15 }, target: { x: 0, y: 0, z: 12 }, autoRotate: false, light: 'evening' }, 3: { cameraPos: { x: 0, y: 0, z: 5 }, target: { x: 0, y: 0, z: 0 }, autoRotate: false, light: 'morning' } }, physics: { gravity: -9.81, playerHeight: 1.6, stepHeight: 0.4, speed: 10, sprintSpeed: 15 } }; // ============================= // 🎨 ULTRA ADVANCED RENDERER SETUP // ============================= THREE.Cache.enabled = true; const scene = new THREE.Scene(); const aspect = window.innerWidth / window.innerHeight; const camera = new THREE.PerspectiveCamera(CONFIG.camera.fov, aspect, CONFIG.camera.near, CONFIG.camera.far); camera.position.set(0, 15, 30); // ULTRA ADVANCED RENDERER CONFIGURATION const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false, powerPreference: "high-performance", stencil: false, depth: true }); // ADVANCED RENDER SETTINGS renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.outputColorSpace = THREE.SRGBColorSpace; // Modern color space renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.2; renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.VSMShadowMap; // Advanced shadow mapping renderer.shadowMap.autoUpdate = true; renderer.physicallyCorrectLights = true; // PBR lighting document.body.appendChild(renderer.domElement); // ============================= // 🌅 PROCEDURAL HDRI ENVIRONMENT MAP // ============================= console.log('🌅 Creating procedural HDRI environment...'); function createProceduralHDRI() { const size = 512; const canvas = document.createElement('canvas'); canvas.width = canvas.height = size; const context = canvas.getContext('2d'); // Create gradient sky const gradient = context.createLinearGradient(0, 0, 0, size); gradient.addColorStop(0, '#87CEEB'); // Sky blue top gradient.addColorStop(0.7, '#98D8E8'); // Light blue gradient.addColorStop(1, '#F0F8FF'); // Almost white bottom context.fillStyle = gradient; context.fillRect(0, 0, size, size); // Add sun const sunGradient = context.createRadialGradient(size * 0.7, size * 0.2, 0, size * 0.7, size * 0.2, size * 0.15); sunGradient.addColorStop(0, 'rgba(255, 255, 200, 0.8)'); sunGradient.addColorStop(1, 'rgba(255, 255, 200, 0)'); context.fillStyle = sunGradient; context.fillRect(0, 0, size, size); const texture = new THREE.CanvasTexture(canvas); texture.mapping = THREE.EquirectangularReflectionMapping; texture.colorSpace = THREE.SRGBColorSpace; return texture; } const envMap = createProceduralHDRI(); scene.environment = envMap; scene.background = envMap; console.log('✅ HDRI Environment created and applied'); // ============================= // 🌟 ULTRA ADVANCED LIGHTING SYSTEM // ============================= console.log('🌟 Setting up ultra advanced lighting system...'); // AMBIENT LIGHTING - Realistic environment lighting const ambientLight = new THREE.AmbientLight(0xffffff, 0.4); scene.add(ambientLight); // HEMISPHERE LIGHT - Sky and ground lighting const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6); scene.add(hemisphereLight); // MAIN DIRECTIONAL LIGHT - Sun simulation const directionalLight = new THREE.DirectionalLight(0xffffff, 2.5); directionalLight.position.set(50, 100, 50); directionalLight.castShadow = true; // ULTRA HIGH QUALITY SHADOWS directionalLight.shadow.mapSize.width = 4096; directionalLight.shadow.mapSize.height = 4096; directionalLight.shadow.camera.near = 0.1; directionalLight.shadow.camera.far = 200; directionalLight.shadow.camera.left = -100; directionalLight.shadow.camera.right = 100; directionalLight.shadow.camera.top = 100; directionalLight.shadow.camera.bottom = -100; directionalLight.shadow.bias = -0.0001; directionalLight.shadow.normalBias = 0.02; directionalLight.shadow.radius = 8; scene.add(directionalLight); // ADDITIONAL FILL LIGHTS for realistic lighting const fillLight1 = new THREE.DirectionalLight(0xffffff, 0.8); fillLight1.position.set(-30, 50, -30); scene.add(fillLight1); const fillLight2 = new THREE.DirectionalLight(0xffffff, 0.8); fillLight2.position.set(30, 50, -30); scene.add(fillLight2); // RIM LIGHT for edge enhancement const rimLight = new THREE.DirectionalLight(0xffffff, 1.2); rimLight.position.set(0, 20, -50); scene.add(rimLight); // SPOT LIGHT for interior scenes const spotLight = new THREE.SpotLight(0xffffff, 1.5, 50, Math.PI / 6, 0.3, 2); spotLight.position.set(0, 30, 0); spotLight.castShadow = true; spotLight.shadow.mapSize.width = 2048; spotLight.shadow.mapSize.height = 2048; spotLight.visible = false; scene.add(spotLight); console.log('✅ Ultra advanced lighting system initialized'); // ============================= // 🎬 ADVANCED POST-PROCESSING // ============================= console.log('🎬 Setting up advanced post-processing pipeline...'); const composer = new THREE.EffectComposer(renderer); const renderPass = new THREE.RenderPass(scene, camera); composer.addPass(renderPass); // ADVANCED BLOOM EFFECT const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 0.8, // strength 0.6, // radius 0.7 // threshold ); composer.addPass(bloomPass); console.log('✅ Advanced post-processing pipeline ready'); // ============================= // ⚙️ PHYSICS ENGINE // ============================= const world = new CANNON.World(); world.gravity.set(0, CONFIG.physics.gravity, 0); world.broadphase = new CANNON.NaiveBroadphase(); world.solver.iterations = 10; const playerBody = new CANNON.Body({ mass: 1 }); playerBody.addShape(new CANNON.Box(new CANNON.Vec3(0.25, CONFIG.physics.playerHeight / 2, 0.25))); playerBody.position.set(0, CONFIG.physics.playerHeight / 2, 0); world.addBody(playerBody); // ============================= // 🎮 ADVANCED CONTROLS // ============================= const orbitControls = new THREE.OrbitControls(camera, renderer.domElement); orbitControls.enablePan = false; orbitControls.enableZoom = true; orbitControls.zoomSpeed = 0.5; orbitControls.minDistance = 5; orbitControls.maxDistance = 100; orbitControls.autoRotateSpeed = 0.3; orbitControls.target.set(0, 0, 0); orbitControls.enableDamping = true; orbitControls.dampingFactor = 0.05; const pointerControls = new THREE.PointerLockControls(camera, renderer.domElement); let isFPS = false; // ============================= // 📁 SCENE DEFINITIONS // ============================= const scenes = [ [ { model: BASE_URL + 'City.glb', layer: 'city' }, { model: BASE_URL + 'Landmarks.glb', layer: 'kopstal' }, { model: BASE_URL + 'Landmarks.glb', layer: 'mainroute' }, { model: BASE_URL + 'Landmarks.glb', layer: 'landmark' }, { model: BASE_URL + 'Landmarks.glb', layer: 'settlements' } ], [ { model: BASE_URL + 'mahalle.glb', layer: 'mahalle' }, { model: BASE_URL + 'bldg_1.glb', layer: 'bldg_1' }, { model: BASE_URL + 'bldg_highlighted.glb', layer: 'bldg_highlighted' }, { model: BASE_URL + 'car_parking.glb', layer: 'car_parking' }, { model: BASE_URL + 'bldg_highlighted.glb', layer: 'bldg_highlighted_market' } ], [ { model: BASE_URL + 'bldg_section.glb', layer: 'bldg_section' }, { model: BASE_URL + 'dimensions.glb', layer: 'dimensions' }, { model: BASE_URL + 'furniture_2.glb', layer: 'furniture_1' }, { model: BASE_URL + 'furniture_2.glb', layer: 'furniture_2' }, { model: BASE_URL + 'furniture_3.glb', layer: 'furniture_3' }, { model: BASE_URL + 'furniture_select.glb', layer: 'furniture_select' }, { model: BASE_URL + 'bldg_section.glb', layer: 'fps' } ], [ { model: BASE_URL + 'logo.glb', layer: 'logo' } ] ]; // ============================= // 📍 HOTSPOT DATA // ============================= const hotspots = { '0_0': [ { name: 'airport', position: { x: 13.351, y: 0.356852, z: -0.161044 }, info: 'Havalimanı: 15 km, 20 dk' }, { name: 'kirchberg', position: { x: 4.64357, y: 0.356852, z: 0.92694 }, info: 'Kirchberg: 5 km, 10 dk' }, { name: 'hamilius', position: { x: -1.38266, y: 0.356852, z: -3.87947 }, info: 'Hamilius: 3 km, 8 dk' }, { name: 'belle_etoile', position: { x: -15.3761, y: 0.356852, z: -0.053178 }, info: 'Belle Etoile: 4 km, 12 dk' }, { name: 'ikea_arlon', position: { x: -46.8144, y: 0.356852, z: 4.1936 }, info: 'IKEA Arlon: 25 km, 30 dk' }, { name: 'kopstal', position: { x: -11.2501, y: 0.356852, z: 11.934 }, info: 'Kopstal: 2 km, 5 dk' }, { name: 'gare', position: { x: 0.3541, y: 0.356852, z: -6.42474 }, info: 'Gare Centrale: 7 km, 15 dk' } ], '1_2': [ { name: 'hotspot1', position: { x: 0, y: 0, z: 0 }, info: 'Bar: 4.5 yıldız, 120 yorum' }, { name: 'hotspot2', position: { x: 0, y: 0, z: 0 }, info: 'Restoran: 4.7 yıldız, 200 yorum' }, { name: 'hotspot3', position: { x: 0, y: 0, z: 0 }, info: 'Kafe: 4.3 yıldız, 80 yorum' }, { name: 'hotspot4', position: { x: 0, y: 0, z: 0 }, info: 'Spor Salonu: 4.6 yıldız, 150 yorum' }, { name: 'hotspot5', position: { x: 0, y: 0, z: 0 }, info: 'Park: 4.8 yıldız, 300 yorum' } ], '1_3': [ { name: 'pt_line_1', position: { x: 0, y: 0, z: 0 }, info: 'Place Etoile, Duraklar: 5 dk ara' }, { name: 'pt_line_2', position: { x: 0, y: 0, z: 0 }, info: 'Merkez Otobüs, Duraklar: 10 dk ara' } ], '1_4': [ { name: 'market1', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 80 m², 2+1, 1500€/ay' }, { name: 'market2', position: { x: 0, y: 0, z: 0 }, info: 'Satılık: 100 m², 3+1, 450,000€' }, { name: 'market3', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 60 m², 1+1, 1200€/ay' }, { name: 'market4', position: { x: 0, y: 0, z: 0 }, info: 'Satılık: 120 m², 4+1, 600,000€' }, { name: 'market5', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 90 m², 2+1, 1700€/ay' } ], '2_1': [ { name: 'hotspot1', position: { x: 0, y: 0, z: 12 }, info: 'Oda 1: 25 m²' }, { name: 'hotspot2', position: { x: 0, y: 0, z: 12 }, info: 'Oda 2: 20 m²' }, { name: 'hotspot3', position: { x: 0, y: 0, z: 12 }, info: 'Salon: 40 m²' }, { name: 'hotspot4', position: { x: 0, y: 0, z: 12 }, info: 'Teras: 15 m²' }, { name: 'hotspot5', position: { x: 0, y: 0, z: 12 }, info: 'Mutfak: 12 m²' } ] }; // ============================= // 🎯 HOTSPOT SPRITES // ============================= const hotspotSpriteMaterial = new THREE.SpriteMaterial({ map: new THREE.TextureLoader().load('https://raw.githubusercontent.com/decentralize-dfw/realestatedemo/refs/heads/main/pin.png'), transparent: true, depthTest: false, sizeAttenuation: false }); const hotspotSprites = []; // ============================= // 🪑 FURNITURE MODELS // ============================= const furnitureModels = { room1: { 1: BASE_URL + 'room1_f1.glb', 2: BASE_URL + 'room1_f2.glb', 3: BASE_URL + 'room1_f3.glb' }, room2: { 1: BASE_URL + 'room2_f1.glb', 2: BASE_URL + 'room2_f2.glb', 3: BASE_URL + 'room2_f3.glb' }, salon: { 1: BASE_URL + 'salon_f1.glb', 2: BASE_URL + 'salon_f2.glb', 3: BASE_URL + 'salon_f3.glb' }, terrace: { 1: BASE_URL + 'terrace_f1.glb', 2: BASE_URL + 'terrace_f2.glb', 3: BASE_URL + 'terrace_f3.glb' } }; let currentFurniture = { room1: 'none', room2: 'none', salon: 'none', terrace: 'none' }; // ============================= // 🚀 ULTRA ADVANCED GLTF LOADER // ============================= console.log('🚀 Initializing ultra advanced GLTF loader...'); const loader = new THREE.GLTFLoader(); const loadingManager = new THREE.LoadingManager(); // Loading progress tracking loadingManager.onLoad = () => console.log('🎉 All resources loaded successfully!'); loadingManager.onProgress = (url, loaded, total) => { console.log(`📈 Loading progress: ${Math.round(loaded/total*100)}% - ${url}`); }; loadingManager.onError = (url) => console.error('❌ Loading error:', url); let models = {}; let currentScene = 0; let currentSubScene = 0; let isTransitioning = false; let loadingProgress = 0; let texturesLoaded = 0; let totalTextures = 0; // ============================= // 🔧 ERROR MANAGEMENT // ============================= function showError(message) { const errorDiv = document.getElementById('error'); errorDiv.innerHTML = message + '<br><button onclick="this.parentElement.style.display = \'none\'">Kapat</button>'; errorDiv.style.display = 'block'; setTimeout(() => errorDiv.style.display = 'none', 10000); } // ============================= // 💡 ADVANCED LIGHTING CONTROL // ============================= function setAdvancedLighting(time) { console.log(`🌅 Setting ${time} lighting...`); const lightConfig = CONFIG.lighting[time]; // Animate main directional light new TWEEN.Tween(directionalLight.position) .to({ x: lightConfig.x, y: lightConfig.y, z: lightConfig.z }, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); new TWEEN.Tween(directionalLight) .to({ intensity: lightConfig.intensity }, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); // Color temperature animation new TWEEN.Tween(directionalLight.color) .to(new THREE.Color(lightConfig.color), 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); // Adjust ambient lighting based on time const ambientIntensity = time === 'noon' ? 0.4 : time === 'morning' ? 0.3 : 0.2; new TWEEN.Tween(ambientLight) .to({ intensity: ambientIntensity }, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); // Evening spot light if (time === 'evening') { spotLight.visible = true; new TWEEN.Tween(spotLight) .to({ intensity: 1.5 }, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); } else { new TWEEN.Tween(spotLight) .to({ intensity: 0 }, 1000) .easing(TWEEN.Easing.Quadratic.InOut) .onComplete(() => { if (time !== 'evening') spotLight.visible = false; }) .start(); } console.log(`✅ ${time} lighting applied`); } // ============================= // 🧹 ADVANCED MEMORY MANAGEMENT // ============================= function disposeModel(model) { if (!model) return; console.log('🧹 Disposing model and freeing memory...'); model.traverse((child) => { if (child.isMesh) { if (child.geometry) { child.geometry.dispose(); } if (child.material) { if (Array.isArray(child.material)) { child.material.forEach(material => { disposeMaterial(material); }); } else { disposeMaterial(child.material); } } } }); } function disposeMaterial(material) { if (material.map) material.map.dispose(); if (material.normalMap) material.normalMap.dispose(); if (material.roughnessMap) material.roughnessMap.dispose(); if (material.metalnessMap) material.metalnessMap.dispose(); if (material.emissiveMap) material.emissiveMap.dispose(); if (material.aoMap) material.aoMap.dispose(); if (material.displacementMap) material.displacementMap.dispose(); material.dispose(); } // ============================= // 🎨 ULTRA ADVANCED MATERIAL PROCESSING // ============================= function processUltraAdvancedMaterials(model, layerName) { console.log(`🎨 Processing ultra advanced materials for ${layerName}...`); let materialCount = 0; let texturePromises = []; model.traverse((child) => { if (child.isMesh) { materialCount++; child.castShadow = true; child.receiveShadow = true; child.frustumCulled = true; // Performance optimization if (child.material) { const material = child.material; console.log(`🔍 Analyzing material for ${child.name || 'unnamed'}:`, { type: material.type, hasColor: !!material.color, hasMap: !!material.map, hasNormalMap: !!material.normalMap, hasRoughnessMap: !!material.roughnessMap, hasMetalnessMap: !!material.metalnessMap, hasEmissiveMap: !!material.emissiveMap, hasAoMap: !!material.aoMap }); // ULTRA ADVANCED PBR MATERIAL ENHANCEMENT if (material.type === 'MeshStandardMaterial') { // Set environment map for PBR reflections material.envMap = envMap; material.envMapIntensity = 1.0; // Enhanced PBR properties if (material.roughness === undefined) material.roughness = 0.5; if (material.metalness === undefined) material.metalness = 0.1; // Color correction if (material.color) { // Enhance colors slightly for better visibility const color = material.color; color.r = Math.min(1.0, color.r * 1.1); color.g = Math.min(1.0, color.g * 1.1); color.b = Math.min(1.0, color.b * 1.1); } // Advanced texture handling if (material.map) { const texture = material.map; texture.colorSpace = THREE.SRGBColorSpace; texture.flipY = false; texture.generateMipmaps = true; texture.minFilter = THREE.LinearMipmapLinearFilter; texture.magFilter = THREE.LinearFilter; texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; // Wait for texture to load if (texture.image) { totalTextures++; const texturePromise = new Promise((resolve) => { if (texture.image.complete) { texturesLoaded++; resolve(); } else { texture.image.onload = () => { texturesLoaded++; console.log(`📸 Texture loaded for ${child.name}: ${texturesLoaded}/${totalTextures}`); resolve(); }; } }); texturePromises.push(texturePromise); } } // Normal map enhancement if (material.normalMap) { material.normalMap.colorSpace = THREE.LinearSRGBColorSpace; material.normalScale.set(1.0, 1.0); } // Roughness map enhancement if (material.roughnessMap) { material.roughnessMap.colorSpace = THREE.LinearSRGBColorSpace; } // Metalness map enhancement if (material.metalnessMap) { material.metalnessMap.colorSpace = THREE.LinearSRGBColorSpace; } // AO map enhancement if (material.aoMap) { material.aoMap.colorSpace = THREE.LinearSRGBColorSpace; material.aoMapIntensity = 1.0; } // Emissive map enhancement if (material.emissiveMap) { material.emissiveMap.colorSpace = THREE.SRGBColorSpace; material.emissiveIntensity = 1.0; } // Advanced material properties material.transparent = false; material.opacity = 1.0; material.side = THREE.FrontSide; // Performance optimization material.alphaTest = 0.1; material.depthWrite = true; material.depthTest = true; // Force material update material.needsUpdate = true; console.log(`✨ Enhanced PBR material for ${child.name || 'unnamed'}`); } // Handle other material types else if (material.type === 'MeshBasicMaterial') { material.envMap = envMap; material.reflectivity = 0.5; material.needsUpdate = true; console.log(`🎯 Enhanced basic material for ${child.name || 'unnamed'}`); } } } }); console.log(`📊 Processed ${materialCount} materials for ${layerName}`); // Return promise that resolves when all textures are loaded return Promise.all(texturePromises).then(() => { console.log(`🎉 All textures loaded for ${layerName}!`); }); } // ============================= // 📦 ULTRA ADVANCED SCENE LOADING // ============================= function loadScene(sceneIndex, subSceneIndex) { if (isTransitioning) return; console.log(`🚀 Loading scene ${sceneIndex}-${subSceneIndex} with ultra advanced system...`); isTransitioning = true; loadingProgress = 0; texturesLoaded = 0; totalTextures = 0; document.getElementById('loading').style.display = 'block'; document.getElementById('loadingProgressBar').style.width = '0%'; // UI Updates const furnitureMenu = document.getElementById('furnitureMenu'); if (sceneIndex === 2 && subSceneIndex === 5) { furnitureMenu.style.display = 'block'; furnitureMenu.classList.add('visible'); } else { furnitureMenu.style.display = 'none'; furnitureMenu.classList.remove('visible'); } document.getElementById('infoPopup').style.display = 'none'; document.getElementById('infoPopup').classList.remove('visible'); // Clear hotspots hotspotSprites.forEach(sprite => scene.remove(sprite)); hotspotSprites.length = 0; // Clear models Object.keys(models).forEach(key => { disposeModel(models[key]); scene.remove(models[key]); }); models = {}; // Reset physics world.bodies.forEach(body => { if (body !== playerBody) world.removeBody(body); }); // Reset controls isFPS = false; pointerControls.unlock(); orbitControls.enabled = true; // Touch controls document.getElementById('touchControls').style.display = (sceneIndex === 2 && subSceneIndex === 6) ? 'block' : 'none'; // Set advanced lighting setAdvancedLighting(CONFIG.sceneConfig[sceneIndex].light); function addHotspots(sceneKey) { if (hotspots[sceneKey] && !(sceneIndex === 2 && subSceneIndex === 6)) { hotspots[sceneKey].forEach(hotspot => { const sprite = new THREE.Sprite(hotspotSpriteMaterial); sprite.position.set(hotspot.position.x, hotspot.position.y + 1, hotspot.position.z); sprite.scale.set(0.5, 0.5, 0.5); sprite.name = hotspot.name; scene.add(sprite); hotspotSprites.push(sprite); }); } } async function loadUltraAdvancedModel(url, layer) { console.log(`🔄 Loading ultra advanced model: ${layer} from ${url}`); return new Promise((resolve, reject) => { loader.load( url, async (gltf) => { console.log(`✅ GLTF loaded successfully: ${layer}`); const model = gltf.scene; // Process materials and wait for textures try { await processUltraAdvancedMaterials(model, layer); models[layer] = model; scene.add(model); loadingProgress += 1 / scenes[sceneIndex].length; document.getElementById('loadingProgressBar').style.width = Math.min(loadingProgress * 100, 100) + '%'; console.log(`🎉 Model ${layer} fully processed and added to scene`); resolve(); } catch (error) { console.error(`❌ Error processing materials for ${layer}:`, error); reject(error); } }, (progress) => { const percentage = (progress.loaded / progress.total * 100); console.log(`📈 Loading progress ${layer}: ${percentage.toFixed(1)}%`); }, (error) => { console.error(`❌ Error loading model ${layer}:`, error); // Create fallback geometry const geometry = new THREE.BoxGeometry(10, 10, 10); const material = new THREE.MeshStandardMaterial({ color: 0xff4444, roughness: 0.5, metalness: 0.1, envMap: envMap }); const placeholder = new THREE.Mesh(geometry, material); placeholder.castShadow = true; placeholder.receiveShadow = true; models[layer] = placeholder; scene.add(placeholder); loadingProgress += 1 / scenes[sceneIndex].length; document.getElementById('loadingProgressBar').style.width = Math.min(loadingProgress * 100, 100) + '%'; showError(`Model yüklenemedi: ${layer}`); resolve(); // Continue with other models } ); }); } // Load scenes based on index async function loadSceneModels() { try { if (sceneIndex === 0) { const modelsToLoad = scenes[0].slice(0, subSceneIndex + 1); await Promise.all(modelsToLoad.map(modelDef => loadUltraAdvancedModel(modelDef.model, modelDef.layer) )); addHotspots('0_0'); } else if (sceneIndex === 1) { const modelsToLoad = scenes[1].slice(0, subSceneIndex + 1); await Promise.all(modelsToLoad.map(modelDef => loadUltraAdvancedModel(modelDef.model, modelDef.layer) )); if (subSceneIndex === 1) addHotspots('1_2'); if (subSceneIndex === 2) addHotspots('1_3'); if (subSceneIndex === 3) addHotspots('1_4'); } else if (sceneIndex === 2) { // Load mahalle_2 first await loadUltraAdvancedModel(BASE_URL + 'mahalle_2.glb', 'mahalle_2'); // Make mahalle_2 transparent if (models['mahalle_2']) { models['mahalle_2'].traverse((child) => { if (child.isMesh && child.material) { child.material.opacity = 0.3; child.material.transparent = true; child.material.needsUpdate = true; } }); } // Load other models const modelsToLoad = scenes[2].slice(0, subSceneIndex + 1); await Promise.all(modelsToLoad.map(modelDef => loadUltraAdvancedModel(modelDef.model, modelDef.layer) )); if (subSceneIndex >= 1) addHotspots('2_1'); } else if (sceneIndex === 3) { await loadUltraAdvancedModel(scenes[3][0].model, scenes[3][0].layer); // Logo specific processing if (models['logo']) { models['logo'].traverse((child) => { if (child.isMesh) { child.material = new THREE.MeshBasicMaterial({ map: child.material.map, transparent: true, side: THREE.DoubleSide }); } }); } // Show info popup const infoPopup = document.getElementById('infoPopup'); infoPopup.innerHTML = 'Talk with us: <a href="#" target="_blank">İletişim</a><br><button onclick="this.parentElement.style.display = \'none\'; this.parentElement.classList.remove(\'visible\')">Kapat</button>'; infoPopup.style.display = 'block'; infoPopup.style.left = '50%'; infoPopup.style.top = '50%'; infoPopup.classList.add('visible'); } finishLoading(); } catch (error) { console.error('❌ Error loading scene models:', error); finishLoading(); } } function finishLoading() { console.log('🎬 Finishing scene loading with camera animation...'); // Advanced camera animation new TWEEN.Tween(camera.position) .to(CONFIG.sceneConfig[sceneIndex].cameraPos, 2000) .easing(TWEEN.Easing.Cubic.InOut) .start(); new TWEEN.Tween(orbitControls.target) .to(CONFIG.sceneConfig[sceneIndex].target, 2000) .easing(TWEEN.Easing.Cubic.InOut) .start(); orbitControls.autoRotate = CONFIG.sceneConfig[sceneIndex].autoRotate; if (CONFIG.sceneConfig[sceneIndex].autoRotate) { orbitControls.maxPolarAngle = Math.PI / 3; orbitControls.minPolarAngle = Math.PI / 6; } else { orbitControls.maxPolarAngle = Math.PI; orbitControls.minPolarAngle = 0; } camera.updateProjectionMatrix(); orbitControls.update(); document.getElementById('loading').style.display = 'none'; isTransitioning = false; currentScene = sceneIndex; currentSubScene = subSceneIndex; console.log(`🎉 Scene ${sceneIndex}-${subSceneIndex} loaded successfully with ultra advanced system!`); } // Start loading loadSceneModels(); } // ============================= // 🪑 FURNITURE UPDATE // ============================= function updateFurniture(room, option) { if (isTransitioning) return; console.log(`🪑 Updating furniture: ${room} to option ${option}`); currentFurniture[room] = option; if (models[room]) { disposeModel(models[room]); scene.remove(models[room]); delete models[room]; } if (option !== 'none' && furnitureModels[room] && furnitureModels[room][option]) { loadUltraAdvancedModel(furnitureModels[room][option], room).then(() => { document.getElementById('versionLabel').innerText = 'Seçili'; document.getElementById(room + 'Select').value = option; console.log(`✅ Furniture updated: ${room}`); }); } } // ============================= // 🎮 FPS CONTROLS // ============================= let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false, isSprinting = false; const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); const clock = new THREE.Clock(); document.addEventListener('keydown', (event) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = true; break; case 'ArrowDown': case 'KeyS': moveBackward = true; break; case 'ArrowLeft': case 'KeyA': moveLeft = true; break; case 'ArrowRight': case 'KeyD': moveRight = true; break; case 'ShiftLeft': isSprinting = true; break; } }); document.addEventListener('keyup', (event) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = false; break; case 'ArrowDown': case 'KeyS': moveBackward = false; break; case 'ArrowLeft': case 'KeyA': moveLeft = false; break; case 'ArrowRight': case 'KeyD': moveRight = false; break; case 'ShiftLeft': isSprinting = false; break; case 'Escape': pointerControls.unlock(); break; } }); pointerControls.addEventListener('lock', () => { orbitControls.enabled = false; isFPS = true; playerBody.position.set(camera.position.x, CONFIG.physics.playerHeight / 2, camera.position.z); document.getElementById('touchControls').style.display = 'block'; }); pointerControls.addEventListener('unlock', () => { orbitControls.enabled = true; isFPS = false; camera.position.set( CONFIG.sceneConfig[2].cameraPos.x, CONFIG.sceneConfig[2].cameraPos.y, CONFIG.sceneConfig[2].cameraPos.z ); orbitControls.target.set( CONFIG.sceneConfig[2].target.x, CONFIG.sceneConfig[2].target.y, CONFIG.sceneConfig[2].target.z ); camera.updateProjectionMatrix(); document.getElementById('touchControls').style.display = 'none'; }); function updateFPS() { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; const delta = clock.getDelta(); velocity.x = 0; velocity.z = 0; direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.normalize(); const speed = isSprinting ? CONFIG.physics.sprintSpeed : CONFIG.physics.speed; if (moveForward || moveBackward) velocity.z -= direction.z * speed * delta; if (moveLeft || moveRight) velocity.x -= direction.x * speed * delta; playerBody.velocity.set(velocity.x, playerBody.velocity.y, velocity.z); world.step(delta); camera.position.set( playerBody.position.x, CONFIG.physics.playerHeight, playerBody.position.z ); } // ============================= // 📱 TOUCH CONTROLS // ============================= let isTouching = false; let touchStartX = 0, touchStartY = 0; const joystick = document.getElementById('touchJoystick'); const joystickInner = document.getElementById('touchJoystickInner'); joystick.addEventListener('touchstart', (e) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; isTouching = true; const touch = e.touches[0]; touchStartX = touch.clientX; touchStartY = touch.clientY; }); joystick.addEventListener('touchmove', (e) => { if (!isTouching) return; const touch = e.touches[0]; const deltaX = touch.clientX - touchStartX; const deltaY = touch.clientY - touchStartY; const maxDist = 30; const dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); const cappedDist = Math.min(dist, maxDist); const angle = Math.atan2(deltaY, deltaX); joystickInner.style.left = (20 + Math.cos(angle) * cappedDist) + 'px'; joystickInner.style.top = (20 + Math.sin(angle) * cappedDist) + 'px'; moveForward = deltaY < -10; moveBackward = deltaY > 10; moveLeft = deltaX < -10; moveRight = deltaX > 10; }); joystick.addEventListener('touchend', () => { isTouching = false; moveForward = moveBackward = moveLeft = moveRight = false; joystickInner.style.left = '20px'; joystickInner.style.top = '20px'; }); // ============================= // 🎯 HOTSPOT INTERACTION // ============================= const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); let lastHoveredSprite = null; window.addEventListener('mousemove', (event) => { if (isFPS || isTransitioning) return; mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(hotspotSprites, true); if (intersects.length > 0) { const sprite = intersects[0].object; if (sprite !== lastHoveredSprite) { if (lastHoveredSprite) { new TWEEN.Tween(lastHoveredSprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); } new TWEEN.Tween(sprite.scale) .to({ x: 0.6, y: 0.6, z: 0.6 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); lastHoveredSprite = sprite; } } else if (lastHoveredSprite) { new TWEEN.Tween(lastHoveredSprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); lastHoveredSprite = null; } }); window.addEventListener('click', (event) => { if (isFPS || isTransitioning) return; mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(hotspotSprites, true); const infoPopup = document.getElementById('infoPopup'); if (intersects.length > 0) { const sprite = intersects[0].object; const hotspot = hotspots[currentScene + '_' + currentSubScene]?.find(h => h.name === sprite.name); if (hotspot) { new TWEEN.Tween(sprite.scale) .to({ x: 0.7, y: 0.7, z: 0.7 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start() .onComplete(() => { new TWEEN.Tween(sprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); }); infoPopup.innerHTML = hotspot.info + '<br><button onclick="this.parentElement.style.display = \'none\'; this.parentElement.classList.remove(\'visible\')">Kapat</button>'; infoPopup.style.display = 'block'; infoPopup.style.left = (event.clientX + 10) + 'px'; infoPopup.style.top = (event.clientY + 10) + 'px'; infoPopup.classList.add('visible'); if (currentScene === 2 && currentSubScene === 6) { camera.position.set(hotspot.position.x, CONFIG.physics.playerHeight, hotspot.position.z + 2); playerBody.position.set(hotspot.position.x, CONFIG.physics.playerHeight / 2, hotspot.position.z + 2); pointerControls.lock(); } } } else { if (currentScene === 1 && currentSubScene === 3) { infoPopup.style.display = 'block'; infoPopup.classList.add('visible'); } else { infoPopup.style.display = 'none'; infoPopup.classList.remove('visible'); } } }); // ============================= // 🎪 HOTSPOT UPDATES // ============================= function updateHotspots() { hotspotSprites.forEach(sprite => { sprite.lookAt(camera.position); }); } // ============================= // 🎬 ANIMATION EFFECTS // ============================= function createClouds() { const cloudGeo = new THREE.PlaneGeometry(20, 20); const cloudMat = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0 }); const clouds = []; for (let i = 0; i < 10; i++) { const cloud = new THREE.Mesh(cloudGeo, cloudMat); cloud.position.set(Math.random() * 40 - 20, Math.random() * 20, Math.random() * 40 - 20); scene.add(cloud); clouds.push(cloud); } return clouds; } function createFadeOverlay() { const overlayGeo = new THREE.PlaneGeometry(100, 100); const overlayMat = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0 }); const overlay = new THREE.Mesh(overlayGeo, overlayMat); overlay.position.set(0, 0, 5); scene.add(overlay); return overlay; } function createOrbitAnimation(fromPos, toPos, fromTarget, toTarget, duration) { new TWEEN.Tween(camera.position) .to(toPos, duration) .easing(TWEEN.Easing.Cubic.InOut) .start(); new TWEEN.Tween(orbitControls.target) .to(toTarget, duration) .easing(TWEEN.Easing.Cubic.InOut) .start(); } // ============================= // 🧭 NAVIGATION FUNCTIONS // ============================= function goToNext() { if (isTransitioning) return; console.log(`🧭 Navigating to next scene from ${currentScene}-${currentSubScene}`); if (currentScene === 0 && currentSubScene < scenes[0].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); loadScene(0, currentSubScene + 1); }); } else if (currentScene === 0 && currentSubScene === scenes[0].length - 1) { const clouds = createClouds(); const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 800) .onUpdate((obj) => { clouds.forEach(c => c.material.opacity = obj.opacity); overlay.material.opacity = obj.opacity; }) .start() .onComplete(() => { clouds.forEach(c => scene.remove(c)); scene.remove(overlay); createOrbitAnimation( camera.position, CONFIG.sceneConfig[1].cameraPos, orbitControls.target, CONFIG.sceneConfig[1].target, 2000 ); loadScene(1, 0); }); } else if (currentScene === 1 && currentSubScene < scenes[1].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); loadScene(1, currentSubScene + 1); }); } else if (currentScene === 1 && currentSubScene === scenes[1].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 800) .onUpdate((obj) => { overlay.material.opacity = obj.opacity; }) .start() .onComplete(() => { scene.remove(overlay); createOrbitAnimation( camera.position, CONFIG.sceneConfig[2].cameraPos, orbitControls.target, CONFIG.sceneConfig[2].target, 2000 ); loadScene(2, 0); }); } else if (currentScene === 2 && currentSubScene < scenes[2].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); loadScene(2, currentSubScene + 1); }); } else if (currentScene === 2 && currentSubScene === scenes[2].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); createOrbitAnimation( camera.position, CONFIG.sceneConfig[3].cameraPos, orbitControls.target, CONFIG.sceneConfig[3].target, 2000 ); loadScene(3, 0); }); } } function goToPrevious() { if (isTransitioning) return; console.log(`🧭 Navigating to previous scene from ${currentScene}-${currentSubScene}`); if (currentScene === 0 && currentSubScene > 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); loadScene(0, currentSubScene - 1); }); } else if (currentScene === 1 && currentSubScene > 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); loadScene(1, currentSubScene - 1); }); } else if (currentScene === 1 && currentSubScene === 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); createOrbitAnimation( camera.position, CONFIG.sceneConfig[0].cameraPos, orbitControls.target, CONFIG.sceneConfig[0].target, 2000 ); loadScene(0, scenes[0].length - 1); }); } else if (currentScene === 2 && currentSubScene > 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); loadScene(2, currentSubScene - 1); }); } else if (currentScene === 2 && currentSubScene === 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 800) .onUpdate((obj) => { overlay.material.opacity = obj.opacity; }) .start() .onComplete(() => { scene.remove(overlay); createOrbitAnimation( camera.position, CONFIG.sceneConfig[1].cameraPos, orbitControls.target, CONFIG.sceneConfig[1].target, 2000 ); loadScene(1, scenes[1].length - 1); }); } else if (currentScene === 3) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { scene.remove(overlay); createOrbitAnimation( camera.position, CONFIG.sceneConfig[2].cameraPos, orbitControls.target, CONFIG.sceneConfig[2].target, 2000 ); loadScene(2, scenes[2].length - 1); }); } } // ============================= // 🎪 ULTRA ADVANCED ANIMATION LOOP // ============================= function animate() { requestAnimationFrame(animate); // Update all systems TWEEN.update(); updateFPS(); updateHotspots(); if (!isFPS) { orbitControls.update(); } // Physics simulation world.step(1/60); // Advanced post-processing render composer.render(); } // ============================= // 📐 WINDOW RESIZE HANDLER // ============================= window.addEventListener('resize', () => { console.log('📐 Handling window resize...'); const aspect = window.innerWidth / window.innerHeight; camera.aspect = aspect; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); composer.setSize(window.innerWidth, window.innerHeight); console.log('✅ Window resize handled'); }); // ============================= // 📱 ADVANCED TOUCH ZOOM // ============================= let pinchStartDistance = 0; window.addEventListener('touchstart', (e) => { if (e.touches.length === 2) { pinchStartDistance = Math.hypot( e.touches[0].pageX - e.touches[1].pageX, e.touches[0].pageY - e.touches[1].pageY ); } }); window.addEventListener('touchmove', (e) => { if (e.touches.length === 2 && !isFPS) { const pinchEndDistance = Math.hypot( e.touches[0].pageX - e.touches[1].pageX, e.touches[0].pageY - e.touches[1].pageY ); const zoomDelta = pinchEndDistance / pinchStartDistance; const newDistance = orbitControls.getDistance() / zoomDelta; orbitControls.dollyTo(THREE.MathUtils.clamp(newDistance, orbitControls.minDistance, orbitControls.maxDistance)); pinchStartDistance = pinchEndDistance; } }); window.addEventListener('touchend', (e) => { if (e.touches.length < 2) { pinchStartDistance = 0; } }); // ============================= // 🚀 SYSTEM INITIALIZATION // ============================= console.log('🚀 Starting ultra advanced PBR real estate system...'); // Start animation loop animate(); // Load initial scene loadScene(0, 0); console.log('🎉 Ultra Advanced PBR Real Estate System fully initialized!'); console.log('💎 Features: Ultra Advanced PBR Materials, HDRI Environment, Advanced Lighting, High Quality Shadows, Post-Processing, Physics, WebXR Ready'); </script> </body> </html>

V222

 

<!DOCTYPE html><html lang="tr"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lüksemburg Gayrimenkul Demo - Ultra Quality</title> <style> body { margin: 0; padding: 0; height: 100vh; overflow: hidden; font-family: Arial, sans-serif; touch-action: none; } canvas { display: block; } #infoPopup { display: none; position: absolute; background: rgba(255, 255, 255, 0.9); padding: 20px; border-radius: 10px; max-width: 400px; font-size: 14px; color: #333; box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4); z-index: 1000; transform: translateY(20px); opacity: 0; transition: transform 0.3s, opacity 0.3s; } #infoPopup.visible { transform: translateY(0); opacity: 1; } #infoPopup a { color: #007bff; text-decoration: none; } #infoPopup a:hover { text-decoration: underline; } #infoPopup button { margin-top: 15px; padding: 8px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background 0.3s; } #infoPopup button:hover { background: #0056b3; } #navigation { position: absolute; bottom: 20px; right: 20px; display: flex; gap: 15px; z-index: 1000; } #navigation button { padding: 12px 24px; cursor: pointer; background: rgba(0, 0, 0, 0.8); color: white; border: none; border-radius: 8px; font-size: 16px; transition: background 0.3s, transform 0.2s; } #navigation button:hover { background: rgba(0, 0, 0, 1); transform: scale(1.05); } #furnitureMenu { position: absolute; bottom: 20px; left: 20px; background: rgba(0, 0, 0, 0.9); padding: 20px; border-radius: 10px; color: white; z-index: 1000; display: none; transform: translateY(20px); opacity: 0; transition: transform 0.3s, opacity 0.3s; } #furnitureMenu.visible { transform: translateY(0); opacity: 1; } #furnitureMenu select { margin: 8px 0; padding: 10px; background: #333; color: white; border: none; border-radius: 5px; width: 100%; cursor: pointer; } #furnitureMenu select:focus { outline: none; background: #444; } #versionLabel { font-weight: bold; margin-bottom: 15px; font-size: 16px; } #loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.9); color: white; padding: 20px 40px; border-radius: 10px; font-size: 16px; z-index: 1000; display: none; text-align: center; } #loadingProgress { margin-top: 10px; width: 200px; height: 10px; background: #333; border-radius: 5px; overflow: hidden; } #loadingProgressBar { height: 100%; background: #007bff; width: 0; transition: width 0.3s; } #error { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(255, 0, 0, 0.9); color: white; padding: 15px 30px; border-radius: 8px; font-size: 14px; z-index: 1000; display: none; text-align: center; } #error button { margin-top: 10px; padding: 8px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; transition: background 0.3s; } #error button:hover { background: #0056b3; } #touchControls { position: absolute; bottom: 80px; left: 20px; display: none; z-index: 1000; } #touchJoystick { width: 80px; height: 80px; background: rgba(0, 0, 0, 0.5); border-radius: 50%; position: relative; touch-action: none; } #touchJoystickInner { width: 40px; height: 40px; background: #007bff; border-radius: 50%; position: absolute; top: 20px; left: 20px; } #qualityControls { position: absolute; top: 20px; left: 20px; background: rgba(0, 0, 0, 0.8); color: white; padding: 15px; border-radius: 8px; font-size: 12px; z-index: 1000; } #qualityControls select { background: #333; color: white; border: none; padding: 5px; margin: 5px; border-radius: 3px; } </style></head><body> <div id="qualityControls"> <div>🎨 Ultra Quality WebXR</div> <div>Shadow Quality: <select id="shadowQuality"> <option value="low">Low (1024)</option> <option value="medium">Medium (2048)</option> <option value="high" selected>High (4096)</option> <option value="ultra">Ultra (8192)</option> </select></div> <div>Anti-aliasing: <select id="aaLevel"> <option value="1">Off</option> <option value="2">2x MSAA</option> <option value="4" selected>4x MSAA</option> <option value="8">8x MSAA</option> </select></div> <div>Post-FX: <select id="postFX"> <option value="none">None</option> <option value="basic">Basic</option> <option value="advanced" selected>Advanced</option> <option value="ultra">Ultra</option> </select></div> </div> <div id="navigation"> <button onclick="goToPrevious()">Geri</button> <button onclick="goToNext()">İleri</button> </div> <div id="furnitureMenu"> <div>İç Mekan Versiyonu: <span id="versionLabel">Seçili</span></div> <select id="room1Select" onchange="updateFurniture('room1', this.value)"> <option value="1">Oda 1 - Versiyon 1</option> <option value="2">Oda 1 - Versiyon 2</option> <option value="3">Oda 1 - Versiyon 3</option> </select> <select id="room2Select" onchange="updateFurniture('room2', this.value)"> <option value="1">Oda 2 - Versiyon 1</option> <option value="2">Oda 2 - Versiyon 2</option> <option value="3">Oda 2 - Versiyon 3</option> </select> <select id="salonSelect" onchange="updateFurniture('salon', this.value)"> <option value="1">Salon - Versiyon 1</option> <option value="2">Salon - Versiyon 2</option> <option value="3">Salon - Versiyon 3</option> </select> <select id="terraceSelect" onchange="updateFurniture('terrace', this.value)"> <option value="1">Teras - Versiyon 1</option> <option value="2">Teras - Versiyon 2</option> <option value="3">Teras - Versiyon 3</option> </select> </div> <div id="infoPopup"></div> <div id="loading"> Yükleniyor... <div id="loadingProgress"><div id="loadingProgressBar"></div></div> </div> <div id="error"></div> <div id="touchControls"> <div id="touchJoystick"><div id="touchJoystickInner"></div></div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/DRACOLoader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/KTX2Loader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/RGBELoader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/SSAOPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/TAARenderPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/SMAAPass.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/LuminosityHighPassShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/FXAAShader.js"></script> <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/16.3.5/Tween.min.js"></script> <script> // 🎯 ULTRA QUALITY WEBXR CONFIGURATION const BASE_URL = 'https://raw.githubusercontent.com/decentralize-dfw/realestatedemo/main/'; const CONFIG = { quality: { maxPixelRatio: 2.0, shadowMapSize: 4096, anisotropy: 16, antialias: 4, enableHDR: true, enableSSAO: true, enableBloom: true, enableTAA: true }, camera: { fov: 75, near: 0.1, far: 1000 }, lighting: { // Hyperfy kalitesinde gerçekçi lighting morning: { sun: { x: 50, y: 30, z: 20, intensity: 2.5, color: 0xfff4e6, castShadow: true }, ambient: { intensity: 0.4, color: 0x404040 }, hemisphere: { sky: 0x87CEEB, ground: 0x362d1d, intensity: 0.8 } }, noon: { sun: { x: 0, y: 100, z: 0, intensity: 3.2, color: 0xffffff, castShadow: true }, ambient: { intensity: 0.6, color: 0x404040 }, hemisphere: { sky: 0x87CEEB, ground: 0x362d1d, intensity: 1.0 } }, evening: { sun: { x: -50, y: 20, z: -30, intensity: 1.8, color: 0xffd4a3, castShadow: true }, ambient: { intensity: 0.3, color: 0x404040 }, hemisphere: { sky: 0xff7f50, ground: 0x362d1d, intensity: 0.6 } } }, sceneConfig: { 0: { cameraPos: { x: 0, y: 15, z: 30 }, target: { x: 0, y: 0, z: 0 }, autoRotate: true, light: 'noon' }, 1: { cameraPos: { x: 0, y: 10, z: 15 }, target: { x: 0, y: 0, z: 0 }, autoRotate: true, light: 'noon' }, 2: { cameraPos: { x: 0, y: 12, z: 15 }, target: { x: 0, y: 0, z: 12 }, autoRotate: false, light: 'evening' }, 3: { cameraPos: { x: 0, y: 0, z: 5 }, target: { x: 0, y: 0, z: 0 }, autoRotate: false, light: 'morning' } }, physics: { gravity: -9.81, playerHeight: 1.6, stepHeight: 0.4, speed: 10, sprintSpeed: 15, maxSlope: Math.PI / 4, headBobAmplitude: 0.1, headBobFrequency: 2 } }; // 🚀 ULTRA QUALITY SCENE SETUP THREE.Cache.enabled = true; const scene = new THREE.Scene(); // Professional HDRI-style background scene.background = new THREE.Color(0x87CEEB); scene.fog = new THREE.Fog(0x87CEEB, 100, 500); // Atmospheric fog const aspect = window.innerWidth / window.innerHeight; const camera = new THREE.PerspectiveCamera(CONFIG.camera.fov, aspect, CONFIG.camera.near, CONFIG.camera.far); camera.position.set(0, 15, 30); // 🔥 ULTRA RENDERER SETUP const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false, powerPreference: "high-performance", stencil: false, depth: true, logarithmicDepthBuffer: false }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, CONFIG.quality.maxPixelRatio)); // Ultra Quality Render Settings renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.VSMShadowMap; // En kaliteli shadow type renderer.shadowMap.autoUpdate = true; // HDR Pipeline renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.2; renderer.outputEncoding = THREE.sRGBEncoding; // Advanced Render States renderer.physicallyCorrectLights = true; renderer.gammaFactor = 2.2; document.body.appendChild(renderer.domElement); // 🌟 HDRI ENVIRONMENT MAP (Professional IBL) const pmremGenerator = new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); // Create procedural HDRI-like environment const createHDRIEnvironment = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = 1024; canvas.height = 512; // Create gradient sky const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); gradient.addColorStop(0, '#87CEEB'); // Sky blue gradient.addColorStop(0.3, '#b6d8f0'); // Light blue gradient.addColorStop(0.7, '#f0f8ff'); // Alice blue gradient.addColorStop(1, '#fffacd'); // Lemon chiffon ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); // Add sun const sunGrad = ctx.createRadialGradient(512, 100, 0, 512, 100, 200); sunGrad.addColorStop(0, 'rgba(255,255,200,0.8)'); sunGrad.addColorStop(1, 'rgba(255,255,200,0)'); ctx.fillStyle = sunGrad; ctx.fillRect(0, 0, canvas.width, canvas.height); const texture = new THREE.CanvasTexture(canvas); texture.mapping = THREE.EquirectangularReflectionMapping; texture.encoding = THREE.sRGBEncoding; const envMap = pmremGenerator.fromEquirectangular(texture).texture; scene.environment = envMap; texture.dispose(); return envMap; }; const envMap = createHDRIEnvironment(); // 🎨 ULTRA QUALITY POST-PROCESSING PIPELINE const composer = new THREE.EffectComposer(renderer); // Main render pass const renderPass = new THREE.RenderPass(scene, camera); composer.addPass(renderPass); // SSAO (Screen Space Ambient Occlusion) for realism const ssaoPass = new THREE.SSAOPass(scene, camera, window.innerWidth, window.innerHeight); ssaoPass.kernelRadius = 16; ssaoPass.minDistance = 0.001; ssaoPass.maxDistance = 0.1; composer.addPass(ssaoPass); // Bloom for emissive materials const bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 0.4, // strength 0.8, // radius 0.1 // threshold ); composer.addPass(bloomPass); // TAA (Temporal Anti-Aliasing) for ultra smooth edges const taaRenderPass = new THREE.TAARenderPass(scene, camera); taaRenderPass.sampleLevel = 2; composer.addPass(taaRenderPass); // FXAA as final polish const fxaaPass = new THREE.ShaderPass(THREE.FXAAShader); fxaaPass.material.uniforms['resolution'].value.x = 1 / window.innerWidth; fxaaPass.material.uniforms['resolution'].value.y = 1 / window.innerHeight; composer.addPass(fxaaPass); // 💡 ULTRA REALISTIC LIGHTING SYSTEM let currentLights = {}; const setupLighting = (lightConfig) => { // Clear existing lights Object.values(currentLights).forEach(light => { scene.remove(light); }); currentLights = {}; // Sun light (main directional) const sunLight = new THREE.DirectionalLight( lightConfig.sun.color, lightConfig.sun.intensity ); sunLight.position.set(lightConfig.sun.x, lightConfig.sun.y, lightConfig.sun.z); sunLight.castShadow = lightConfig.sun.castShadow; // Ultra quality shadows sunLight.shadow.mapSize.width = CONFIG.quality.shadowMapSize; sunLight.shadow.mapSize.height = CONFIG.quality.shadowMapSize; sunLight.shadow.camera.near = 0.1; sunLight.shadow.camera.far = 500; sunLight.shadow.camera.left = -100; sunLight.shadow.camera.right = 100; sunLight.shadow.camera.top = 100; sunLight.shadow.camera.bottom = -100; sunLight.shadow.bias = -0.0001; sunLight.shadow.normalBias = 0.02; sunLight.shadow.radius = 8; sunLight.shadow.blurSamples = 25; scene.add(sunLight); currentLights.sun = sunLight; // Ambient light for general illumination const ambientLight = new THREE.AmbientLight( lightConfig.ambient.color, lightConfig.ambient.intensity ); scene.add(ambientLight); currentLights.ambient = ambientLight; // Hemisphere light for realistic sky/ground bounce const hemisphereLight = new THREE.HemisphereLight( lightConfig.hemisphere.sky, lightConfig.hemisphere.ground, lightConfig.hemisphere.intensity ); scene.add(hemisphereLight); currentLights.hemisphere = hemisphereLight; // Fill lights for professional 3-point lighting const keyLight = new THREE.DirectionalLight(0xffffff, 0.5); keyLight.position.set(-30, 40, 30); scene.add(keyLight); currentLights.key = keyLight; const fillLight = new THREE.DirectionalLight(0xffd4a3, 0.3); fillLight.position.set(30, 20, -30); scene.add(fillLight); currentLights.fill = fillLight; const rimLight = new THREE.DirectionalLight(0x87CEEB, 0.2); rimLight.position.set(0, 10, -50); scene.add(rimLight); currentLights.rim = rimLight; }; // Initialize with noon lighting setupLighting(CONFIG.lighting.noon); // 🎯 PHYSICS ENGINE const world = new CANNON.World(); world.gravity.set(0, CONFIG.physics.gravity, 0); world.broadphase = new CANNON.NaiveBroadphase(); world.solver.iterations = 10; const playerBody = new CANNON.Body({ mass: 1 }); playerBody.addShape(new CANNON.Box(new CANNON.Vec3(0.25, CONFIG.physics.playerHeight / 2, 0.25))); playerBody.position.set(0, CONFIG.physics.playerHeight / 2, 0); world.addBody(playerBody); // 🎮 ADVANCED CONTROLS const orbitControls = new THREE.OrbitControls(camera, renderer.domElement); orbitControls.enablePan = false; orbitControls.enableZoom = true; orbitControls.zoomSpeed = 0.5; orbitControls.minDistance = 5; orbitControls.maxDistance = 100; orbitControls.autoRotateSpeed = 0.5; orbitControls.target.set(0, 0, 0); orbitControls.enableDamping = true; orbitControls.dampingFactor = 0.05; const pointerControls = new THREE.PointerLockControls(camera, renderer.domElement); let isFPS = false; // 🏗️ SCENE DEFINITIONS const scenes = [ [ { model: BASE_URL + 'City.glb', layer: 'city' }, { model: BASE_URL + 'Landmarks.glb', layer: 'kopstal' }, { model: BASE_URL + 'Landmarks.glb', layer: 'mainroute' }, { model: BASE_URL + 'Landmarks.glb', layer: 'landmark' }, { model: BASE_URL + 'Landmarks.glb', layer: 'settlements' } ], [ { model: BASE_URL + 'mahalle.glb', layer: 'mahalle' }, { model: BASE_URL + 'bldg_1.glb', layer: 'bldg_1' }, { model: BASE_URL + 'bldg_highlighted.glb', layer: 'bldg_highlighted' }, { model: BASE_URL + 'car_parking.glb', layer: 'car_parking' }, { model: BASE_URL + 'bldg_highlighted.glb', layer: 'bldg_highlighted_market' } ], [ { model: BASE_URL + 'bldg_section.glb', layer: 'bldg_section' }, { model: BASE_URL + 'dimensions.glb', layer: 'dimensions' }, { model: BASE_URL + 'furniture_2.glb', layer: 'furniture_1' }, { model: BASE_URL + 'furniture_2.glb', layer: 'furniture_2' }, { model: BASE_URL + 'furniture_3.glb', layer: 'furniture_3' }, { model: BASE_URL + 'furniture_select.glb', layer: 'furniture_select' }, { model: BASE_URL + 'bldg_section.glb', layer: 'fps' } ], [ { model: BASE_URL + 'logo.glb', layer: 'logo' } ] ]; // 📍 HOTSPOT DATA const hotspots = { '0_0': [ { name: 'airport', position: { x: 13.351, y: 0.356852, z: -0.161044 }, info: 'Havalimanı: 15 km, 20 dk' }, { name: 'kirchberg', position: { x: 4.64357, y: 0.356852, z: 0.92694 }, info: 'Kirchberg: 5 km, 10 dk' }, { name: 'hamilius', position: { x: -1.38266, y: 0.356852, z: -3.87947 }, info: 'Hamilius: 3 km, 8 dk' }, { name: 'belle_etoile', position: { x: -15.3761, y: 0.356852, z: -0.053178 }, info: 'Belle Etoile: 4 km, 12 dk' }, { name: 'ikea_arlon', position: { x: -46.8144, y: 0.356852, z: 4.1936 }, info: 'IKEA Arlon: 25 km, 30 dk' }, { name: 'kopstal', position: { x: -11.2501, y: 0.356852, z: 11.934 }, info: 'Kopstal: 2 km, 5 dk' }, { name: 'gare', position: { x: 0.3541, y: 0.356852, z: -6.42474 }, info: 'Gare Centrale: 7 km, 15 dk' } ], '1_2': [ { name: 'hotspot1', position: { x: 0, y: 0, z: 0 }, info: 'Bar: 4.5 yıldız, 120 yorum' }, { name: 'hotspot2', position: { x: 0, y: 0, z: 0 }, info: 'Restoran: 4.7 yıldız, 200 yorum' }, { name: 'hotspot3', position: { x: 0, y: 0, z: 0 }, info: 'Kafe: 4.3 yıldız, 80 yorum' }, { name: 'hotspot4', position: { x: 0, y: 0, z: 0 }, info: 'Spor Salonu: 4.6 yıldız, 150 yorum' }, { name: 'hotspot5', position: { x: 0, y: 0, z: 0 }, info: 'Park: 4.8 yıldız, 300 yorum' } ], '1_3': [ { name: 'pt_line_1', position: { x: 0, y: 0, z: 0 }, info: 'Place Etoile, Duraklar: 5 dk ara' }, { name: 'pt_line_2', position: { x: 0, y: 0, z: 0 }, info: 'Merkez Otobüs, Duraklar: 10 dk ara' } ], '1_4': [ { name: 'market1', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 80 m², 2+1, 1500€/ay' }, { name: 'market2', position: { x: 0, y: 0, z: 0 }, info: 'Satılık: 100 m², 3+1, 450,000€' }, { name: 'market3', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 60 m², 1+1, 1200€/ay' }, { name: 'market4', position: { x: 0, y: 0, z: 0 }, info: 'Satılık: 120 m², 4+1, 600,000€' }, { name: 'market5', position: { x: 0, y: 0, z: 0 }, info: 'Kiralık: 90 m², 2+1, 1700€/ay' } ], '2_1': [ { name: 'hotspot1', position: { x: 0, y: 0, z: 12 }, info: 'Oda 1: 25 m²' }, { name: 'hotspot2', position: { x: 0, y: 0, z: 12 }, info: 'Oda 2: 20 m²' }, { name: 'hotspot3', position: { x: 0, y: 0, z: 12 }, info: 'Salon: 40 m²' }, { name: 'hotspot4', position: { x: 0, y: 0, z: 12 }, info: 'Teras: 15 m²' }, { name: 'hotspot5', position: { x: 0, y: 0, z: 12 }, info: 'Mutfak: 12 m²' } ] }; // 📌 ULTRA QUALITY HOTSPOT SPRITES const hotspotTexture = new THREE.TextureLoader().load( 'https://raw.githubusercontent.com/decentralize-dfw/realestatedemo/refs/heads/main/pin.png' ); hotspotTexture.anisotropy = renderer.capabilities.getMaxAnisotropy(); const hotspotSpriteMaterial = new THREE.SpriteMaterial({ map: hotspotTexture, transparent: true, depthTest: false, sizeAttenuation: false }); const hotspotSprites = []; // 🪑 FURNITURE MODELS const furnitureModels = { room1: { 1: BASE_URL + 'room1_f1.glb', 2: BASE_URL + 'room1_f2.glb', 3: BASE_URL + 'room1_f3.glb' }, room2: { 1: BASE_URL + 'room2_f1.glb', 2: BASE_URL + 'room2_f2.glb', 3: BASE_URL + 'room2_f3.glb' }, salon: { 1: BASE_URL + 'salon_f1.glb', 2: BASE_URL + 'salon_f2.glb', 3: BASE_URL + 'salon_f3.glb' }, terrace: { 1: BASE_URL + 'terrace_f1.glb', 2: BASE_URL + 'terrace_f2.glb', 3: BASE_URL + 'terrace_f3.glb' } }; let currentFurniture = { room1: 'none', room2: 'none', salon: 'none', terrace: 'none' }; // 🔧 ADVANCED GLTF LOADER WITH DRACO SUPPORT const loader = new THREE.GLTFLoader(); // DRACO compression support for lighter models const dracoLoader = new THREE.DRACOLoader(); dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/'); dracoLoader.setDecoderConfig({ type: 'js' }); loader.setDRACOLoader(dracoLoader); // KTX2 texture compression support const ktx2Loader = new THREE.KTX2Loader(); ktx2Loader.setTranscoderPath('https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/libs/basis/'); ktx2Loader.detectSupport(renderer); loader.setKTX2Loader(ktx2Loader); let models = {}; let currentScene = 0; let currentSubScene = 0; let isTransitioning = false; let loadingProgress = 0; // 🚨 ERROR MANAGEMENT function showError(message) { const errorDiv = document.getElementById('error'); errorDiv.innerHTML = message + '<br><button onclick="this.parentElement.style.display = \'none\'">Kapat</button>'; errorDiv.style.display = 'block'; setTimeout(() => errorDiv.style.display = 'none', 10000); } // 💡 DYNAMIC LIGHTING CONTROL function setLighting(timeOfDay) { const lightConfig = CONFIG.lighting[timeOfDay]; setupLighting(lightConfig); // Smooth transition new TWEEN.Tween({ progress: 0 }) .to({ progress: 1 }, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); } // 🗑️ ADVANCED MEMORY MANAGEMENT function disposeModel(model) { if (!model) return; model.traverse((child) => { if (child.isMesh) { if (child.geometry) { child.geometry.dispose(); } if (child.material) { const materials = Array.isArray(child.material) ? child.material : [child.material]; materials.forEach(material => { // Dispose all textures Object.keys(material).forEach(key => { if (material[key] && material[key].isTexture) { material[key].dispose(); } }); material.dispose(); }); } } }); } // 🎨 ULTRA QUALITY MATERIAL PROCESSING function processUltraQualityMaterials(model, layer) { let materialCount = 0; model.traverse((child) => { if (child.isMesh) { materialCount++; child.castShadow = true; child.receiveShadow = true; child.frustumCulled = true; if (child.material) { const material = child.material; console.log(`🎨 Processing ultra-quality material for ${child.name || 'unnamed'}:`, { type: material.type, hasAlbedo: !!material.map, hasNormal: !!material.normalMap, hasRoughness: !!material.roughnessMap, hasMetalness: !!material.metalnessMap, hasEmission: !!material.emissiveMap, hasAO: !!material.aoMap }); // ULTRA QUALITY PBR MATERIALS if (material.isMeshStandardMaterial) { // Enhanced PBR properties material.envMap = envMap; material.envMapIntensity = 1.0; // Ensure proper material properties if (!material.roughness) material.roughness = 0.8; if (!material.metalness) material.metalness = 0.0; // Enhanced texture settings if (material.map) { material.map.anisotropy = CONFIG.quality.anisotropy; material.map.encoding = THREE.sRGBEncoding; material.map.generateMipmaps = true; } if (material.normalMap) { material.normalMap.anisotropy = CONFIG.quality.anisotropy; material.normalScale.set(1, 1); } if (material.roughnessMap) { material.roughnessMap.anisotropy = CONFIG.quality.anisotropy; } if (material.metalnessMap) { material.metalnessMap.anisotropy = CONFIG.quality.anisotropy; } if (material.emissiveMap) { material.emissiveMap.anisotropy = CONFIG.quality.anisotropy; material.emissiveMap.encoding = THREE.sRGBEncoding; } if (material.aoMap) { material.aoMap.anisotropy = CONFIG.quality.anisotropy; material.aoIntensity = 1.0; } // Physical properties material.transparent = material.opacity < 1.0; material.side = material.transparent ? THREE.DoubleSide : THREE.FrontSide; material.alphaTest = material.transparent ? 0.1 : 0; // Force material update material.needsUpdate = true; console.log(`✨ Enhanced PBR material for ${child.name || 'unnamed'}`); } // Handle emissive materials for bloom effect if (material.emissive && material.emissive.getHex() > 0) { material.emissiveIntensity = 2.0; console.log(`💡 Enhanced emissive material for ${child.name || 'unnamed'}`); } } } }); console.log(`🎯 Processed ${materialCount} ultra-quality materials for ${layer}`); return materialCount; } // 🏗️ ADVANCED SCENE LOADING function loadScene(sceneIndex, subSceneIndex) { if (isTransitioning) return; isTransitioning = true; loadingProgress = 0; document.getElementById('loading').style.display = 'block'; document.getElementById('loadingProgressBar').style.width = '0%'; // UI Updates const furnitureMenu = document.getElementById('furnitureMenu'); if (sceneIndex === 2 && subSceneIndex === 5) { furnitureMenu.style.display = 'block'; furnitureMenu.classList.add('visible'); } else { furnitureMenu.style.display = 'none'; furnitureMenu.classList.remove('visible'); } document.getElementById('infoPopup').style.display = 'none'; document.getElementById('infoPopup').classList.remove('visible'); // Clear hotspots hotspotSprites.forEach(sprite => { scene.remove(sprite); if (sprite.material.map) sprite.material.map.dispose(); sprite.material.dispose(); }); hotspotSprites.length = 0; // Clear models with advanced cleanup Object.keys(models).forEach(key => { disposeModel(models[key]); scene.remove(models[key]); }); models = {}; // Reset physics world.bodies.forEach(body => { if (body !== playerBody) world.removeBody(body); }); // Reset controls isFPS = false; pointerControls.unlock(); orbitControls.enabled = true; // Touch controls document.getElementById('touchControls').style.display = (sceneIndex === 2 && subSceneIndex === 6) ? 'block' : 'none'; // Set lighting for scene setLighting(CONFIG.sceneConfig[sceneIndex].light); function addHotspots(sceneKey) { if (hotspots[sceneKey] && !(sceneIndex === 2 && subSceneIndex === 6)) { hotspots[sceneKey].forEach(hotspot => { const sprite = new THREE.Sprite(hotspotSpriteMaterial.clone()); sprite.position.set(hotspot.position.x, hotspot.position.y + 1, hotspot.position.z); sprite.scale.set(0.5, 0.5, 0.5); sprite.name = hotspot.name; scene.add(sprite); hotspotSprites.push(sprite); }); } } function loadModel(url, layer, callback) { console.log(`🚀 Loading ultra-quality model: ${layer} from ${url}`); loader.load( url, (gltf) => { console.log(`✅ Ultra-quality model loaded: ${layer}`); console.log(`📊 Model stats:`, { animations: gltf.animations.length, scenes: gltf.scenes.length, cameras: gltf.cameras.length, materials: gltf.scene.children.length }); const model = gltf.scene; // Process with ultra-quality materials const processedMaterials = processUltraQualityMaterials(model, layer); // Add physics bodies for complex models if (sceneIndex === 2) { model.traverse((child) => { if (child.isMesh && child.geometry) { child.geometry.computeBoundingBox(); const bbox = child.geometry.boundingBox; if (bbox) { const size = bbox.max.clone().sub(bbox.min); const shape = new CANNON.Box(new CANNON.Vec3( size.x * 0.5, size.y * 0.5, size.z * 0.5 )); const body = new CANNON.Body({ mass: 0 }); body.addShape(shape); body.position.copy(child.getWorldPosition(new THREE.Vector3())); world.addBody(body); } } }); } models[layer] = model; scene.add(model); loadingProgress += 1 / scenes[sceneIndex].length; document.getElementById('loadingProgressBar').style.width = Math.min(loadingProgress * 100, 100) + '%'; if (callback) callback(); }, (progress) => { const percent = (progress.loaded / progress.total * 100); console.log(`📈 Loading progress ${layer}: ${percent.toFixed(1)}%`); }, (error) => { console.error(`❌ Error loading ultra-quality model ${layer}:`, error); showError(`Ultra-quality model yüklenemedi: ${layer}`); // Create simple placeholder const geometry = new THREE.BoxGeometry(5, 5, 5); const material = new THREE.MeshStandardMaterial({ color: 0xcccccc, envMap: envMap, roughness: 0.5, metalness: 0.0 }); const placeholder = new THREE.Mesh(geometry, material); placeholder.castShadow = true; placeholder.receiveShadow = true; models[layer] = placeholder; scene.add(placeholder); loadingProgress += 1 / scenes[sceneIndex].length; document.getElementById('loadingProgressBar').style.width = Math.min(loadingProgress * 100, 100) + '%'; if (callback) callback(); } ); } // Load scenes based on index if (sceneIndex === 0) { let loaded = 0; const totalToLoad = Math.min(subSceneIndex + 1, scenes[0].length); for (let i = 0; i < totalToLoad; i++) { loadModel(scenes[0][i].model, scenes[0][i].layer, () => { loaded++; if (loaded === totalToLoad) { addHotspots('0_0'); finishLoading(); } }); } } else if (sceneIndex === 1) { let loaded = 0; const totalToLoad = Math.min(subSceneIndex + 1, scenes[1].length); for (let i = 0; i < totalToLoad; i++) { loadModel(scenes[1][i].model, scenes[1][i].layer, () => { loaded++; if (loaded === totalToLoad) { if (subSceneIndex === 1) addHotspots('1_2'); if (subSceneIndex === 2) addHotspots('1_3'); if (subSceneIndex === 3) addHotspots('1_4'); finishLoading(); } }); } } else if (sceneIndex === 2) { // Load background neighborhood with transparency loadModel(BASE_URL + 'mahalle_2.glb', 'mahalle_2', () => { if (models['mahalle_2']) { models['mahalle_2'].traverse((child) => { if (child.isMesh && child.material) { child.material.transparent = true; child.material.opacity = 0.3; child.material.needsUpdate = true; } }); } let loaded = 0; const totalToLoad = Math.min(subSceneIndex + 1, scenes[2].length); for (let i = 0; i < totalToLoad; i++) { loadModel(scenes[2][i].model, scenes[2][i].layer, () => { loaded++; if (loaded === totalToLoad) { if (subSceneIndex >= 1) addHotspots('2_1'); finishLoading(); } }); } }); } else if (sceneIndex === 3) { loadModel(scenes[3][0].model, scenes[3][0].layer, () => { // Logo specific processing with emissive materials if (models['logo']) { models['logo'].traverse((child) => { if (child.isMesh) { child.material = new THREE.MeshStandardMaterial({ map: child.material.map, transparent: true, emissive: new THREE.Color(0x444444), emissiveIntensity: 0.5, envMap: envMap }); } }); } // Show info popup const infoPopup = document.getElementById('infoPopup'); infoPopup.innerHTML = 'Talk with us: <a href="#" target="_blank">İletişim</a><br><button onclick="this.parentElement.style.display = \'none\'; this.parentElement.classList.remove(\'visible\')">Kapat</button>'; infoPopup.style.display = 'block'; infoPopup.style.left = '50%'; infoPopup.style.top = '50%'; infoPopup.classList.add('visible'); finishLoading(); }); } function finishLoading() { // Smooth camera transition new TWEEN.Tween(camera.position) .to(CONFIG.sceneConfig[sceneIndex].cameraPos, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); new TWEEN.Tween(orbitControls.target) .to(CONFIG.sceneConfig[sceneIndex].target, 2000) .easing(TWEEN.Easing.Quadratic.InOut) .start(); orbitControls.autoRotate = CONFIG.sceneConfig[sceneIndex].autoRotate; if (CONFIG.sceneConfig[sceneIndex].autoRotate) { orbitControls.maxPolarAngle = Math.PI / 3; orbitControls.minPolarAngle = Math.PI / 6; } else { orbitControls.maxPolarAngle = Math.PI; orbitControls.minPolarAngle = 0; } camera.updateProjectionMatrix(); orbitControls.update(); document.getElementById('loading').style.display = 'none'; isTransitioning = false; currentScene = sceneIndex; currentSubScene = subSceneIndex; console.log(`🎉 Ultra-quality scene ${sceneIndex}-${subSceneIndex} loaded successfully!`); } } // 🪑 FURNITURE UPDATE WITH ULTRA QUALITY function updateFurniture(room, option) { if (isTransitioning) return; currentFurniture[room] = option; if (models[room]) { disposeModel(models[room]); scene.remove(models[room]); delete models[room]; } if (option !== 'none' && furnitureModels[room] && furnitureModels[room][option]) { const url = furnitureModels[room][option]; console.log(`🪑 Loading ultra-quality furniture: ${room} option ${option}`); loader.load(url, (gltf) => { const model = gltf.scene; processUltraQualityMaterials(model, room); models[room] = model; scene.add(model); document.getElementById('versionLabel').innerText = 'Seçili'; document.getElementById(room + 'Select').value = option; console.log(`✅ Ultra-quality furniture loaded: ${room}`); }); } } // 🎮 FPS CONTROLS WITH ADVANCED PHYSICS let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false, isSprinting = false; const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); const clock = new THREE.Clock(); let headBobTime = 0; document.addEventListener('keydown', (event) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = true; break; case 'ArrowDown': case 'KeyS': moveBackward = true; break; case 'ArrowLeft': case 'KeyA': moveLeft = true; break; case 'ArrowRight': case 'KeyD': moveRight = true; break; case 'ShiftLeft': isSprinting = true; break; } }); document.addEventListener('keyup', (event) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; switch (event.code) { case 'ArrowUp': case 'KeyW': moveForward = false; break; case 'ArrowDown': case 'KeyS': moveBackward = false; break; case 'ArrowLeft': case 'KeyA': moveLeft = false; break; case 'ArrowRight': case 'KeyD': moveRight = false; break; case 'ShiftLeft': isSprinting = false; break; case 'Escape': pointerControls.unlock(); break; } }); pointerControls.addEventListener('lock', () => { orbitControls.enabled = false; isFPS = true; playerBody.position.set(camera.position.x, CONFIG.physics.playerHeight / 2, camera.position.z); document.getElementById('touchControls').style.display = 'block'; }); pointerControls.addEventListener('unlock', () => { orbitControls.enabled = true; isFPS = false; camera.position.set( CONFIG.sceneConfig[2].cameraPos.x, CONFIG.sceneConfig[2].cameraPos.y, CONFIG.sceneConfig[2].cameraPos.z ); orbitControls.target.set( CONFIG.sceneConfig[2].target.x, CONFIG.sceneConfig[2].target.y, CONFIG.sceneConfig[2].target.z ); camera.updateProjectionMatrix(); document.getElementById('touchControls').style.display = 'none'; }); function updateFPS() { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; const delta = clock.getDelta(); velocity.x = 0; velocity.z = 0; direction.z = Number(moveForward) - Number(moveBackward); direction.x = Number(moveRight) - Number(moveLeft); direction.normalize(); const speed = isSprinting ? CONFIG.physics.sprintSpeed : CONFIG.physics.speed; if (moveForward || moveBackward) velocity.z -= direction.z * speed * delta; if (moveLeft || moveRight) velocity.x -= direction.x * speed * delta; playerBody.velocity.set(velocity.x, playerBody.velocity.y, velocity.z); world.step(delta); // Advanced head bobbing if (moveForward || moveBackward || moveLeft || moveRight) { headBobTime += delta * CONFIG.physics.headBobFrequency; const bobOffset = Math.sin(headBobTime) * CONFIG.physics.headBobAmplitude; camera.position.set( playerBody.position.x, CONFIG.physics.playerHeight + bobOffset, playerBody.position.z ); } else { camera.position.set( playerBody.position.x, CONFIG.physics.playerHeight, playerBody.position.z ); } } // 📱 TOUCH CONTROLS let isTouching = false; let touchStartX = 0, touchStartY = 0; const joystick = document.getElementById('touchJoystick'); const joystickInner = document.getElementById('touchJoystickInner'); joystick.addEventListener('touchstart', (e) => { if (currentScene !== 2 || currentSubScene !== 6 || !isFPS) return; isTouching = true; const touch = e.touches[0]; touchStartX = touch.clientX; touchStartY = touch.clientY; }); joystick.addEventListener('touchmove', (e) => { if (!isTouching) return; const touch = e.touches[0]; const deltaX = touch.clientX - touchStartX; const deltaY = touch.clientY - touchStartY; const maxDist = 30; const dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); const cappedDist = Math.min(dist, maxDist); const angle = Math.atan2(deltaY, deltaX); joystickInner.style.left = (20 + Math.cos(angle) * cappedDist) + 'px'; joystickInner.style.top = (20 + Math.sin(angle) * cappedDist) + 'px'; moveForward = deltaY < -10; moveBackward = deltaY > 10; moveLeft = deltaX < -10; moveRight = deltaX > 10; }); joystick.addEventListener('touchend', () => { isTouching = false; moveForward = moveBackward = moveLeft = moveRight = false; joystickInner.style.left = '20px'; joystickInner.style.top = '20px'; }); // 📍 ULTRA QUALITY HOTSPOT INTERACTION const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); let lastHoveredSprite = null; window.addEventListener('mousemove', (event) => { if (isFPS || isTransitioning) return; mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(hotspotSprites, true); if (intersects.length > 0) { const sprite = intersects[0].object; if (sprite !== lastHoveredSprite) { if (lastHoveredSprite) { new TWEEN.Tween(lastHoveredSprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); } new TWEEN.Tween(sprite.scale) .to({ x: 0.7, y: 0.7, z: 0.7 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); lastHoveredSprite = sprite; } } else if (lastHoveredSprite) { new TWEEN.Tween(lastHoveredSprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); lastHoveredSprite = null; } }); window.addEventListener('click', (event) => { if (isFPS || isTransitioning) return; mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(hotspotSprites, true); const infoPopup = document.getElementById('infoPopup'); if (intersects.length > 0) { const sprite = intersects[0].object; const hotspot = hotspots[currentScene + '_' + currentSubScene]?.find(h => h.name === sprite.name); if (hotspot) { // Fancy click animation new TWEEN.Tween(sprite.scale) .to({ x: 1.0, y: 1.0, z: 1.0 }, 100) .easing(TWEEN.Easing.Quadratic.InOut) .start() .onComplete(() => { new TWEEN.Tween(sprite.scale) .to({ x: 0.5, y: 0.5, z: 0.5 }, 200) .easing(TWEEN.Easing.Quadratic.InOut) .start(); }); infoPopup.innerHTML = hotspot.info + '<br><button onclick="this.parentElement.style.display = \'none\'; this.parentElement.classList.remove(\'visible\')">Kapat</button>'; infoPopup.style.display = 'block'; infoPopup.style.left = (event.clientX + 10) + 'px'; infoPopup.style.top = (event.clientY + 10) + 'px'; infoPopup.classList.add('visible'); if (currentScene === 2 && currentSubScene === 6) { camera.position.set(hotspot.position.x, CONFIG.physics.playerHeight, hotspot.position.z + 2); playerBody.position.set(hotspot.position.x, CONFIG.physics.playerHeight / 2, hotspot.position.z + 2); pointerControls.lock(); } } } else { if (currentScene === 1 && currentSubScene === 3) { infoPopup.style.display = 'block'; infoPopup.classList.add('visible'); } else { infoPopup.style.display = 'none'; infoPopup.classList.remove('visible'); } } }); // 🔄 UPDATE HOTSPOTS function updateHotspots() { hotspotSprites.forEach(sprite => { sprite.lookAt(camera.position); }); } // 🎭 ANIMATION EFFECTS function createClouds() { const cloudGeo = new THREE.PlaneGeometry(20, 20); const cloudMat = new THREE.MeshLambertMaterial({ color: 0xffffff, transparent: true, opacity: 0 }); const clouds = []; for (let i = 0; i < 20; i++) { const cloud = new THREE.Mesh(cloudGeo, cloudMat.clone()); cloud.position.set( Math.random() * 100 - 50, Math.random() * 30 + 20, Math.random() * 100 - 50 ); cloud.rotation.z = Math.random() * Math.PI; scene.add(cloud); clouds.push(cloud); } return clouds; } function createFadeOverlay() { const overlayGeo = new THREE.PlaneGeometry(200, 200); const overlayMat = new THREE.MeshBasicMaterial({ color: 0x000000, transparent: true, opacity: 0 }); const overlay = new THREE.Mesh(overlayGeo, overlayMat); overlay.position.set(0, 0, -10); camera.add(overlay); return overlay; } function createOrbitAnimation(fromPos, toPos, fromTarget, toTarget, duration) { new TWEEN.Tween(camera.position) .to(toPos, duration) .easing(TWEEN.Easing.Cubic.InOut) .start(); new TWEEN.Tween(orbitControls.target) .to(toTarget, duration) .easing(TWEEN.Easing.Cubic.InOut) .start(); } // 🧭 NAVIGATION FUNCTIONS function goToNext() { if (isTransitioning) return; if (currentScene === 0 && currentSubScene < scenes[0].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(0, currentSubScene + 1); }); } else if (currentScene === 0 && currentSubScene === scenes[0].length - 1) { const clouds = createClouds(); const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 1 }, 1000) .onUpdate((obj) => { clouds.forEach(c => c.material.opacity = obj.opacity * 0.3); overlay.material.opacity = obj.opacity * 0.5; }) .start() .onComplete(() => { clouds.forEach(c => { scene.remove(c); c.material.dispose(); c.geometry.dispose(); }); camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[1].cameraPos, orbitControls.target, CONFIG.sceneConfig[1].target, 1500 ); loadScene(1, 0); }); } else if (currentScene === 1 && currentSubScene < scenes[1].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(1, currentSubScene + 1); }); } else if (currentScene === 1 && currentSubScene === scenes[1].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => { overlay.material.opacity = obj.opacity; }) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[2].cameraPos, orbitControls.target, CONFIG.sceneConfig[2].target, 1500 ); loadScene(2, 0); }); } else if (currentScene === 2 && currentSubScene < scenes[2].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(2, currentSubScene + 1); }); } else if (currentScene === 2 && currentSubScene === scenes[2].length - 1) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[3].cameraPos, orbitControls.target, CONFIG.sceneConfig[3].target, 1500 ); loadScene(3, 0); }); } } function goToPrevious() { if (isTransitioning) return; if (currentScene === 0 && currentSubScene > 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(0, currentSubScene - 1); }); } else if (currentScene === 1 && currentSubScene > 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(1, currentSubScene - 1); }); } else if (currentScene === 1 && currentSubScene === 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[0].cameraPos, orbitControls.target, CONFIG.sceneConfig[0].target, 1500 ); loadScene(0, scenes[0].length - 1); }); } else if (currentScene === 2 && currentSubScene > 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); loadScene(2, currentSubScene - 1); }); } else if (currentScene === 2 && currentSubScene === 0) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => { overlay.material.opacity = obj.opacity; }) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[1].cameraPos, orbitControls.target, CONFIG.sceneConfig[1].target, 1500 ); loadScene(1, scenes[1].length - 1); }); } else if (currentScene === 3) { const overlay = createFadeOverlay(); new TWEEN.Tween({ opacity: 0 }) .to({ opacity: 0.8 }, 500) .onUpdate((obj) => overlay.material.opacity = obj.opacity) .start() .onComplete(() => { camera.remove(overlay); overlay.material.dispose(); overlay.geometry.dispose(); createOrbitAnimation( camera.position, CONFIG.sceneConfig[2].cameraPos, orbitControls.target, CONFIG.sceneConfig[2].target, 1500 ); loadScene(2, scenes[2].length - 1); }); } } // 🏃‍♂️ ULTRA QUALITY ANIMATION LOOP const stats = { fps: 0, frameTime: 0 }; let lastTime = performance.now(); function animate() { requestAnimationFrame(animate); const currentTime = performance.now(); const deltaTime = currentTime - lastTime; lastTime = currentTime; // Performance monitoring stats.fps = Math.round(1000 / deltaTime); stats.frameTime = Math.round(deltaTime * 100) / 100; // Update systems TWEEN.update(); updateFPS(); updateHotspots(); if (!isFPS) { orbitControls.update(); } // Ultra quality rendering composer.render(); // Update quality display occasionally if (Math.random() < 0.01) { // 1% chance per frame const qualityDiv = document.getElementById('qualityControls'); if (qualityDiv) { qualityDiv.innerHTML = ` <div>🎨 Ultra Quality WebXR</div> <div>FPS: ${stats.fps} | Frame: ${stats.frameTime}ms</div> <div>Triangles: ${renderer.info.render.triangles.toLocaleString()}</div> <div>Draw Calls: ${renderer.info.render.calls}</div> `; } } } // 🔧 WINDOW RESIZE WITH ULTRA QUALITY window.addEventListener('resize', () => { const aspect = window.innerWidth / window.innerHeight; camera.aspect = aspect; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); composer.setSize(window.innerWidth, window.innerHeight); // Update post-processing passes fxaaPass.material.uniforms['resolution'].value.x = 1 / window.innerWidth; fxaaPass.material.uniforms['resolution'].value.y = 1 / window.innerHeight; ssaoPass.setSize(window.innerWidth, window.innerHeight); }); // 📱 TOUCH ZOOM WITH QUALITY let pinchStartDistance = 0; window.addEventListener('touchstart', (e) => { if (e.touches.length === 2) { pinchStartDistance = Math.hypot( e.touches[0].pageX - e.touches[1].pageX, e.touches[0].pageY - e.touches[1].pageY ); } }); window.addEventListener('touchmove', (e) => { if (e.touches.length === 2 && !isFPS) { const pinchEndDistance = Math.hypot( e.touches[0].pageX - e.touches[1].pageX, e.touches[0].pageY - e.touches[1].pageY ); const zoomDelta = pinchEndDistance / pinchStartDistance; const newDistance = orbitControls.getDistance() / zoomDelta; orbitControls.dollyTo(THREE.MathUtils.clamp(newDistance, orbitControls.minDistance, orbitControls.maxDistance)); pinchStartDistance = pinchEndDistance; } }); window.addEventListener('touchend', (e) => { if (e.touches.length < 2) { pinchStartDistance = 0; } }); // 🚀 INITIALIZE ULTRA QUALITY APPLICATION console.log('🎯 Initializing Ultra Quality WebXR Real Estate Demo...'); console.log('📊 Renderer capabilities:', { maxAnisotropy: renderer.capabilities.getMaxAnisotropy(), maxTextures: renderer.capabilities.maxTextures, maxVertexTextures: renderer.capabilities.maxVertexTextures, maxTextureSize: renderer.capabilities.maxTextureSize, maxCubemapSize: renderer.capabilities.maxCubemapSize }); animate(); loadScene(0, 0); console.log('✅ Ultra Quality WebXR Real Estate Demo initialized successfully!'); </script></body></html>

yükleniyorda takılı kaldı?? yüklenmiyor. kosolda gördüklerim: * ☁️ "Running fiddle" * "🚀 Initializing Ultra Advanced PBR Real Estate System..." * "🌅 Creating procedural HDRI environment..." * "✅ HDRI Environment created and applied" * "🌟 Setting up ultra advanced lighting system..." * "✅ Ultra advanced lighting system initialized" * "🎬 Setting up advanced post-processing pipeline..." * "✅ Advanced post-processing pipeline ready" * "🚀 Initializing ultra advanced GLTF loader..." * "🚀 Starting ultra advanced PBR real estate system..." * "🚀 Loading scene 0-0 with ultra advanced system..." * "🌅 Setting noon lighting..." * "✅ noon lighting applied" * "🔄 Loading ultra advanced model: city from https://raw.githubusercontent.com/decentralize-dfw/realestatedemo/main/City.glb" * "🎉 Ultra Advanced PBR Real Estate System fully initialized!" * "💎 Features: Ultra Advanced PBR Materials, HDRI Environment, Advanced Lighting, High Quality Shadows, Post-Processing, Physics, WebXR Ready" * "📈 Loading progress city: 6.5%" * "📈 Loading progress city: 100.0%" * "✅ GLTF loaded successfully: city" * "🎨 Processing ultra advanced materials for city..." * "🔍 Analyzing material for cityglb:", { hasAoMap: false, hasColor: true, hasEmissiveMap: true, hasMap: true, hasMetalnessMap: false, hasNormalMap: false, hasRoughnessMap: false, type: "MeshStandardMaterial" } * "✨ Enhanced PBR material for cityglb" * "📊 Processed 1 materials for city" konsolda yüklendi gözükse de Yüklenioyr.. takılı kaldı, açılmıyor. ultra kaliteli fakat web için de optimize olmalıdır. sorun bu mu bilmiyorum ama, açılmıyor, çözmen gerekiyor.

DEMO-v2

25072025

prompt1:
 

three.js kütüphanesi kullanalım nispeten advanced bir altyağı ile başlayıp ona alışalım. şimdi belirli bir sıralama ile bir binanın tanıtımını bir senaryo ile görselleştireceğiz. bu 3 boyutlu internet üzerinden görselleştirmenin senaryosunu anlatayım biraz. ya da sen çok güzel bir senaryo oluştur. şidmi sadece hayal gücümüzü kullanalım. en başta lüksembourg haritası görürüz, örneğin Kopstal komünü yapalım, burası bir kırmızı nokta olarak gözükür yeri, burasının lüksembourg şehir merkezine, en yakın merkeze, civardaki interest pointlere uzaklıkları, havalimanınıa, gar a uzaklıkları araba ile. bu hafif kubik üç boyutlu bir modeldir, ve kullancıllar bunu hafif bir şekilde rotate edebilir. burdan sonra, o kırmızı noktaya yaklaşırız, oraya doğru hızlı zoom olur, böyle bulutlar araya girer, ve mahalle ölceğinde binayı dışarıdan görürüz (model değişmiştir, upscale modeli kaybolur, yerine mahalle.glb gelir), kamera etrafında yavaşca dönebilmektedir. kullancı mouse ile kamerayı istedği gibi döndürebilir. bıraktığı zaman, gene aynıo hızda dönmeye devam eder. briinci bilgide, altta otoparkı ve binayı highlightd olarak görürüz. ileri dediğimizde çevredeki şeyleri görürüz, karşıda bar, sokağın çaprızında bir restoran, biraz geride bir eczane vardır. hepsi basit iconalrdır, üzerine mousela gelidniği zaman bilgileri çıkar, tıklanınca bilgi orda diğer ekrana geçene kadar durur. üst başlık olarak ismi altında ne olduğu altında google ya da trip advisor yıldızı, üç tane ana comment, ve linki çıkar. otobüs durağı ya da herhangi toplu taşıma bilgisini de burada görürüz. bir sonrakine geçtiğimzide, gene aynı model üzerinden, burada, internetten bulunmuş referans göstererek mesela, civardaki çevre konutların m2i oda sayıları ve aylık kaça kiralandığı bilgisi verilerek genel olarak market anlatılır. etraftaki kullanılabilinir yeşil alanlar, yürüyüş rotaları, bisiklet rotaları da burada gösterilir. fakat kamera açısı ve yüksekliği aynıdır. yani karşıdan 90 derece, üstten de 45 dereec açıyladır, sadece y'si döner binanın etrafında. bir sonrakine geçtiğimiz zaman bu sefer model değişir. ufaak bir load ekranı çıkar, bu esnada mahalle modeli yerine mahallenin daha soyut ve beyaz shosted olduğu fakat binanın üüçüncü katının yani evin olduğu katın gözüktüğü ve detaylı olduğu bir modeldir. burada binanın tepeden görürüz. önemli yerlerden metre bilgisi verilmiştir. bu sayede ölçeği anlarız. evin içi boştur, malzemelerini görürüz. nerden ışık aldığını hangi yöne baktığının simulasyonunu görürüz. eşya yoktur, ya da sadece içinde olan eşyalar vardır, örneğin mutfak takımı tualet duş falan, fakat evin içi boştur. üç farklı senaryo öneririz evin içine, furnitureların, yani salonun ya da yemek odasının değişştiği odaların değişiik değişik kullanıldığı.

odaların içinde her birinin belirli noktalar vardır, işte tualtte, salonda, girişte, odalarda, hangisine tıklarsak, orada respawn oluruz ve vr dan da, pc den de içinde yürüyebiliriz, gezebiliriz, 1ps modunda, yani sadece glb viewer gibi aslonda. ve aynı oyunlardaki gibi sol üstte bir map olur nereye baktığımızı, ve nerde oldujğumuzu gösterir. hangi furniture modeeu açık kaldıysa, boş mu ya da üç opsiyondan brii mi, onları da görürüz orda, ve sol aşağıdaki ufak bir menüden bunu değiştirebiliriz bu furniture modelarını. geriye bastığımızda bir önceki moda, ileriye gittiğmzde de bir sonraki moda geçer hep, bu anlattığım senaryolarad. neyse. en son, ileryie bastığı zaman, bizim logomuz gözükür, talk with us gibi birşey çıkar.

promp2:
kamerada perspektif istemiyorum, ortogtaphic olmaldır.rotate ve zoom in / out özellikleri olacaktır, ama pan istemiyorum. sayfa 1 lüksembourg üst ölçek layerlar ( her birisi ayrı bir glb dosyası) 0- kopstal işareti kırmızı bir pin, üzerinde kopstal yazar. 1- ana yollar (mainroute.glb) 2- landmarklar (landmark.glb) 3- ana yerleşkeler (örnek: hamilius, kirchberg, airport, belle etoile shopping, Ikea( Arlon,Belçika), Metz (FR), Trier (GER), Bruxelles (BLG)), Ve bunlar info point gibi olmalıdır, beyaz bir yarı transparent kutucuk pop up gibi, haritadaki yerinden, altında bir araba icon'u, yanında da dakikası yazmalıdır. -her ileriye git dediğimizde sırayla bu bilgiler ve glbler gelir sırayla. ileriye veya geriye kittiğimizde kameranın açısı resetlenmez, o kullanıcı nerede bırakırsa nasıl oynarsa hep ordadır. geriye git diyince bir önceki gözükür, yani bir sonra gelen kaybolur. ama ileriye gittikçe bu sayfa özelinde hepsi üst üste gelir sırayla. bu 3d bir dosyadır, kamerayı oynattıkça bilgi pop upları da kamerayla aynı şekilde döner. bir daha ileriye tıkladıkça, kopstalın kırmızı pin'ine doğru zoom yapar, o sırada, ekran beyazlaşır, ve beyazlıktan aydınlığa geçerken Mahalle modeline yavaşça zoom yapılır, ve zoom bittiğinde mahalleyi modelini görürüz. Mahalle Ölçeği. (burda ileriye basıldıkça üst üste gelmezler, her birini sırayla görürüz.ileriye veya geriye kittiğimizde kameranın açısı resetlenmez, o kullanıcı nerede bırakırsa nasıl oynarsa hep ordadır.) mahelle.glb 1-Mahalle Genel Görünüş 2-Bizim Bina highlighted bir şekilde, konutlar ghosted, ve diğer instrümanlar (bar, restoran, belediye, kilise, eczane) 3- Ulaşım : Binanın altındaki otopark (bina ghosted olur, Car parking.glb gelir), Toplu taşıma, otobüs hatları ( modelde pt_line_1.glb, pt_line_2.glb vesaire gelecektir, kendi renkelri vardır bu hatların, başında ve sonunda Nerden geldiği ve nereye gittiği yazar, Örneğin Place D'Etoile yazıyor, altında da daha küçük bir yazıyla sırayla duraklar yazar. ve bu da aynı şekilde beyaz bir yarı tarnsparent köşeleri rounded bir kutu pop up ın içindedir.), bisiklet rotaları, yürüyüş rotaları. 4-Etraf emlak piyasası, Bu sefer hiçbirşey ghosted değildir, gene bizim bina highlighteddır, etrafta, koordinatları 3d modelden verilecek noktalarda pop uplar vardır, Rent yazar mesela, altında adresi, altında da fiyatı yazar. bu mahalle ölçeği sekansında, ana binamızın bldg_1.glb, bldg_highlighted.glb diye iki modu vardır, highlitedlarda highligth olan respawn olur anlık yüklenir yani 2 ve 4 te, 1de bldg_1.glb direk gözükür, 3 te ise aynı model, sadece o ghosted olacak şekilde gözükür, diğerleri tanımlanan gibidir. Ekran 3- Binanın içi Burada tüm sahneyi belirli bir açıdan göreceğizdir, sonra kullanıcı rotate edebilir istediği gibi. pan yapma her zaman olduğu gibi yasaktır. ve burda bldg modelleri gidecek, yerine bldg_section gelmelidir. başta burayı furnituresuz göreceğiz. mahallede de modeli değişmelidir. mahalle_2.glb olmaldır. ilk başta sadece modeli görürüz. ileriye tıklayınca dimensions.glb gelir, ve her odanın üzerinde bilgisi yazar aynı beyaz pop up şeklinde. örnek room 1 yazar altında 25 m2 yazar. o kadar. tabi her zaman hepsinden olduğu gibi, bunlar da kamerayı döndürdükçe döner ve her zaman düz görürürz. neyse. ileriye tıkladığımızda, furniture 1 versiyonunu görürüz evin içinde. 4 tane mekan vardır evde, bunlar room1, room2, salon, terrace- yani editlenebilir mekanlar, mutfak ve toilet sabit, neyse her birinin 1 numaralı furniture dosyaları gözükür. Sol altta, Interior Version 1 yazar. Dolaysııyla her birisi için ayrı dosya vardır. Room1_f1.glb, Room1_f2.glb, Room1_f3.glb, Room2_f1.glb, Room2_f2.glb, Room2_f3.glb, Terrace_f1.glb, Terrace_f2.glb, Terrace_f3.glb, Salon_f1.glb, Salon_f2.glb, Salon_f3.glb, gene ileriye tıklandığında Interior Version 2 yazar ve f2 glb lerinni görürüz, bir ileride daha 3'ü görürüz. bri kez daha ileriye tıkladığımzda, furniturelar kaybolur, boş olarak görürüz, ve her odanın üstünde, Room 1 YAZIYOR YA altında üç kutucuk vardır v1, v2, v3 diye kullanıcı birbirinden bağımsız bir şekilde hepsini seçebilir. tüm ayrık mekanlar için geçerlidir bu. Sonrasında ise, ileriye rıkladığı zman içinde gezme başlar. bir avatar görmeyiz, 1ps kameraında 1.6 yüksekliğinde gir kamera iler geri sağ sol yaptıkça ilerler, mousela rotate olur. burda gezzdiği mekanlar, bir öncekinde nasıl kürate ettiyse odur. duvarların ve malzemelerin collideri vardır. dolayısıyla içinden geçemez. tüm malzemeler olabilecek en advanced şekilde materyalleri gösterilemdliri. glb modellerinde draco, meshpt ve ktx2 compressionlarına izin verilmelidir. anlıyor musun senaryoyu. şimdi sana anlattığımı çok detaylı bir şekilde, her şeyi baştan sona oldukça düzenli ve hiç bir eksiği olmayan bir prompt olarak organize et lütfen. ve en sonda sırayla gerekli dosyaları, gerekli bilgileri listele.

prompt3:

böyle bir kod var wix html viewer için. bu bir 3d real estate tanıtım senaryosudur. şimdilik glb modellerin veya hotspotların bir önemi yoktur en son onlarüncellenecektir.

Özellikler ve Notlar * Kamera: OrthographicCamera, sadece rotate ve zoom, pan kapalı. Zoom sınırları (zoomMin: 0.5, zoomMax: 2.0) ayarlandı. * Işıklandırma: Hyperfy v2 seviyesinde gerçekçi. DirectionalLight, HemisphereLight, ShadowMap (2048x2048, yumuşak gölgeler), EnvironmentMap ile yansımalar. Saat bazlı ışık (sabah 9, öğlen 12, akşam 5). * Malzemeler: PBR (roughness, metalness, emissive), Blender’da hazır. GLB’ler Draco/meshopt/KTX2 sıkıştırmalı. * Sahneler: * Sahne 1: Katmanlı yükleme (kopstal, mainroute, landmark, settlements), hotspot’lar üst üste birikir, kamera açısı resetlenmez. * Sahne 2: 4 alt sahne (genel, bina/çevre, ulaşım, emlak piyasası). Bina bldg_1 veya bldg_highlighted, ghosted efektler, toplu taşıma hatları (renkli, durak bilgileriyle). * Sahne 3: 7 alt sahne (boş, ölçüler, mobilya 1-2-3, mobilya seçimi, 1PS). Mobilya kombinasyonları için dropdown menü, 1PS modunda collider’lı gezinme (duvarlardan geçilemez). * Sahne 4: Logo ve iletişim. * Hotspot’lar: Kamerayla senkron dönen, yarı şeffaf, yuvarlak köşeli bilgi kartları. Tıklama ile sabitlenir. * Geçişler: TWEEN.js ile yumuşak geçişler, bulut animasyonu için 10 adet PlaneGeometry. * 1PS Gezinme: 1.6m yükseklikte, WASD ile hareket, mousela rotasyon, çarpışma kontrolü (Box3). * Hata Yönetimi: GLB yükleme hataları konsola yazılır. * Performans: Draco/meshopt/KTX2 sıkıştırması, yüksek çözünürlüklü gölgeler, optimize edilmiş render döngüsü.

prompt4:
DAHA DEMİNKİ KODE YARIM KALDI SENDEN BASİT BİR UYGULAMA DEĞİL, OLABİLECEK WEBXR ÜZERİNDEKİ EN KOMPLEX UYGULAMAYI İSTİYORUM, EN GERÇEKÇİ MATERYELLER, HYPERFY STANDARTI VEYA ÜSTÜ, YANİ WEBXRDA YAPILABİLECEK MAKSİMUM KALİTE, AYNI ZAMANDA DA HAFİF, AMBİENT GERÇEKÇİ IŞIKLAR GÖLGELER, TÜM MALZEMELERİN BİRABİR GÖZÜKMESİ, SORUNDAN KAÇIŞ ARIYORSUN, KODU DA BÖYLELİKLE BASİTLEŞTİREREK KALİTESİNİ DÜŞÜRÜYORSUN. ULTRA KALİTELİ BİR IŞIK MALZEME RENDER MEKAN MOTORU UYGULAMALISIN, THREEJS KÜTÜPHANESİNİN EN SON EN ADVANCED ŞEYLERİNİ KULLANMALISIN, MALZEMELER PBR NROMAL MAP EMİSSİON HEPSİNİ GÖSTERMELİ, BÖYLE OLMAZ. ZATEN KOD DA HATA VERDİ. "[Line 467] Uncaught ReferenceError: spotLight is not defined".

DEMO

24072025

image (4).jpg

 virtually ever after... 

image (3).jpg

 virtually
ever after... 

 virtually ever after... 

metalDFW.png
  • X
  • Logomark-Blue_edited_edited_edited
  • mail-envelope-symbol-logo-4AB011B4E0-seeklogo_edited
  • Instagram

we leverage 'religious robotics" to reimagine the borders and parameters of the spatial reflections of virtual daily life.

© 2024 DFW, All rights reserved   Milan, Italy  EST.2021  

bottom of page