| @@ -106,7 +106,8 @@ To make changes and run the example, click on the CodePen button on the top righ | |||
| - [Packages](#threepipe-packages) | |||
| - [@threepipe/plugin-tweakpane](#threepipeplugin-tweakpane) Tweakpane UI Plugin | |||
| - [@threepipe/plugin-tweakpane-editor](#threepipeplugin-tweakpane-editor) - Tweakpane Editor Plugin | |||
| - [@threepipe/plugin-extra-importers](#threepipeplugin-extra-importers) - Plugin for loading even more file types. | |||
| - [@threepipe/plugin-extra-importers](#threepipeplugin-extra-importers) - Plugin for loading more file types supported by loaders in three.js | |||
| - [@threepipe/plugin-blend-importer](#threepipeplugin-blend-importer) - Blender to add support for loading .blend file | |||
| ## Getting Started | |||
| @@ -2626,3 +2627,39 @@ const model1 = await viewer.load<IObject3D>('data:model/3mf;base64,...') | |||
| ``` | |||
| Remove the `<IObject3D>` if using javascript and not typescript. | |||
| ## @threepipe/plugin-blend-importer | |||
| Exports [BlendImporterPlugin](https://threepipe.org/plugins/blend-importer/docs/classes/BlendLoadPlugin.html) which adds support for loading .blend files. | |||
| It uses [js.blend](https://github.com/acweathersby/js.blend) for parsing blend file structure. | |||
| Note: This is still a WIP. | |||
| Currently working: `Mesh`, `BufferGeometry` and basic `PointLight`. | |||
| To be added: `PhysicalMaterial`, `UnlitMaterial` (similar to blender-gltf-io plugin) | |||
| Example: https://threepipe.org/examples/#blend-load/ | |||
| Source Code: [plugins/blend-importer/src/index.ts](plugins/blend-importer/src/index.ts) | |||
| API Reference: [@threepipe/plugin-blend-importer](https://threepipe.org/plugins/blend-importer/docs) | |||
| NPM: `npm install @threepipe/plugin-blend-importer` | |||
| ```typescript | |||
| import {ThreeViewer} from 'threepipe' | |||
| import {BlendLoadPlugin} from '@threepipe/plugin-blend-importer' | |||
| const viewer = new ThreeViewer({...}) | |||
| viewer.addPluginSync(BlendLoadPlugin) | |||
| // Now load any .blend file. | |||
| const model = await viewer.load<IObject3D>('path/to/file.blend') | |||
| // To load the file as a data url, use the correct mimetype | |||
| const model1 = await viewer.load<IObject3D>('data:application/x-blender;base64,...') | |||
| ``` | |||
| [//]: # ( TODO: The plugin should parse and references to other assets and find them relative to the .blend file or the current location.) | |||
| @@ -0,0 +1,54 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Blend Load</title> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <!-- Import maps polyfill --> | |||
| <!-- Remove this when import maps will be widely supported --> | |||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||
| <script type="importmap"> | |||
| { | |||
| "imports": { | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-blend-importer": "./../../plugins/blend-importer/dist/index.mjs" | |||
| } | |||
| } | |||
| </script> | |||
| <style id="example-style"> | |||
| html, body, #canvas-container, #mcanvas { | |||
| width: 100%; | |||
| height: 100%; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| <script id="example-script" type="module"> | |||
| import {_testFinish, ThreeViewer} from 'threepipe' | |||
| import {BlendLoadPlugin} from '@threepipe/plugin-blend-importer' | |||
| const viewer = new ThreeViewer({canvas: document.getElementById('mcanvas')}) | |||
| viewer.addPluginsSync([BlendLoadPlugin]) | |||
| async function init() { | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| const result = await viewer.load('https://asset-samples.threepipe.org/minimal/default-cube.blend', { | |||
| autoCenter: true, | |||
| autoScale: false, | |||
| }) | |||
| console.log(result) | |||
| } | |||
| init().then(_testFinish) | |||
| </script> | |||
| </head> | |||
| <body> | |||
| <div id="canvas-container"> | |||
| <canvas id="mcanvas"></canvas> | |||
| </div> | |||
| </body> | |||
| @@ -249,6 +249,7 @@ | |||
| <li><a href="./stl-load/">STL Load </a></li> | |||
| <li><a href="./ktx2-load/">KTX2 Load </a></li> | |||
| <li><a href="./ktx-load/">KTX Load </a></li> | |||
| <li><a href="./blend-load/">BLEND Load </a></li> | |||
| <li><a href="./extra-importer-plugins/">Extra(3ds, 3mf, collada, amf, bvh, vox, gcode, mdd, pcd, tilt, wrl, ldraw, vtk, xyz) Load </a></li> | |||
| </ul> | |||
| <h2 class="category">Export</h2> | |||
| @@ -19,7 +19,8 @@ | |||
| "threepipe": "./../../dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs", | |||
| "@threepipe/plugin-tweakpane-editor": "./../../plugins/tweakpane-editor/dist/index.mjs", | |||
| "@threepipe/plugin-extra-importers": "./../../plugins/extra-importers/dist/index.mjs" | |||
| "@threepipe/plugin-extra-importers": "./../../plugins/extra-importers/dist/index.mjs", | |||
| "@threepipe/plugin-blend-importer": "./../../plugins/blend-importer/dist/index.mjs" | |||
| } | |||
| } | |||
| @@ -11,6 +11,7 @@ import { | |||
| KTX2LoadPlugin, | |||
| KTXLoadPlugin, | |||
| NormalBufferPlugin, | |||
| PickingPlugin, | |||
| PLYLoadPlugin, | |||
| ProgressivePlugin, | |||
| RenderTargetPreviewPlugin, | |||
| @@ -24,6 +25,7 @@ import { | |||
| } from 'threepipe' | |||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||
| import {TweakpaneEditorPlugin} from '@threepipe/plugin-tweakpane-editor' | |||
| import {BlendLoadPlugin} from '@threepipe/plugin-blend-importer' | |||
| import {extraImportPlugins} from '@threepipe/plugin-extra-importers' | |||
| async function init() { | |||
| @@ -46,9 +48,10 @@ async function init() { | |||
| await viewer.addPlugins([ | |||
| new ProgressivePlugin(), | |||
| new GLTFAnimationPlugin(), | |||
| new CameraViewPlugin(), | |||
| new ViewerUiConfigPlugin(), | |||
| GLTFAnimationPlugin, | |||
| PickingPlugin, | |||
| CameraViewPlugin, | |||
| ViewerUiConfigPlugin, | |||
| // new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin | |||
| new DepthBufferPlugin(HalfFloatType, true, true), | |||
| new NormalBufferPlugin(HalfFloatType, false), | |||
| @@ -60,6 +63,7 @@ async function init() { | |||
| Rhino3dmLoadPlugin, | |||
| STLLoadPlugin, | |||
| USDZLoadPlugin, | |||
| BlendLoadPlugin, | |||
| ...extraImportPlugins, | |||
| ]) | |||
| @@ -69,6 +73,7 @@ async function init() { | |||
| editor.loadPlugins({ | |||
| ['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin], | |||
| ['Interaction']: [PickingPlugin], | |||
| ['GBuffer']: [DepthBufferPlugin, NormalBufferPlugin], | |||
| ['Post-processing']: [TonemapPlugin, ProgressivePlugin, FrameFadePlugin], | |||
| ['Animation']: [GLTFAnimationPlugin, CameraViewPlugin], | |||
| @@ -1,12 +1,12 @@ | |||
| { | |||
| "name": "threepipe", | |||
| "version": "0.0.13", | |||
| "version": "0.0.14", | |||
| "lockfileVersion": 2, | |||
| "requires": true, | |||
| "packages": { | |||
| "": { | |||
| "name": "threepipe", | |||
| "version": "0.0.13", | |||
| "version": "0.0.14", | |||
| "license": "Apache-2.0", | |||
| "dependencies": { | |||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1016/package.tgz", | |||
| @@ -1,6 +1,6 @@ | |||
| { | |||
| "name": "threepipe", | |||
| "version": "0.0.13", | |||
| "version": "0.0.14", | |||
| "description": "A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.", | |||
| "main": "src/index.ts", | |||
| "module": "dist/index.mjs", | |||
| @@ -0,0 +1,30 @@ | |||
| { | |||
| "name": "@threepipe/plugin-blend-importer", | |||
| "version": "0.0.1", | |||
| "lockfileVersion": 2, | |||
| "requires": true, | |||
| "packages": { | |||
| "": { | |||
| "name": "@threepipe/plugin-blend-importer", | |||
| "version": "0.0.1", | |||
| "license": "Apache-2.0", | |||
| "dependencies": { | |||
| "threepipe": "file:./../../src/" | |||
| }, | |||
| "devDependencies": {} | |||
| }, | |||
| "../../src": {}, | |||
| "../tweakpane/src": { | |||
| "extraneous": true | |||
| }, | |||
| "node_modules/threepipe": { | |||
| "resolved": "../../src", | |||
| "link": true | |||
| } | |||
| }, | |||
| "dependencies": { | |||
| "threepipe": { | |||
| "version": "file:../../src" | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,57 @@ | |||
| { | |||
| "name": "@threepipe/plugin-blend-importer", | |||
| "description": "Basic importer for .blend file", | |||
| "version": "0.0.1", | |||
| "devDependencies": { | |||
| }, | |||
| "dependencies": { | |||
| "threepipe": "file:./../../src/" | |||
| }, | |||
| "clean-package": { | |||
| "remove": [ | |||
| "clean-package", | |||
| "scripts", | |||
| "devDependencies", | |||
| "//", | |||
| "markdown-to-html" | |||
| ], | |||
| "replace": { | |||
| "dependencies": { | |||
| "threepipe": "^0.0.13" | |||
| } | |||
| } | |||
| }, | |||
| "type": "module", | |||
| "main": "dist/index.js", | |||
| "module": "dist/index.mjs", | |||
| "types": "dist/index.d.ts", | |||
| "files": [ | |||
| "dist", | |||
| "src" | |||
| ], | |||
| "scripts": { | |||
| "new:pack": "npm run prepare && clean-package && npm pack && clean-package restore", | |||
| "new:publish": "npm run prepare && clean-package && npm publish --access public && clean-package restore", | |||
| "prepare": "npm run build", | |||
| "build": "rimraf dist && NODE_ENV=production rollup -c", | |||
| "dev": "rollup -c -w", | |||
| "docs": "rimraf docs && npx typedoc" | |||
| }, | |||
| "author": "repalash <palash@shaders.app>", | |||
| "license": "Apache-2.0", | |||
| "keywords": [ | |||
| "three", | |||
| "three.js", | |||
| "threepipe", | |||
| "editor", | |||
| "plugin" | |||
| ], | |||
| "bugs": { | |||
| "url": "https://github.com/repalash/threepipe/issues" | |||
| }, | |||
| "homepage": "https://github.com/repalash/threepipe#readme", | |||
| "repository": { | |||
| "type": "git", | |||
| "url": "git://github.com/repalash/threepipe.git" | |||
| } | |||
| } | |||
| @@ -0,0 +1,99 @@ | |||
| // rollup.config.js | |||
| import commonjs from '@rollup/plugin-commonjs'; | |||
| import json from '@rollup/plugin-json'; | |||
| import resolve from '@rollup/plugin-node-resolve'; | |||
| import typescript from '@rollup/plugin-typescript'; | |||
| import license from 'rollup-plugin-license' | |||
| import packageJson from './package.json' assert {type: 'json'}; | |||
| import path from 'path' | |||
| import {fileURLToPath} from 'url'; | |||
| import postcss from 'rollup-plugin-postcss' | |||
| import replace from '@rollup/plugin-replace' | |||
| import terser from "@rollup/plugin-terser"; | |||
| const __filename = fileURLToPath(import.meta.url); | |||
| const __dirname = path.dirname(__filename); | |||
| const {name, version, author} = packageJson | |||
| // const {main, module, browser} = packageJson["clean-package"].replace | |||
| const isProduction = process.env.NODE_ENV === 'production' | |||
| const settings = { | |||
| globals: { | |||
| "three": "threepipe", | |||
| "threepipe": "threepipe" | |||
| }, | |||
| sourcemap: true | |||
| } | |||
| export default { | |||
| input: './src/index.ts', | |||
| output: [ | |||
| // { | |||
| // file: main, | |||
| // name: main, | |||
| // ...settings, | |||
| // format: 'cjs', | |||
| // plugins: [ | |||
| // isProduction && terser() | |||
| // ] | |||
| // }, | |||
| { | |||
| file: './dist/index.mjs', | |||
| ...settings, | |||
| name: name, | |||
| format: 'es', | |||
| plugins: [ | |||
| isProduction && terser() | |||
| ] | |||
| }, | |||
| { | |||
| file: './dist/index.js', | |||
| ...settings, | |||
| name: name, | |||
| format: 'umd', | |||
| plugins: [ | |||
| isProduction && terser() | |||
| ] | |||
| } | |||
| ], | |||
| external: Object.keys(settings.globals), | |||
| plugins: [ | |||
| replace({ | |||
| 'from \'three\'': 'from \'threepipe\'', | |||
| delimiters: ['', ''], | |||
| }), | |||
| replace({ | |||
| 'process.env.NODE_ENV': JSON.stringify('production'), | |||
| }), | |||
| postcss({ | |||
| modules: false, | |||
| autoModules: true, // todo; issues with typescript import css, because inject is false | |||
| inject: false, | |||
| minimize: isProduction, | |||
| // Or with custom options for `postcss-modules` | |||
| }), | |||
| json(), | |||
| resolve({}), | |||
| typescript({ | |||
| }), | |||
| commonjs({ | |||
| include: 'node_modules/**', | |||
| extensions: ['.js'], | |||
| ignoreGlobal: false, | |||
| sourceMap: false | |||
| }), | |||
| license({ | |||
| banner: ` | |||
| @license | |||
| ${name} v${version} | |||
| Copyright 2022<%= moment().format('YYYY') > 2022 ? '-' + moment().format('YYYY') : null %> ${author} | |||
| ${packageJson.license} License | |||
| `, | |||
| thirdParty: { | |||
| output: path.join(__dirname, 'dist', 'dependencies.txt'), | |||
| includePrivate: true, // Default is false. | |||
| }, | |||
| }) | |||
| ] | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| import {AnyOptions, BaseImporterPlugin, FileLoader, ILoader, Importer, Object3D, Scene} from 'threepipe' | |||
| import {parseBlend} from './js-blend/main.js' | |||
| import {createObjects} from './loader' | |||
| /** | |||
| * Adds support for loading Blend `.blend`, `application/x-blender` files and data uris | |||
| */ | |||
| export class BlendLoadPlugin extends BaseImporterPlugin { | |||
| public static readonly PluginType = 'BlendLoadPlugin' | |||
| constructor() { | |||
| super() | |||
| } | |||
| protected _importer = new Importer(class extends FileLoader implements ILoader { | |||
| async loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<any> { | |||
| this.setResponseType('arraybuffer') | |||
| const res = (await super.loadAsync(url, onProgress)) as ArrayBuffer | |||
| const blend = await parseBlend(res) | |||
| const objects = await createObjects(blend) | |||
| const root = new Object3D() | |||
| root.add(...objects) | |||
| // console.log(res, blend, root) | |||
| blend.scene = root | |||
| return blend | |||
| } | |||
| transform(res: any, _: AnyOptions): Scene { | |||
| // console.log(res) | |||
| // res.scene.userData.kinematics = res.kinematics | |||
| // res.scene.userData.library = res.library | |||
| return res.scene | |||
| } | |||
| }, ['blend'], ['application/x-blender'], true) | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| declare module '*.txt' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.glsl' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.vert' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.frag' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| declare module '*.module.scss' { | |||
| const content: any | |||
| export default content | |||
| export const stylesheet: string | |||
| } | |||
| declare module '*.module.css' { | |||
| const content: any | |||
| export default content | |||
| export const stylesheet: string | |||
| } | |||
| declare module '*.css' { | |||
| const content: string | |||
| export default content | |||
| } | |||
| // export {} | |||
| // hack for typedoc | |||
| // eslint-disable-next-line @typescript-eslint/naming-convention | |||
| // declare type OffscreenCanvas = HTMLCanvasElement | |||
| @@ -0,0 +1 @@ | |||
| export {BlendLoadPlugin} from './BlendLoadPlugin' | |||
| @@ -0,0 +1,21 @@ | |||
| MIT License | |||
| Copyright (c) 2020 Anthony C, Weathersby | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| in the Software without restriction, including without limitation the rights | |||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| copies of the Software, and to permit persons to whom the Software is | |||
| furnished to do so, subject to the following conditions: | |||
| The above copyright notice and this permission notice shall be included in all | |||
| copies or substantial portions of the Software. | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| SOFTWARE. | |||
| @@ -0,0 +1 @@ | |||
| export function parseBlend(arrayBuffer: ArrayBuffer): Promise<any> | |||
| @@ -0,0 +1,21 @@ | |||
| /* eslint-disable camelcase */ | |||
| /** | |||
| * JS.Blend | |||
| * Original Repo: https://github.com/acweathersby/js.blend | |||
| * Slightly modified for three.js and js updates, minor refactor. | |||
| * Object-creation part re-written for latest three.js and typescript | |||
| * MIT License | |||
| * Copyright (c) 2020 Anthony C, Weathersby | |||
| * @license | |||
| */ | |||
| import parser from './parser/parser.js'; | |||
| export async function parseBlend (buffer, name = '') { | |||
| return new Promise((res, rej) => { | |||
| parser.onParseReady = (file, error) => { | |||
| if (error) rej(error) | |||
| else res(file) | |||
| } | |||
| parser.loadBlendFromArrayBuffer(buffer, name) | |||
| }) | |||
| } | |||
| @@ -0,0 +1,783 @@ | |||
| /* eslint-disable camelcase, no-unused-vars, no-empty, no-constant-condition */ | |||
| const DNA1 = 826363460; | |||
| const ENDB = 1111772741; | |||
| /* Note: Blender coordinates treat the Z axis as the vertical an Y as depth. */ | |||
| // web worker not functional in this version | |||
| let USE_WEBWORKER = false; | |||
| let worker = null; | |||
| // FR = new FileReader(), | |||
| const return_object = { | |||
| loadBlendFromArrayBuffer: function (array_buffer) { | |||
| return_object.ready = false; | |||
| if (USE_WEBWORKER) { | |||
| worker.postMessage(array_buffer, array_buffer); | |||
| } else { | |||
| worker.onmessage({ | |||
| data: array_buffer, | |||
| }); | |||
| } | |||
| }, | |||
| // loadBlendFromBlob: function (blob) { | |||
| // FR.onload = function () { | |||
| // return_object.loadBlendFromArrayBuffer(this.result); | |||
| // }; | |||
| // FR.readAsArrayBuffer(blob); | |||
| // }, | |||
| ready: true, | |||
| onParseReady: function () {}, | |||
| }; | |||
| function worker_code () { | |||
| 'use strict'; | |||
| let data = null, | |||
| _data = null, | |||
| BIG_ENDIAN = false, | |||
| pointer_size = 0, | |||
| struct_names = [], | |||
| offset = 0, | |||
| working_blend_file = null, | |||
| current_SDNA_template = null, | |||
| templates = {}, | |||
| finished_objects = [], | |||
| FILE = null, | |||
| ERROR = null, | |||
| AB = null; | |||
| const self = this; | |||
| function parseFile (msg) { | |||
| if (typeof msg.data == 'object') { | |||
| // reset global variables | |||
| AB = null; | |||
| data = null; | |||
| BIG_ENDIAN = false; | |||
| pointer_size = 0; | |||
| struct_names = []; | |||
| offset = 0; | |||
| working_blend_file = null; | |||
| finished_objects = []; | |||
| current_SDNA_template = null; | |||
| // set data | |||
| _data = msg.data; | |||
| AB = _data.slice(); | |||
| data = new DataView(_data); | |||
| FILE = new BLENDER_FILE(AB); | |||
| // start parsing | |||
| readFile(); | |||
| // export parsed data | |||
| self.postMessage(FILE, ERROR); | |||
| } | |||
| } | |||
| /* | |||
| Export object for a parsed __blender_file__. | |||
| */ | |||
| var BLENDER_FILE = function (AB) { | |||
| this.AB = AB; | |||
| // this.double = new Float64Array(AB); | |||
| this.byte = new Uint8Array(AB); | |||
| this.dv = new DataView(AB); | |||
| this.objects = {}; | |||
| this.memory_lookup = {}, | |||
| this.object_array = []; | |||
| this.template = null; | |||
| }; | |||
| BLENDER_FILE.prototype = { | |||
| addObject: function (obj) { | |||
| this.object_array.push(obj); | |||
| if (!this.objects[obj.blender_name]) this.objects[obj.blender_name] = []; | |||
| this.objects[obj.blender_name].push(obj); | |||
| }, | |||
| getPointer: function (offset) { | |||
| const pointerLow = this.dv.getUint32(offset, this.template.endianess); | |||
| if (this.template.pointer_size > 4) { | |||
| const pointerHigh = this.dv.getUint32(offset + 4, this.template.endianess); | |||
| if (this.template.endianess) { | |||
| return (pointerLow) + 'l|h' + pointerHigh; | |||
| } else { | |||
| return (pointerHigh) + 'h|l' + pointerLow; | |||
| } | |||
| } else { | |||
| return pointerLow; | |||
| } | |||
| }, | |||
| }; | |||
| self.onmessage = parseFile; | |||
| // this.onmessage = parseFile; | |||
| /* | |||
| These functions map offsets in the blender __blender_file__ to basic types (byte,short,int,float) through TypedArrays; | |||
| This allows the underlying binary data to be changed. | |||
| */ | |||
| function float64Prop (offset, Blender_Array_Length, length) { | |||
| return { | |||
| get: function () { | |||
| return (Blender_Array_Length > 1) ? | |||
| new Float64Array(this.__blender_file__.AB, this.__data_address__ + offset, length) : | |||
| this.__blender_file__.dv.getFloat64(this.__data_address__ + offset, this.__blender_file__.template.endianess); | |||
| }, | |||
| set: function (float) { | |||
| if (Blender_Array_Length > 1) {} else { | |||
| this.__blender_file__.dv.setFloat64(this.__data_address__ + offset, float, this.__blender_file__.template.endianess); | |||
| } | |||
| }, | |||
| }; | |||
| } | |||
| function floatProp (offset, Blender_Array_Length, length) { | |||
| return { | |||
| get: function () { | |||
| return (Blender_Array_Length > 1) ? | |||
| new Float32Array(this.__blender_file__.AB, this.__data_address__ + offset, length) : | |||
| this.__blender_file__.dv.getFloat32(this.__data_address__ + offset, this.__blender_file__.template.endianess); | |||
| }, | |||
| set: function (float) { | |||
| if (Blender_Array_Length > 1) {} else { | |||
| this.__blender_file__.dv.setFloat32(this.__data_address__ + offset, float, this.__blender_file__.template.endianess); | |||
| } | |||
| }, | |||
| }; | |||
| } | |||
| function intProp (offset, Blender_Array_Length, length) { | |||
| return { | |||
| get: function () { | |||
| return (Blender_Array_Length > 1) ? | |||
| new Int32Array(this.__blender_file__.AB, this.__data_address__ + offset, length) : | |||
| this.__blender_file__.dv.getInt32(this.__data_address__ + offset, this.__blender_file__.template.endianess); | |||
| }, | |||
| set: function (float) { | |||
| if (Blender_Array_Length > 1) {} else { | |||
| this.__blender_file__.dv.setInt32(this.__data_address__ + offset, float, this.__blender_file__.template.endianess); | |||
| } | |||
| }, | |||
| }; | |||
| } | |||
| function uIntProp (offset, Blender_Array_Length, length) { | |||
| return { | |||
| get: function () { | |||
| return (Blender_Array_Length > 1) ? | |||
| new Uint32Array(this.__blender_file__.AB, this.__data_address__ + offset, length) : | |||
| this.__blender_file__.dv.getUint32(this.__data_address__ + offset, this.__blender_file__.template.endianess); | |||
| }, | |||
| set: function (float) { | |||
| if (Blender_Array_Length > 1) {} else { | |||
| this.__blender_file__.dv.setUint32(this.__data_address__ + offset, float, this.__blender_file__.template.endianess); | |||
| } | |||
| }, | |||
| }; | |||
| } | |||
| function shortProp (offset, Blender_Array_Length, length) { | |||
| return { | |||
| get: function () { | |||
| return (Blender_Array_Length > 1) ? | |||
| new Int16Array(this.__blender_file__.AB, this.__data_address__ + offset, length) : | |||
| this.__blender_file__.dv.getInt16(this.__data_address__ + offset, this.__blender_file__.template.endianess); | |||
| }, | |||
| set: function (float) { | |||
| if (Blender_Array_Length > 1) {} else { | |||
| this.__blender_file__.dv.setInt16(this.__data_address__ + offset, float, this.__blender_file__.template.endianess); | |||
| } | |||
| }, | |||
| }; | |||
| } | |||
| const uShortProp = (offset, Blender_Array_Length, length) => { | |||
| return { | |||
| get: function () { | |||
| return (Blender_Array_Length > 1) ? | |||
| new Uint16Array(this.__blender_file__.AB, this.__data_address__ + offset, length) : | |||
| this.__blender_file__.dv.getUint16(this.__data_address__ + offset, this.__blender_file__.template.endianess); | |||
| }, | |||
| set: function (float) { | |||
| if (Blender_Array_Length > 1) { | |||
| } else { | |||
| this.__blender_file__.dv.setUint16(this.__data_address__ + offset, float, this.__blender_file__.template.endianess); | |||
| } | |||
| }, | |||
| }; | |||
| }; | |||
| function charProp (offset, Blender_Array_Length, length) { | |||
| return { | |||
| get: function () { | |||
| if (Blender_Array_Length > 1) { | |||
| let start = this.__data_address__ + offset; | |||
| let end = start; | |||
| let buffer_guard = 0; | |||
| while (this.__blender_file__.byte[end] != 0 && buffer_guard++ < length) end++; | |||
| return toString(this.__blender_file__.AB, start, end); | |||
| } | |||
| return this.__blender_file__.byte[(this.__data_address__ + offset)]; | |||
| }, | |||
| set: function (byte) { | |||
| if (Blender_Array_Length > 1) { | |||
| const string = byte + ''; | |||
| let i = 0; | |||
| const l = string.length; | |||
| while (i < length) { | |||
| if (i < l) { | |||
| this.__blender_file__.byte[(this.__data_address__ + offset + i)] = string.charCodeAt(i) | 0; | |||
| } else { | |||
| this.__blender_file__.byte[(this.__data_address__ + offset + i)] = 0; | |||
| } | |||
| i++; | |||
| } | |||
| } else { | |||
| this.__blender_file__.byte[(this.__data_address__ + offset)] = byte | 0; | |||
| } | |||
| }, | |||
| }; | |||
| } | |||
| function pointerProp2 (offset) { | |||
| return { | |||
| get: function () { | |||
| let pointer = this.__blender_file__.getPointer(this.__data_address__ + offset, this.__blender_file__); | |||
| const link = this.__blender_file__.memory_lookup[pointer]; | |||
| const results = []; | |||
| if (link) { | |||
| const address = link.__data_address__; | |||
| let j = 0; | |||
| while (true) { | |||
| pointer = this.__blender_file__.getPointer(address + j * 8, this.__blender_file__); | |||
| let obj = this.__blender_file__.memory_lookup[pointer]; | |||
| if (!obj) break; | |||
| results.push(obj); | |||
| j++; | |||
| } | |||
| } | |||
| return results; | |||
| }, | |||
| set: function () {}, | |||
| }; | |||
| } | |||
| function pointerProp (offset, Blender_Array_Length, length) { | |||
| return { | |||
| get: function () { | |||
| if (Blender_Array_Length > 1) { | |||
| let array = []; | |||
| let j = 0; | |||
| let off = offset; | |||
| while (j < Blender_Array_Length) { | |||
| let pointer = this.__blender_file__.getPointer(this.__data_address__ + off, this.__blender_file__); | |||
| array.push(this.__blender_file__.memory_lookup[pointer]); | |||
| off += length; | |||
| j++; | |||
| } | |||
| return array; | |||
| } else { | |||
| let pointer = this.__blender_file__.getPointer(this.__data_address__ + offset, this.__blender_file__); | |||
| return this.__blender_file__.memory_lookup[pointer]; | |||
| } | |||
| }, | |||
| set: function () {}, | |||
| }; | |||
| } | |||
| function compileProp (obj, name, type, offset, array_size, IS_POINTER, pointer_size, length) { | |||
| if (!IS_POINTER) { | |||
| switch (type) { | |||
| case 'double': | |||
| Object.defineProperty(obj, name, float64Prop(offset, array_size, length >> 3)); | |||
| break; | |||
| case 'float': | |||
| Object.defineProperty(obj, name, floatProp(offset, array_size, length >> 2)); | |||
| break; | |||
| case 'int': | |||
| Object.defineProperty(obj, name, intProp(offset, array_size, length >> 2)); | |||
| break; | |||
| case 'short': | |||
| case 'ushort': | |||
| Object.defineProperty(obj, name, shortProp(offset, array_size, length >> 1)); | |||
| break; | |||
| case 'char': | |||
| case 'uchar': | |||
| Object.defineProperty(obj, name, charProp(offset, array_size, length)); | |||
| break; | |||
| default: | |||
| // compile list to | |||
| obj[name] = {}; | |||
| obj.__list__.push(name, type, length, offset, array_size, IS_POINTER); | |||
| } | |||
| obj._length += length; | |||
| offset += length; | |||
| } else { | |||
| Object.defineProperty(obj, name, pointerProp(offset, array_size, pointer_size)); | |||
| offset += pointer_size * array_size; | |||
| } | |||
| return offset; | |||
| } | |||
| // Store final DNA structs | |||
| const MASTER_SDNA_SCHEMA = function (version) { | |||
| this.version = version; | |||
| this.SDNA_SET = false; | |||
| this.byte_size = 0; | |||
| this.struct_index = 0; | |||
| this.structs = {}; | |||
| this.SDNA = {}; | |||
| this.endianess = false; | |||
| }; | |||
| MASTER_SDNA_SCHEMA.prototype = { | |||
| getSDNAStructureConstructor: function (name, struct) { | |||
| if (struct) { | |||
| const blen_struct = Function('function ' + name + '(){}; return ' + name)(); | |||
| blen_struct.prototype = new BLENDER_STRUCTURE(); | |||
| blen_struct.prototype.blender_name = name; | |||
| blen_struct.prototype.__pointers = []; | |||
| blen_struct.prototype.__list__ = []; | |||
| let offset = 0; | |||
| // Create properties of struct | |||
| for (let i = 0; i < struct.length; i += 3) { | |||
| let _name = struct[i]; | |||
| const n = _name, | |||
| type = struct[i + 1]; | |||
| let length = struct[i + 2], | |||
| array_length = 0, | |||
| match = null, | |||
| Blender_Array_Length = 1, | |||
| Suparray_match = 1, | |||
| PointerToArray = false, | |||
| Pointer_Match = 0; | |||
| const DNA = this.SDNA[name] = { | |||
| constructor: blen_struct, | |||
| }; | |||
| let original_name = _name; | |||
| // mini type parser | |||
| if ((match = _name.match(/(\*?)(\*?)(\w+)(\[(\w*)\])?(\[(\w*)\])?/))) { | |||
| // base name | |||
| _name = match[3]; | |||
| // pointer type | |||
| if (match[1]) { | |||
| Pointer_Match = 10; | |||
| blen_struct.prototype.__pointers.push(_name); | |||
| } | |||
| if (match[2]) { | |||
| PointerToArray = true; | |||
| } | |||
| // arrays | |||
| if (match[4]) { | |||
| if (match[6]) { | |||
| Suparray_match = parseInt(match[5]); | |||
| Blender_Array_Length = parseInt(match[7]); | |||
| } else { | |||
| Blender_Array_Length = parseInt(match[5]); | |||
| } | |||
| } | |||
| array_length = Blender_Array_Length * length; | |||
| length = array_length * Suparray_match; | |||
| } | |||
| DNA[n] = { | |||
| type: type, | |||
| length: length, | |||
| isArray: (Blender_Array_Length > 0), | |||
| }; | |||
| if (PointerToArray) { | |||
| Object.defineProperty(blen_struct.prototype, _name, pointerProp2(offset)); | |||
| offset += pointer_size; | |||
| } else if (Suparray_match > 1) { | |||
| const array_names = new Array(Suparray_match); | |||
| // construct sub_array object that will return the correct structs | |||
| for (let j = 0; j < Suparray_match; j++) { | |||
| let array_name_ = `__${_name}[${j}]__`; | |||
| array_names[j] = array_name_; | |||
| offset = compileProp(blen_struct.prototype, array_name_, type, offset, Blender_Array_Length, Pointer_Match, pointer_size, array_length); | |||
| } | |||
| Object.defineProperty(blen_struct.prototype, _name, { | |||
| get: (function (array_names) { | |||
| return function () { | |||
| const array = []; | |||
| for (let i = 0; i < array_names.length; i++) { | |||
| array.push(this[array_names[i]]); | |||
| } | |||
| return array; | |||
| }; | |||
| })(array_names), | |||
| }); | |||
| } else { | |||
| offset = compileProp(blen_struct.prototype, _name, type, offset, Blender_Array_Length, Pointer_Match, pointer_size, length); | |||
| } | |||
| } | |||
| return this.SDNA[name].constructor; | |||
| } else { | |||
| if (!this.SDNA[name]) { | |||
| return null; | |||
| } | |||
| return this.SDNA[name].constructor; | |||
| } | |||
| }, | |||
| }; | |||
| var BLENDER_STRUCTURE = function () { | |||
| this.__blender_file__ = null; | |||
| this.__list__ = null; | |||
| this.__super_array_list__ = null; | |||
| this.blender_name = ''; | |||
| this.__pointers = null; | |||
| this.address = null; | |||
| this.length = 0; | |||
| this.__data_address__ = 0; | |||
| this.blender_name = ''; | |||
| this._length = 0; | |||
| }; | |||
| /* | |||
| Returns a pre-constructed BLENDER_STRUCTURE or creates a new BLENDER_STRUCTURE to match the DNA struct type | |||
| */ | |||
| const pointer_function = (pointer) => () => { | |||
| return FILE.memory_lookup[pointer]; | |||
| }; | |||
| function getPointer (offset) { | |||
| const pointerLow = data.getUint32(offset, BIG_ENDIAN); | |||
| if (pointer_size > 4) { | |||
| const pointerHigh = data.getUint32(offset + 4, BIG_ENDIAN); | |||
| if (BIG_ENDIAN) { | |||
| return (pointerLow) + '' + pointerHigh; | |||
| } else { | |||
| return (pointerHigh) + '' + pointerLow; | |||
| } | |||
| } else { | |||
| return pointerLow; | |||
| } | |||
| } | |||
| BLENDER_STRUCTURE.prototype = { | |||
| setData: function (pointer, _data_offset, data_block_length, BLENDER_FILE) { | |||
| if (this.__list__ === null) return this; | |||
| BLENDER_FILE.addObject(this); | |||
| this.__blender_file__ = BLENDER_FILE; | |||
| const struct = this.__list__; | |||
| let j = 0, | |||
| i = 0, | |||
| obj, name = '', | |||
| type, length, Blender_Array_Length, Pointer_Match, offset, constructor; | |||
| this.__data_address__ = _data_offset; | |||
| if (struct === null) return this; | |||
| for (i = 0; i < struct.length; i += 6) { | |||
| obj = null; | |||
| name = struct[i]; | |||
| type = struct[i + 1]; | |||
| Blender_Array_Length = struct[i + 4]; | |||
| Pointer_Match = struct[i + 5]; | |||
| offset = this.__data_address__ + struct[i + 3]; | |||
| if (Blender_Array_Length > 1) { | |||
| this[name] = []; | |||
| j = 0; | |||
| while (j < Blender_Array_Length) { | |||
| if (current_SDNA_template.getSDNAStructureConstructor(type)) { | |||
| constructor = current_SDNA_template.getSDNAStructureConstructor(type); | |||
| this[name].push((new constructor()).setData(0, offset, offset + length / Blender_Array_Length, BLENDER_FILE)); | |||
| } else this[name].push(null); | |||
| offset += length / Blender_Array_Length; | |||
| j++; | |||
| } | |||
| } else { | |||
| if (current_SDNA_template.getSDNAStructureConstructor(type)) { | |||
| constructor = current_SDNA_template.getSDNAStructureConstructor(type); | |||
| this[name] = (new constructor()).setData(0, offset, length + offset, BLENDER_FILE); | |||
| } else this[name] = null; | |||
| } | |||
| } | |||
| // break connection to configuration list | |||
| this.__list__ = null; | |||
| return this; | |||
| }, | |||
| get aname () { | |||
| if (this.id) return this.id.name.slice(2); | |||
| else return undefined; | |||
| }, | |||
| }; | |||
| function toString (buffer, _in, _out) { | |||
| return String.fromCharCode.apply(String, new Uint8Array(buffer, _in, _out - _in)); | |||
| } | |||
| // Begin parsing blender __blender_file__ | |||
| function readFile () { | |||
| let count = 0; | |||
| let offset2 = 0; | |||
| const root = 0; | |||
| const i = 0; | |||
| let data_offset = 0; | |||
| let sdna_index = 0; | |||
| let code = ''; | |||
| let block_length = 0; | |||
| let curr_count = 0; | |||
| let curr_count2 = 0; | |||
| FILE.memory_lookup = {}; | |||
| struct_names = []; | |||
| offset = 0; | |||
| // Make sure we have a .blend __blender_file__. All blend files have the first 12bytes | |||
| // set with BLENDER-v### in Utf-8 | |||
| if (toString(_data, offset, 7) !== 'BLENDER') return ERROR = 'File supplied is not a .blend compatible Blender file.'; | |||
| // otherwise get templete from save version. | |||
| offset += 7; | |||
| pointer_size = ((toString(_data, offset++, offset)) == '_') ? 4 : 8; | |||
| BIG_ENDIAN = toString(_data, offset++, offset) !== 'V'; | |||
| const version = toString(_data, offset, offset + 3); | |||
| // create new master template if none exist for current blender version; | |||
| if (!templates[version]) { | |||
| templates[version] = new MASTER_SDNA_SCHEMA(version); | |||
| } | |||
| current_SDNA_template = templates[version]; | |||
| FILE.template = current_SDNA_template; | |||
| offset += 3; | |||
| // Set SDNA structs if template hasn't been set. | |||
| // Todo: Move the following block into the MASTER_SDNA_SCHEMA object. | |||
| //* Like so:*/ current_SDNA_template.set(AB); | |||
| if (!current_SDNA_template.SDNA_SET) { | |||
| current_SDNA_template.endianess = BIG_ENDIAN; | |||
| current_SDNA_template.pointer_size = pointer_size; | |||
| // find DNA1 data block | |||
| offset2 = offset; | |||
| while (true) { | |||
| sdna_index = data.getInt32(offset2 + pointer_size + 8, BIG_ENDIAN); | |||
| // eslint-disable-next-line no-control-regex | |||
| code = toString(_data, offset2, offset2 + 4).replace(/\u0000/g, ''); | |||
| block_length = data.getInt32(offset2 + 4, true); | |||
| offset2 += 16 + (pointer_size); | |||
| if (code === 'DNA1') { | |||
| // DNA found; This is the core of the __blender_file__ and contains all the structure for the various data types used in Blender. | |||
| count = 0; | |||
| let types = [], | |||
| fields = [], | |||
| names = [], | |||
| lengths = [], | |||
| name = '', | |||
| curr_name = ''; | |||
| // skip SDNA and NAME identifiers | |||
| offset2 += 8; | |||
| // Number of structs. | |||
| count = data.getInt32(offset2, true); | |||
| offset2 += 4; | |||
| curr_count = 0; | |||
| // Build up list of names for structs | |||
| while (curr_count < count) { | |||
| curr_name = ''; | |||
| while (data.getInt8(offset2) !== 0) { | |||
| curr_name += toString(_data, offset2, offset2 + 1); | |||
| offset2++; | |||
| } | |||
| names.push(curr_name); | |||
| offset2++; | |||
| curr_count++; | |||
| } | |||
| // Adjust for 4byte alignment | |||
| if ((offset2 % 4) > 0) offset2 = (4 - (offset2 % 4)) + offset2; | |||
| offset2 += 4; | |||
| // Number of struct types | |||
| count = data.getInt32(offset2, true); | |||
| offset2 += 4; | |||
| curr_count = 0; | |||
| // Build up list of types | |||
| while (curr_count < count) { | |||
| curr_name = ''; | |||
| while (data.getInt8(offset2) !== 0) { | |||
| curr_name += toString(_data, offset2, offset2 + 1); | |||
| offset2++; | |||
| } | |||
| types.push(curr_name); | |||
| offset2++; | |||
| curr_count++; | |||
| } | |||
| // Adjust for 4byte alignment | |||
| if ((offset2 % 4) > 0) offset2 = (4 - (offset2 % 4)) + offset2; | |||
| offset2 += 4; | |||
| curr_count = 0; | |||
| // Build up list of byte lengths for types | |||
| while (curr_count < count) { | |||
| lengths.push(data.getInt16(offset2, BIG_ENDIAN)); | |||
| offset2 += 2; | |||
| curr_count++; | |||
| } | |||
| // Adjust for 4byte alignment | |||
| if ((offset2 % 4) > 0) offset2 = (4 - (offset2 % 4)) + offset2; | |||
| offset2 += 4; | |||
| // Number of structures | |||
| const structure_count = data.getInt32(offset2, BIG_ENDIAN); | |||
| offset2 += 4; | |||
| curr_count = 0; | |||
| // Create constructor objects from list of SDNA structs | |||
| while (curr_count < structure_count) { | |||
| const struct_name = types[data.getInt16(offset2, BIG_ENDIAN)]; | |||
| offset2 += 2; | |||
| const obj = []; | |||
| count = data.getInt16(offset2, BIG_ENDIAN); | |||
| offset2 += 2; | |||
| curr_count2 = 0; | |||
| struct_names.push(struct_name); | |||
| // Fill an array with name, type, and length for each SDNA struct property | |||
| while (curr_count2 < count) { | |||
| obj.push(names[data.getInt16(offset2 + 2, BIG_ENDIAN)], types[data.getInt16(offset2, BIG_ENDIAN)], lengths[data.getInt16(offset2, BIG_ENDIAN)]); | |||
| offset2 += 4; | |||
| curr_count2++; | |||
| } | |||
| // Create a SDNA constructor by passing [type,name,lenth] array as second argument | |||
| current_SDNA_template.getSDNAStructureConstructor(struct_name, obj); | |||
| curr_count++; | |||
| } | |||
| current_SDNA_template.SDNA_SET = true; | |||
| current_SDNA_template.SDNA_NAMES = struct_names; | |||
| break; | |||
| } | |||
| offset2 += block_length; | |||
| } | |||
| } | |||
| // parse the rest of the data, starting back at the top. | |||
| // TODO: turn into "on-demand" parsing. | |||
| while (true) { | |||
| if ((offset % 4) > 0) { | |||
| offset = (4 - (offset % 4)) + offset; | |||
| } | |||
| data_offset = offset; | |||
| sdna_index = data.getInt32(offset + pointer_size + 8, BIG_ENDIAN); | |||
| let code_uint = data.getUint32(offset, BIG_ENDIAN); | |||
| offset2 = offset + 16 + (pointer_size); | |||
| offset += data.getInt32(offset + 4, true) + 16 + (pointer_size); | |||
| if (code_uint === DNA1); // skip - already processed at this point | |||
| else if (code_uint === ENDB) break; // end of __blender_file__ found | |||
| else { | |||
| // Create a Blender object using a constructor template from current_SDNA_template | |||
| const data_start = data_offset + pointer_size + 16; | |||
| // Get a SDNA constructor by name; | |||
| const constructor = current_SDNA_template.getSDNAStructureConstructor(current_SDNA_template.SDNA_NAMES[sdna_index]); | |||
| const size = data.getInt32(data_offset + 4, BIG_ENDIAN); | |||
| count = data.getInt32(data_offset + 12 + pointer_size, BIG_ENDIAN); | |||
| if (count > 0) { | |||
| let obj = new constructor(); | |||
| const length = constructor.prototype._length; | |||
| const address = FILE.getPointer(data_offset + 8); | |||
| obj.address = address + ''; | |||
| obj.setData(address, data_start, data_start + size, FILE); | |||
| if (count > 1) { | |||
| let array = []; | |||
| array.push(obj); | |||
| for (let u = 1; u < count; u++) { | |||
| obj = new constructor(); | |||
| obj.setData(address, data_start + length * u, data_start + (length * u) + length, FILE); | |||
| array.push(obj); | |||
| } | |||
| FILE.memory_lookup[address] = array; | |||
| } else { | |||
| FILE.memory_lookup[address] = obj; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| worker = new worker_code(); | |||
| worker.postMessage = function (message) { | |||
| return_object.onParseReady(message); | |||
| }; | |||
| export default return_object; | |||
| @@ -0,0 +1,91 @@ | |||
| /* eslint-disable camelcase, no-unused-vars, no-empty, no-constant-condition */ | |||
| import {createThreeJSBufferGeometry} from './mesh.js'; | |||
| import {createThreeJSMaterial} from './material.js'; | |||
| import {Mesh} from 'threepipe'; | |||
| import {createThreeJSLamp} from './light.js'; | |||
| const blender_object_types = { | |||
| mesh: 1, | |||
| lamp: 10, | |||
| }; | |||
| function createObject (blender_file, object) { | |||
| if (object.data) { | |||
| // get the mesh | |||
| const buffered_geometry = createThreeJSBufferGeometry(object.data, [0, 0, 0]); | |||
| console.log(object) | |||
| const blend_material = object.data.mat[0]; | |||
| const material = blend_material ? createThreeJSMaterial(blend_material) : null; | |||
| const mesh = new Mesh(buffered_geometry, material); | |||
| mesh.castShadow = true; | |||
| mesh.receiveShadow = true; | |||
| mesh.rotateZ(object.rot[2]); | |||
| mesh.rotateY(object.rot[1]); | |||
| mesh.rotateX(object.rot[0]); | |||
| mesh.scale.fromArray(object.size, 0); | |||
| mesh.position.fromArray([object.loc[0], (object.loc[2]), (-object.loc[1])], 0); | |||
| return mesh; | |||
| } | |||
| return null; | |||
| } | |||
| function loadObject (object_name, blender_file, cache) { | |||
| const objects = blender_file.Object; | |||
| for (let i = 0; i < objects.length; i++) { | |||
| let object = objects[i]; | |||
| if (object.aname === object_name) { | |||
| switch (object.type) { | |||
| case blender_object_types.mesh: | |||
| return createObject(object, blender_file); | |||
| case blender_object_types.lamp: | |||
| return createThreeJSLamp(object, blender_file); | |||
| default: | |||
| console.warn('Unsupported object type', object.type); | |||
| } | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| function loadScene (three_scene, blender_file, cache) { | |||
| for (let i = 0; i < blender_file.objects.Object.length; i++) { | |||
| let object = blender_file.objects.Object[i]; | |||
| // Load Lights | |||
| if (object.type === blender_object_types.lamp) { | |||
| let light = createThreeJSLamp(object, blender_file); | |||
| three_scene.add(light); | |||
| } | |||
| // Load Meshes | |||
| if (object.type === blender_object_types.mesh) { | |||
| let mesh = createObject(blender_file, object); | |||
| if(mesh) { | |||
| three_scene.add(mesh); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| export default function (blender_file) { | |||
| const cache = {}; | |||
| return { | |||
| loadScene: (three_scene) => loadScene(three_scene, blender_file, cache), | |||
| loadObject: (object_name) => loadObject(object_name, blender_file, cache), | |||
| }; | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| /* eslint-disable camelcase, no-unused-vars, no-empty, no-constant-condition */ | |||
| import {PointLight} from 'threepipe'; | |||
| const blender_light_types = { | |||
| point: 0, | |||
| sun: 1, | |||
| spot: 0, | |||
| hemi: 0, | |||
| area: 0, | |||
| }; | |||
| export function createThreeJSLamp (blend_lamp) { | |||
| console.log(blend_lamp) | |||
| let ldata = blend_lamp.data; | |||
| let pos_array = [blend_lamp.loc[0], blend_lamp.loc[2], -blend_lamp.loc[1]]; | |||
| let color = ((ldata.r * 255) << 16) | ((ldata.g * 255) << 8) | ((ldata.b * 255) << 0); | |||
| // let intesity = 20; | |||
| let intesity = ldata.energy; | |||
| let distance = 0; | |||
| let three_light = null; | |||
| switch (ldata.type) { | |||
| case blender_light_types.point: | |||
| three_light = new PointLight(color, intesity, distance); | |||
| three_light.position.fromArray(pos_array, 0); | |||
| three_light.castShadow = true; | |||
| break; | |||
| case blender_light_types.sun: | |||
| three_light = new PointLight(color, intesity, distance); | |||
| three_light.position.fromArray(pos_array, 0); | |||
| three_light.castShadow = true; | |||
| three_light.shadow.mapSize.width = 1024; | |||
| three_light.shadow.mapSize.height = 1024; | |||
| three_light.shadow.camera.near = 0.01; | |||
| three_light.shadow.camera.far = 500; | |||
| break; | |||
| default: | |||
| console.warn('Unsupported light type', ldata.type); | |||
| } | |||
| return three_light; | |||
| } | |||
| @@ -0,0 +1,175 @@ | |||
| /* eslint-disable camelcase, no-unused-vars, no-empty, no-constant-condition */ | |||
| import {createThreeJSTexture} from './texture.js'; | |||
| import {CubeReflectionMapping, EquirectangularReflectionMapping, MeshPhysicalMaterial} from 'threepipe'; | |||
| const texture_mappings = { | |||
| diff_color: 1, | |||
| normal: 2, | |||
| mirror: 8, | |||
| diff_intensity: 16, | |||
| spec_intensity: 32, | |||
| emit: 32, | |||
| alpha: 128, | |||
| spec_hardness: 256, | |||
| ray_mirror: 512, | |||
| translucency: 1024, | |||
| ambient: 2048, | |||
| displacement: 4096, | |||
| warp: 8192, | |||
| }; | |||
| let blender_specular_types = { | |||
| cooktorr: 0, | |||
| phong: 1, | |||
| blinn: 2, | |||
| toon: 3, | |||
| wardiso: 4, | |||
| }; | |||
| function applyColorMapping (blender_texture, three_texture, material) { | |||
| if (blender_texture.mapto & texture_mappings.diff_color) { | |||
| material.map = three_texture; | |||
| } | |||
| } | |||
| function applySpecMapping (blender_texture, three_texture, material) { | |||
| if (blender_texture.mapto & texture_mappings.spec_color && material.type != 'MeshStandardMaterial') { | |||
| material.specularMap = three_texture; | |||
| } | |||
| if (blender_texture.mapto & texture_mappings.spec_intensity && material.type != 'MeshStandardMaterial') { | |||
| material.roughnessMap = three_texture; | |||
| } | |||
| } | |||
| function applyAlphaMapping (blender_texture, three_texture, material) { | |||
| if (blender_texture.mapto & texture_mappings.alpha) { | |||
| material.alphaMap = three_texture; | |||
| } | |||
| } | |||
| function applyNormalMapping (blender_texture, three_texture, material) { | |||
| if (blender_texture.mapto & texture_mappings.normal) { | |||
| material.normalMap = three_texture; | |||
| material.normalScale = { | |||
| x: blender_texture.norfac, | |||
| y: blender_texture.norfac, | |||
| }; | |||
| } | |||
| } | |||
| function applyMirrorMapping (blender_texture, three_texture, material) { | |||
| if (blender_texture.mapto & texture_mappings.mirror) { | |||
| material.envMap = three_texture; | |||
| material.envMapIntensity = blender_texture.mirrfac; | |||
| } | |||
| } | |||
| const blender_texture_coordinates = { | |||
| GENERATED: 1, | |||
| REFLECTION: 2, | |||
| NORMAL: 4, | |||
| GLOBAL: 8, | |||
| UV: 16, | |||
| OBJECT: 32, | |||
| WINDOW: 1024, | |||
| TANGENT: 4096, | |||
| PARTICLE: 8192, | |||
| STRESS: 16384, | |||
| }; | |||
| const blender_texture_mapping = { | |||
| FLAT: 0, | |||
| CUBE: 1, | |||
| TUBE: 2, | |||
| SPHERE: 3, | |||
| }; | |||
| function applyTexture (blender_texture, material) { | |||
| // extract blender_texture data. Use Only if image has been supplied. | |||
| if (blender_texture && blender_texture.tex && blender_texture.tex.ima) { | |||
| let three_texture = createThreeJSTexture(blender_texture.tex.ima); | |||
| if(blender_texture.texco == blender_texture_coordinates.REFLECTION) { | |||
| switch(blender_texture.mapping) { | |||
| case blender_texture_mapping.FLAT: | |||
| three_texture.mapping = EquirectangularReflectionMapping; | |||
| break; | |||
| case blender_texture_mapping.SPHERE: | |||
| three_texture.mapping = CubeReflectionMapping; | |||
| break; | |||
| default: break; | |||
| } | |||
| // three_texture.mapping = EquirectangularRefractionMapping; | |||
| } | |||
| applyColorMapping(blender_texture, three_texture, material); | |||
| applySpecMapping(blender_texture, three_texture, material); | |||
| applyAlphaMapping(blender_texture, three_texture, material); | |||
| applyNormalMapping(blender_texture, three_texture, material); | |||
| applyMirrorMapping(blender_texture, three_texture, material); | |||
| } | |||
| } | |||
| export function createThreeJSMaterial (blend_mat) { | |||
| console.log(blend_mat) | |||
| let material = new MeshPhysicalMaterial(); | |||
| material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b); | |||
| material.roughness = blend_mat.roughness !== undefined ? blend_mat.roughness : 0.4 | |||
| material.metalness = blend_mat.metallic !== undefined ? blend_mat.metallic : 0.0 | |||
| material.opacity = blend_mat.alpha !== undefined ? blend_mat.alpha : 0.0 | |||
| material.tranparent = material.opacity < 1.0 | |||
| // todo textures and nodes | |||
| // const textures = blend_mat.mtex; | |||
| // switch (blend_mat.spec_shader) { | |||
| // case blender_specular_types.lambert: | |||
| // material = new MeshLambertMaterial(); | |||
| // material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b); | |||
| // break; | |||
| // case blender_specular_types.blinn: | |||
| // case blender_specular_types.phong: | |||
| // | |||
| // material = new MeshStandardMaterial(); | |||
| // material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b); | |||
| // // material.specular.setRGB(blend_mat.specr, blend_mat.specg, blend_mat.specb); | |||
| // material.roughness = (1 - (blend_mat.har / 512)); | |||
| // material.metalness = 1 - blend_mat.ref; | |||
| // if(blend_mat.alpha < 0.98) { | |||
| // material.transparent = true; | |||
| // material.opacity = blend_mat.alpha; | |||
| // console.log(blend_mat, material) | |||
| // } | |||
| // break; | |||
| // case blender_specular_types.wardiso: | |||
| // case blender_specular_types.cooktorr: | |||
| // material = new MeshPhongMaterial(); | |||
| // material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b); | |||
| // material.specular.setRGB(blend_mat.specr, blend_mat.specg, blend_mat.specb); | |||
| // material.shininess = blend_mat.har / 512; | |||
| // material.reflectivity = blend_mat.ref * 100; | |||
| // break; | |||
| // default: | |||
| // material = new MeshStandardMaterial(); | |||
| // // material = new MeshLambertMaterial(); | |||
| // material.color.setRGB(blend_mat.r, blend_mat.g, blend_mat.b); | |||
| // material.roughness = 0.5; | |||
| // material.metalness = 0; | |||
| // break; | |||
| // } | |||
| // const at = (texture) => applyTexture(texture, material); | |||
| // | |||
| // if (textures && textures.length) textures.map(at); | |||
| return material; | |||
| } | |||
| @@ -0,0 +1,209 @@ | |||
| /* eslint-disable camelcase, no-unused-vars, no-empty, no-constant-condition */ | |||
| import {BufferAttribute, BufferGeometry} from 'threepipe'; | |||
| export function createBufferGeometry (blender_mesh, origin) { | |||
| // get materials | |||
| let pick_material = 0, | |||
| mesh = blender_mesh, | |||
| faces = mesh.mpoly, | |||
| loops = mesh.mloop, | |||
| UV = mesh.mloopuv, | |||
| verts = mesh.mvert; | |||
| const geometry = new BufferGeometry(); | |||
| if (!faces) return geometry; | |||
| let index_count = 0; | |||
| // precalculate the size of the array needed for faces | |||
| let face_indice_count = 0; | |||
| let face_indice_counta = 0; | |||
| for (let i = 0; i < faces.length; i++) { | |||
| const face = faces[i] || faces; | |||
| const len = face.totloop; | |||
| let indexi = 1; | |||
| face_indice_counta += (len * 2 / 3) | 0; | |||
| while (indexi < len) { | |||
| face_indice_count += 3; | |||
| indexi += 2; | |||
| } | |||
| } | |||
| // extract face info and dump into array buffer; | |||
| const face_buffer = new Uint32Array(face_indice_count); | |||
| const uv_buffer = new Float32Array(face_indice_count * 2); | |||
| const normal_buffer = new Float32Array(face_indice_count * 3); | |||
| const verts_array_buff = new Float32Array(face_indice_count * 3); | |||
| for (let i = 0; i < faces.length; i++) { | |||
| const face = faces[i] || faces; | |||
| const len = face.totloop; | |||
| const start = face.loopstart; | |||
| let indexi = 1; | |||
| const offset = 0; | |||
| while (indexi < len) { | |||
| const face_normals = []; | |||
| const face_index_array = []; | |||
| const face_uvs = []; | |||
| let index = 0; | |||
| for (let l = 0; l < 3; l++) { | |||
| // Per Vertice | |||
| if ((indexi - 1) + l < len) { | |||
| index = start + (indexi - 1) + l; | |||
| } else { | |||
| index = start; | |||
| } | |||
| const v = loops[index].v; | |||
| const vert = verts[v]; | |||
| face_buffer[index_count] = index_count; | |||
| // get normals, which are 16byte ints, and norm them back into floats. | |||
| verts_array_buff[index_count * 3 + 0] = vert.co[0] + origin[0]; | |||
| verts_array_buff[index_count * 3 + 1] = vert.co[2] + origin[2]; | |||
| verts_array_buff[index_count * 3 + 2] = -vert.co[1] + -origin[1]; | |||
| normal_buffer[index_count * 3 + 0] = vert.no[0]; | |||
| normal_buffer[index_count * 3 + 1] = vert.no[2]; | |||
| normal_buffer[index_count * 3 + 2] = (-vert.no[1]); | |||
| if (UV) { | |||
| const uv = UV[index].uv; | |||
| uv_buffer[index_count * 2 + 0] = uv[0]; | |||
| uv_buffer[index_count * 2 + 1] = uv[1]; | |||
| } | |||
| index_count++; | |||
| } | |||
| indexi += 2; | |||
| } | |||
| } | |||
| geometry.setAttribute('position', new BufferAttribute(verts_array_buff, 3)); | |||
| geometry.setIndex(new BufferAttribute(face_buffer, 1)); | |||
| geometry.setAttribute('normal', new BufferAttribute(normal_buffer, 3)); | |||
| geometry.setAttribute('uv', new BufferAttribute(uv_buffer, 2)); | |||
| // geometry.blend_mat = materials[pick_material]; | |||
| return geometry; | |||
| } | |||
| // function createThreeJSGeometry (blender_mesh, origin) { | |||
| // // get materials | |||
| // const mats = blender_mesh.mat, | |||
| // materials = []; | |||
| // for (var i = 0; i < mats.length; i++) { | |||
| // const material = createThreeJSMaterial(mats[i]); | |||
| // materials.push(material); | |||
| // } | |||
| // | |||
| // let pick_material = 0, | |||
| // mesh = blender_mesh, | |||
| // faces = mesh.mpoly, | |||
| // loops = mesh.mloop, | |||
| // UV = mesh.mloopuv, | |||
| // verts = mesh.mvert, | |||
| // vert_array = [], | |||
| // face_array = [], | |||
| // uv_array = [], | |||
| // normal_array = []; | |||
| // | |||
| // const geometry = new Geometry(); | |||
| // | |||
| // if (!faces) return geometry; | |||
| // | |||
| // | |||
| // let index_count = 0; | |||
| // | |||
| // let verts_array_buff = new Float32Array(verts.length * 3); | |||
| // | |||
| // for (var i = 0; i < verts.length; i++) { | |||
| // let vert = verts[i]; | |||
| // vert_array.push(new Vector3(vert.co[0] + origin[0], vert.co[2] + origin[2], -vert.co[1] - origin[1])); | |||
| // } | |||
| // | |||
| // for (var i = 0; i < faces.length; i++) { | |||
| // const face = faces[i] || faces; | |||
| // const len = face.totloop; | |||
| // const start = face.loopstart; | |||
| // let indexi = 1; | |||
| // | |||
| // pick_material = face.mat_nr; | |||
| // | |||
| // while (indexi < len) { | |||
| // const face_normals = []; | |||
| // const face_index_array = []; | |||
| // const face_uvs = []; | |||
| // | |||
| // let index = 0; | |||
| // | |||
| // for (let l = 0; l < 3; l++) { | |||
| // // Per Vertice | |||
| // | |||
| // if ((indexi - 1) + l < len) { | |||
| // index = start + (indexi - 1) + l; | |||
| // } else { | |||
| // index = start; | |||
| // } | |||
| // | |||
| // const v = loops[index].v; | |||
| // var vert = verts[v]; | |||
| // | |||
| // face_index_array.push(v); | |||
| // | |||
| // index_count++; | |||
| // | |||
| // // get normals, which are 16byte ints, and norm them back into floats. | |||
| // | |||
| // const n1 = vert.no[0], | |||
| // n2 = vert.no[2], | |||
| // n3 = -vert.no[1]; | |||
| // | |||
| // const nl = 1; | |||
| // | |||
| // Math.sqrt((n1 * n1) + (n2 * n2) + (n3 * n3)); | |||
| // | |||
| // face_normals.push(new Vector3(n1 / nl, n2 / nl, n3 / nl)); | |||
| // | |||
| // if (UV) { | |||
| // const uv = UV[index].uv; | |||
| // face_uvs.push(new Vector2(uv[0], uv[1])); | |||
| // } | |||
| // } | |||
| // uv_array.push(face_uvs); | |||
| // face_array.push(new Face3( | |||
| // face_index_array[0], face_index_array[1], face_index_array[2], | |||
| // face_normals, | |||
| // )); | |||
| // | |||
| // indexi += 2; | |||
| // } | |||
| // } | |||
| // geometry.blend_mat = materials[pick_material]; | |||
| // geometry.vertices = vert_array; | |||
| // geometry.faces = face_array; | |||
| // if (uv_array.length > 0) { | |||
| // geometry.faceVertexUvs = [uv_array]; | |||
| // } | |||
| // | |||
| // geometry.uvsNeedUpdate = true; | |||
| // | |||
| // // Well, using blender file normals does not work. Will need to investigate why normals from the blender file do not provide correct results. | |||
| // // For now, have Three calculate normals. | |||
| // | |||
| // geometry.computeVertexNormals(); | |||
| // | |||
| // | |||
| // return geometry; | |||
| // } | |||
| @@ -0,0 +1,60 @@ | |||
| /* eslint-disable camelcase, no-unused-vars, no-empty, no-constant-condition */ | |||
| import {arrayBufferToBase64} from 'ts-browser-helpers'; | |||
| import {Texture} from 'threepipe'; | |||
| let blender_texture_cache = {}; | |||
| export function createThreeJSTexture (image) { | |||
| let parsed_blend_file = image.__blender_file__; | |||
| let texture = null; | |||
| let name = image.aname; | |||
| if (image.packedfile) { | |||
| if (blender_texture_cache[name]) { | |||
| texture = blender_texture_cache[name]; | |||
| } else { | |||
| // get the extension | |||
| let ext = name.split('.').pop(); | |||
| let data = image.packedfile; | |||
| let size = data.size; | |||
| let offset = data.data.__data_address__; | |||
| let raw_data = parsed_blend_file.byte.slice(offset, offset + size); | |||
| let encodedData = arrayBufferToBase64(raw_data); | |||
| let dataURI; | |||
| switch (ext) { | |||
| case 'png': | |||
| dataURI = 'data:image/png;base64,' + encodedData; | |||
| break; | |||
| case 'jpg': | |||
| dataURI = 'data:image/jpeg;base64,' + encodedData; | |||
| break; | |||
| default: | |||
| console.warn('Unsupported image type', ext); | |||
| } | |||
| // eslint-disable-next-line no-undef | |||
| let img = new Image(); | |||
| img.src = dataURI; | |||
| texture = new Texture(img); | |||
| img.onload = () => { | |||
| texture.needsUpdate = true; | |||
| // todo colorspace? | |||
| }; | |||
| blender_texture_cache[name] = texture; | |||
| } | |||
| } | |||
| return texture; | |||
| } | |||
| @@ -0,0 +1,68 @@ | |||
| import {BufferAttribute, BufferGeometry, Vector3Tuple} from 'threepipe' | |||
| export function createBufferGeometry(mesh: any, origin: Vector3Tuple) { | |||
| const | |||
| faces = Array.isArray(mesh.mpoly) ? mesh.mpoly as any[] : [mesh.mpoly], | |||
| loops = mesh.mloop, | |||
| uv = mesh.mloopuv, | |||
| vertices = mesh.mvert | |||
| const geometry = new BufferGeometry() | |||
| if (!faces) return geometry | |||
| const size = faces.reduce((acc, face) => acc + Math.floor(face.totloop * 3.0 / 2), 0) | |||
| const indices = new Uint32Array(size) | |||
| const uvs = new Float32Array(size * 2) | |||
| const normals = new Float32Array(size * 3) | |||
| const positions = new Float32Array(size * 3) | |||
| let currentIndex = 0 | |||
| for (const face of faces) { | |||
| const len = face.totloop | |||
| const start = face.loopstart | |||
| let indexi = 1 | |||
| while (indexi < len) { | |||
| let index = 0 | |||
| for (let l = 0; l < 3; l++) { | |||
| // Per Vertex | |||
| index = start | |||
| if (indexi - 1 + l < len) | |||
| index += indexi - 1 + l | |||
| const loop = loops[index] | |||
| const vert = vertices[loop.v] | |||
| indices[currentIndex] = currentIndex | |||
| positions[currentIndex * 3 + 0] = vert.co[0] + origin[0] | |||
| positions[currentIndex * 3 + 1] = vert.co[2] + origin[2] | |||
| positions[currentIndex * 3 + 2] = -vert.co[1] + -origin[1] | |||
| normals[currentIndex * 3 + 0] = vert.no[0] | |||
| normals[currentIndex * 3 + 1] = vert.no[2] | |||
| normals[currentIndex * 3 + 2] = -vert.no[1] | |||
| if (uv) { | |||
| uvs[currentIndex * 2 + 0] = uv[index].uv[0] | |||
| uvs[currentIndex * 2 + 1] = uv[index].uv[1] | |||
| } | |||
| currentIndex++ | |||
| } | |||
| indexi += 2 | |||
| } | |||
| } | |||
| geometry.setAttribute('position', new BufferAttribute(positions, 3)) | |||
| geometry.setIndex(new BufferAttribute(indices, 1)) | |||
| geometry.setAttribute('normal', new BufferAttribute(normals, 3)) | |||
| geometry.setAttribute('uv', new BufferAttribute(uvs, 2)) | |||
| return geometry | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| import {createLight} from './light' | |||
| import {createMesh} from './mesh' | |||
| import {Object3D} from 'three' | |||
| const blenderObjectTypes = { | |||
| mesh: 1, | |||
| lamp: 10, | |||
| } | |||
| export async function createObjects(file: any) { | |||
| const objects: (Object3D|null)[] = [] | |||
| const blendObjects = file.objects.Object | |||
| for (const object of blendObjects) { | |||
| switch (object.type) { | |||
| case blenderObjectTypes.mesh: | |||
| objects.push(createMesh(object)) | |||
| break | |||
| case blenderObjectTypes.lamp: | |||
| objects.push(createLight(object)) | |||
| break | |||
| default: | |||
| console.warn('Unsupported object type', object.type) | |||
| } | |||
| } | |||
| return objects.filter(o => !!o) as Object3D[] | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| import {PointLight} from 'threepipe' | |||
| const blenderLightTypes = { | |||
| point: 0, | |||
| sun: 1, | |||
| spot: 0, | |||
| hemi: 0, | |||
| area: 0, | |||
| } | |||
| export function createLight(lamp: any) { | |||
| const ldata = lamp.data | |||
| const position = [lamp.loc[0], lamp.loc[2], -lamp.loc[1]] | |||
| const color = ldata.r * 255 << 16 | ldata.g * 255 << 8 | ldata.b * 255 << 0 | |||
| const intensity = ldata.energy | |||
| const distance = 0 | |||
| let light = null | |||
| switch (ldata.type) { | |||
| case blenderLightTypes.point: | |||
| light = new PointLight(color, intensity, distance) | |||
| light.position.fromArray(position, 0) | |||
| light.castShadow = true | |||
| break | |||
| case blenderLightTypes.sun: | |||
| light = new PointLight(color, intensity, distance) | |||
| light.position.fromArray(position, 0) | |||
| light.castShadow = true | |||
| light.shadow.mapSize.width = 1024 | |||
| light.shadow.mapSize.height = 1024 | |||
| light.shadow.camera.near = 0.01 | |||
| light.shadow.camera.far = 500 | |||
| break | |||
| default: | |||
| console.warn('Unsupported light type', ldata.type) | |||
| } | |||
| return light | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| import {MeshPhysicalMaterial} from 'threepipe' | |||
| // todo see blender gltf exporter and convert to js. structure is the same | |||
| export function createMaterial(mat: any) { | |||
| const material = new MeshPhysicalMaterial() | |||
| material.color.setRGB(mat.r, mat.g, mat.b) | |||
| material.roughness = mat.roughness !== undefined ? mat.roughness : 0.4 | |||
| material.metalness = mat.metallic !== undefined ? mat.metallic : 0.0 | |||
| material.opacity = mat.alpha !== undefined ? mat.alpha : 0.0 | |||
| material.transparent = material.opacity < 1.0 | |||
| return material | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| import {Mesh} from 'threepipe' | |||
| import {createBufferGeometry} from './geometry' | |||
| import {createMaterial} from './material' | |||
| export function createMesh(object: any) { | |||
| if (!object.data) return null | |||
| const geometry = createBufferGeometry(object.data, [0, 0, 0]) | |||
| const mat = object.data.mat[0] | |||
| const material = mat ? createMaterial(mat) : undefined | |||
| const mesh = new Mesh(geometry, material) | |||
| mesh.castShadow = true | |||
| mesh.receiveShadow = true | |||
| mesh.rotateZ(object.rot[2]) | |||
| mesh.rotateY(object.rot[1]) | |||
| mesh.rotateX(object.rot[0]) | |||
| mesh.scale.fromArray(object.size, 0) | |||
| mesh.position.fromArray([object.loc[0], object.loc[2], -object.loc[1]], 0) | |||
| return mesh | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| { | |||
| "compilerOptions": { | |||
| "baseUrl": "./src", | |||
| "rootDir": "./src", | |||
| "allowJs": false, | |||
| "checkJs": false, | |||
| "skipLibCheck": true, | |||
| "allowSyntheticDefaultImports": true, | |||
| "experimentalDecorators": true, | |||
| "isolatedModules": true, | |||
| "module": "es2020", | |||
| "noImplicitAny": true, | |||
| "declaration": true, | |||
| "declarationMap": true, | |||
| "declarationDir": "dist", | |||
| "outDir": "dist", | |||
| "noImplicitThis": true, | |||
| "noUnusedLocals": true, | |||
| "noUnusedParameters": true, | |||
| "removeComments": false, | |||
| "preserveConstEnums": true, | |||
| "moduleResolution": "node", | |||
| "emitDecoratorMetadata": false, | |||
| "sourceMap": true, | |||
| "target": "ES2020", | |||
| "strictNullChecks": true, | |||
| "lib": [ | |||
| "es2020", | |||
| "esnext", | |||
| "dom" | |||
| ] | |||
| }, | |||
| "include": [ | |||
| "src/**/*" | |||
| ], | |||
| "exclude": [ | |||
| "node_modules", | |||
| "**/*.spec.ts", | |||
| "dist" | |||
| ] | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| { | |||
| "extends": [ | |||
| "../../typedoc.json" | |||
| ], | |||
| "entryPoints": [ | |||
| "src/index.ts" | |||
| ], | |||
| "name": "Threepipe Blend File Importer", | |||
| "readme": "none" | |||
| } | |||
| @@ -173,9 +173,9 @@ export {Box3} from 'three' | |||
| export {Box2} from 'three' | |||
| export {Line3} from 'three' | |||
| export {Euler} from 'three' | |||
| export {Vector4} from 'three' | |||
| export {Vector3} from 'three' | |||
| export {Vector2} from 'three' | |||
| export {Vector4, type Vector4Tuple} from 'three' | |||
| export {Vector3, type Vector3Tuple} from 'three' | |||
| export {Vector2, type Vector2Tuple} from 'three' | |||
| export {Quaternion} from 'three' | |||
| export {Color} from 'three' | |||
| export {ColorManagement} from 'three' | |||
| @@ -1 +1 @@ | |||
| export const VERSION = '0.0.13' | |||
| export const VERSION = '0.0.14' | |||