├── .gitignore ├── .jshintignore ├── .npmignore ├── LICENSE ├── README.md ├── build └── glsl2js.js ├── dist ├── clay-viewer.js └── clay-viewer.min.js ├── editor ├── asset │ └── texture │ │ ├── Barce_Rooftop_C.hdr │ │ ├── Factory_Catwalk.hdr │ │ ├── Grand_Canyon_C.hdr │ │ ├── Hall.hdr │ │ ├── Ice_Lake.hdr │ │ ├── Old_Industrial_Hall.hdr │ │ └── pisa.hdr ├── bundle.js ├── css │ ├── controlKit.css │ ├── iconfont │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ └── iconfont.woff │ └── main.css ├── electron.js ├── icon.icns ├── icon.ico ├── img │ ├── chessboard.jpg │ └── loading.gif ├── index-electron.html ├── index.html ├── lib │ ├── FileAPI.js │ ├── FileSaver.js │ ├── browserfs.min.js │ ├── browserfs.min.js.map │ ├── controlKit.js │ ├── filer.min.js │ ├── idb.filesystem.js │ ├── ion.rangeSlider.js │ ├── ion.rangeSlider │ │ ├── css │ │ │ ├── ion.rangeSlider.css │ │ │ ├── ion.rangeSlider.skinFlat.css │ │ │ ├── ion.rangeSlider.skinHTML5.css │ │ │ ├── ion.rangeSlider.skinModern.css │ │ │ ├── ion.rangeSlider.skinNice.css │ │ │ ├── ion.rangeSlider.skinSimple.css │ │ │ └── normalize.css │ │ └── img │ │ │ ├── sprite-skin-flat.png │ │ │ ├── sprite-skin-modern.png │ │ │ ├── sprite-skin-nice.png │ │ │ └── sprite-skin-simple.png │ ├── jquery.min.js │ ├── jszip.min.js │ ├── pace.min.js │ └── sweetalert.min.js ├── package.json ├── src │ ├── browser │ │ ├── convert.js │ │ ├── download.js │ │ └── openURL.js │ ├── debug │ │ ├── BoundingBoxGizmo.js │ │ ├── Lines3DGeometry.js │ │ ├── edge.glsl │ │ ├── edge.glsl.js │ │ ├── lines3d.glsl │ │ ├── lines3d.glsl.js │ │ └── renderOutline.js │ ├── electron │ │ ├── convert.js │ │ ├── download.js │ │ └── openURL.js │ ├── env.js │ ├── getDefaultMaterialConfig.js │ ├── getDefaultSceneConfig.js │ ├── glTFHelper.js │ ├── main.js │ ├── project.js │ ├── timeline.js │ └── ui │ │ └── Texture.js └── webpack.config.js ├── examples ├── asset │ ├── DamagedHelmet │ │ ├── DamagedHelmet.bin │ │ ├── DamagedHelmet.gltf │ │ ├── Default_AO.jpg │ │ ├── Default_albedo.jpg │ │ ├── Default_emissive.jpg │ │ ├── Default_metalRoughness.jpg │ │ └── Default_normal.jpg │ └── texture │ │ └── pisa.hdr ├── bg.jpg ├── lib │ └── dat.gui.js └── view.html ├── index.js ├── jsconfig.json ├── package.json ├── rollup.config.js ├── screenshots ├── editor.jpg └── editor2.jpg └── src ├── GestureMgr.js ├── HotspotManager.js ├── Viewer.js ├── defaultSceneConfig.js ├── graphic ├── DOF.glsl ├── DOF.glsl.js ├── EdgePass.js ├── EffectCompositor.js ├── RenderMain.js ├── SSAO.glsl ├── SSAO.glsl.js ├── SSAOPass.js ├── SSR.glsl ├── SSR.glsl.js ├── SSRPass.js ├── SceneHelper.js ├── TemporalSuperSampling.js ├── composite.js ├── compositeMobile.js ├── edge.glsl ├── edge.glsl.js ├── ground.glsl ├── ground.glsl.js ├── halton.js ├── helper.js └── poissonKernel.js └── util └── getBoundingBoxWithSkinning.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | .project 4 | node_modules 5 | .idea/ 6 | .vscode/ 7 | npm-debug.log 8 | .jshintrc 9 | editor/electron 10 | editor/dist 11 | 12 | 13 | examples/baidu_asset 14 | examples/baidu_asset2 15 | examples/baidu_asset3 16 | examples/baidu_asset4 17 | examples/sample_asset 18 | examples/other 19 | examples/all.html 20 | examples/all2.html 21 | examples/all3.html 22 | examples/all4.html 23 | examples/test-view.html -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | dist/*.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /editor 2 | /examples 3 | /screenshots -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Baidu Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clay Viewer 2 | 3 | 3D model viewer with high quality rendering based on [ClayGL](https://github.com/pissang/claygl) and [glTF2.0/GLB](https://github.com/KhronosGroup/glTF) export. 4 | 5 | 6 | ## App 7 | 8 | [Download App](https://github.com/pissang/clay-viewer/releases/) on Windows and macOS with FBX/DAE/OBj import and glTF2.0/GLB export. Use it as a common model preview tool! 9 | 10 | ## Editor 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ## Loader 21 | 22 | ```js 23 | var viewer = new ClayViewer(document.getElementById('main'), { 24 | // Full config at 25 | // https://github.com/pissang/clay-viewer/blob/master/src/defaultSceneConfig.js 26 | devicePixelRatio: 1, 27 | // Enable shadow 28 | shadow: true, 29 | shadowQuality: 'high', 30 | // Environment panorama texture url. 31 | environment: 'env.jpg', 32 | mainLight: { 33 | intensity: 2.0 34 | }, 35 | ambientCubemapLight: { 36 | exposure: 1, 37 | diffuseIntensity: 0.2, 38 | texture: 'asset/texture/example1.jpg' 39 | }, 40 | postEffect: { 41 | // Enable post effect 42 | enable: true, 43 | bloom: { 44 | // Enable bloom 45 | enable: true 46 | }, 47 | screenSpaceAmbientOcculusion: { 48 | // Enable screen space ambient occulusion 49 | enable: true 50 | } 51 | } 52 | }); 53 | 54 | // Load a glTF model 55 | // Model will be fit in 10x10x10 automatically after load. 56 | // Return an eventful object. 57 | viewer.loadModel('asset/xiniu/xiniu_walk_as.gltf', { 58 | // Shading mode. 'standard'|'lambert' 59 | shader: 'standard' 60 | }) 61 | // Model loaded. not include textures. 62 | .on('loadmodel', function (modelStat) { 63 | // Set camera options. 64 | viewer.setCameraControl({ 65 | // Alpha is rotation from bottom to up. 66 | alpha: 10, 67 | // Beta is rotation from left to right. 68 | beta: 30, 69 | distance: 20, 70 | // Min distance of zoom. 71 | minDistance: 1, 72 | // Max distance of zoom. 73 | maxDistance: 100, 74 | 75 | // Center of target. 76 | center: [0, 0, 0], 77 | 78 | // If auto rotate. 79 | autoRotate: false, 80 | 81 | // Degree per second. 82 | autoRotateSpeed: 60, 83 | 84 | // Direction of autoRotate. cw or ccw when looking top down. 85 | autoRotateDirection: 'cw', 86 | 87 | // Start auto rotating after still for the given time 88 | autoRotateAfterStill: 30 89 | }); 90 | 91 | // Set main light options. 92 | viewer.setMainLight({ 93 | // Main light intensity 94 | intensity: 1, 95 | // Main light color string 96 | color: '#fff', 97 | // Alpha is rotation from bottom to up. 98 | alpha: 45, 99 | // Beta is rotation from left to right. 100 | beta: 45 101 | }); 102 | // Set ambient light options 103 | viewer.setAmbientLight({ 104 | // Ambient light intensity 105 | intensity: 0.8 106 | }); 107 | 108 | viewer.start(); 109 | 110 | // Load extra animation glTF 111 | viewer.loadAnimation('asset/xiniu/xiniu_stand_as.gltf') 112 | .on('success', function () { 113 | console.log('Changed animation') 114 | }); 115 | // Animation pause and start 116 | viewer.pauseAnimation(); 117 | viewer.resumeAnimation(); 118 | 119 | // Print model stat. 120 | console.log('Model loaded:'); 121 | console.log('三角面:', modelStat.triangleCount); 122 | console.log('顶点:', modelStat.vertexCount); 123 | console.log('场景节点:', modelStat.nodeCount); 124 | console.log('Mesh:', modelStat.meshCount); 125 | console.log('材质:', modelStat.materialCount); 126 | console.log('纹理:', modelStat.textureCount); 127 | }) 128 | .on('ready', function () { 129 | console.log('All loaded inlcuding textures.'); 130 | }) 131 | .on('error', function () { 132 | console.log('Model load error'); 133 | }); 134 | 135 | ``` 136 | 137 | [Here](https://github.com/pissang/clay-viewer/blob/master/src/defaultSceneConfig.js) is the full graphic configuration 138 | 139 | ## Converter 140 | 141 | ClayGL provide a python tool for converting FBX to glTF 2.0. 142 | 143 | https://github.com/pissang/claygl/blob/master/tools/fbx2gltf.py 144 | 145 | Needs [python3.3](https://www.python.org/download/releases/3.3.0/) and [FBX SDK 2018.1.1](http://usa.autodesk.com/adsk/servlet/pc/item?siteID=123112&id=26416130) 146 | 147 | ``` 148 | usage: fbx2gltf.py [-h] [-e EXCLUDE] [-t TIMERANGE] [-o OUTPUT] 149 | [-f FRAMERATE] [-p POSE] [-q] [-b] 150 | file 151 | 152 | FBX to glTF converter 153 | 154 | positional arguments: 155 | file 156 | 157 | optional arguments: 158 | -h, --help show this help message and exit 159 | -e EXCLUDE, --exclude EXCLUDE 160 | Data excluded. Can be: scene,animation 161 | -t TIMERANGE, --timerange TIMERANGE 162 | Export animation time, in format 163 | 'startSecond,endSecond' 164 | -o OUTPUT, --output OUTPUT 165 | Ouput glTF file path 166 | -f FRAMERATE, --framerate FRAMERATE 167 | Animation frame per second 168 | -p POSE, --pose POSE Start pose time 169 | -q, --quantize Quantize accessors with WEB3D_quantized_attributes 170 | extension 171 | -b, --binary Export glTF-binary 172 | --beautify Beautify json output. 173 | --noflipv If not flip v in texcoord. 174 | ``` 175 | 176 | 177 | ## Seperate scene and animation 178 | 179 | Export scene 180 | 181 | ```bash 182 | # exclude animation 183 | fbx2gltf2.py -e animation -p 0 xxx.fbx 184 | ``` 185 | 186 | Export animation 187 | 188 | ```bash 189 | # exclude scene, 0 to 20 second, 20 framerate. 190 | fbx2gltf2.py -e scene -t 0,20 -f 20 -o xxx_ani.gltf xxx.fbx 191 | ``` 192 | 193 | Load scene and animation asynchronously 194 | 195 | ```js 196 | viewer.loadModel('asset/xiniu/xiniu.gltf') 197 | // Model loaded. not include textures. 198 | .on('loadmodel', function (modelStat) { 199 | viewer.start(); 200 | // Load extra animation glTF 201 | viewer.loadAnimation('asset/xiniu/xiniu_ani.gltf'); 202 | }); 203 | ``` 204 | 205 | ## Build 206 | 207 | ```bash 208 | npm install 209 | # Build loader 210 | npm run build 211 | # Build editor 212 | webpack --config editor/webpack.config.js 213 | ``` 214 | -------------------------------------------------------------------------------- /build/glsl2js.js: -------------------------------------------------------------------------------- 1 | var glob = require('glob'); 2 | var fs = require('fs'); 3 | 4 | glob(__dirname + '/../{src,editor}/**/*.glsl', function (err, files) { 5 | files.forEach(function (filePath) { 6 | var glslCode = fs.readFileSync(filePath, 'utf-8'); 7 | // TODO Remove comment 8 | glslCode = glslCode.replace(/\/\/.*\n/g, ''); 9 | glslCode = glslCode.replace(/ +/g, ' '); 10 | 11 | // var dir = path.dirname(filePath); 12 | // var baseName = path.basename(filePath, '.essl'); 13 | fs.writeFileSync( 14 | filePath + '.js', 15 | 'export default ' + JSON.stringify(glslCode) + ';\n', 16 | 'utf-8' 17 | ); 18 | }); 19 | }); -------------------------------------------------------------------------------- /editor/asset/texture/Barce_Rooftop_C.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/asset/texture/Barce_Rooftop_C.hdr -------------------------------------------------------------------------------- /editor/asset/texture/Factory_Catwalk.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/asset/texture/Factory_Catwalk.hdr -------------------------------------------------------------------------------- /editor/asset/texture/Grand_Canyon_C.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/asset/texture/Grand_Canyon_C.hdr -------------------------------------------------------------------------------- /editor/asset/texture/Hall.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/asset/texture/Hall.hdr -------------------------------------------------------------------------------- /editor/asset/texture/Ice_Lake.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/asset/texture/Ice_Lake.hdr -------------------------------------------------------------------------------- /editor/asset/texture/Old_Industrial_Hall.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/asset/texture/Old_Industrial_Hall.hdr -------------------------------------------------------------------------------- /editor/asset/texture/pisa.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/asset/texture/pisa.hdr -------------------------------------------------------------------------------- /editor/css/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1508676521288'); /* IE9*/ 4 | src: url('iconfont.eot?t=1508676521288#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'), 6 | url('iconfont.ttf?t=1508676521288') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1508676521288#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-download:before { content: "\e66e"; } 19 | 20 | .icon-pause:before { content: "\e618"; } 21 | 22 | .icon-reset:before { content: "\e60b"; } 23 | 24 | .icon-resume:before { content: "\e6a5"; } 25 | 26 | -------------------------------------------------------------------------------- /editor/css/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/css/iconfont/iconfont.eot -------------------------------------------------------------------------------- /editor/css/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /editor/css/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/css/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /editor/css/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/css/iconfont/iconfont.woff -------------------------------------------------------------------------------- /editor/css/main.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | margin: 0; 4 | background: #000; 5 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 6 | overflow: hidden; 7 | user-select: none; 8 | } 9 | #main { 10 | position: absolute; 11 | left: 0px; 12 | right: 250px; 13 | top: 0; 14 | bottom: 0; 15 | } 16 | 17 | #viewport { 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | width: 100%; 22 | height: 100%; 23 | } 24 | 25 | #loading { 26 | top: 0; 27 | left: 0; 28 | right: 0; 29 | bottom: 0; 30 | position: absolute; 31 | text-align: center; 32 | z-index: 1000; 33 | background-color: rgba(0,0,0,0.5); 34 | } 35 | #loading-text { 36 | position: absolute; 37 | top: 50%; 38 | margin-top: 50px; 39 | left: 0; 40 | right: 0; 41 | text-transform: uppercase; 42 | font-size: 24px; 43 | color: #aaa; 44 | text-align: center; 45 | } 46 | 47 | #toolbar { 48 | position: absolute; 49 | right: 280px; 50 | top: 10px; 51 | border-radius: 3px; 52 | background-color: rgba(255,255,255,0.5); 53 | display: none; 54 | z-index: 10; 55 | /* box-shadow: 0px 0px 10px rgba(122,122,122,0.4); */ 56 | } 57 | 58 | #toolbar .iconfont { 59 | display: inline-block; 60 | padding: 10px; 61 | text-align: center; 62 | cursor: pointer; 63 | border-radius: 3px; 64 | } 65 | #toolbar .iconfont:hover { 66 | background-color: rgba(255,255,255,0.5); 67 | } 68 | 69 | 70 | #tip { 71 | position: absolute; 72 | width: 500px; 73 | left: 50%; 74 | top: 300px; 75 | margin-left: -250px; 76 | text-align: center; 77 | font-size: 20px; 78 | padding: 30px 10px; 79 | 80 | border-radius: 3px; 81 | background-color: rgba(255,255,255,0.5); 82 | z-index: 10; 83 | 84 | display: none; 85 | } 86 | 87 | #tip .hint { 88 | font-size: 14px; 89 | margin-top: 20px; 90 | } 91 | 92 | #tip ul { 93 | margin: 0; 94 | padding: 0; 95 | } 96 | #tip li { 97 | list-style: none; 98 | margin: 0; 99 | padding: 0; 100 | } 101 | 102 | #credits { 103 | position: absolute; 104 | width: 400px; 105 | bottom: 10px; 106 | left: 50%; 107 | margin-left: -200px; 108 | text-align: center; 109 | z-index: 1000; 110 | font-size: 12px; 111 | color: #555; 112 | } 113 | 114 | #credits .source { 115 | color: #000; 116 | font-size: 14px; 117 | margin-right: 14px; 118 | } 119 | #credits a { 120 | color: #111; 121 | } 122 | 123 | #timeline { 124 | position: absolute; 125 | width: 600px; 126 | right: 50px; 127 | bottom: 50px; 128 | height: 115px; 129 | display: none; 130 | border-radius: 5px; 131 | background-color: rgba(0,0,0,0.7); 132 | } 133 | 134 | #timeline-range-wrap { 135 | position: relative; 136 | height: 45px; 137 | margin-top: 10px; 138 | } 139 | 140 | #timeline-progress-wrap { 141 | position: relative; 142 | height: 50px; 143 | } 144 | 145 | #timeline-range { 146 | left: 45px; 147 | right: 10px; 148 | position: absolute; 149 | } 150 | 151 | #timeline-progress { 152 | left: 45px; 153 | right: 10px; 154 | position: absolute; 155 | } 156 | 157 | #timeline-pause-resume { 158 | position: absolute; 159 | left: 10px; 160 | color: #fff; 161 | font-size: 26px; 162 | bottom: 5px; 163 | cursor: pointer; 164 | } 165 | 166 | #timeline-progress .irs-bar { 167 | opacity: 0.3; 168 | } 169 | #timeline-progress .irs-min, 170 | #timeline-progress .irs-max { 171 | display: none; 172 | } 173 | #timeline-progress .irs-single { 174 | top: 42px; 175 | } 176 | #timeline-progress .irs-single::after { 177 | top: -6px; 178 | bottom: auto; 179 | border-top-color: transparent; 180 | border-bottom-color: #ed5565; 181 | } 182 | 183 | 184 | #controlKit .panel .group-list .group .sub-group-list .sub-group ul li.drag-hover { 185 | background: #585656; 186 | } 187 | #controlKit .panel .group-list .group .sub-group-list .sub-group ul li:after { 188 | display: block; 189 | visibility: hidden; 190 | content: ''; 191 | height: 0; 192 | clear: both; 193 | } 194 | #controlKit .panel .group-list .group .sub-group-list .sub-group { 195 | padding: 5px; 196 | } 197 | #controlKit .panel .group-list .group .sub-group-list .sub-group .wrap .label { 198 | width: 45%; 199 | } 200 | #controlKit .panel .group-list .group .sub-group-list .sub-group .wrap .wrap, 201 | #controlKit .panel .svg-wrap, #controlKit .panel .canvas-wrap, 202 | #controlKit .panel .wrap-slider { 203 | width: 55%; 204 | } 205 | 206 | .texture-wrap { 207 | width: 55%; 208 | float: right; 209 | /* cursor: pointer; */ 210 | overflow-x: hidden; 211 | padding-top: 3px; 212 | } 213 | .texture-wrap img { 214 | height: 25px; 215 | max-width: 25px; 216 | display: inline-block; 217 | vertical-align: top; 218 | } 219 | .texture-wrap .texture-upload { 220 | display: inline-block; 221 | width: 75px!important; 222 | line-height: 25px; 223 | text-align: left; 224 | text-transform: none!important; 225 | font-weight: normal!important; 226 | padding-left: 4px!important; 227 | font-size: 11px; 228 | text-overflow: ellipsis; 229 | overflow: hidden; 230 | direction: rtl; 231 | } 232 | .texture-wrap .texture-delete { 233 | display: inline-block; 234 | width: 15px!important; 235 | vertical-align: top; 236 | text-align: center; 237 | line-height: 25px; 238 | text-transform: none!important; 239 | } 240 | .texture-wrap .texture-delete::after { 241 | content: 'x'; 242 | } 243 | 244 | 245 | 246 | .pace { 247 | -webkit-pointer-events: none; 248 | pointer-events: none; 249 | -webkit-user-select: none; 250 | -moz-user-select: none; 251 | user-select: none; 252 | } 253 | 254 | .pace-inactive { 255 | display: none; 256 | } 257 | 258 | .pace .pace-progress { 259 | background: #29d; 260 | position: fixed; 261 | z-index: 2000; 262 | top: 0; 263 | right: 100%; 264 | width: 100%; 265 | height: 2px; 266 | } 267 | 268 | .pace .pace-progress-inner { 269 | display: block; 270 | position: absolute; 271 | right: 0px; 272 | width: 100px; 273 | height: 100%; 274 | box-shadow: 0 0 10px #29d, 0 0 5px #29d; 275 | opacity: 1.0; 276 | transform: rotate(3deg) translate(0px, -4px); 277 | } 278 | 279 | .pace .pace-activity, 280 | #background-progress .spinner { 281 | z-index: 2000; 282 | width: 14px; 283 | height: 14px; 284 | border: solid 2px transparent; 285 | border-top-color: #29d; 286 | border-left-color: #29d; 287 | border-radius: 10px; 288 | animation: pace-spinner 400ms linear infinite; 289 | } 290 | 291 | .pace .pace-activity { 292 | display: block; 293 | position: fixed; 294 | top: 15px; 295 | right: 15px; 296 | } 297 | 298 | #background-progress { 299 | display: none; 300 | position: absolute; 301 | right: 15px; 302 | bottom: 15px; 303 | color: #029d; 304 | font-size: 12px; 305 | } 306 | #background-progress div { 307 | display: inline-block; 308 | margin-right: 3px; 309 | } 310 | 311 | @-webkit-keyframes pace-spinner { 312 | 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 313 | 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } 314 | } 315 | @-moz-keyframes pace-spinner { 316 | 0% { -moz-transform: rotate(0deg); transform: rotate(0deg); } 317 | 100% { -moz-transform: rotate(360deg); transform: rotate(360deg); } 318 | } 319 | @-o-keyframes pace-spinner { 320 | 0% { -o-transform: rotate(0deg); transform: rotate(0deg); } 321 | 100% { -o-transform: rotate(360deg); transform: rotate(360deg); } 322 | } 323 | @-ms-keyframes pace-spinner { 324 | 0% { -ms-transform: rotate(0deg); transform: rotate(0deg); } 325 | 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); } 326 | } 327 | @keyframes pace-spinner { 328 | 0% { transform: rotate(0deg); transform: rotate(0deg); } 329 | 100% { transform: rotate(360deg); transform: rotate(360deg); } 330 | } 331 | 332 | 333 | .sk-folding-cube { 334 | top: 50%; 335 | left: 50%; 336 | margin-left: -20px; 337 | margin-top: -20px; 338 | width: 40px; 339 | height: 40px; 340 | position: absolute; 341 | -webkit-transform: rotateZ(45deg); 342 | transform: rotateZ(45deg); } 343 | .sk-folding-cube .sk-cube { 344 | float: left; 345 | width: 50%; 346 | height: 50%; 347 | position: relative; 348 | -webkit-transform: scale(1.1); 349 | -ms-transform: scale(1.1); 350 | transform: scale(1.1); } 351 | .sk-folding-cube .sk-cube:before { 352 | content: ''; 353 | position: absolute; 354 | top: 0; 355 | left: 0; 356 | width: 100%; 357 | height: 100%; 358 | background-color: #fff; 359 | -webkit-animation: sk-foldCubeAngle 2.4s infinite linear both; 360 | animation: sk-foldCubeAngle 2.4s infinite linear both; 361 | -webkit-transform-origin: 100% 100%; 362 | -ms-transform-origin: 100% 100%; 363 | transform-origin: 100% 100%; } 364 | .sk-folding-cube .sk-cube2 { 365 | -webkit-transform: scale(1.1) rotateZ(90deg); 366 | transform: scale(1.1) rotateZ(90deg); } 367 | .sk-folding-cube .sk-cube3 { 368 | -webkit-transform: scale(1.1) rotateZ(180deg); 369 | transform: scale(1.1) rotateZ(180deg); } 370 | .sk-folding-cube .sk-cube4 { 371 | -webkit-transform: scale(1.1) rotateZ(270deg); 372 | transform: scale(1.1) rotateZ(270deg); } 373 | .sk-folding-cube .sk-cube2:before { 374 | -webkit-animation-delay: 0.3s; 375 | animation-delay: 0.3s; } 376 | .sk-folding-cube .sk-cube3:before { 377 | -webkit-animation-delay: 0.6s; 378 | animation-delay: 0.6s; } 379 | .sk-folding-cube .sk-cube4:before { 380 | -webkit-animation-delay: 0.9s; 381 | animation-delay: 0.9s; } 382 | @-webkit-keyframes sk-foldCubeAngle { 383 | 0%, 10% { 384 | -webkit-transform: perspective(140px) rotateX(-180deg); 385 | transform: perspective(140px) rotateX(-180deg); 386 | opacity: 0; } 387 | 25%, 75% { 388 | -webkit-transform: perspective(140px) rotateX(0deg); 389 | transform: perspective(140px) rotateX(0deg); 390 | opacity: 1; } 391 | 90%, 100% { 392 | -webkit-transform: perspective(140px) rotateY(180deg); 393 | transform: perspective(140px) rotateY(180deg); 394 | opacity: 0; } } 395 | @keyframes sk-foldCubeAngle { 396 | 0%, 10% { 397 | -webkit-transform: perspective(140px) rotateX(-180deg); 398 | transform: perspective(140px) rotateX(-180deg); 399 | opacity: 0; } 400 | 25%, 75% { 401 | -webkit-transform: perspective(140px) rotateX(0deg); 402 | transform: perspective(140px) rotateX(0deg); 403 | opacity: 1; } 404 | 90%, 100% { 405 | -webkit-transform: perspective(140px) rotateY(180deg); 406 | transform: perspective(140px) rotateY(180deg); 407 | opacity: 0; } } 408 | 409 | -------------------------------------------------------------------------------- /editor/electron.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | 3 | const app = electron.app; 4 | const BrowserWindow = electron.BrowserWindow; 5 | 6 | const path = require('path'); 7 | const url = require('url'); 8 | 9 | let mainWindow; 10 | 11 | function createWindow() { 12 | mainWindow = new BrowserWindow({ 13 | width: 1200, 14 | height: 700 15 | }); 16 | 17 | mainWindow.loadURL(url.format({ 18 | pathname: path.join(__dirname, 'index-electron.html'), 19 | protocol: 'file:', 20 | slashes: true 21 | })); 22 | 23 | mainWindow.on('closed', () => { 24 | mainWindow = null; 25 | }); 26 | } 27 | 28 | app.on('ready', createWindow); 29 | // Quit when all windows are closed. 30 | app.on('window-all-closed', function () { 31 | // On OS X it is common for applications and their menu bar 32 | // to stay active until the user quits explicitly with Cmd + Q 33 | // if (process.platform !== 'darwin') { 34 | app.quit(); 35 | // } 36 | }) 37 | 38 | app.on('activate', function () { 39 | // On OS X it's common to re-create a window in the app when the 40 | // dock icon is clicked and there are no other windows open. 41 | if (mainWindow === null) { 42 | createWindow(); 43 | } 44 | }); -------------------------------------------------------------------------------- /editor/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/icon.icns -------------------------------------------------------------------------------- /editor/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/icon.ico -------------------------------------------------------------------------------- /editor/img/chessboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/img/chessboard.jpg -------------------------------------------------------------------------------- /editor/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pissang/clay-viewer/86456edc4b28494e5facbe75fc4f009fe8cf3f3e/editor/img/loading.gif -------------------------------------------------------------------------------- /editor/index-electron.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Clay Viewer 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | Drag model and texture files or folder on the viewport. 18 |
19 | Supported format: FBX, DAE, OBJ, glTF2.0

20 | Try glTF2.0 Models from: 21 | 26 |
27 |
28 |
29 | Source Code 30 | Built on top of 31 | ClayGL, 32 | glTF2.0, 33 | ControlKit 34 |
35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
LOADING
61 |
62 |
63 | 64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 75 | 76 | 79 | 80 | -------------------------------------------------------------------------------- /editor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Clay Viewer 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | Drag glTF2.0 and texture files or folder on the viewport. 18 |
19 | Get glTF2.0 models from 20 | 28 |
29 |
30 |
31 | Download app to support more model formats 32 |
33 |
34 |
35 | Source Code 36 | Built on top of 37 | ClayGL, 38 | glTF2.0, 39 | ControlKit 40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 |
48 |
49 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
LOADING
67 |
68 |
69 | 70 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /editor/lib/FileSaver.js: -------------------------------------------------------------------------------- 1 | /* FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 1.3.2 4 | * 2016-06-16 18:25:19 5 | * 6 | * By Eli Grey, http://eligrey.com 7 | * License: MIT 8 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 9 | */ 10 | 11 | /*global self */ 12 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 13 | 14 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 15 | 16 | var saveAs = saveAs || (function(view) { 17 | "use strict"; 18 | // IE <10 is explicitly unsupported 19 | if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 20 | return; 21 | } 22 | var 23 | doc = view.document 24 | // only get URL when necessary in case Blob.js hasn't overridden it yet 25 | , get_URL = function() { 26 | return view.URL || view.webkitURL || view; 27 | } 28 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 29 | , can_use_save_link = "download" in save_link 30 | , click = function(node) { 31 | var event = new MouseEvent("click"); 32 | node.dispatchEvent(event); 33 | } 34 | , is_safari = /constructor/i.test(view.HTMLElement) || view.safari 35 | , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) 36 | , throw_outside = function(ex) { 37 | (view.setImmediate || view.setTimeout)(function() { 38 | throw ex; 39 | }, 0); 40 | } 41 | , force_saveable_type = "application/octet-stream" 42 | // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to 43 | , arbitrary_revoke_timeout = 1000 * 40 // in ms 44 | , revoke = function(file) { 45 | var revoker = function() { 46 | if (typeof file === "string") { // file is an object URL 47 | get_URL().revokeObjectURL(file); 48 | } else { // file is a File 49 | file.remove(); 50 | } 51 | }; 52 | setTimeout(revoker, arbitrary_revoke_timeout); 53 | } 54 | , dispatch = function(filesaver, event_types, event) { 55 | event_types = [].concat(event_types); 56 | var i = event_types.length; 57 | while (i--) { 58 | var listener = filesaver["on" + event_types[i]]; 59 | if (typeof listener === "function") { 60 | try { 61 | listener.call(filesaver, event || filesaver); 62 | } catch (ex) { 63 | throw_outside(ex); 64 | } 65 | } 66 | } 67 | } 68 | , auto_bom = function(blob) { 69 | // prepend BOM for UTF-8 XML and text/* types (including HTML) 70 | // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 71 | if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 72 | return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); 73 | } 74 | return blob; 75 | } 76 | , FileSaver = function(blob, name, no_auto_bom) { 77 | if (!no_auto_bom) { 78 | blob = auto_bom(blob); 79 | } 80 | // First try a.download, then web filesystem, then object URLs 81 | var 82 | filesaver = this 83 | , type = blob.type 84 | , force = type === force_saveable_type 85 | , object_url 86 | , dispatch_all = function() { 87 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 88 | } 89 | // on any filesys errors revert to saving with object URLs 90 | , fs_error = function() { 91 | if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { 92 | // Safari doesn't allow downloading of blob urls 93 | var reader = new FileReader(); 94 | reader.onloadend = function() { 95 | var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); 96 | var popup = view.open(url, '_blank'); 97 | if(!popup) view.location.href = url; 98 | url=undefined; // release reference before dispatching 99 | filesaver.readyState = filesaver.DONE; 100 | dispatch_all(); 101 | }; 102 | reader.readAsDataURL(blob); 103 | filesaver.readyState = filesaver.INIT; 104 | return; 105 | } 106 | // don't create more object URLs than needed 107 | if (!object_url) { 108 | object_url = get_URL().createObjectURL(blob); 109 | } 110 | if (force) { 111 | view.location.href = object_url; 112 | } else { 113 | var opened = view.open(object_url, "_blank"); 114 | if (!opened) { 115 | // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html 116 | view.location.href = object_url; 117 | } 118 | } 119 | filesaver.readyState = filesaver.DONE; 120 | dispatch_all(); 121 | revoke(object_url); 122 | } 123 | ; 124 | filesaver.readyState = filesaver.INIT; 125 | 126 | if (can_use_save_link) { 127 | object_url = get_URL().createObjectURL(blob); 128 | setTimeout(function() { 129 | save_link.href = object_url; 130 | save_link.download = name; 131 | click(save_link); 132 | dispatch_all(); 133 | revoke(object_url); 134 | filesaver.readyState = filesaver.DONE; 135 | }); 136 | return; 137 | } 138 | 139 | fs_error(); 140 | } 141 | , FS_proto = FileSaver.prototype 142 | , saveAs = function(blob, name, no_auto_bom) { 143 | return new FileSaver(blob, name || blob.name || "download", no_auto_bom); 144 | } 145 | ; 146 | // IE 10+ (native saveAs) 147 | if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 148 | return function(blob, name, no_auto_bom) { 149 | name = name || blob.name || "download"; 150 | 151 | if (!no_auto_bom) { 152 | blob = auto_bom(blob); 153 | } 154 | return navigator.msSaveOrOpenBlob(blob, name); 155 | }; 156 | } 157 | 158 | FS_proto.abort = function(){}; 159 | FS_proto.readyState = FS_proto.INIT = 0; 160 | FS_proto.WRITING = 1; 161 | FS_proto.DONE = 2; 162 | 163 | FS_proto.error = 164 | FS_proto.onwritestart = 165 | FS_proto.onprogress = 166 | FS_proto.onwrite = 167 | FS_proto.onabort = 168 | FS_proto.onerror = 169 | FS_proto.onwriteend = 170 | null; 171 | 172 | return saveAs; 173 | }( 174 | typeof self !== "undefined" && self 175 | || typeof window !== "undefined" && window 176 | || this.content 177 | )); 178 | // `self` is undefined in Firefox for Android content script context 179 | // while `this` is nsIContentFrameMessageManager 180 | // with an attribute `content` that corresponds to the window 181 | 182 | if (typeof module !== "undefined" && module.exports) { 183 | module.exports.saveAs = saveAs; 184 | } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) { 185 | define("FileSaver.js", function() { 186 | return saveAs; 187 | }); 188 | } -------------------------------------------------------------------------------- /editor/lib/filer.min.js: -------------------------------------------------------------------------------- 1 | /*! (c) 2016 Copyright (c) 2016 Eric Bidelman. All rights reserved. 2 | 3 | * @version v0.4.5 (Apache) */ 4 | var $jscomp={scope:{}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(a,c,b){if(b.get||b.set)throw new TypeError("ES3 does not support getters and setters.");a!=Array.prototype&&a!=Object.prototype&&(a[c]=b.value)};$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global?global:a};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_"; 5 | $jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.symbolCounter_=0;$jscomp.Symbol=function(a){return $jscomp.SYMBOL_PREFIX+(a||"")+$jscomp.symbolCounter_++}; 6 | $jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var a=$jscomp.global.Symbol.iterator;a||(a=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&$jscomp.defineProperty(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(a){var c=0;return $jscomp.iteratorPrototype(function(){return c