| ], | ], | ||||
| 'parserOptions': { | 'parserOptions': { | ||||
| 'ecmaVersion': 2018, | 'ecmaVersion': 2018, | ||||
| 'sourceType': 'module', // Allows for the use of imports | |||||
| }, | }, | ||||
| 'plugins': [ | 'plugins': [ | ||||
| 'html', | 'html', |
| [](https://opensource.org/license/apache-2-0/) | [](https://opensource.org/license/apache-2-0/) | ||||
| [](https://twitter.com/repalash) | [](https://twitter.com/repalash) | ||||
| ThreePipe is a 3D framework built on top of [three.js](https://threejs.org/) in TypeScript with a focus on quality rendering, modularity and extensibility. | |||||
| ThreePipe is a 3D framework built on top of [three.js](https://threejs.org/) in TypeScript with a focus on rendering quality, modularity and extensibility. | |||||
| Key features include: | Key features include: | ||||
| - Simple, intuitive API for creating 3D model viewers/configurators/editors on web pages, with many built-in presets for common workflows and use-cases. | - Simple, intuitive API for creating 3D model viewers/configurators/editors on web pages, with many built-in presets for common workflows and use-cases. |
| import {setupCodePreview} from 'https://cdn.jsdelivr.net/gh/repalash/example-code-previewer/dist/index.js'; | import {setupCodePreview} from 'https://cdn.jsdelivr.net/gh/repalash/example-code-previewer/dist/index.js'; | ||||
| const rootPath = 'https://threepipe.org/' | |||||
| const examplePath = 'examples/' | |||||
| const codePath = 'https://github.com/repalash/threepipe/tree/master/' | |||||
| const exampleScript = document.getElementById('example-script') | const exampleScript = document.getElementById('example-script') | ||||
| const scripts = exampleScript && exampleScript.dataset.scripts ? exampleScript.dataset.scripts.split(';') : [] | const scripts = exampleScript && exampleScript.dataset.scripts ? exampleScript.dataset.scripts.split(';') : [] | ||||
| if(exampleScript.textContent) scripts.push(exampleScript) | if(exampleScript.textContent) scripts.push(exampleScript) | ||||
| const css = exampleStyle ? exampleStyle.textContent : '' | const css = exampleStyle ? exampleStyle.textContent : '' | ||||
| const importMap = document.querySelector('script[type="importmap"]') | const importMap = document.querySelector('script[type="importmap"]') | ||||
| const imports = JSON.parse(importMap.textContent||'{}').imports||{} | const imports = JSON.parse(importMap.textContent||'{}').imports||{} | ||||
| imports['threepipe'] = 'https://threepipe.org/dist/index.mjs' | |||||
| Object.entries(imports).forEach(([k,v])=>imports[k] = v.replace(/^\.\/\.\.\/\.\.\//, rootPath)) // ./../../ -> rootPath | |||||
| function replaceImports(code) { | function replaceImports(code) { | ||||
| for (const [name, link] of Object.entries(imports)) code = code.replaceAll(` from '${name}'`, ` from '${link}'`) | for (const [name, link] of Object.entries(imports)) code = code.replaceAll(` from '${name}'`, ` from '${link}'`) | ||||
| return code | return code | ||||
| .replaceAll(` from '../../`, ` from '${rootPath}`) | |||||
| .replaceAll(` from '../`, ` from '${rootPath+examplePath}`) | |||||
| } | } | ||||
| setupCodePreview( | setupCodePreview( | ||||
| document.getElementById('canvas-container') || document.querySelector('.code-preview-container'), | document.getElementById('canvas-container') || document.querySelector('.code-preview-container'), | ||||
| scripts, | scripts, | ||||
| scripts.map(s=>s.textContent ? 'js' : s.split('.').pop()), // title | scripts.map(s=>s.textContent ? 'js' : s.split('.').pop()), // title | ||||
| scripts.map(s=>(typeof s === 'string' && s.endsWith('.js')) ? s : 'https://github.com/repalash/threepipe/tree/master/examples/'+ window.location.pathname.split('/examples/').pop().replace('index.html', '')+(s.textContent ? 'index.html' : s)), // todo: github link | |||||
| (c) => '// Threepipe example: ' + window.location.href + '\n' + replaceImports(c).replaceAll(` from '../`, ` from 'https://threepipe.org/examples/`), | |||||
| scripts.map(s=>(typeof s === 'string' && s.endsWith('.js')) ? s : (codePath+examplePath+window.location.pathname.split('/examples/').pop().replace('index.html', '')+(s.textContent ? 'index.html' : s))), // todo: github link | |||||
| (c) => '// Threepipe example: ' + window.location.href + '\n' + replaceImports(c), | |||||
| { | { | ||||
| title: 'ThreePipe: ' + document.title, | title: 'ThreePipe: ' + document.title, | ||||
| css, | css, |
| { | { | ||||
| "name": "threepipe", | "name": "threepipe", | ||||
| "version": "0.0.8-dev.1", | |||||
| "version": "0.0.8", | |||||
| "lockfileVersion": 2, | "lockfileVersion": 2, | ||||
| "requires": true, | "requires": true, | ||||
| "packages": { | "packages": { | ||||
| "": { | "": { | ||||
| "name": "threepipe", | "name": "threepipe", | ||||
| "version": "0.0.8-dev.1", | |||||
| "version": "0.0.8", | |||||
| "license": "Apache-2.0", | "license": "Apache-2.0", | ||||
| "dependencies": { | "dependencies": { | ||||
| "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1011/package.tgz", | "@types/three": "https://github.com/repalash/three-ts-types/releases/download/v0.152.1011/package.tgz", | ||||
| "@rollup/plugin-node-resolve": "^15.0.2", | "@rollup/plugin-node-resolve": "^15.0.2", | ||||
| "@rollup/plugin-terser": "^0.4.3", | "@rollup/plugin-terser": "^0.4.3", | ||||
| "@rollup/plugin-typescript": "^11.1.1", | "@rollup/plugin-typescript": "^11.1.1", | ||||
| "@tweakpane/core": "1.1.8", | |||||
| "@types/stats.js": "^0.17.0", | "@types/stats.js": "^0.17.0", | ||||
| "@typescript-eslint/eslint-plugin": "^5.59.7", | "@typescript-eslint/eslint-plugin": "^5.59.7", | ||||
| "@typescript-eslint/parser": "^5.59.5", | "@typescript-eslint/parser": "^5.59.5", | ||||
| "stats.js": "^0.17.0", | "stats.js": "^0.17.0", | ||||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | ||||
| "tslib": "^2.5.0", | "tslib": "^2.5.0", | ||||
| "tweakpane": "^3.1.9", | |||||
| "typedoc": "^0.24.7", | "typedoc": "^0.24.7", | ||||
| "typescript": "^5.0.4", | "typescript": "^5.0.4", | ||||
| "typescript-plugin-css-modules": "^5.0.1", | "typescript-plugin-css-modules": "^5.0.1", | ||||
| "node": ">=10.13.0" | "node": ">=10.13.0" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/@tweakpane/core": { | |||||
| "version": "1.1.8", | |||||
| "resolved": "https://registry.npmjs.org/@tweakpane/core/-/core-1.1.8.tgz", | |||||
| "integrity": "sha512-psvBf6Cbm3YSZOTmDFWkcGzHYMnw7gVZM3jw+TfbzErIC+sMXPQb85h4ayW04w2u7AGg8jD0gHXSCg5wd+rafg==", | |||||
| "dev": true | |||||
| }, | |||||
| "node_modules/@tweenjs/tween.js": { | "node_modules/@tweenjs/tween.js": { | ||||
| "version": "18.6.4", | "version": "18.6.4", | ||||
| "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz", | "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz", | ||||
| "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", | ||||
| "dev": true | "dev": true | ||||
| }, | }, | ||||
| "node_modules/tweakpane": { | |||||
| "version": "3.1.9", | |||||
| "resolved": "https://registry.npmjs.org/tweakpane/-/tweakpane-3.1.9.tgz", | |||||
| "integrity": "sha512-vMzh3X8uHo9HDY+9S9V0bc+UBScs8VYmMeOEW+BvynczV0aiLHweYv4eKpyoqpcRrQlkLhUsx8Dvv/1/qiCESg==", | |||||
| "dev": true, | |||||
| "funding": { | |||||
| "url": "https://github.com/sponsors/cocopon" | |||||
| } | |||||
| }, | |||||
| "node_modules/type-check": { | "node_modules/type-check": { | ||||
| "version": "0.4.0", | "version": "0.4.0", | ||||
| "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", | ||||
| "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", | "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", | ||||
| "dev": true | "dev": true | ||||
| }, | }, | ||||
| "@tweakpane/core": { | |||||
| "version": "1.1.8", | |||||
| "resolved": "https://registry.npmjs.org/@tweakpane/core/-/core-1.1.8.tgz", | |||||
| "integrity": "sha512-psvBf6Cbm3YSZOTmDFWkcGzHYMnw7gVZM3jw+TfbzErIC+sMXPQb85h4ayW04w2u7AGg8jD0gHXSCg5wd+rafg==", | |||||
| "dev": true | |||||
| }, | |||||
| "@tweenjs/tween.js": { | "@tweenjs/tween.js": { | ||||
| "version": "18.6.4", | "version": "18.6.4", | ||||
| "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz", | "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz", | ||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| "tweakpane": { | |||||
| "version": "3.1.9", | |||||
| "resolved": "https://registry.npmjs.org/tweakpane/-/tweakpane-3.1.9.tgz", | |||||
| "integrity": "sha512-vMzh3X8uHo9HDY+9S9V0bc+UBScs8VYmMeOEW+BvynczV0aiLHweYv4eKpyoqpcRrQlkLhUsx8Dvv/1/qiCESg==", | |||||
| "dev": true | |||||
| }, | |||||
| "type-check": { | "type-check": { | ||||
| "version": "0.4.0", | "version": "0.4.0", | ||||
| "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", |
| { | { | ||||
| "name": "threepipe", | "name": "threepipe", | ||||
| "version": "0.0.8-dev.1", | |||||
| "version": "0.0.8", | |||||
| "description": "A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.", | "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", | "main": "src/index.ts", | ||||
| "module": "dist/index.mjs", | "module": "dist/index.mjs", | ||||
| "serve-docs": "ws -d docs -p 8080", | "serve-docs": "ws -d docs -p 8080", | ||||
| "serve": "ws -d . -p 9229", | "serve": "ws -d . -p 9229", | ||||
| "docs": "npx typedoc && markdown-to-html", | "docs": "npx typedoc && markdown-to-html", | ||||
| "prepare": "npm run build && npm run build-examples" | |||||
| "build-plugins": "node scripts/build-plugins.mjs", | |||||
| "prepare": "npm run build && npm run build-plugins && npm run build-examples" | |||||
| }, | }, | ||||
| "clean-package": { | "clean-package": { | ||||
| "remove": [ | "remove": [ | ||||
| "stats.js": "^0.17.0", | "stats.js": "^0.17.0", | ||||
| "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | "three": "https://github.com/repalash/three.js-modded/releases/download/v0.152.2012/package.tgz", | ||||
| "tslib": "^2.5.0", | "tslib": "^2.5.0", | ||||
| "tweakpane": "^3.1.9", | |||||
| "@tweakpane/core": "1.1.8", | |||||
| "typedoc": "^0.24.7", | "typedoc": "^0.24.7", | ||||
| "typescript": "^5.0.4", | "typescript": "^5.0.4", | ||||
| "typescript-plugin-css-modules": "^5.0.1", | "typescript-plugin-css-modules": "^5.0.1", |
| ], | ], | ||||
| "replace": { | "replace": { | ||||
| "dependencies": { | "dependencies": { | ||||
| "threepipe": "^0.0.8-dev.1", | |||||
| "threepipe": "^0.0.8", | |||||
| "@threepipe/plugin-tweakpane": "^0.1.0" | "@threepipe/plugin-tweakpane": "^0.1.0" | ||||
| } | } | ||||
| } | } | ||||
| "src" | "src" | ||||
| ], | ], | ||||
| "scripts": { | "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", | "prepare": "npm run build", | ||||
| "build": "rimraf dist && NODE_ENV=production rollup -c", | "build": "rimraf dist && NODE_ENV=production rollup -c", | ||||
| "dev": "rollup -c -w" | "dev": "rollup -c -w" |
| "dependencies": { | "dependencies": { | ||||
| "threepipe": "file:./../../src/" | "threepipe": "file:./../../src/" | ||||
| }, | }, | ||||
| "clean-package": { | |||||
| "remove": [ | |||||
| "clean-package", | |||||
| "scripts", | |||||
| "devDependencies", | |||||
| "//", | |||||
| "markdown-to-html" | |||||
| ], | |||||
| "replace": { | |||||
| "dependencies": { | |||||
| "threepipe": "^0.0.8-dev.1" | |||||
| } | |||||
| } | |||||
| }, | |||||
| "type": "module", | "type": "module", | ||||
| "main": "dist/index.js", | "main": "dist/index.js", | ||||
| "module": "dist/index.mjs", | "module": "dist/index.mjs", | ||||
| "types": "dist/index.d.ts", | "types": "dist/index.d.ts", | ||||
| "source": "src/index.ts", | |||||
| "files": [ | "files": [ | ||||
| "dist", | "dist", | ||||
| "src" | "src" | ||||
| ], | ], | ||||
| "scripts": { | "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", | "prepare": "npm run build", | ||||
| "build": "rimraf dist && NODE_ENV=production rollup -c", | "build": "rimraf dist && NODE_ENV=production rollup -c", | ||||
| "dev": "rollup -c -w" | "dev": "rollup -c -w" | ||||
| }, | }, | ||||
| "//": { | |||||
| "dependencies": { | |||||
| "tweakpane-image-plugin": "https://github.com/repalash/tweakpane-image-plugin/releases/download/v1.1.404/package.tgz", | |||||
| "uiconfig-tweakpane": "^0.0.4" | |||||
| }, | |||||
| "local_dependencies": { | |||||
| "tweakpane-image-plugin": "file:./../tweakpane-image-plugin", | |||||
| "uiconfig-tweakpane": "^file:./../uiconfig-tweakpane" | |||||
| } | |||||
| }, | |||||
| "author": "repalash <palash@shaders.app>", | "author": "repalash <palash@shaders.app>", | ||||
| "license": "Apache-2.0", | "license": "Apache-2.0", | ||||
| "keywords": [ | "keywords": [ | ||||
| "repository": { | "repository": { | ||||
| "type": "git", | "type": "git", | ||||
| "url": "git://github.com/repalash/threepipe.git" | "url": "git://github.com/repalash/threepipe.git" | ||||
| }, | |||||
| "clean-package": { | |||||
| "remove": [ | |||||
| "clean-package", | |||||
| "scripts", | |||||
| "devDependencies", | |||||
| "//", | |||||
| "markdown-to-html" | |||||
| ], | |||||
| "replace": { | |||||
| "dependencies": { | |||||
| "threepipe": "^0.0.8" | |||||
| } | |||||
| } | |||||
| }, | |||||
| "//": { | |||||
| "dependencies": { | |||||
| "tweakpane-image-plugin": "https://github.com/repalash/tweakpane-image-plugin/releases/download/v1.1.404/package.tgz", | |||||
| "uiconfig-tweakpane": "^0.0.4" | |||||
| }, | |||||
| "local_dependencies": { | |||||
| "tweakpane-image-plugin": "file:./../tweakpane-image-plugin", | |||||
| "uiconfig-tweakpane": "^file:./../uiconfig-tweakpane" | |||||
| } | |||||
| } | } | ||||
| } | } |
| import {FolderApi} from 'tweakpane' | |||||
| import * as TweakpaneImagePlugin from 'tweakpane-image-plugin' | import * as TweakpaneImagePlugin from 'tweakpane-image-plugin' | ||||
| import {UiConfigRendererTweakpane} from 'uiconfig-tweakpane' | import {UiConfigRendererTweakpane} from 'uiconfig-tweakpane' | ||||
| import { | import { | ||||
| await p.fromJSON?.((p as any)._defaultState) | await p.fromJSON?.((p as any)._defaultState) | ||||
| ui.uiRefresh?.(true, 'postFrame') | ui.uiRefresh?.(true, 'postFrame') | ||||
| } | } | ||||
| const topBtn = (ui.uiRef as FolderApi).controller_.view.element | |||||
| const topBtn = (ui.uiRef as any).controller_.view.element | |||||
| const opBtn = createDiv({ | const opBtn = createDiv({ | ||||
| innerHTML: '⋮', | innerHTML: '⋮', | ||||
| classList: ['pluginOptionsButton'], | classList: ['pluginOptionsButton'], |
| upgradeTexture, | upgradeTexture, | ||||
| WebGLRenderTarget, | WebGLRenderTarget, | ||||
| } from 'threepipe' | } from 'threepipe' | ||||
| import type {FolderApi} from 'tweakpane' | |||||
| import type {UiObjectConfig} from 'uiconfig.js' | import type {UiObjectConfig} from 'uiconfig.js' | ||||
| import {TweakpaneUiPlugin} from './TweakpaneUiPlugin' | import {TweakpaneUiPlugin} from './TweakpaneUiPlugin' | ||||
| } | } | ||||
| } | } | ||||
| export const tpImageInputGenerator = (viewer: ThreeViewer) => (parent: FolderApi, config: UiObjectConfig, renderer: TweakpaneUiPlugin, params?: any) => { | |||||
| export const tpImageInputGenerator = (viewer: ThreeViewer) => (parent: any /* FolderApi */, config: UiObjectConfig, renderer: TweakpaneUiPlugin, params?: any) => { | |||||
| // if (config.value !== undefined) throw 'Not supported yet' | // if (config.value !== undefined) throw 'Not supported yet' | ||||
| if (!config.__proxy) { | if (!config.__proxy) { |
| import {execEachPlugin} from "./utils.mjs"; | |||||
| // Each plugin should have "prepare" that will also build the plugin | |||||
| execEachPlugin('npm install') // install dependencies |
| import path from "node:path"; | |||||
| import fs from "node:fs"; | |||||
| import {execSync} from "node:child_process"; | |||||
| export function loopPluginDirs(callback){ | |||||
| const __dirname = path.dirname(new URL(import.meta.url).pathname); | |||||
| const pluginsDir = path.join(__dirname, '../plugins') | |||||
| const pluginFolders = fs.readdirSync(pluginsDir) | |||||
| for (const pluginFolder of pluginFolders) { | |||||
| const pluginDir = path.join(pluginsDir, pluginFolder) | |||||
| const packageJsonPath = path.join(pluginDir, 'package.json') | |||||
| if (!fs.existsSync(packageJsonPath)) continue; | |||||
| console.log(`NPM install ${pluginFolder}...`) | |||||
| callback(pluginDir) | |||||
| } | |||||
| } | |||||
| export function execEachPlugin(command){ | |||||
| loopPluginDirs((pluginDir) => { | |||||
| execSync(command, {cwd: pluginDir, stdio: 'inherit'}) | |||||
| }) | |||||
| } |