top of page
goals:
separate models to 4 of to 16- lazy loading
mona gibi hepsi t pose yüklenmeli
4 tane emote atılmalı, ve material düşmesi gibi arasından seçilmeli
transparent background


no mat, no anim, no mat anim, hepsi test edildi,
sıralama değiştiridl, ilk 3-4 çıkıyor, modelden bağımsız, sonra çıkmıyor

 
HYPERFY ANALYSİS

Let me analyze these two code files:

1. First file (paste.txt) appears to be a JavaScript library called ThreeMeshUI - a UI library built on top of Three.js for creating 3D user interfaces. Key observations:

- It provides components for creating text, blocks and other UI elements in 3D space
- Uses Three.js for rendering 
- Has features for:
  - Text rendering with custom fonts
  - Layout system (flexbox-like)
  - Styling (colors, borders, padding etc.)
  - Event handling
  - Components like Block, Text, Inline elements
- Seems to be focused on creating HUD-like interfaces in 3D environments

2. Second file (paste-2.txt) is a WebAssembly (Wasm) build of PhysX - NVIDIA's physics engine. Key points:

- It's a compiled C++ physics engine exposed to JavaScript through WebAssembly
- Contains bindings and glue code to interact with the PhysX physics engine from JavaScript
- Handles memory management between JS and Wasm
- Provides physics simulation capabilities like:
  - Rigid body dynamics
  - Collisions


Continuing the analysis:

PhysX (paste-2.txt):
- Includes extensive error handling and type checking between JS and C++
- Has an Emscripten-generated binding layer for calling PhysX functions from JavaScript 
- Features include:
  - Memory management utilities
  - Runtime type information (RTTI) support
  - Exception handling across the JS-Wasm boundary
  - Dynamic linking capabilities
  - Debugging and logging facilities

ThreeMeshUI (paste.txt):
- Has a component-based architecture similar to web UI frameworks
- Key components include:
  - Block: Basic container element
  - Text: For rendering text content
  - InlineBlock: For inline-level containers
  - Various style properties like:
    - Margins and padding
    - Borders and border radius 
    - Background colors and images
    - Text alignment and formatting
    - Font properties (size, weight, family etc)
- Uses a flexbox-inspired layout system for positioning elements
- Handles text rendering with:
  - Font atlas generation
  - Glyph positioning
  - Text wrapping
  - Unicode support
  - Kerning

Yes! Let me highlight the material-related code from both libraries:

ThreeMeshUI has several material-related components:

1. Font Material Handling:
```javascript
class MSSDFBasicMaterial extends THREE.ShaderMaterial {
  static get mediation() {
    return MaterialProperties.mediation;
  }
 
  constructor(options = {}) {
    super({
      uniforms: {
        glyphMap: { value: null },
        diffuse: { value: null },
        opacity: { value: 1 },
        unitRange: { value: new THREE.Vector2(0, 0) },
        alphaTest: { value: 0.02 }
      },
      transparent: true,
      clipping: true,
      vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      extensions: {
        derivatives: true 
      }
    });
  }

  // Material property setters
  set color(v) { this.uniforms.diffuse.value = v; }
  get color() { return this.uniforms.diffuse.value; }
 
  set opacity(v) { this.uniforms.opacity.value = v; }
  get opacity() { return this.uniforms.opacity.value; }
}
```

2. Background Material Properties:
```javascript
// Background material properties
const BackgroundMaterial = {
  clippingPlanes: { m: "clippingPlanes" },
  backgroundAlphaTest: { m: "alphaTest", t: setAlphaTest },
  backgroundSide: { m: "side" }, 
  backgroundImage: { m: "map" },
  backgroundColor: { m: "color" },
  backgroundOpacity: { m: "opacity" },
  backgroundSize: {
    m: "computedBackgroundSize",
    t: function(material, prop, value) {
      setBackgroundSize(material, value);
    }
  }
};
```

3. Border Rendering:
```javascript
// Border material properties
const BorderMaterial = {
  borderWidth: { m: "borderWidth", t: setUniform },
  borderColor: { m: "borderColor", t: setUniform },
  borderRadius: {
    m: "borderRadius",
    t: setUniform 
  },
  borderOpacity: { m: "borderOpacity", t: setUniform }
};
```

4. Custom Shader Chunks for Materials:
```javascript
const ShaderChunks = {
  msdfAlphaglyphVertexGlsl: `
    varying vec2 vUvG;
    void main() {
      vUvG = uvG;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,

  msdfAlphaglyphFragmentGlsl: `
    uniform sampler2D glyphMap;
    uniform vec2 unitRange;
    varying vec2 vUvG;
    
    float median(float r, float g, float b) {
      return max(min(r, g), min(max(r, g), b));
    }
  `
};
```

Key Material Features:
- Support for MSDF (Multi-channel signed distance field) text rendering
- Background materials with image mapping
- Border rendering with customizable properties
- Alpha testing and transparency
- Custom shader implementations
- Uniform management for material properties
- Material property mediation system

The PhysX code also includes some material-related physics properties:

```javascript
// Physics material properties
const PhysicsMaterialDesc = {
  staticFriction: 0.5,
  dynamicFriction: 0.5,
  restitution: 0.6
};

// Material binding to physics engine
function createPhysicsMaterial(px) {
  return px.createMaterial(
    this.staticFriction,
    this.dynamicFriction,
    this.restitution
  );
}
```

The materials in ThreeMeshUI focus on visual rendering while PhysX materials handle physics properties like friction and restitution. ThreeMeshUI has more extensive material handling for UI purposes with custom shaders and properties for text rendering, backgrounds, and borders.
 
gradıent.png

please scroll down  to explore designs.

<!DOCTYPE html>
<html lang="en" oncontextmenu="return false;" onselectstart="return false" ondragstart="return false">
<head>
    <meta charset="UTF-8">
    <title>Digital Forgery Work 3D Viewer - Sculptures</title>
    
    <!-- SEO and Open Graph Tags -->
    <meta name="description" content="Digital Forgery Work 3D Viewer - View exclusive 3D sculpture designs.">
    <meta name="keywords" content="digital forgery, 3D design, digital art, NFT, sculpture">
    <meta property="og:title" content="Digital Forgery Work 3D Viewer - Sculptures">
    <meta property="og:type" content="website">
    <meta property="og:description" content="Explore and interact with exclusive 3D digital sculptures.">
    
    
    <style>
        html, body {
            margin: 0;
            padding: 0;
            scrollbar-width: none !important;
            -ms-overflow-style: none !important;
            overflow: -moz-scrollbars-none;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            -khtml-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            background: transparent;
        }
        
        html::-webkit-scrollbar,
        body::-webkit-scrollbar {
            width: 0 !important;
            height: 0 !important;
            display: none !important;
            background: transparent !important;
        }

        body { 
            height: 1000vh;
            overflow-y: scroll;
            overflow-x: hidden;
            -webkit-overflow-scrolling: touch;
            pointer-events: auto;
        }
        
        * {
            scrollbar-width: none !important;
            -ms-overflow-style: none !important;
        }
        
        *::-webkit-scrollbar {
            display: none !important;
        }

        #canvas-container {
            width: 1590px;
            height: 830px;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            overflow: hidden;
            background: transparent;
        }

        canvas { 
            display: block;
            width: 100%;
            height: 100%;
            background: transparent;
        }

        .object-info {
            position: fixed;
            right: 50px;
            top: 50%;
            transform: translateY(-50%);
            opacity: 0;
            text-align: right;
            pointer-events: none;
            z-index: 1000;
            padding: 10px;
        }

        .object-name {
            font-family: helvetica;
            font-weight: bold;
            font-size: 25px;
            letter-spacing: -0.25px;
            color: black;
            margin-bottom: 7px;
            white-space: nowrap;
        }

        .object-type {
            font-family: helvetica;
            font-weight: 500;
            font-size: 15px;
            letter-spacing: -0.05px;
            color: black;
            margin-bottom: 7px;
            white-space: nowrap;
        }

        .object-colorways {
            font-family: helvetica;
            font-weight: 500;
            font-style: italic;
            font-size: 12px;
            letter-spacing: -0.15px;
            color: #595959;
            margin-bottom: 7px;
            white-space: nowrap;
        }

        .object-link {
            font-family: helvetica;
            font-weight: 500;
            font-style: italic;
            font-size: 12px;
            letter-spacing: -0.15px;
            color: rgb(9, 7, 102);
            text-decoration: none;
            cursor: pointer;
            white-space: nowrap;
            pointer-events: none;
        }

        .object-link:hover {
            text-decoration: underline;
        }

.market-button {
    font-family: helvetica;
    font-weight: 500;
    font-style: italic;
    font-size: 11px;
    letter-spacing: -0.15px;
    color: black;
    padding: 7px 14px;
    background-color: rgba(255, 255, 255, 0.7);
    border: 1px solid #000000;
    border-radius: 10px;
    cursor: pointer;
    transition: all 0.2s ease;
    pointer-events: auto;
    margin-top: 10px;
}

.market-button:hover {
    background-color: rgba(0, 0, 0, 0.6);
    color: white;
    border-color: transparent;
}

       #wireframe-toggle {
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    padding: 7px 7px;
    background-color: rgba(0, 0, 0, 0.5);
    color: rgba(255, 255, 255, 0.5);
    border: 1px solid #808080;
    border-radius: 5px;
    cursor: pointer;
    font-family: helvetica;
    font-size: 12px;
    font-style: italic;
    letter-spacing: -0.15px;
    z-index: 1000;
    transition: all 0.3s;
}

#wireframe-toggle:hover {
    background-color: rgba(0, 0, 0, 0.6) !important;
    color: white !important;
    border-color: transparent !important;
}

#wireframe-toggle.active {
    background-color: transparent;
    color: black;
}

#wireframe-toggle.active:hover {
    background-color: rgba(0, 0, 0, 0.6) !important;
    color: white !important;
    border-color: transparent !important;
}
    </style>
</head>
<body>
    <div id="canvas-container"></div>
   <button id="wireframe-toggle">Material</button>
    
    <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/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/RGBELoader.js"></script>
    <script>
    // Security and protection scripts with IIFE for proper scoping
    (function() {
        const preventDefault = (e) => e.preventDefault();

        document.addEventListener('contextmenu', preventDefault);
        document.addEventListener('keydown', function(e) {
            if ((e.ctrlKey && (e.key === 'u' || e.key === 'U' || e.key === 'i' || e.key === 'I' || e.key === 's' || e.key === 'S')) ||
                e.key === 'F12' || 
                (e.ctrlKey && e.shiftKey && (e.key === 'i' || e.key === 'I' || e.key === 'j' || e.key === 'J' || e.key === 'c' || e.key === 'C'))) {
                preventDefault(e);
            }
        });

        ['keypress', 'mousedown', 'keydown'].forEach(event => {
            document.addEventListener(event, (e) => {
                if (e.keyCode === 123) preventDefault(e);
            });
        });

        ['selectstart', 'dragstart'].forEach(event => {
            window.addEventListener(event, preventDefault);
        });
    })();

    // Main application with proper scoping
    (function initializeViewer() {
        // Helper function to create DOM elements with properties
        function createElement(tag, className, text) {
            const element = document.createElement(tag);
            if (className) element.className = className;
            if (text) element.textContent = text;
            return element;
        }

        // Object data with proper organization
        const objectData = {
            "1": {
        "name": "Osiris Rake",
        "type": "sculpture1",
        "colorways": "OG, BLCK, GRY, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=osiris"
    },
    "2": {
        "name": "Midas Spade",
        "type": "sculpture2",
        "colorways": "OG, DARK, LIGHT, CANDLE",
        "link": "https://opensea.io/collection/dfw-design?search[query]=rake"
    },
    "3": {
        "name": "Angry Napkin",
        "type": "sculpture3",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=angry%20napkin"
    },
    "4": {
        "name": "W6-O1-M6_(chemical_structure)",
        "type": "sculpture4",
        "colorways": "OG, BLCK, GRY, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=w6-o1-m6"
    },
    "5": {
        "name": "Statue of Hangover",
        "type": "sculpture5",
        "colorways": "SOHiii, SOHii, SOHi",
        "link": "https://opensea.io/collection/dfw-design?search[query]=statue"
    },
    "6": {
        "name": "Praified",
        "type": "sculpture6",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=praified"
    },
    "7": {
        "name": "Sanctified",
        "type": "sculpture7",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=sanctified"
    },
    "8": {
        "name": "me-tree-x",
        "type": "sculpture8",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=me-tree-x"
    },
    "9": {
        "name": "moleculer-data-plant",
        "type": "sculpture9",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=moleculer-data-plant"
    },
    "10": {
        "name": "Neo Klein Cube",
        "type": "sculpture10",
        "colorways": "OG, BLCK, GRY, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=neo%20klein"
    },
    "11": {
        "name": "Chromie Bridge Aural Section",
        "type": "sculpture11",
        "colorways": "OG, ALIEN, GRY, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=chromie%20bridge&search[stringTraits][0][name]=Type&search[stringTraits][0][values][0]=Sculpture"
    },
    "12": {
        "name": "CROOKEDASWEFUNASYU",
        "type": "sculpture12",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=crook&search[stringTraits][0][name]=Type&search[stringTraits][0][values][0]=Sculpture"
    },
    "13": {
        "name": "LEGEND",
        "type": "sculpture13",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=legend&search[stringTraits][0][name]=Type&search[stringTraits][0][values][0]=Sculpture"
    },
    "14": {
        "name": "2002",
        "type": "sculpture14",
        "colorways": "OG, BLCK, TEA, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=2002&search[stringTraits][0][name]=Type&search[stringTraits][0][values][0]=Sculpture"
    },
    "15": {
        "name": "0ff_with_his_head",
        "type": "sculpture15",
        "colorways": "OG, BLCK, WHT, RED",
        "link": "https://opensea.io/collection/dfw-design?search[query]=0ff"
    },
    "16": {
        "name": "Mystical Arcases",
        "type": "sculpture16",
        "colorways": "BLCK, RED, WHT, MIX",
        "link": "https://opensea.io/collection/dfw-design?search[query]=mystical&search[stringTraits][0][name]=Type&search[stringTraits][0][values][0]=Sculpture"
    }
};

        // Configuration object
        const config = {
            model: {
                url: 'https://raw.githubusercontent.com/decentralize-dfw/3dwebhome/main/C2W-OPT-rotated_256-v5.glb'
            },
            animation: {
                frameCount: 31,
                speedMultiplier: .5
            },
            camera: {
                start: { x: 0.5, y: 8, z: 7 },
                end: { x: 0.5, y: 104, z: 7 },
                rotation: { x: -45, y: 0, z: 0 },
                orthographicScale: 3
            },
            dimensions: {
                width: 1590,
                height: 830
            }
        };

        // Debug helper
        let lastLogTime = 0;
        const LOG_THROTTLE = 1000;

        function debugLog(message, data = {}) {
            const now = Date.now();
            if (now - lastLogTime > LOG_THROTTLE) {
                console.log(`[DEBUG] ${message}`, data);
                lastLogTime = now;
            }
        }

        // Opacity calculation based on camera position
        function calculateOpacity(objectId, cameraY) {
    const objectId_num = parseInt(objectId);
    
    // Calculate center point for each object
    // Starting at 8, incrementing by 6 for each object
    const objectCenter = 8 + (6 * (objectId_num - 1));
    
    // Define opacity transition points
    const zeroStart = objectCenter - 2.6;  // Point where opacity begins
    const oneStart = objectCenter - 2.3;   // Point where opacity reaches 1%
    const oneEnd = objectCenter + 2.3;     // Point where opacity starts decreasing from 100%
    const zeroEnd = objectCenter + 2.6;    // Point where opacity reaches 0%

    // Return 0 if outside the opacity range
    if (cameraY <= zeroStart || cameraY >= zeroEnd) return 0;
    
    // Calculate opacity for fade in
    if (cameraY > zeroStart && cameraY <= objectCenter) {
        if (cameraY <= oneStart) {
            // Fade from 0% to 1%
            return 0.01 * ((cameraY - zeroStart) / (oneStart - zeroStart));
        } else {
            // Fade from 1% to 100%
            const range = objectCenter - oneStart;
            const distance = cameraY - oneStart;
            return Math.min(1, 0.01 + (0.99 * (distance / range)));
        }
    }
    
    // Calculate opacity for fade out
    if (cameraY > objectCenter && cameraY < zeroEnd) {
        if (cameraY >= oneEnd) {
            // Fade from 1% to 0%
            return 0.01 * ((zeroEnd - cameraY) / (zeroEnd - oneEnd));
        } else {
            // Fade from 100% to 1%
            const range = oneEnd - objectCenter;
            const distance = oneEnd - cameraY;
            return Math.max(0.01, 0.01 + (0.99 * (distance / range)));
        }
    }

    return 0;
}
        // Create elements 
       function createObjectInfoElements() {
    debugLog('Creating info elements');
    
    const existingElements = document.querySelectorAll('.object-info');
    existingElements.forEach(el => el.remove());
    
    const mainContainer = document.createElement('div');
    mainContainer.id = 'info-container';
    
    Object.entries(objectData).forEach(([objectId, objectInfo]) => {
        const container = createElement('div', 'object-info', null);
        container.id = `object-info-${objectId}`;
        
        const nameElement = createElement('div', 'object-name', objectInfo.name);
        const typeElement = createElement('div', 'object-type', objectInfo.type);
        const colorwaysElement = createElement('div', 'object-colorways', objectInfo.colorways);
        
        const marketButton = createElement('button', 'market-button', 'open in market');
        marketButton.id = `market-button-${objectId}`;
        marketButton.onclick = () => window.open(objectInfo.link, '_blank', 'noopener');
        
        container.appendChild(nameElement);
        container.appendChild(typeElement);
        container.appendChild(colorwaysElement);
        container.appendChild(marketButton);
        
        mainContainer.appendChild(container);
    });
    
    document.body.appendChild(mainContainer);
}

        // Update visibility
        // Helper function to update object info visibility
function updateObjectInfo(objectId, opacity) {
    const info = document.getElementById(`object-info-${objectId}`);
    if (info) {
        info.style.opacity = opacity;
        const button = document.getElementById(`market-button-${objectId}`);
        if (button) {
            button.style.opacity = opacity;
            button.style.pointerEvents = opacity > 0.01 ? 'auto' : 'none';
        }
    }
}

        // Three.js setup
        const scene = new THREE.Scene();
        const aspect = config.dimensions.width / config.dimensions.height;
        const camera = new THREE.OrthographicCamera(
            -config.camera.orthographicScale * aspect,
            config.camera.orthographicScale * aspect,
            config.camera.orthographicScale,
            -config.camera.orthographicScale,
            0.1,
            1000
        );

        const renderer = new THREE.WebGLRenderer({ 
            antialias: true,
            alpha: true,
            premultipliedAlpha: false,
            logarithmicDepthBuffer: true
        });
        renderer.setSize(config.dimensions.width, config.dimensions.height);
        renderer.setClearColor(0x000000, 0);
        renderer.physicallyCorrectLights = true;
        renderer.toneMapping = THREE.ACESFilmicToneMapping;
        renderer.toneMappingExposure = 1.2;
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.setPixelRatio(window.devicePixelRatio);
        document.getElementById('canvas-container').appendChild(renderer.domElement);

        camera.position.set(config.camera.start.x, config.camera.start.y, config.camera.start.z);
        camera.rotation.set(
            THREE.MathUtils.degToRad(config.camera.rotation.x),
            THREE.MathUtils.degToRad(config.camera.rotation.y),
            THREE.MathUtils.degToRad(config.camera.rotation.z)
        );

        // Lighting setup
        // Lighting setup with much higher intensities
const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);  // Doubled from 1.0
scene.add(ambientLight);

const mainLight = new THREE.DirectionalLight(0xffffff, 1.5);  // More than doubled from 1.2
mainLight.position.set(0, 10, 10);
mainLight.castShadow = true;
scene.add(mainLight);

const frontFill = new THREE.DirectionalLight(0xffffff, 1.0);  // Nearly doubled from 0.8
frontFill.position.set(0, 0, 5);
scene.add(frontFill);

const topFill = new THREE.DirectionalLight(0xffffff, 1.5);    // Nearly doubled from 0.8
topFill.position.set(0, 5, 0);
scene.add(topFill);

const backFill = new THREE.DirectionalLight(0xffffff, 1.2);   // Doubled from 0.6
backFill.position.set(0, 0, -5);
scene.add(backFill);

// Increased point light intensities
const pointLight1 = new THREE.PointLight(0xffffff, 1.0);      // Doubled from 0.5
pointLight1.position.set(5, 5, 5);
scene.add(pointLight1);

const pointLight2 = new THREE.PointLight(0xffffff, 1.0);      // Doubled from 0.5
pointLight2.position.set(-5, 5, -5);
scene.add(pointLight2);

// Add extra fill lights for better coverage
const pointLight3 = new THREE.PointLight(0xffffff, 1.0);      // New light
pointLight3.position.set(0, -5, 0);
scene.add(pointLight3);

const pointLight4 = new THREE.PointLight(0xffffff, 1.0);      // New light
pointLight4.position.set(0, 5, -5);
scene.add(pointLight4);

        // Add wireframe state tracking
      let isWireframe = true;
        let currentModel = null;

        // Wireframe toggle button functionality
        const wireframeToggle = document.getElementById('wireframe-toggle');
        
        function toggleWireframe() {
    isWireframe = !isWireframe;
    wireframeToggle.textContent = isWireframe ? 'Material' : 'Wireframe';
    wireframeToggle.classList.toggle('active', isWireframe);
    
    if (currentModel) {
        currentModel.traverse((child) => {
            if (child.isMesh) {
                if (isWireframe) {
                    // Store original material
                    child.userData.originalMaterial = child.material;
                    
                    // Create simple wireframe material
                    child.material = new THREE.MeshBasicMaterial({
                        wireframe: true,
                        color: 0x000000,
                        transparent: true,
                        opacity: 0.2,
                        side: THREE.DoubleSide
                    });
                } else {
                    // Restore original material
                    if (child.userData.originalMaterial) {
                        child.material = child.userData.originalMaterial;
                    }
                }
            }
        });
    }
}

        wireframeToggle.addEventListener('click', toggleWireframe);

        // Model loading setup
        const dracoLoader = new THREE.DRACOLoader();
        dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');

        const loader = new THREE.GLTFLoader();
        loader.setDRACOLoader(dracoLoader);

        let mixer;

        // Create info elements before loading model
        createObjectInfoElements();

        loader.load(config.model.url, function(gltf) {
    const model = gltf.scene;
    currentModel = model;
    
    model.traverse((child) => {
        if (child.isMesh) {
            // Enhance material settings for better visibility
            child.material.transparent = true;
            child.material.opacity = 1;
            child.material.metalness = 0.4;
            child.material.roughness = 0.2;
            child.material.envMapIntensity = 3.0;
            child.material.reflectivity = 1.0;
            child.material.side = THREE.DoubleSide;
            child.material.alphaToCoverage = true;
            child.material.depthWrite = true;
            
            // Ensure proper shadows and rendering
            child.castShadow = true;
            child.receiveShadow = true;
        }
    });
    
    scene.add(model);

    if (gltf.animations && gltf.animations.length) {
        mixer = new THREE.AnimationMixer(model);
        gltf.animations.forEach(clip => {
            const action = mixer.clipAction(clip);
            action.play();
        });
    }
});
        // Animation and camera movement setup
        let currentY = config.camera.start.y;
        let targetY = currentY;
        const smoothness = 0.05;
        let lastCameraUpdate = 0;
        const CAMERA_UPDATE_THROTTLE = 16;

        function lerp(start, end, factor) {
            return start + (factor * (end - start));
        }

        function updateCamera() {
    const now = Date.now();
    if (now - lastCameraUpdate < CAMERA_UPDATE_THROTTLE) return;
    
    const scrollFraction = window.pageYOffset / (document.documentElement.scrollHeight - window.innerHeight);
    const currentCameraY = config.camera.start.y + (scrollFraction * (config.camera.end.y - config.camera.start.y));
    
    if (mixer) {
        const animationTime = (scrollFraction * 31) * 0.3;
        mixer.setTime(animationTime % 31);
    }

    targetY = currentCameraY;
    currentY = lerp(currentY, targetY, 0.05);
    camera.position.y = currentY;

    // Update opacity for all objects
    Object.keys(objectData).forEach(objectId => {
        const opacity = calculateOpacity(objectId, currentY);
        updateObjectInfo(objectId, opacity);
    });
    
    lastCameraUpdate = now;
}
function animate() {
    requestAnimationFrame(animate);
    updateCamera();
    renderer.render(scene, camera);
}

// Start animation loop
animate();

// Handle window resizing
window.addEventListener('resize', function() {
    const aspect = config.dimensions.width / config.dimensions.height;
    
    // Update camera frustum
    camera.left = -config.camera.orthographicScale * aspect;
    camera.right = config.camera.orthographicScale * aspect;
    camera.top = config.camera.orthographicScale;
    camera.bottom = -config.camera.orthographicScale;
    
    // Update camera and renderer
    camera.updateProjectionMatrix();
    renderer.setSize(config.dimensions.width, config.dimensions.height);
});

    })();
    </script>
</body>
</html>

<!DOCTYPE html>
<html lang="en" oncontextmenu="return false;" onselectstart="return false" ondragstart="return false">
<head>
    <meta charset="UTF-8">
    <title>Digital Forgery Work 3D Viewer - Sculptures</title>
    
    <meta name="description" content="Digital Forgery Work 3D Viewer - View exclusive 3D sculpture designs.">
    <meta name="keywords" content="digital forgery, 3D design, digital art, NFT, sculpture">
    <meta property="og:title" content="Digital Forgery Work 3D Viewer - Sculptures">
    <meta property="og:type" content="website">
    <meta property="og:description" content="Explore and interact with exclusive 3D digital sculptures.">
    
    <style>
        html, body {
            margin: 0;
            padding: 0;
            scrollbar-width: none !important;
            -ms-overflow-style: none !important;
            overflow: -moz-scrollbars-none;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            -khtml-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            background: transparent;
        }
        
        html::-webkit-scrollbar,
        body::-webkit-scrollbar {
            width: 0 !important;
            height: 0 !important;
            display: none !important;
            background: transparent !important;
        }

        body { 
            height: 1000vh;
            overflow-y: scroll;
            overflow-x: hidden;
            -webkit-overflow-scrolling: touch;
            pointer-events: auto;
        }
        
        * {
            scrollbar-width: none !important;
            -ms-overflow-style: none !important;
        }
        
        *::-webkit-scrollbar {
            display: none !important;
        }

        #canvas-container {
            width: 1590px;
            height: 830px;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            overflow: hidden;
            background: transparent;
        }

        canvas { 
            display: block;
            width: 100%;
            height: 100%;
            background: transparent;
        }

        .object-info {
            position: fixed;
            right: 50px;
            top: 50%;
            transform: translateY(-50%);
            opacity: 0;
            text-align: right;
            pointer-events: none;
            z-index: 1000;
            padding: 10px;
        }

        .object-name {
            font-family: helvetica;
            font-weight: bold;
            font-size: 25px;
            letter-spacing: -0.25px;
            color: black;
            margin-bottom: 7px;
            white-space: nowrap;
        }

        .object-type {
            font-family: helvetica;
            font-weight: 500;
            font-size: 15px;
            letter-spacing: -0.05px;
            color: black;
            margin-bottom: 7px;
            white-space: nowrap;
        }

        .object-colorways {
            font-family: helvetica;
            font-weight: 500;
            font-style: italic;
            font-size: 12px;
            letter-spacing: -0.15px;
            color: #595959;
            margin-bottom: 7px;
            white-space: nowrap;
        }

        .market-button {
            font-family: helvetica;
            font-weight: 500;
            font-style: italic;
            font-size: 11px;
            letter-spacing: -0.15px;
            color: black;
            padding: 7px 14px;
            background-color: rgba(255, 255, 255, 0.7);
            border: 1px solid #000000;
            border-radius: 10px;
            cursor: pointer;
            transition: all 0.2s ease;
            pointer-events: auto;
            margin-top: 10px;
            opacity: 0;
        }

        .market-button:hover {
            background-color: rgba(0, 0, 0, 0.6);
            color: white;
            border-color: transparent;
        }

        #wireframe-toggle {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            padding: 7px 7px;
            background-color: rgba(0, 0, 0, 0.5);
            color: rgba(255, 255, 255, 0.5);
            border: 1px solid #808080;
            border-radius: 5px;
            cursor: pointer;
            font-family: helvetica;
            font-size: 12px;
            font-style: italic;
            letter-spacing: -0.15px;
            z-index: 1000;
            transition: all 0.3s;
        }

        #wireframe-toggle:hover {
            background-color: rgba(0, 0, 0, 0.6) !important;
            color: white !important;
            border-color: transparent !important;
        }

        #wireframe-toggle.active {
            background-color: transparent;
            color: black;
        }

        #wireframe-toggle.active:hover {
            background-color: rgba(0, 0, 0, 0.6) !important;
            color: white !important;
            border-color: transparent !important;
        }

        #loading-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(255, 255, 255, 0.9);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 2000;
        }

        .loading-spinner {
            width: 50px;
            height: 50px;
            border: 5px solid #f3f3f3;
            border-top: 5px solid #3498db;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-bottom: 20px;
        }

        .loading-text {
            font-family: helvetica;
            font-size: 16px;
            color: #333;
        }

        .loading-progress {
            width: 200px;
            height: 4px;
            background: #f3f3f3;
            margin-top: 10px;
            border-radius: 2px;
            overflow: hidden;
        }

        .loading-progress-bar {
            width: 0%;
            height: 100%;
            background: #3498db;
            transition: width 0.3s ease;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <div id="canvas-container"></div>
    <button id="wireframe-toggle">Material</button>
    <div id="loading-overlay">
        <div class="loading-spinner"></div>
        <div class="loading-text">Loading 3D Model...</div>
        <div class="loading-progress">
            <div class="loading-progress-bar"></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/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/RGBELoader.js"></script>
    <script>
    class SceneManager {
        constructor(config) {
            this.scene = new THREE.Scene();
            this.config = config;
            this.setupCamera();
            this.setupRenderer();
            this.setupLights();
            this.setupEnvironment();
        }

        setupCamera() {
            const aspect = this.config.dimensions.width / this.config.dimensions.height;
            this.camera = new THREE.OrthographicCamera(
                -this.config.camera.orthographicScale * aspect,
                this.config.camera.orthographicScale * aspect,
                this.config.camera.orthographicScale,
                -this.config.camera.orthographicScale,
                0.1,
                1000
            );
            this.camera.position.set(
                this.config.camera.start.x,
                this.config.camera.start.y,
                this.config.camera.start.z
            );
            this.camera.rotation.set(
                THREE.MathUtils.degToRad(this.config.camera.rotation.x),
                THREE.MathUtils.degToRad(this.config.camera.rotation.y),
                THREE.MathUtils.degToRad(this.config.camera.rotation.z)
            );
        }

        setupRenderer() {
            this.renderer = new THREE.WebGLRenderer({
                antialias: true,
                alpha: true,
                premultipliedAlpha: false,
                logarithmicDepthBuffer: true,
                preserveDrawingBuffer: true
            });
            this.renderer.setSize(this.config.dimensions.width, this.config.dimensions.height);
            this.renderer.setClearColor(0x000000, 0);
            this.renderer.physicallyCorrectLights = true;
            this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
            this.renderer.toneMappingExposure = 1.2;
            this.renderer.outputEncoding = THREE.sRGBEncoding;
            this.renderer.shadowMap.enabled = true;
            this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
            this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
            document.getElementById('canvas-container').appendChild(this.renderer.domElement);
        }

        setupLights() {
            const lights = [];
            lights.push(new THREE.AmbientLight(0xffffff, 1.0));

            const mainLight = new THREE.DirectionalLight(0xffffff, 1.5);
            mainLight.position.set(0, 10, 10);
            mainLight.castShadow = true;
            mainLight.shadow.mapSize.width = 2048;
            mainLight.shadow.mapSize.height = 2048;
            lights.push(mainLight);

            ['front', 'top', 'back'].forEach((pos, i) => {
                let light = new THREE.DirectionalLight(0xffffff,[1.0, 1.5, 1.2][i]);
                light.position.set(
                   [0, 0, 0][i],
                   [0, 5, 0][i],
                   [5, 0, -5][i]
                );
                lights.push(light);
            });

           [[5, 5, 5], [-5, 5, -5],[0, -5, 0],[0, 5, -5]].forEach(coord => {
                let light = new THREE.PointLight(0xffffff, 1.0);
                light.position.set(...coord);
                lights.push(light);
            });

            lights.forEach(light => this.scene.add(light));
        }

        setupEnvironment() {
            const rgbeLoader = new THREE.RGBELoader();
            rgbeLoader.load(
                'https://raw.githubusercontent.com/decentralize-dfw/3dwebhome/main/cyberpunk-hdr.hdr',
                (texture) => {
                    texture.mapping = THREE.EquirectangularReflectionMapping;
                    this.scene.environment = texture;
                    this.scene.background = texture;
                    this.scene.backgroundBlurriness = 0.5;
                }
            );
        }

        resize() {
            const aspect = this.config.dimensions.width / this.config.dimensions.height;
            this.camera.left = -this.config.camera.orthographicScale * aspect;
            this.camera.right = this.config.camera.orthographicScale * aspect;
            this.camera.top = this.config.camera.orthographicScale;
            this.camera.bottom = -this.config.camera.orthographicScale;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(this.config.dimensions.width, this.config.dimensions.height);
        }
    }

    class MaterialManager {
        constructor() {
            this.materialCache = new Map();
            this.wireframeMaterialCache = new Map();
        }

        setupAdvancedMaterials(model) {
            model.traverse((child) => {
                if (child.isMesh) {
                    const originalMaterial = child.material;
                    this.materialCache.set(child.uuid, originalMaterial.clone());

                    const wireframeMaterial = new THREE.MeshPhongMaterial({
                        color: 0x000000,
                        wireframe: true,
                        transparent: true,
                        opacity: 0.65,
                        side: THREE.DoubleSide,
                        skinning: true,
                        morphTargets: true,
                        depthTest: true,
                        depthWrite: true
                    });
                    this.wireframeMaterialCache.set(child.uuid, wireframeMaterial);

                    child.material = wireframeMaterial;
                    this.setupAdvancedMaterialProperties(originalMaterial, child);
                }
            });

            return { materialCache: this.materialCache, wireframeMaterialCache: this.wireframeMaterialCache };
        }

        setupAdvancedMaterialProperties(material, mesh) {
            material.envMapIntensity = 1.5;
            material.needsUpdate = true;
            
            if (material.type === 'MeshStandardMaterial' || material.type === 'MeshPhysicalMaterial') {
                material.envMapIntensity = 3.0;
                material.metalness = Math.min(Math.max(material.metalness || 0.4, 0), 1);
                material.roughness = Math.min(Math.max(material.roughness || 0.2, 0), 1);
                material.reflectivity = material.reflectivity || 1.0;
                
                if (material.transmission > 0) {
                    material.transmission = Math.min(Math.max(material.transmission, 0), 1);
                    material.thickness = material.thickness || 0.01;
                }
            }

            if (material.transparent || material.alphaTest > 0) {
                if (material.alphaTest > 0) {
                    material.transparent = true;
                    material.depthWrite = true;
                    material.alphaTest = Math.max(material.alphaTest, 0.5);
                } else {
                    material.transparent = true;
                    material.depthWrite = false;
                }
                material.depthTest = true;
                material.needsUpdate = true;
            }

            mesh.castShadow = true;
            mesh.receiveShadow = true;
            mesh.updateMatrixWorld(true);
        }
    }

    class AnimationController {
        constructor(model, animations) {
            this.mixer = new THREE.AnimationMixer(model);
            this.clock = new THREE.Clock();
            this.animations = animations;
            this.actions = new Map();
            
            if (animations) {
                animations.forEach(clip => {
                    const action = this.mixer.clipAction(clip);
                    this.actions.set(clip.name, action);
                    action.play();
                });
            }
        }

        update() {
            if (this.mixer) {
                this.mixer.update(this.clock.getDelta());
            }
        }

        setTime(time) {
            if (this.mixer) {
                this.mixer.setTime(time);
            }
        }
    }

    class ModelLoader {
        constructor(onProgress, onError) {
            this.dracoLoader = new THREE.DRACOLoader();
            this.dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
            
            this.loader = new THREE.GLTFLoader();
            this.loader.setDRACOLoader(this.dracoLoader);
            
            this.onProgress = onProgress;
            this.onError = onError;
        }

        async loadModel(url) {
            return new Promise((resolve, reject) => {
                this.loader.load(
                    url,
                    (gltf) => resolve(gltf),
                    (xhr) => {
                        if (xhr.lengthComputable && this.onProgress) {
                            const percentComplete = (xhr.loaded / xhr.total) * 100;
                            this.onProgress(percentComplete);
                        }
                    },
                    (error) => {
                        if (this.onError) this.onError(error);
                        reject(error);
                    }
                );
            });
        }
    }

    class UIController {
        constructor() {
            this.loadingOverlay = document.getElementById('loading-overlay');
            this.loadingProgressBar = document.querySelector('.loading-progress-bar');
            this.wireframeToggle = document.getElementById('wireframe-toggle');
            
            this.wireframeToggle.addEventListener('click', () => {
                if (this.onWireframeToggle) {
                    this.onWireframeToggle();
                }
            });
        }

        updateLoadingProgress(progress) {
            this.loadingProgressBar.style.width = `${progress}%`;
        }

        showLoadingError(message) {
            const loadingText = document.querySelector('.loading-text');
            loadingText.classList.add('error-message');
            loadingText.textContent = message;
            document.querySelector('.loading-spinner').style.display = 'none';
            document.querySelector('.loading-progress').style.display = 'none';
        }

        hideLoading() {
            this.loadingOverlay.style.opacity = '0';
            setTimeout(() => {
                this.loadingOverlay.style.display = 'none';
            }, 300);
        }

        setWireframeToggleCallback(callback) {
            this.onWireframeToggle = callback;
        }
    }

    class ModelViewer {
        constructor(config, objectData) {
            this.config = config;
            this.objectData = objectData;
            this.isWireframe = true;
            this.sceneManager = new SceneManager(config);
            this.materialManager = new MaterialManager();
            this.uiController = new UIController();
            this.setupModelLoader();
            this.setupEventListeners();
            this.animate = this.animate.bind(this);
        }

        setupModelLoader() {
            this.modelLoader = new ModelLoader(
                (progress) => this.uiController.updateLoadingProgress(progress),
                (error) => {
                    console.error('Error loading model:', error);
                    this.uiController.showLoadingError('Failed to load 3D model. Please refresh the page.');
                }
            );
        }

        async init() {
            try {
                const gltf = await this.modelLoader.loadModel(this.config.model.url);
                this.currentModel = gltf.scene;
                
                const materials = this.materialManager.setupAdvancedMaterials(this.currentModel);
                this.animationController = new AnimationController(this.currentModel, gltf.animations);
                
                this.sceneManager.scene.add(this.currentModel);
                this.uiController.hideLoading();
                
                this.animate();
            } catch (error) {
                console.error('Failed to initialize:', error);
                this.uiController.showLoadingError('Failed to initialize viewer. Please refresh the page.');
            }
        }

        setupEventListeners() {
            window.addEventListener('resize', () => this.sceneManager.resize());
            this.uiController.setWireframeToggleCallback(() => this.toggleWireframe());
        }

        toggleWireframe() {
            this.isWireframe = !this.isWireframe;
            
            if (!this.currentModel) return;

            this.currentModel.traverse((child) => {
                if (child.isMesh) {
                    const newMaterial = this.isWireframe
                        ? this.materialManager.wireframeMaterialCache.get(child.uuid)
                        : this.materialManager.materialCache.get(child.uuid);

                    if (newMaterial) {
                        child.material = newMaterial;
                        child.material.needsUpdate = true;
                        child.updateMatrixWorld(true);
                    }
                }
            });
        }

        animate() {
            requestAnimationFrame(this.animate);
            
            try {
                this.updateCamera();
                if (this.animationController) {
                    this.animationController.update();
                }
                this.sceneManager.renderer.render(this.sceneManager.scene, this.sceneManager.camera);
            } catch (error) {
                console.error('Animation error:', error);
                this.uiController.showLoadingError('An error occurred during animation.');
            }
        }

        updateCamera() {
            const scrollFraction = window.pageYOffset / (document.documentElement.scrollHeight - window.innerHeight);
            const currentCameraY = this.config.camera.start.y + 
                (scrollFraction * (this.config.camera.end.y - this.config.camera.start.y));
            
            if (this.animationController) {
                const animationTime = (scrollFraction * this.config.animation.frameCount) * 
                    this.config.animation.speedMultiplier;
                this.animationController.setTime(animationTime % this.config.animation.frameCount);
            }

            this.currentY = this.currentY || this.config.camera.start.y;
            this.targetY = currentCameraY;
            this.currentY = this.lerp(this.currentY, this.targetY, this.config.camera.smoothness || 0.05);
            this.sceneManager.camera.position.y = this.currentY;

            this.updateObjectVisibility(this.currentY);
        }

        updateObjectVisibility(cameraY) {
            Object.entries(this.objectData).forEach(([objectId, objectInfo]) => {
                const opacity = this.calculateOpacity(objectId, cameraY);
                this.updateObjectInfo(objectId, opacity);
            });
        }

        calculateOpacity(objectId, cameraY) {
            const objectId_num = parseInt(objectId);
            const objectCenter = 8 + (6 * (objectId_num - 1));
            const zeroStart = objectCenter - 2.6;
            const oneStart = objectCenter - 2.3;
            const oneEnd = objectCenter + 2.3;
            const zeroEnd = objectCenter + 2.6;

            if (cameraY <= zeroStart || cameraY >= zeroEnd) return 0;
            
            if (cameraY > zeroStart && cameraY <= objectCenter) {
                if (cameraY <= oneStart) {
                    return 0.01 * ((cameraY - zeroStart) / (oneStart - zeroStart));
                } else {
                    const range = objectCenter - oneStart;
                    const distance = cameraY - oneStart;
                    return Math.min(1, 0.01 + (0.99 * (distance / range)));
                }
            }
            
            if (cameraY > objectCenter && cameraY < zeroEnd) {
                if (cameraY >= oneEnd) {
                    return 0.01 * ((zeroEnd - cameraY) / (zeroEnd - oneEnd));
                } else {
                    const range = oneEnd - objectCenter;
                    const distance = oneEnd - cameraY;
                    return Math.max(0.01, 0.01 + (0.99 * (distance / range)));
                }
            }
            return 0;
        }

        updateObjectInfo(objectId, opacity) {
            const info = document.getElementById(`object-info-${objectId}`);
            if (info) {
                info.style.opacity = opacity;
                const button = document.getElementById(`market-button-${objectId}`);
                if (button) {
                    button.style.opacity = opacity;
                    button.style.pointerEvents = opacity > 0.01 ? 'auto' : 'none';
                }
            }
        }

        lerp(start, end, factor) {
            return start + (factor * (end - start));
        }
    }

    const config = {
        model: {
            url: 'https://raw.githubusercontent.com/decentralize-dfw/3dwebhome/main/C2W-test-deletedfirst4-128px-v4.glb'
        },
        animation: {
            frameCount: 31,
            speedMultiplier: 0.5
        },
        camera: {
            start: { x: -0, y: 15, z: 20 },
            end: { x: 0, y: 104, z: 20 },
            rotation: { x: -60, y: 0, z: 0 },
            orthographicScale: 4,
            smoothness: 0.05
        },
        dimensions: {
            width: 1590,
            height: 830
        },
        debug: {
            enabled: false,
            showStats: false
        }
    };

    const objectData = {
        "1": {
            "name": "Osiris Rake",
            "type": "sculpture1",
            "colorways": "OG, BLCK, GRY, RED",
            "link": "https://opensea.io/collection/dfw-design?search[query]=osiris"
        },
        // Add more objects here as needed
    };

    document.addEventListener('DOMContentLoaded', async () => {
        try {
            const viewer = new ModelViewer(config, objectData);
            await viewer.init().catch(error => {
                console.error('Failed to initialize viewer:', error);
                const loadingText = document.querySelector('.loading-text');
                loadingText.textContent = 'Failed to load the viewer. Please refresh the page.';
                loadingText.style.color = '#e74c3c';
            });

            if (config.debug.enabled) {
                window.viewer = viewer;
                if (config.debug.showStats) {
                    const Stats = require('stats.js');
                    const stats = new Stats();
                    document.body.appendChild(stats.dom);
                    function animate() {
                        stats.begin();
                        stats.end();
                        requestAnimationFrame(animate);
                    }
                    animate();
                }
            }
        } catch (error) {
            console.error('Critical error during viewer initialization:', error);
            const loadingText = document.querySelector('.loading-text');
            loadingText.textContent = 'A critical error occurred. Please try again later.';
            loadingText.style.color = '#e74c3c';
        }
    });

    window.addEventListener('unload', () => {
        if (window.viewer) {
            viewer.sceneManager.scene.traverse((object) => {
                if (object.geometry) object.geometry.dispose();
                if (object.material) {
                    Object.entries(object.material).forEach(([key, value]) => {
                        if (value && value.dispose instanceof Function) value.dispose();
                    });
                    object.material.dispose();
                }
            });
            viewer.sceneManager.renderer.dispose();
        }
    });
    </script>
</body>
</html>```

bottom of page