CoolFace
Apppublic

venkat112/venkat_glb_creation

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
glb_generator_threejs.js419 linesDownload Raw Back to scripts
1// Three.js GLB Generator2// Generates GLB files using Three.js and GLTFExporter3// Note: This requires three.js to be installed4// If three.js is not available, the server will fallback to Blender5 6export class ThreeJSGLBGenerator {7  constructor() {8    this.three = null;9    this.GLTFExporter = null;10    this.scene = null;11    this.objects = [];12    this.lights = [];13  }14 15  async loadThreeJS() {16    if (this.three && this.GLTFExporter) {17      return true;18    }19 20    try {21      const threeModule = await import('three');22      const exporterModule = await import('three/examples/jsm/exporters/GLTFExporter.js');23      24      this.three = threeModule;25      this.GLTFExporter = exporterModule.GLTFExporter;26      return true;27    } catch (error) {28      return false;29    }30  }31 32  async generate(sceneData, outputPath) {33    const fs = (await import('fs')).default;34    35    const loaded = await this.loadThreeJS();36    if (!loaded) {37      throw new Error('Three.js is not available. Install with: npm install three');38    }39 40    const THREE = this.three;41    const GLTFExporter = this.GLTFExporter;42 43    try {44      this.scene = new THREE.Scene();45      this.objects = [];46      this.lights = [];47 48      this.setupEnvironment(sceneData, THREE);49 50      if (sceneData.ground) {51        this.createGround(sceneData.ground, THREE);52      }53 54      if (sceneData.objects) {55        sceneData.objects.forEach(obj => this.createObject(obj, THREE));56      }57 58      if (sceneData.avatar && sceneData.avatar.present) {59        this.createAvatar(sceneData.avatar, THREE);60      }61 62      if (sceneData.lighting) {63        this.createLights(sceneData.lighting, THREE);64      }65 66      if (sceneData.camera) {67        this.createCamera(sceneData.camera, THREE);68      }69 70      await this.exportGLB(outputPath, THREE, GLTFExporter, fs);71 72      return { success: true, path: outputPath };73    } catch (error) {74      return { success: false, error: error.message };75    }76  }77 78  setupEnvironment(sceneData, THREE) {79    if (sceneData.lighting?.color) {80      this.scene.background = new THREE.Color(81        sceneData.lighting.color[0],82        sceneData.lighting.color[1],83        sceneData.lighting.color[2]84      );85    }86 87    if (sceneData.effects?.fog) {88      this.scene.fog = new THREE.FogExp2(89        new THREE.Color(0.8, 0.8, 0.9),90        0.0591      );92    }93  }94 95  createGround(groundData, THREE) {96    const size = groundData.size || 20;97    const geometry = new THREE.PlaneGeometry(size, size);98    99    const color = groundData.color || [0.5, 0.5, 0.5, 1];100    const material = new THREE.MeshStandardMaterial({101      color: new THREE.Color(color[0], color[1], color[2]),102      roughness: groundData.roughness || 0.8,103      metalness: groundData.metallic || 0.0104    });105 106    const mesh = new THREE.Mesh(geometry, material);107    mesh.rotation.x = -Math.PI / 2;108    mesh.name = "Ground";109    this.scene.add(mesh);110    this.objects.push(mesh);111  }112 113  createObject(objData, THREE) {114    let geometry;115 116    switch (objData.type) {117      case 'cube':118        const size = objData.size || 1;119        geometry = new THREE.BoxGeometry(size, size, size);120        break;121      case 'sphere':122        geometry = new THREE.SphereGeometry(objData.radius || 0.5, 32, 32);123        break;124      case 'cylinder':125        geometry = new THREE.CylinderGeometry(126          objData.radius || 0.5,127          objData.radius || 0.5,128          objData.size || 1,129          32130        );131        break;132      case 'cone':133        geometry = new THREE.ConeGeometry(134          objData.radius || 0.5,135          objData.size || 1,136          32137        );138        break;139      case 'plane':140        geometry = new THREE.PlaneGeometry(141          objData.size || 10,142          objData.size || 10143        );144        break;145      default:146        geometry = new THREE.BoxGeometry(1, 1, 1);147    }148 149    const color = objData.color || [0.8, 0.8, 0.8, 1];150    const material = new THREE.MeshStandardMaterial({151      color: new THREE.Color(color[0], color[1], color[2]),152      roughness: objData.roughness || 0.5,153      metalness: objData.metallic || 0.0,154      transparent: color[3] < 1,155      opacity: color[3] || 1156    });157 158    if (objData.emission) {159      material.emissive = new THREE.Color(160        objData.emission[0],161        objData.emission[1],162        objData.emission[2]163      );164      material.emissiveIntensity = objData.emissionStrength || 1.0;165    }166 167    const mesh = new THREE.Mesh(geometry, material);168    mesh.position.set(169      objData.location[0] || 0,170      objData.location[1] || 0,171      objData.location[2] || 0172    );173    mesh.rotation.set(174      objData.rotation[0] || 0,175      objData.rotation[1] || 0,176      objData.rotation[2] || 0177    );178    mesh.scale.set(179      objData.scale[0] || 1,180      objData.scale[1] || 1,181      objData.scale[2] || 1182    );183    mesh.name = objData.name || 'Object';184 185    this.scene.add(mesh);186    this.objects.push(mesh);187  }188 189  createAvatar(avatarData, THREE) {190    const group = new THREE.Group();191    group.name = avatarData.name || 'Avatar';192 193    const torso = new THREE.Mesh(194      new THREE.CylinderGeometry(0.25, 0.25, 0.8, 32),195      new THREE.MeshStandardMaterial({ color: 0xffccaa })196    );197    torso.position.y = 1.0;198    group.add(torso);199 200    const head = new THREE.Mesh(201      new THREE.SphereGeometry(0.18, 32, 32),202      new THREE.MeshStandardMaterial({ color: 0xffccaa })203    );204    head.position.y = 1.9;205    group.add(head);206 207    const leftArm = new THREE.Mesh(208      new THREE.BoxGeometry(0.2, 0.4, 0.2),209      new THREE.MeshStandardMaterial({ color: 0xffccaa })210    );211    leftArm.position.set(-0.45, 1.3, 0);212    group.add(leftArm);213 214    const rightArm = new THREE.Mesh(215      new THREE.BoxGeometry(0.2, 0.4, 0.2),216      new THREE.MeshStandardMaterial({ color: 0xffccaa })217    );218    rightArm.position.set(0.45, 1.3, 0);219    group.add(rightArm);220 221    const leftLeg = new THREE.Mesh(222      new THREE.BoxGeometry(0.25, 0.75, 0.25),223      new THREE.MeshStandardMaterial({ color: 0x4444ff })224    );225    leftLeg.position.set(-0.18, 0.5, 0);226    group.add(leftLeg);227 228    const rightLeg = new THREE.Mesh(229      new THREE.BoxGeometry(0.25, 0.75, 0.25),230      new THREE.MeshStandardMaterial({ color: 0x4444ff })231    );232    rightLeg.position.set(0.18, 0.5, 0);233    group.add(rightLeg);234 235    group.position.set(236      avatarData.position[0] || 0,237      avatarData.position[1] || 0,238      avatarData.position[2] || 0239    );240    group.scale.set(241      avatarData.scale || 1,242      avatarData.scale || 1,243      avatarData.scale || 1244    );245 246    this.scene.add(group);247    this.objects.push(group);248  }249 250  createLights(lightingData, THREE) {251    const ambientLight = new THREE.AmbientLight(252      new THREE.Color(lightingData.color || [1, 1, 1]),253      lightingData.ambient_strength || 0.3254    );255    this.scene.add(ambientLight);256    this.lights.push(ambientLight);257 258    if (lightingData.lights && lightingData.lights.length > 0) {259      lightingData.lights.forEach(light => {260        let lightObj;261 262        switch (light.type) {263          case 'SUN':264          case 'DIRECTIONAL':265            lightObj = new THREE.DirectionalLight(266              new THREE.Color(light.color || lightingData.color || [1, 1, 1]),267              light.energy || 1268            );269            lightObj.position.set(270              light.location[0] || 10,271              light.location[1] || -10,272              light.location[2] || 10273            );274            break;275          case 'POINT':276            lightObj = new THREE.PointLight(277              new THREE.Color(light.color || lightingData.color || [1, 1, 1]),278              light.energy || 50,279              100280            );281            lightObj.position.set(282              light.location[0] || 0,283              light.location[1] || 0,284              light.location[2] || 2285            );286            break;287          case 'SPOT':288            lightObj = new THREE.SpotLight(289              new THREE.Color(light.color || lightingData.color || [1, 1, 1]),290              light.energy || 50291            );292            lightObj.position.set(293              light.location[0] || 0,294              light.location[1] || 0,295              light.location[2] || 2296            );297            break;298          default:299            return;300        }301 302        if (lightObj) {303          this.scene.add(lightObj);304          this.lights.push(lightObj);305        }306      });307    }308  }309 310  createCamera(cameraData, THREE) {311    const camera = new THREE.PerspectiveCamera(312      cameraData.fov || 50,313      16 / 9,314      0.1,315      1000316    );317    camera.position.set(318      cameraData.position[0] || 4,319      cameraData.position[1] || -4,320      cameraData.position[2] || 2.2321    );322    camera.rotation.set(323      cameraData.rotation[0] || 1.05,324      cameraData.rotation[1] || 0,325      cameraData.rotation[2] || 0.78326    );327    this.scene.add(camera);328  }329 330  async exportGLB(outputPath, THREE, GLTFExporter, fs) {331    return new Promise((resolve, reject) => {332      // Polyfill FileReader for Node.js environment333      if (typeof global !== 'undefined' && !global.FileReader) {334        global.FileReader = class FileReader {335          constructor() {336            this.result = null;337            this.onload = null;338            this.onerror = null;339          }340          readAsArrayBuffer(blob) {341            // In Node.js, we'll handle this differently342            if (blob && blob.arrayBuffer) {343              blob.arrayBuffer().then(buffer => {344                this.result = buffer;345                if (this.onload) this.onload({ target: { result: buffer } });346              }).catch(err => {347                if (this.onerror) this.onerror(err);348              });349            }350          }351        };352      }353 354      const exporter = new GLTFExporter();355      const options = {356        binary: true,357        includeCustomExtensions: false, // Disable to avoid FileReader issues358        onlyVisible: false,359        truncateDrawRange: true,360        trs: false,361        animations: []362      };363 364      try {365        exporter.parse(366          this.scene,367          (result) => {368            try {369              // Handle both ArrayBuffer and Uint8Array370              let buffer;371              if (result instanceof ArrayBuffer) {372                buffer = Buffer.from(result);373              } else if (result instanceof Uint8Array) {374                buffer = Buffer.from(result);375              } else if (typeof result === 'string') {376                // If it's a string (JSON), convert to buffer377                buffer = Buffer.from(result, 'utf8');378              } else if (result && result.buffer) {379                // Handle TypedArray380                buffer = Buffer.from(result.buffer);381              } else {382                // Try to convert directly383                buffer = Buffer.from(result);384              }385              386              if (!buffer || buffer.length === 0) {387                reject(new Error('GLB export resulted in empty buffer'));388                return;389              }390              391              fs.writeFileSync(outputPath, buffer);392              resolve();393            } catch (error) {394              reject(new Error(`Failed to write GLB file: ${error.message}`));395            }396          },397          (error) => {398            // Check if error is about FileReader399            const errorMsg = error?.message || String(error);400            if (errorMsg.includes('FileReader')) {401              reject(new Error('GLTFExporter requires browser environment. Please install three.js properly or use Blender fallback.'));402            } else {403              reject(new Error(`GLTFExporter error: ${errorMsg}`));404            }405          },406          options407        );408      } catch (error) {409        const errorMsg = error?.message || String(error);410        if (errorMsg.includes('FileReader')) {411          reject(new Error('GLTFExporter requires browser environment. Please install three.js properly.'));412        } else {413          reject(error);414        }415      }416    });417  }418}419