提交 2297b1ad 编写于 作者: M Mr.doob

r130

上级 372f5685
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -258,7 +258,7 @@
have to set it manually to make use of this method.
</p>
<h3>[method:Object3D getObjectByProperty]( [param:String name], [param:Float value] )</h3>
<h3>[method:Object3D getObjectByProperty]( [param:String name], [param:Any value] )</h3>
<p>
name -- the property name to search for. <br />
value -- value of the given property. <br /><br />
......
......@@ -133,7 +133,7 @@
</tr>
<tr>
<td>mat4</td>
<td>[page:Matrix3 THREE.Matrix4]</td>
<td>[page:Matrix4 THREE.Matrix4]</td>
</tr>
<tr>
<td>mat4</td>
......
......@@ -44,7 +44,12 @@ scene.add( axesHelper );
<h2>Methods</h2>
<p>See the base [page:LineSegments] class for common methods.</p>
<h3>[method:AxesHelper dispose]()</h3>
<h3>[method:this setColors]( [param:Color xAxisColor], [param:Color yAxisColor], [param:Color zAxisColor] )</h3>
<p>
Sets the axes colors to [page:Color xAxisColor], [page:Color yAxisColor], [page:Color zAxisColor].
</p>
<h3>[method:null dispose]()</h3>
<p>
Disposes of the internally-created [page:Line.material material] and [page:Line.geometry geometry] used by this helper.
</p>
......
......@@ -24,7 +24,7 @@
//Create a PointLight and turn on shadows for the light
const light = new THREE.PointLight( 0xffffff, 1, 100 );
light.position.set( 0, 10, 0 );
light.position.set( 0, 10, 4 );
light.castShadow = true; // default false
scene.add( light );
......
......@@ -319,12 +319,12 @@ x, 0, 0, 0,
<h3>[method:this makeShear]( [param:Float xy], [param:Float xz], [param:Float yx], [param:Float yz], [param:Float zx], [param:Float zy] )</h3>
<p>
[page:Float x] - the amount to shear X by Y.<br />
[page:Float x] - the amount to shear X by Z.<br />
[page:Float x] - the amount to shear Y by X.<br />
[page:Float x] - the amount to shear Y by Z.<br />
[page:Float y] - the amount to shear Z by X.<br />
[page:Float z] - the amount to shear Z by Y.<br /><br />
[page:Float xy] - the amount to shear X by Y.<br />
[page:Float xz] - the amount to shear X by Z.<br />
[page:Float yx] - the amount to shear Y by X.<br />
[page:Float yz] - the amount to shear Y by Z.<br />
[page:Float zx] - the amount to shear Z by X.<br />
[page:Float zy] - the amount to shear Z by Y.<br /><br />
Sets this matrix as a shear transform:
<code>
......
......@@ -409,7 +409,7 @@
[page:WebGLRenderer.autoClearDepth autoClearDepth] properties to false. To forcibly clear one ore more buffers call [page:WebGLRenderer.clear .clear].
</p>
<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Fog fog], [param:BufferGeometry geometry], [param:Material material], [param:Object3D object], [param:Object group] )</h3>
<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Scene scene], [param:BufferGeometry geometry], [param:Material material], [param:Object3D object], [param:Object group] )</h3>
<p>Render a buffer geometry group using the camera and with the specified material.</p>
<h3>[method:null renderBufferImmediate]( [param:Object3D object], [param:WebGLProgram program] )</h3>
......
......@@ -247,7 +247,7 @@
대부분의 객체는 기본값으로 이름이 빈 문자열입니다. 이 메서드를 사용하기 위해서는 직접 이름을 지정해야 합니다.
</p>
<h3>[method:Object3D getObjectByProperty]( [param:String name], [param:Float value] )</h3>
<h3>[method:Object3D getObjectByProperty]( [param:String name], [param:Any value] )</h3>
<p>
name -- 검색하고자하는 이름 프로퍼티. <br />
value -- 프로퍼티의 값. <br /><br />
......
......@@ -129,7 +129,7 @@
</tr>
<tr>
<td>mat4</td>
<td>[page:Matrix3 THREE.Matrix4]</td>
<td>[page:Matrix4 THREE.Matrix4]</td>
</tr>
<tr>
<td>mat4</td>
......
......@@ -247,7 +247,7 @@
请注意,大多数的对象中name默认是一个空字符串,要使用这个方法,你将需要手动地设置name属性。
</p>
<h3>[method:Object3D getObjectByProperty]( [param:String name], [param:Float value] )</h3>
<h3>[method:Object3D getObjectByProperty]( [param:String name], [param:Any value] )</h3>
<p>
name —— 将要用于查找的属性的名称。<br />
value —— 给定的属性的值。 <br /><br />
......
......@@ -130,7 +130,7 @@
</tr>
<tr>
<td>mat4</td>
<td>[page:Matrix3 THREE.Matrix4]</td>
<td>[page:Matrix4 THREE.Matrix4]</td>
</tr>
<tr>
<td>mat4</td>
......
......@@ -53,7 +53,7 @@
[page:Float width] - (可选参数) 光源宽度。缺省值为 10。<br />
[page:Float height] - (可选参数) 光源高度。缺省值为 10。<br /><br />
创建一个新的平光。
创建一个新的平光。
</p>
<h2>属性(Properties)</h2>
......
......@@ -25,7 +25,7 @@
//Create a PointLight and turn on shadows for the light
const light = new THREE.PointLight( 0xffffff, 1, 100 );
light.position.set( 0, 10, 0 );
light.position.set( 0, 10, 4 );
light.castShadow = true; // default false
scene.add( light );
......
......@@ -363,7 +363,7 @@
即便forceClear设为true, 也可以通过将[page:WebGLRenderer.autoClearColor autoClearColor]、[page:WebGLRenderer.autoClearStencil autoClearStencil]或[page:WebGLRenderer.autoClearDepth autoClearDepth]属性的值设为false来阻止对应缓存被清除。
</p>
<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Fog fog], [param:BufferGeometry geometry], [param:Material material], [param:Object3D object], [param:Object group] )</h3>
<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Scene scene], [param:BufferGeometry geometry], [param:Material material], [param:Object3D object], [param:Object group] )</h3>
<p>使用相机和指定材质渲染缓冲几何组。</p>
<h3>[method:null renderBufferImmediate]( [param:Object3D object], [param:WebGLProgram program] )</h3>
......
......@@ -14,7 +14,7 @@
<p class="desc">
Renders arrows to visualize an object's vertex tangent vectors.
Requires that tangents have been specified in a [page:BufferAttribute custom attribute] or
have been calculated using [page:BufferGeometryUtils.computeTangents computeTangents].<br /><br />
have been calculated using [page:BufferGeometry.computeTangents computeTangents].<br /><br />
This helper supports [page:BufferGeometry] only.
</p>
......
......@@ -14,7 +14,7 @@
<p class="desc">
Renders arrows to visualize an object's vertex tangent vectors.
Requires that tangents have been specified in a [page:BufferAttribute custom attribute] or
have been calculated using [page:BufferGeometryUtils.computeTangents computeTangents].<br /><br />
have been calculated using [page:BufferGeometry.computeTangents computeTangents].<br /><br />
This helper supports [page:BufferGeometry] only.
</p>
......
......@@ -252,6 +252,7 @@
}
const localList = list[ language ];
const selectedPage = window.location.hash.substring( 1 );
for ( const section in localList ) {
......@@ -293,6 +294,14 @@
const linkElement = createLink( pageName, pageURL );
listElement.appendChild( linkElement );
// select current page
if ( pageURL === selectedPage ) {
linkElement.classList.add( 'selected' );
}
// Gather the main properties for the current subpage
pageProperties[ pageName ] = {
......
......@@ -61,7 +61,7 @@
<p>Next up is the renderer. This is where the magic happens. In addition to the WebGLRenderer we use here, three.js comes with a few others, often used as fallbacks for users with older browsers or for those who don't have WebGL support for some reason.</p>
<p>In addition to creating the renderer instance, we also need to set the size at which we want it to render our app. It's a good idea to use the width and height of the area we want to fill with our app - in this case, the width and height of the browser window. For performance intensive apps, you can also give <strong>setSize</strong> smaller values, like <strong>window.innerWidth/2</strong> and <strong>window.innerHeight/2</strong>, which will make the app render at half size.</p>
<p>In addition to creating the renderer instance, we also need to set the size at which we want it to render our app. It's a good idea to use the width and height of the area we want to fill with our app - in this case, the width and height of the browser window. For performance intensive apps, you can also give <strong>setSize</strong> smaller values, like <strong>window.innerWidth/2</strong> and <strong>window.innerHeight/2</strong>, which will make the app render at quarter size.</p>
<p>If you wish to keep the size of your app but render it at a lower resolution, you can do so by calling <strong>setSize</strong> with false as <strong>updateStyle</strong> (the third argument). For example, <strong>setSize(window.innerWidth/2, window.innerHeight/2, false)</strong> will render your app at half resolution, given that your &lt;canvas&gt; has 100% width and height.</p>
......@@ -111,7 +111,7 @@
cube.rotation.y += 0.01;
</code>
<p>This will be run every frame (normally 60 times per second), and give the cube a nice rotation animation. Basically, anything you want to move or change while the app is running has to go through the animate loop. You can of course call other functions from there, so that you don't end up with a <strong>animate</strong> function that's hundreds of lines.</p>
<p>This will be run every frame (normally 60 times per second), and give the cube a nice rotation animation. Basically, anything you want to move or change while the app is running has to go through the animate loop. You can of course call other functions from there, so that you don't end up with an <strong>animate</strong> function that's hundreds of lines.</p>
<h2>The result</h2>
<p>Congratulations! You have now completed your first three.js application. It's simple, but you have to start somewhere.</p>
......
......@@ -88,7 +88,7 @@
<p>
有时你或许想要自己写一个自定义后期处理着色器,并将其包含到后期处理过程链中。
对于这个需求,你可以使用*ShaderPass*。在引入该文件以及你的自定义着色后,可以使用下列代码来设置该过程:
对于这个需求,你可以使用*ShaderPass*。在引入该文件以及你的自定义着色后,可以使用下列代码来设置该过程:
</p>
<code>
......
......@@ -4,7 +4,6 @@ function Config() {
var storage = {
'language': 'en',
'exportPrecision': 6,
'autosave': true,
......
......@@ -131,9 +131,8 @@ Editor.prototype = {
this.scene.uuid = scene.uuid;
this.scene.name = scene.name;
this.scene.background = ( scene.background !== null ) ? scene.background.clone() : null;
if ( scene.fog !== null ) this.scene.fog = scene.fog.clone();
this.scene.background = scene.background;
this.scene.fog = scene.fog;
this.scene.userData = JSON.parse( JSON.stringify( scene.userData ) );
......@@ -641,12 +640,10 @@ Editor.prototype = {
//
fromJSON: function ( json ) {
var scope = this;
fromJSON: async function ( json ) {
var loader = new THREE.ObjectLoader();
var camera = loader.parse( json.camera );
var camera = await loader.parseAsync( json.camera );
this.camera.copy( camera );
this.signals.cameraResetted.dispatch();
......@@ -654,11 +651,7 @@ Editor.prototype = {
this.history.fromJSON( json.history );
this.scripts = json.scripts;
loader.parse( json.scene, function ( scene ) {
scope.setScene( scene );
} );
this.setScene( await loader.parseAsync( json.scene ) );
},
......
......@@ -6,16 +6,6 @@ import { UIPanel, UIRow, UIHorizontalRule } from './libs/ui.js';
function MenubarFile( editor ) {
function parseNumber( key, value ) {
var precision = config.getKey( 'exportPrecision' );
return typeof value === 'number' ? parseFloat( value.toFixed( precision ) ) : value;
}
//
var config = editor.config;
var strings = editor.strings;
......@@ -111,7 +101,7 @@ function MenubarFile( editor ) {
try {
output = JSON.stringify( output, parseNumber, '\t' );
output = JSON.stringify( output, null, '\t' );
output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
} catch ( e ) {
......@@ -145,7 +135,7 @@ function MenubarFile( editor ) {
try {
output = JSON.stringify( output, parseNumber, '\t' );
output = JSON.stringify( output, null, '\t' );
output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
} catch ( e ) {
......@@ -170,7 +160,7 @@ function MenubarFile( editor ) {
try {
output = JSON.stringify( output, parseNumber, '\t' );
output = JSON.stringify( output, null, '\t' );
output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
} catch ( e ) {
......@@ -228,8 +218,18 @@ function MenubarFile( editor ) {
var exporter = new DRACOExporter();
const options = {
decodeSpeed: 5,
encodeSpeed: 5,
encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING,
quantization: [ 16, 8, 8, 8, 8 ],
exportUvs: true,
exportNormals: true,
exportColor: object.geometry.hasAttribute( 'color' )
};
// TODO: Change to DRACOExporter's parse( geometry, onParse )?
var result = exporter.parse( object );
var result = exporter.parse( object, options );
saveArrayBuffer( result, 'model.drc' );
} );
......@@ -414,7 +414,7 @@ function MenubarFile( editor ) {
output.metadata.type = 'App';
delete output.history;
output = JSON.stringify( output, parseNumber, '\t' );
output = JSON.stringify( output, null, '\t' );
output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
toZip[ 'app.json' ] = strToU8( output );
......
......@@ -23,7 +23,7 @@ function SidebarGeometryBufferGeometry( editor ) {
var text = new UIText( strings.getKey( 'sidebar/geometry/buffer_geometry/attributes' ) ).setWidth( '90px' );
container.add( text );
var container2 = new UISpan().setDisplay( 'inline-block' ).setWidth( '160px' );
var container2 = new UISpan().setDisplay( 'inline-block' ).setVerticalAlign( 'middle' ).setWidth( '160px' );
container.add( container2 );
var index = geometry.index;
......
......@@ -146,12 +146,12 @@ function SidebarGeometry( editor ) {
container.add( new SidebarGeometryBufferGeometry( editor ) );
// size
// Size
var geometryBoundingSphere = new UIText();
var geometryBoundingBox = new UIText().setFontSize( '12px' ).setVerticalAlign( 'middle' );
container.add( new UIText( strings.getKey( 'sidebar/geometry/bounds' ) ).setWidth( '90px' ) );
container.add( geometryBoundingSphere );
container.add( geometryBoundingBox );
// Helpers
......@@ -216,9 +216,14 @@ function SidebarGeometry( editor ) {
}
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
if ( geometry.boundingBox === null ) geometry.computeBoundingBox();
geometryBoundingSphere.setValue( Math.floor( geometry.boundingSphere.radius * 1000 ) / 1000 );
const boundingBox = geometry.boundingBox;
const x = Math.floor( ( boundingBox.max.x - boundingBox.min.x ) * 1000 ) / 1000;
const y = Math.floor( ( boundingBox.max.y - boundingBox.min.y ) * 1000 ) / 1000;
const z = Math.floor( ( boundingBox.max.z - boundingBox.min.z ) * 1000 ) / 1000;
geometryBoundingBox.setInnerHTML( `${x}<br/>${y}<br/>${z}` );
} else {
......
import * as THREE from '../../build/three.module.js';
import { UIPanel, UIBreak, UIRow, UIColor, UISelect, UIText, UINumber } from './libs/ui.js';
import { UIOutliner, UITexture } from './libs/ui.three.js';
......@@ -166,7 +168,6 @@ function SidebarScene( editor ) {
refreshBackgroundUI();
} );
backgroundType.setValue( 'Color' );
backgroundRow.add( new UIText( strings.getKey( 'sidebar/scene/background' ) ).setWidth( '90px' ) );
backgroundRow.add( backgroundType );
......@@ -376,18 +377,26 @@ function SidebarScene( editor ) {
backgroundType.setValue( 'Color' );
backgroundColor.setHexValue( scene.background.getHex() );
backgroundTexture.setValue( null );
backgroundEquirectangularTexture.setValue( null );
}
} else if ( scene.background.isTexture ) {
if ( scene.background.mapping === THREE.EquirectangularReflectionMapping ) {
backgroundType.setValue( 'Equirectangular' );
backgroundEquirectangularTexture.setValue( scene.background );
} else {
// TODO: Add Texture/EquirectangularTexture support
backgroundType.setValue( 'Texture' );
backgroundTexture.setValue( scene.background );
}
}
} else {
backgroundType.setValue( 'None' );
backgroundTexture.setValue( null );
backgroundEquirectangularTexture.setValue( null );
}
......
import { UIPanel, UIRow, UISelect, UISpan, UIText, UIInteger } from './libs/ui.js';
import { UIPanel, UIRow, UISelect, UISpan, UIText } from './libs/ui.js';
import { SidebarSettingsViewport } from './Sidebar.Settings.Viewport.js';
import { SidebarSettingsShortcuts } from './Sidebar.Settings.Shortcuts.js';
......@@ -47,24 +47,6 @@ function SidebarSettings( editor ) {
settings.add( languageRow );
// export precision
var exportPrecisionRow = new UIRow();
var exportPrecision = new UIInteger( config.getKey( 'exportPrecision' ) ).setRange( 2, Infinity );
exportPrecision.onChange( function () {
var value = this.getValue();
editor.config.setKey( 'exportPrecision', value );
} );
exportPrecisionRow.add( new UIText( strings.getKey( 'sidebar/settings/exportPrecision' ) ).setWidth( '90px' ) );
exportPrecisionRow.add( exportPrecision );
settings.add( exportPrecisionRow );
//
container.add( new SidebarSettingsViewport( editor ) );
......
......@@ -311,7 +311,6 @@ function Strings( config ) {
'sidebar/settings': 'Settings',
'sidebar/settings/language': 'Language',
'sidebar/settings/exportPrecision': 'Export Precision',
'sidebar/settings/shortcuts': 'Shortcuts',
'sidebar/settings/shortcuts/translate': 'Translate',
......@@ -646,7 +645,6 @@ function Strings( config ) {
'sidebar/settings': 'Paramètres',
'sidebar/settings/language': 'Langue',
'sidebar/settings/exportPrecision': 'Précision à l\'exportation',
'sidebar/settings/shortcuts': 'Shortcuts',
'sidebar/settings/shortcuts/translate': 'Position',
......@@ -917,8 +915,8 @@ function Strings( config ) {
'sidebar/material/emissive': '自发光',
'sidebar/material/specular': '高光',
'sidebar/material/shininess': '高光大小',
'sidebar/material/clearcoat': '透明图层',
'sidebar/material/clearcoatroughness': '透明图层粗糙度',
'sidebar/material/clearcoat': '清漆',
'sidebar/material/clearcoatroughness': '清漆粗糙度',
'sidebar/material/vertexcolors': '顶点颜色',
'sidebar/material/vertextangents': '顶点切线',
'sidebar/material/matcap': '材质捕获',
......@@ -926,7 +924,7 @@ function Strings( config ) {
'sidebar/material/alphamap': '透明贴图',
'sidebar/material/bumpmap': '凹凸贴图',
'sidebar/material/normalmap': '法线贴图',
'sidebar/material/clearcoatnormalmap': '透明图层法线贴图',
'sidebar/material/clearcoatnormalmap': '清漆法线贴图',
'sidebar/material/displacemap': '置换贴图',
'sidebar/material/roughmap': '粗糙贴图',
'sidebar/material/metalmap': '金属贴图',
......@@ -981,7 +979,6 @@ function Strings( config ) {
'sidebar/settings': '设置',
'sidebar/settings/language': '语言',
'sidebar/settings/exportPrecision': '输出精度',
'sidebar/settings/shortcuts': '快捷键',
'sidebar/settings/shortcuts/translate': '移动',
......
......@@ -565,11 +565,8 @@ function Viewport( editor ) {
if ( backgroundEquirectangularTexture ) {
var renderTarget = new THREE.WebGLCubeRenderTarget( backgroundEquirectangularTexture.image.width );
renderTarget.fromEquirectangularTexture( renderer, backgroundEquirectangularTexture );
renderTarget.toJSON = function () { return null }; // TODO Remove hack
scene.background = renderTarget.texture;
backgroundEquirectangularTexture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = backgroundEquirectangularTexture;
}
......
......@@ -126,6 +126,12 @@ class UIElement {
}
setInnerHTML( value ) {
this.dom.innerHTML = value;
}
getIndexOfChild( element ) {
return Array.prototype.indexOf.call( this.dom.children, element.dom );
......@@ -137,7 +143,7 @@ class UIElement {
// properties
const properties = [ 'position', 'left', 'top', 'right', 'bottom', 'width', 'height', 'border', 'borderLeft',
'borderTop', 'borderRight', 'borderBottom', 'borderColor', 'display', 'overflow', 'margin', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'padding', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'color',
'borderTop', 'borderRight', 'borderBottom', 'borderColor', 'display', 'overflow', 'margin', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'padding', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'verticalAlign', 'color',
'background', 'backgroundColor', 'opacity', 'fontSize', 'fontWeight', 'textAlign', 'textDecoration', 'textTransform', 'cursor', 'zIndex' ];
properties.forEach( function ( property ) {
......@@ -225,7 +231,6 @@ class UIText extends UISpan {
this.dom.className = 'Text';
this.dom.style.cursor = 'default';
this.dom.style.display = 'inline-block';
this.dom.style.verticalAlign = 'middle';
this.setValue( text );
......
......@@ -36,7 +36,7 @@ class UITexture extends UISpan {
input.click();
}, false );
canvas.addEventListener( 'drop', function () {
canvas.addEventListener( 'drop', function ( event ) {
event.preventDefault();
event.stopPropagation();
......
// r129
// r130
const cacheName = 'threejs-editor';
......@@ -244,35 +244,28 @@ self.addEventListener( 'fetch', async function ( event ) {
async function networkFirst( request ) {
return fetch( request ).catch( async function () {
return fetch( request )
.then( async function ( response ) {
const cachedResponse = await caches.match( request );
const cache = await caches.open( cacheName );
if ( cachedResponse === undefined ) {
cache.put( request, response.clone() );
console.warn( '[SW] Not cached:', request.url );
return response;
}
} )
.catch( async function () {
return cachedResponse;
const cachedResponse = await caches.match( request );
} );
}
if ( cachedResponse === undefined ) {
/*
async function cacheFirst( request ) {
console.warn( '[SW] Not cached:', request.url );
const cachedResponse = await caches.match( request );
}
if ( cachedResponse === undefined ) {
return cachedResponse;
console.warn( '[SW] Not cached:', request.url );
return fetch( request );
}
return cachedResponse;
} );
}
*/
......@@ -348,6 +348,7 @@
"webxr_vr_handinput_pointerdrag",
"webxr_vr_handinput_pressbutton",
"webxr_vr_haptics",
"webxr_vr_layers",
"webxr_vr_lorenzattractor",
"webxr_vr_panorama",
"webxr_vr_panorama_depth",
......
......@@ -77,6 +77,8 @@
const NUM_SPHERES = 20;
const SPHERE_RADIUS = 0.2;
const STEPS_PER_FRAME = 5;
const sphereGeometry = new THREE.SphereGeometry( SPHERE_RADIUS, 32, 32 );
const sphereMaterial = new THREE.MeshStandardMaterial( { color: 0x888855, roughness: 0.8, metalness: 0.5 } );
......@@ -360,13 +362,20 @@
function animate() {
const deltaTime = Math.min( 0.1, clock.getDelta() );
const deltaTime = Math.min( 0.05, clock.getDelta() ) / STEPS_PER_FRAME;
// we look for collisions in substeps to mitigate the risk of
// an object traversing another too quickly for detection.
for ( let i = 0 ; i < STEPS_PER_FRAME ; i ++ ) {
controls( deltaTime );
controls( deltaTime );
updatePlayer( deltaTime );
updatePlayer( deltaTime );
updateSpheres( deltaTime );
updateSpheres( deltaTime );
}
renderer.render( scene, camera );
......
......@@ -4,7 +4,7 @@
const _raycaster = new THREE.Raycaster();
const _mouse = new THREE.Vector2();
const _pointer = new THREE.Vector2();
const _offset = new THREE.Vector3();
......@@ -19,6 +19,8 @@
constructor( _objects, _camera, _domElement ) {
super();
_domElement.style.touchAction = 'none'; // disable touch scroll
let _selected = null,
_hovered = null;
const _intersections = []; //
......@@ -35,16 +37,6 @@
_domElement.addEventListener( 'pointerleave', onPointerCancel );
_domElement.addEventListener( 'touchmove', onTouchMove, {
passive: false
} );
_domElement.addEventListener( 'touchstart', onTouchStart, {
passive: false
} );
_domElement.addEventListener( 'touchend', onTouchEnd );
}
function deactivate() {
......@@ -57,12 +49,6 @@
_domElement.removeEventListener( 'pointerleave', onPointerCancel );
_domElement.removeEventListener( 'touchmove', onTouchMove );
_domElement.removeEventListener( 'touchstart', onTouchStart );
_domElement.removeEventListener( 'touchend', onTouchEnd );
_domElement.style.cursor = '';
}
......@@ -81,30 +67,12 @@
function onPointerMove( event ) {
event.preventDefault();
if ( scope.enabled === false ) return;
updatePointer( event );
switch ( event.pointerType ) {
_raycaster.setFromCamera( _pointer, _camera );
case 'mouse':
case 'pen':
onMouseMove( event );
break;
// TODO touch
}
}
function onMouseMove( event ) {
const rect = _domElement.getBoundingClientRect();
_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_raycaster.setFromCamera( _mouse, _camera );
if ( _selected && scope.enabled ) {
if ( _selected ) {
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
......@@ -118,81 +86,71 @@
} );
return;
}
} // hover support
_intersections.length = 0;
_raycaster.setFromCamera( _mouse, _camera );
if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) {
_raycaster.intersectObjects( _objects, true, _intersections );
_intersections.length = 0;
if ( _intersections.length > 0 ) {
_raycaster.setFromCamera( _pointer, _camera );
const object = _intersections[ 0 ].object;
_raycaster.intersectObjects( _objects, true, _intersections );
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
if ( _intersections.length > 0 ) {
if ( _hovered !== object && _hovered !== null ) {
const object = _intersections[ 0 ].object;
scope.dispatchEvent( {
type: 'hoveroff',
object: _hovered
} );
_domElement.style.cursor = 'auto';
_hovered = null;
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
}
if ( _hovered !== object && _hovered !== null ) {
if ( _hovered !== object ) {
scope.dispatchEvent( {
type: 'hoveroff',
object: _hovered
} );
_domElement.style.cursor = 'auto';
_hovered = null;
scope.dispatchEvent( {
type: 'hoveron',
object: object
} );
_domElement.style.cursor = 'pointer';
_hovered = object;
}
}
if ( _hovered !== object ) {
} else {
scope.dispatchEvent( {
type: 'hoveron',
object: object
} );
_domElement.style.cursor = 'pointer';
_hovered = object;
if ( _hovered !== null ) {
}
scope.dispatchEvent( {
type: 'hoveroff',
object: _hovered
} );
_domElement.style.cursor = 'auto';
_hovered = null;
} else {
}
if ( _hovered !== null ) {
}
scope.dispatchEvent( {
type: 'hoveroff',
object: _hovered
} );
_domElement.style.cursor = 'auto';
_hovered = null;
}
}
function onPointerDown( event ) {
event.preventDefault();
switch ( event.pointerType ) {
case 'mouse':
case 'pen':
onMouseDown( event );
break;
// TODO touch
}
}
}
function onMouseDown( event ) {
function onPointerDown() {
event.preventDefault();
if ( scope.enabled === false ) return;
updatePointer( event );
_intersections.length = 0;
_raycaster.setFromCamera( _mouse, _camera );
_raycaster.setFromCamera( _pointer, _camera );
_raycaster.intersectObjects( _objects, true, _intersections );
......@@ -200,6 +158,8 @@
_selected = scope.transformGroup === true ? _objects[ 0 ] : _intersections[ 0 ].object;
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
......@@ -218,25 +178,9 @@
}
function onPointerCancel( event ) {
event.preventDefault();
switch ( event.pointerType ) {
case 'mouse':
case 'pen':
onMouseCancel( event );
break;
// TODO touch
}
}
function onMouseCancel( event ) {
function onPointerCancel() {
event.preventDefault();
if ( scope.enabled === false ) return;
if ( _selected ) {
......@@ -252,90 +196,12 @@
}
function onTouchMove( event ) {
event.preventDefault();
event = event.changedTouches[ 0 ];
const rect = _domElement.getBoundingClientRect();
_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_raycaster.setFromCamera( _mouse, _camera );
if ( _selected && scope.enabled ) {
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
}
scope.dispatchEvent( {
type: 'drag',
object: _selected
} );
return;
}
}
function onTouchStart( event ) {
event.preventDefault();
event = event.changedTouches[ 0 ];
function updatePointer( event ) {
const rect = _domElement.getBoundingClientRect();
_mouse.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_intersections.length = 0;
_raycaster.setFromCamera( _mouse, _camera );
_raycaster.intersectObjects( _objects, true, _intersections );
if ( _intersections.length > 0 ) {
_selected = scope.transformGroup === true ? _objects[ 0 ] : _intersections[ 0 ].object;
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
}
_domElement.style.cursor = 'move';
scope.dispatchEvent( {
type: 'dragstart',
object: _selected
} );
}
}
function onTouchEnd( event ) {
event.preventDefault();
if ( _selected ) {
scope.dispatchEvent( {
type: 'dragend',
object: _selected
} );
_selected = null;
}
_domElement.style.cursor = 'auto';
_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
}
......
......@@ -72,8 +72,6 @@
}
event.preventDefault();
if ( this.activeLook ) {
switch ( event.button ) {
......@@ -96,8 +94,6 @@
this.onMouseUp = function ( event ) {
event.preventDefault();
if ( this.activeLook ) {
switch ( event.button ) {
......@@ -136,7 +132,6 @@
this.onKeyDown = function ( event ) {
//event.preventDefault();
switch ( event.code ) {
case 'ArrowUp':
......
......@@ -55,8 +55,7 @@
return;
} //event.preventDefault();
}
switch ( event.code ) {
......@@ -186,14 +185,6 @@
this.mousedown = function ( event ) {
if ( this.domElement !== document ) {
this.domElement.focus();
}
event.preventDefault();
if ( this.dragToLook ) {
this.mouseStatus ++;
......@@ -235,8 +226,6 @@
this.mouseup = function ( event ) {
event.preventDefault();
if ( this.dragToLook ) {
this.mouseStatus --;
......
......@@ -24,7 +24,9 @@
if ( domElement === undefined ) console.warn( 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' );
if ( domElement === document ) console.error( 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
this.object = object;
this.domElement = domElement; // Set to false to disable this control
this.domElement = domElement;
this.domElement.style.touchAction = 'none'; // disable touch scroll
// Set to false to disable this control
this.enabled = true; // "target" sets the location of focus, where the object orbits around
......@@ -254,10 +256,8 @@
scope.domElement.removeEventListener( 'contextmenu', onContextMenu );
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
scope.domElement.removeEventListener( 'wheel', onMouseWheel );
scope.domElement.removeEventListener( 'touchstart', onTouchStart );
scope.domElement.removeEventListener( 'touchend', onTouchEnd );
scope.domElement.removeEventListener( 'touchmove', onTouchMove );
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
......@@ -300,6 +300,8 @@
const dollyStart = new THREE.Vector2();
const dollyEnd = new THREE.Vector2();
const dollyDelta = new THREE.Vector2();
const pointers = [];
const pointerPositions = {};
function getAutoRotationAngle() {
......@@ -563,71 +565,72 @@
}
function handleTouchStartRotate( event ) {
function handleTouchStartRotate() {
if ( event.touches.length == 1 ) {
if ( pointers.length === 1 ) {
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
rotateStart.set( x, y );
}
}
function handleTouchStartPan( event ) {
function handleTouchStartPan() {
if ( event.touches.length == 1 ) {
if ( pointers.length === 1 ) {
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
panStart.set( x, y );
}
}
function handleTouchStartDolly( event ) {
function handleTouchStartDolly() {
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX;
const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY;
const distance = Math.sqrt( dx * dx + dy * dy );
dollyStart.set( 0, distance );
}
function handleTouchStartDollyPan( event ) {
function handleTouchStartDollyPan() {
if ( scope.enableZoom ) handleTouchStartDolly( event );
if ( scope.enablePan ) handleTouchStartPan( event );
if ( scope.enableZoom ) handleTouchStartDolly();
if ( scope.enablePan ) handleTouchStartPan();
}
function handleTouchStartDollyRotate( event ) {
function handleTouchStartDollyRotate() {
if ( scope.enableZoom ) handleTouchStartDolly( event );
if ( scope.enableRotate ) handleTouchStartRotate( event );
if ( scope.enableZoom ) handleTouchStartDolly();
if ( scope.enableRotate ) handleTouchStartRotate();
}
function handleTouchMoveRotate( event ) {
if ( event.touches.length == 1 ) {
if ( pointers.length == 1 ) {
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateEnd.set( event.pageX, event.pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const position = getSecondPointerPosition( event );
const x = 0.5 * ( event.pageX + position.x );
const y = 0.5 * ( event.pageY + position.y );
rotateEnd.set( x, y );
}
......@@ -643,14 +646,15 @@
function handleTouchMovePan( event ) {
if ( event.touches.length == 1 ) {
if ( pointers.length === 1 ) {
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panEnd.set( event.pageX, event.pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const position = getSecondPointerPosition( event );
const x = 0.5 * ( event.pageX + position.x );
const y = 0.5 * ( event.pageY + position.y );
panEnd.set( x, y );
}
......@@ -663,8 +667,9 @@
function handleTouchMoveDolly( event ) {
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const position = getSecondPointerPosition( event );
const dx = event.pageX - position.x;
const dy = event.pageY - position.y;
const distance = Math.sqrt( dx * dx + dy * dy );
dollyEnd.set( 0, distance );
dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
......@@ -697,13 +702,23 @@
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( pointers.length === 0 ) {
case 'mouse':
case 'pen':
onMouseDown( event );
break;
// TODO touch
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
} //
addPointer( event );
if ( event.pointerType === 'touch' ) {
onTouchStart( event );
} else {
onMouseDown( event );
}
......@@ -713,13 +728,13 @@
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( event.pointerType === 'touch' ) {
case 'mouse':
case 'pen':
onMouseMove( event );
break;
// TODO touch
onTouchMove( event );
} else {
onMouseMove( event );
}
......@@ -727,25 +742,37 @@
function onPointerUp( event ) {
switch ( event.pointerType ) {
if ( scope.enabled === false ) return;
if ( event.pointerType === 'touch' ) {
case 'mouse':
case 'pen':
onMouseUp( event );
break;
// TODO touch
onTouchEnd();
} else {
onMouseUp( event );
}
removePointer( event ); //
if ( pointers.length === 0 ) {
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
}
}
function onMouseDown( event ) {
function onPointerCancel( event ) {
// Prevent the browser from scrolling.
event.preventDefault(); // Manually set the focus since calling preventDefault above
// prevents the browser from setting it automatically.
removePointer( event );
}
function onMouseDown( event ) {
scope.domElement.focus ? scope.domElement.focus() : window.focus();
let mouseAction;
switch ( event.button ) {
......@@ -816,8 +843,6 @@
if ( state !== STATE.NONE ) {
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
scope.dispatchEvent( _startEvent );
}
......@@ -827,7 +852,6 @@
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
switch ( state ) {
......@@ -852,9 +876,6 @@
function onMouseUp( event ) {
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
if ( scope.enabled === false ) return;
handleMouseUp( event );
scope.dispatchEvent( _endEvent );
state = STATE.NONE;
......@@ -880,23 +901,22 @@
function onTouchStart( event ) {
if ( scope.enabled === false ) return;
event.preventDefault(); // prevent scrolling
trackPointer( event );
switch ( event.touches.length ) {
switch ( pointers.length ) {
case 1:
switch ( scope.touches.ONE ) {
case THREE.TOUCH.ROTATE:
if ( scope.enableRotate === false ) return;
handleTouchStartRotate( event );
handleTouchStartRotate();
state = STATE.TOUCH_ROTATE;
break;
case THREE.TOUCH.PAN:
if ( scope.enablePan === false ) return;
handleTouchStartPan( event );
handleTouchStartPan();
state = STATE.TOUCH_PAN;
break;
......@@ -912,13 +932,13 @@
case THREE.TOUCH.DOLLY_PAN:
if ( scope.enableZoom === false && scope.enablePan === false ) return;
handleTouchStartDollyPan( event );
handleTouchStartDollyPan();
state = STATE.TOUCH_DOLLY_PAN;
break;
case THREE.TOUCH.DOLLY_ROTATE:
if ( scope.enableZoom === false && scope.enableRotate === false ) return;
handleTouchStartDollyRotate( event );
handleTouchStartDollyRotate();
state = STATE.TOUCH_DOLLY_ROTATE;
break;
......@@ -944,8 +964,7 @@
function onTouchMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault(); // prevent scrolling
trackPointer( event );
switch ( state ) {
......@@ -982,7 +1001,6 @@
function onTouchEnd( event ) {
if ( scope.enabled === false ) return;
handleTouchEnd( event );
scope.dispatchEvent( _endEvent );
state = STATE.NONE;
......@@ -994,20 +1012,59 @@
if ( scope.enabled === false ) return;
event.preventDefault();
}
function addPointer( event ) {
pointers.push( event );
}
function removePointer( event ) {
delete pointerPositions[ event.pointerId ];
for ( let i = 0; i < pointers.length; i ++ ) {
if ( pointers[ i ].pointerId == event.pointerId ) {
pointers.splice( i, 1 );
return;
}
}
}
function trackPointer( event ) {
let position = pointerPositions[ event.pointerId ];
if ( position === undefined ) {
position = new THREE.Vector2();
pointerPositions[ event.pointerId ] = position;
}
position.set( event.pageX, event.pageY );
}
function getSecondPointerPosition( event ) {
const pointer = event.pointerId === pointers[ 0 ].pointerId ? pointers[ 1 ] : pointers[ 0 ];
return pointerPositions[ pointer.pointerId ];
} //
scope.domElement.addEventListener( 'contextmenu', onContextMenu );
scope.domElement.addEventListener( 'pointerdown', onPointerDown );
scope.domElement.addEventListener( 'pointercancel', onPointerCancel );
scope.domElement.addEventListener( 'wheel', onMouseWheel, {
passive: false
} );
scope.domElement.addEventListener( 'touchstart', onTouchStart, {
passive: false
} );
scope.domElement.addEventListener( 'touchend', onTouchEnd );
scope.domElement.addEventListener( 'touchmove', onTouchMove, {
passive: false
} ); // force an update at start
this.update();
......
......@@ -27,7 +27,9 @@
TOUCH_ZOOM_PAN: 4
};
this.object = object;
this.domElement = domElement; // API
this.domElement = domElement;
this.domElement.style.touchAction = 'none'; // disable touch scroll
// API
this.enabled = true;
this.screen = {
......@@ -76,7 +78,9 @@
_zoomStart = new THREE.Vector2(),
_zoomEnd = new THREE.Vector2(),
_panStart = new THREE.Vector2(),
_panEnd = new THREE.Vector2(); // for reset
_panEnd = new THREE.Vector2(),
_pointers = [],
_pointerPositions = {}; // for reset
this.target0 = this.target.clone();
......@@ -383,13 +387,23 @@
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( _pointers.length === 0 ) {
case 'mouse':
case 'pen':
onMouseDown( event );
break;
// TODO touch
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
} //
addPointer( event );
if ( event.pointerType === 'touch' ) {
onTouchStart( event );
} else {
onMouseDown( event );
}
......@@ -399,13 +413,13 @@
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( event.pointerType === 'touch' ) {
case 'mouse':
case 'pen':
onMouseMove( event );
break;
// TODO touch
onTouchMove( event );
} else {
onMouseMove( event );
}
......@@ -415,18 +429,34 @@
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( event.pointerType === 'touch' ) {
onTouchEnd( event );
} else {
onMouseUp();
} //
case 'mouse':
case 'pen':
onMouseUp( event );
break;
// TODO touch
removePointer( event );
if ( _pointers.length === 0 ) {
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
}
}
function onPointerCancel( event ) {
removePointer( event );
}
function keydown( event ) {
if ( scope.enabled === false ) return;
......@@ -462,8 +492,6 @@
function onMouseDown( event ) {
event.preventDefault();
if ( _state === STATE.NONE ) {
switch ( event.button ) {
......@@ -517,8 +545,6 @@
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
const state = _keyState !== STATE.NONE ? _keyState : _state;
if ( state === STATE.ROTATE && ! scope.noRotate ) {
......@@ -539,10 +565,8 @@
}
function onMouseUp( event ) {
function onMouseUp() {
if ( scope.enabled === false ) return;
event.preventDefault();
_state = STATE.NONE;
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
......@@ -550,7 +574,7 @@
}
function mousewheel( event ) {
function onMouseWheel( event ) {
if ( scope.enabled === false ) return;
if ( scope.noZoom === true ) return;
......@@ -580,17 +604,16 @@
}
function touchstart( event ) {
function onTouchStart( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
trackPointer( event );
switch ( event.touches.length ) {
switch ( _pointers.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_moveCurr.copy( getMouseOnCircle( _pointers[ 0 ].pageX, _pointers[ 0 ].pageY ) );
_movePrev.copy( _moveCurr );
......@@ -599,11 +622,11 @@
default:
// 2 or more
_state = STATE.TOUCH_ZOOM_PAN;
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const dx = _pointers[ 0 ].pageX - _pointers[ 1 ].pageX;
const dy = _pointers[ 0 ].pageY - _pointers[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
const x = ( _pointers[ 0 ].pageX + _pointers[ 1 ].pageX ) / 2;
const y = ( _pointers[ 0 ].pageY + _pointers[ 1 ].pageY ) / 2;
_panStart.copy( getMouseOnScreen( x, y ) );
......@@ -617,27 +640,27 @@
}
function touchmove( event ) {
function onTouchMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
trackPointer( event );
switch ( event.touches.length ) {
switch ( _pointers.length ) {
case 1:
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
break;
default:
// 2 or more
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const position = getSecondPointerPosition( event );
const dx = event.pageX - position.x;
const dy = event.pageY - position.y;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
const x = ( event.pageX + position.x ) / 2;
const y = ( event.pageY + position.y ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
......@@ -647,11 +670,9 @@
}
function touchend( event ) {
function onTouchEnd( event ) {
if ( scope.enabled === false ) return;
switch ( event.touches.length ) {
switch ( _pointers.length ) {
case 0:
_state = STATE.NONE;
......@@ -660,7 +681,7 @@
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
_movePrev.copy( _moveCurr );
......@@ -679,16 +700,58 @@
}
function addPointer( event ) {
_pointers.push( event );
}
function removePointer( event ) {
delete _pointerPositions[ event.pointerId ];
for ( let i = 0; i < _pointers.length; i ++ ) {
if ( _pointers[ i ].pointerId == event.pointerId ) {
_pointers.splice( i, 1 );
return;
}
}
}
function trackPointer( event ) {
let position = _pointerPositions[ event.pointerId ];
if ( position === undefined ) {
position = new THREE.Vector2();
_pointerPositions[ event.pointerId ] = position;
}
position.set( event.pageX, event.pageY );
}
function getSecondPointerPosition( event ) {
const pointer = event.pointerId === _pointers[ 0 ].pointerId ? _pointers[ 1 ] : _pointers[ 0 ];
return _pointerPositions[ pointer.pointerId ];
}
this.dispose = function () {
scope.domElement.removeEventListener( 'contextmenu', contextmenu );
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
scope.domElement.removeEventListener( 'wheel', mousewheel );
scope.domElement.removeEventListener( 'touchstart', touchstart );
scope.domElement.removeEventListener( 'touchend', touchend );
scope.domElement.removeEventListener( 'touchmove', touchmove );
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
scope.domElement.removeEventListener( 'wheel', onMouseWheel );
window.removeEventListener( 'keydown', keydown );
window.removeEventListener( 'keyup', keyup );
......@@ -696,18 +759,10 @@
this.domElement.addEventListener( 'contextmenu', contextmenu );
this.domElement.addEventListener( 'pointerdown', onPointerDown );
this.domElement.addEventListener( 'wheel', mousewheel, {
passive: false
} );
this.domElement.addEventListener( 'touchstart', touchstart, {
passive: false
} );
this.domElement.addEventListener( 'touchend', touchend );
this.domElement.addEventListener( 'touchmove', touchmove, {
this.domElement.addEventListener( 'pointercancel', onPointerCancel );
this.domElement.addEventListener( 'wheel', onMouseWheel, {
passive: false
} );
this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
window.addEventListener( 'keydown', keydown );
window.addEventListener( 'keyup', keyup );
this.handleResize(); // force an update at start
......
......@@ -35,7 +35,7 @@
if ( DracoEncoderModule === undefined ) {
throw new Error( 'THREE.DRACOExporter: required the draco_decoder to work.' );
throw new Error( 'THREE.DRACOExporter: required the draco_encoder to work.' );
}
......
......@@ -1089,12 +1089,20 @@
if ( material.emissive ) {
// note: `emissive` is not scaled by `material.emissiveIntensity` for now to accommodate glTF spec. see #21849.
const emissive = material.emissive.toArray();
// note: emissive components are limited to stay within the 0 - 1 range to accommodate glTF spec. see #21849 and #22000.
const emissive = material.emissive.clone().multiplyScalar( material.emissiveIntensity );
const maxEmissiveComponent = Math.max( emissive.r, emissive.g, emissive.b );
if ( ! equalArray( emissive, [ 0, 0, 0 ] ) ) {
if ( maxEmissiveComponent > 1 ) {
materialDef.emissiveFactor = emissive;
emissive.multiplyScalar( 1 / maxEmissiveComponent );
console.warn( 'THREE.GLTFExporter: Some emissive components exceed 1; emissive has been limited' );
}
if ( maxEmissiveComponent > 0 ) {
materialDef.emissiveFactor = emissive.toArray();
} // emissiveTexture
......
......@@ -396,6 +396,15 @@ ${array.join( '' )}
}
inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
if ( material.isMeshPhysicalMaterial ) {
inputs.push( `${pad}float inputs:clearcoat = ${material.clearcoat}` );
inputs.push( `${pad}float inputs:clearcoatRoughness = ${material.clearcoatRoughness}` );
inputs.push( `${pad}float inputs:ior = ${material.ior}` );
}
return `
def Material "Material_${material.id}"
{
......
......@@ -39,7 +39,9 @@
};
THREE.ShaderLib[ 'line' ] = {
uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.line ] ),
vertexShader: `
vertexShader:
/* glsl */
`
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
......@@ -182,9 +184,10 @@
#include <clipping_planes_vertex>
#include <fog_vertex>
}
`,
fragmentShader: `
}`,
fragmentShader:
/* glsl */
`
uniform vec3 diffuse;
uniform float opacity;
......@@ -260,8 +263,7 @@
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}
`
}`
};
class LineMaterial extends THREE.ShaderMaterial {
......
......@@ -634,7 +634,7 @@
}
/**
* BasisU Texture Extension
* BasisU THREE.Texture Extension
*
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
*/
......@@ -686,7 +686,7 @@
}
/**
* WebP Texture Extension
* WebP THREE.Texture Extension
*
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp
*/
......@@ -986,7 +986,7 @@
}
/**
* Texture Transform Extension
* THREE.Texture Transform Extension
*
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
*/
......@@ -2340,7 +2340,9 @@
onLoad = function ( imageBitmap ) {
resolve( new THREE.CanvasTexture( imageBitmap ) );
const texture = new THREE.Texture( imageBitmap );
texture.needsUpdate = true;
resolve( texture );
};
......@@ -2352,7 +2354,7 @@
} ).then( function ( texture ) {
// Clean up resources and configure Texture.
// Clean up resources and configure THREE.Texture.
if ( isObjectURL === true ) {
URL.revokeObjectURL( sourceURI );
......@@ -2375,6 +2377,11 @@
} );
return texture;
} ).catch( function () {
console.error( 'THREE.GLTFLoader: Couldn\'t load texture', sourceURI );
return null;
} );
this.textureCache[ cacheKey ] = promise;
return promise;
......
......@@ -921,7 +921,7 @@
* @param {BufferGeometry} geometry - some properties are dependend on geometry
* @param {function} onProgress
* @param {function} onError
* @return {Array<MeshToonMaterial>}
* @return {Array<MMDToonMaterial>}
*/
......@@ -937,23 +937,26 @@
const material = data.materials[ i ];
const params = {
userData: {}
userData: {
MMD: {}
}
};
if ( material.name !== undefined ) params.name = material.name;
/*
* THREE.Color
*
* MMD THREE.MeshToonMaterial
* diffuse - color
* MMD MMDToonMaterial
* ambient - emissive * a
* (a = 1.0 without map texture or 0.2 with map texture)
*
* THREE.MeshToonMaterial doesn't have ambient. Set it to emissive instead.
* MMDToonMaterial doesn't have ambient. Set it to emissive instead.
* It'll be too bright if material has map texture so using coef 0.2.
*/
params.color = new THREE.Color().fromArray( material.diffuse );
params.diffuse = new THREE.Color().fromArray( material.diffuse );
params.opacity = material.diffuse[ 3 ];
params.specular = new THREE.Color().fromArray( material.specular );
params.shininess = material.shininess;
params.emissive = new THREE.Color().fromArray( material.ambient );
params.transparent = params.opacity !== 1.0; //
......@@ -1016,15 +1019,21 @@
// map
if ( material.textureIndex !== - 1 ) {
params.map = this._loadTexture( data.textures[ material.textureIndex ], textures );
params.map = this._loadTexture( data.textures[ material.textureIndex ], textures ); // Since PMX spec don't have standard to list map files except color map and env map,
// we need to save file name for further mapping, like matching normal map file names after model loaded.
// ref: https://gist.github.com/felixjones/f8a06bd48f9da9a4539f#texture
params.userData.MMD.mapFileName = data.textures[ material.textureIndex ];
} // envMap TODO: support m.envFlag === 3
if ( material.envTextureIndex !== - 1 && ( material.envFlag === 1 || material.envFlag == 2 ) ) {
params.envMap = this._loadTexture( data.textures[ material.envTextureIndex ], textures );
params.combine = material.envFlag === 1 ? THREE.MultiplyOperation : THREE.AddOperation;
params.matcap = this._loadTexture( data.textures[ material.envTextureIndex ], textures ); // Same as color map above, keep file name in userData for further usage.
params.userData.MMD.matcapFileName = data.textures[ material.envTextureIndex ];
params.matcapCombine = material.envFlag === 1 ? THREE.MultiplyOperation : THREE.AddOperation;
} // gradientMap
......@@ -1070,7 +1079,7 @@
}
materials.push( new THREE.MeshToonMaterial( params ) );
materials.push( new MMDToonMaterial( params ) );
}
......@@ -1811,6 +1820,93 @@
}
class MMDToonMaterial extends THREE.ShaderMaterial {
constructor( parameters ) {
super();
this._matcapCombine = THREE.AddOperation;
this.emissiveIntensity = 1.0;
this.normalMapType = THREE.TangentSpaceNormalMap;
this.combine = THREE.MultiplyOperation;
this.wireframeLinecap = 'round';
this.wireframeLinejoin = 'round';
this.flatShading = false;
this.lights = true;
this.vertexShader = THREE.MMDToonShader.vertexShader;
this.fragmentShader = THREE.MMDToonShader.fragmentShader;
this.defines = Object.assign( {}, THREE.MMDToonShader.defines );
Object.defineProperty( this, 'matcapCombine', {
get: function () {
return this._matcapCombine;
},
set: function ( value ) {
this._matcapCombine = value;
switch ( value ) {
case THREE.MultiplyOperation:
this.defines.MATCAP_BLENDING_MULTIPLY = true;
delete this.defines.MATCAP_BLENDING_ADD;
break;
default:
case THREE.AddOperation:
this.defines.MATCAP_BLENDING_ADD = true;
delete this.defines.MATCAP_BLENDING_MULTIPLY;
break;
}
}
} );
this.uniforms = THREE.UniformsUtils.clone( THREE.MMDToonShader.uniforms ); // merged from MeshToon/Phong/MatcapMaterial
const exposePropertyNames = [ 'specular', 'shininess', 'opacity', 'diffuse', 'map', 'matcap', 'gradientMap', 'lightMap', 'lightMapIntensity', 'aoMap', 'aoMapIntensity', 'emissive', 'emissiveMap', 'bumpMap', 'bumpScale', 'normalMap', 'normalScale', 'displacemantBias', 'displacemantMap', 'displacemantScale', 'specularMap', 'alphaMap', 'envMap', 'reflectivity', 'refractionRatio' ];
for ( const propertyName of exposePropertyNames ) {
Object.defineProperty( this, propertyName, {
get: function () {
return this.uniforms[ propertyName ].value;
},
set: function ( value ) {
this.uniforms[ propertyName ].value = value;
}
} );
}
Object.defineProperty( this, 'color', Object.getOwnPropertyDescriptor( this, 'diffuse' ) );
this.setValues( parameters );
}
copy( source ) {
super.copy( source );
this.matcapCombine = source.matcapCombine;
this.emissiveIntensity = source.emissiveIntensity;
this.normalMapType = source.normalMapType;
this.combine = source.combine;
this.wireframeLinecap = source.wireframeLinecap;
this.wireframeLinejoin = source.wireframeLinejoin;
this.flatShading = source.flatShading;
return this;
}
}
MMDToonMaterial.prototype.isMMDToonMaterial = true;
THREE.MMDLoader = MMDLoader;
} )();
......@@ -362,37 +362,54 @@
volume.dimensions = [ headerObject.sizes[ 0 ], headerObject.sizes[ 1 ], headerObject.sizes[ 2 ] ];
volume.xLength = volume.dimensions[ 0 ];
volume.yLength = volume.dimensions[ 1 ];
volume.zLength = volume.dimensions[ 2 ]; // spacing
volume.zLength = volume.dimensions[ 2 ]; // Identify axis order in the space-directions matrix from the header if possible.
const spacingX = new THREE.Vector3( headerObject.vectors[ 0 ][ 0 ], headerObject.vectors[ 0 ][ 1 ], headerObject.vectors[ 0 ][ 2 ] ).length();
const spacingY = new THREE.Vector3( headerObject.vectors[ 1 ][ 0 ], headerObject.vectors[ 1 ][ 1 ], headerObject.vectors[ 1 ][ 2 ] ).length();
const spacingZ = new THREE.Vector3( headerObject.vectors[ 2 ][ 0 ], headerObject.vectors[ 2 ][ 1 ], headerObject.vectors[ 2 ][ 2 ] ).length();
if ( headerObject.vectors ) {
const xIndex = headerObject.vectors.findIndex( vector => vector[ 0 ] !== 0 );
const yIndex = headerObject.vectors.findIndex( vector => vector[ 1 ] !== 0 );
const zIndex = headerObject.vectors.findIndex( vector => vector[ 2 ] !== 0 );
const axisOrder = [];
axisOrder[ xIndex ] = 'x';
axisOrder[ yIndex ] = 'y';
axisOrder[ zIndex ] = 'z';
volume.axisOrder = axisOrder;
} else {
volume.axisOrder = [ 'x', 'y', 'z' ];
} // spacing
const spacingX = new THREE.Vector3().fromArray( headerObject.vectors[ 0 ] ).length();
const spacingY = new THREE.Vector3().fromArray( headerObject.vectors[ 1 ] ).length();
const spacingZ = new THREE.Vector3().fromArray( headerObject.vectors[ 2 ] ).length();
volume.spacing = [ spacingX, spacingY, spacingZ ]; // Create IJKtoRAS matrix
volume.matrix = new THREE.Matrix4();
let _spaceX = 1;
let _spaceY = 1;
const _spaceZ = 1;
const transitionMatrix = new THREE.Matrix4();
if ( headerObject.space == 'left-posterior-superior' ) {
if ( headerObject.space === 'left-posterior-superior' ) {
_spaceX = - 1;
_spaceY = - 1;
transitionMatrix.set( - 1, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 );
} else if ( headerObject.space === 'left-anterior-superior' ) {
_spaceX = - 1;
transitionMatrix.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 1 );
}
if ( ! headerObject.vectors ) {
volume.matrix.set( _spaceX, 0, 0, 0, 0, _spaceY, 0, 0, 0, 0, _spaceZ, 0, 0, 0, 0, 1 );
volume.matrix.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 );
} else {
const v = headerObject.vectors;
volume.matrix.set( _spaceX * v[ 0 ][ 0 ], _spaceX * v[ 1 ][ 0 ], _spaceX * v[ 2 ][ 0 ], 0, _spaceY * v[ 0 ][ 1 ], _spaceY * v[ 1 ][ 1 ], _spaceY * v[ 2 ][ 1 ], 0, _spaceZ * v[ 0 ][ 2 ], _spaceZ * v[ 1 ][ 2 ], _spaceZ * v[ 2 ][ 2 ], 0, 0, 0, 0, 1 );
const ijk_to_transition = new THREE.Matrix4().set( v[ 0 ][ 0 ], v[ 1 ][ 0 ], v[ 2 ][ 0 ], 0, v[ 0 ][ 1 ], v[ 1 ][ 1 ], v[ 2 ][ 1 ], 0, v[ 0 ][ 2 ], v[ 1 ][ 2 ], v[ 2 ][ 2 ], 0, 0, 0, 0, 1 );
const transition_to_ras = new THREE.Matrix4().multiplyMatrices( ijk_to_transition, transitionMatrix );
volume.matrix = transition_to_ras;
}
......
......@@ -13,7 +13,7 @@
* @param {ArrayBuffer} arrayBuffer The buffer with volume data
*/
var Volume = function ( xLength, yLength, zLength, type, arrayBuffer ) {
function Volume( xLength, yLength, zLength, type, arrayBuffer ) {
if ( arguments.length > 0 ) {
......@@ -32,6 +32,11 @@
this.zLength = Number( zLength ) || 1;
/**
* @member {Array<string>} The order of the Axis dictated by the NRRD header
*/
this.axisOrder = [ 'x', 'y', 'z' ];
/**
* @member {TypedArray} data Data of the volume
*/
......@@ -202,7 +207,7 @@
* @member {Array} RASDimensions This array holds the dimensions of the volume in the RAS space
*/
};
}
Volume.prototype = {
constructor: Volume,
......@@ -306,8 +311,8 @@
axisInIJK.set( 1, 0, 0 );
firstDirection.set( 0, 0, - 1 );
secondDirection.set( 0, - 1, 0 );
firstSpacing = this.spacing[ 2 ];
secondSpacing = this.spacing[ 1 ];
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'z' ) ];
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'y' ) ];
IJKIndex = new THREE.Vector3( RASIndex, 0, 0 );
planeMatrix.multiply( new THREE.Matrix4().makeRotationY( Math.PI / 2 ) );
positionOffset = ( volume.RASDimensions[ 0 ] - 1 ) / 2;
......@@ -318,8 +323,8 @@
axisInIJK.set( 0, 1, 0 );
firstDirection.set( 1, 0, 0 );
secondDirection.set( 0, 0, 1 );
firstSpacing = this.spacing[ 0 ];
secondSpacing = this.spacing[ 2 ];
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'x' ) ];
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'z' ) ];
IJKIndex = new THREE.Vector3( 0, RASIndex, 0 );
planeMatrix.multiply( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
positionOffset = ( volume.RASDimensions[ 1 ] - 1 ) / 2;
......@@ -331,8 +336,8 @@
axisInIJK.set( 0, 0, 1 );
firstDirection.set( 1, 0, 0 );
secondDirection.set( 0, - 1, 0 );
firstSpacing = this.spacing[ 0 ];
secondSpacing = this.spacing[ 1 ];
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'x' ) ];
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'y' ) ];
IJKIndex = new THREE.Vector3( 0, 0, RASIndex );
positionOffset = ( volume.RASDimensions[ 2 ] - 1 ) / 2;
planeMatrix.setPosition( new THREE.Vector3( 0, 0, RASIndex - positionOffset ) );
......
......@@ -9,7 +9,7 @@
* @see Volume
*/
var VolumeSlice = function ( volume, index, axis ) {
function VolumeSlice( volume, index, axis ) {
var slice = this;
/**
......@@ -96,7 +96,7 @@
* @returns {Number} the index corresponding to the voxel in volume.data of the given position in the slice
*/
};
}
VolumeSlice.prototype = {
constructor: VolumeSlice,
......
......@@ -53,7 +53,7 @@
for ( let i = 0; i < positionAttribute.count; i ++ ) {
const v = new THREE.Vector3().fromBufferAttribute( positionAttribute, i );
const vertex = new Vertex( v, i );
const vertex = new Vertex( v );
vertices.push( vertex );
} // add faces
......@@ -120,7 +120,9 @@
for ( let i = 0; i < vertices.length; i ++ ) {
const vertex = vertices[ i ].position;
position.push( vertex.x, vertex.y, vertex.z );
position.push( vertex.x, vertex.y, vertex.z ); // cache final index to GREATLY speed up faces reconstruction
vertices[ i ].id = i;
} //
......@@ -128,10 +130,7 @@
for ( let i = 0; i < faces.length; i ++ ) {
const face = faces[ i ];
const a = vertices.indexOf( face.v1 );
const b = vertices.indexOf( face.v2 );
const c = vertices.indexOf( face.v3 );
index.push( a, b, c );
index.push( face.v1.id, face.v2.id, face.v3.id );
} //
......@@ -440,10 +439,10 @@
class Vertex {
constructor( v, id ) {
constructor( v ) {
this.position = v;
this.id = id; // old index id
this.id = - 1; // external use position in vertices list (for e.g. face generation)
this.faces = []; // faces vertex is connected
......
......@@ -158,12 +158,17 @@
uniform mat4 textureMatrix;
varying vec4 vUv;
#include <common>
#include <logdepthbuf_pars_vertex>
void main() {
vUv = textureMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
#include <logdepthbuf_vertex>
}`,
fragmentShader:
/* glsl */
......@@ -172,6 +177,8 @@
uniform sampler2D tDiffuse;
varying vec4 vUv;
#include <logdepthbuf_pars_fragment>
float blendOverlay( float base, float blend ) {
return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );
......@@ -186,6 +193,8 @@
void main() {
#include <logdepthbuf_fragment>
vec4 base = texture2DProj( tDiffuse, vUv );
gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );
......
( function () {
/**
* MMD Toon Shader
*
* This shader is extended from MeshPhongMaterial, and merged algorithms with
* MeshToonMaterial and MeshMetcapMaterial.
* Ideas came from https://github.com/mrdoob/three.js/issues/19609
*
* Combining steps:
* * Declare matcap uniform.
* * Add gradientmap_pars_fragment.
* * Use gradient irradiances instead of dotNL irradiance from MeshPhongMaterial.
* (Replace lights_phong_pars_fragment with lights_mmd_toon_pars_fragment)
* * Add mmd_toon_matcap_fragment.
*/
const lights_mmd_toon_pars_fragment = `
varying vec3 vViewPosition;
#ifndef FLAT_SHADED
varying vec3 vNormal;
#endif
struct BlinnPhongMaterial {
vec3 diffuseColor;
vec3 specularColor;
float specularShininess;
float specularStrength;
};
void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {
vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;
#ifndef PHYSICALLY_CORRECT_LIGHTS
irradiance *= PI; // punctual light
#endif
reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;
}
void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {
reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
}
#define RE_Direct RE_Direct_BlinnPhong
#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong
#define Material_LightProbeLOD( material ) (0)
`;
const mmd_toon_matcap_fragment = `
#ifdef USE_MATCAP
vec3 viewDir = normalize( vViewPosition );
vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );
vec3 y = cross( viewDir, x );
vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; // 0.495 to remove artifacts caused by undersized matcap disks
vec4 matcapColor = texture2D( matcap, uv );
matcapColor = matcapTexelToLinear( matcapColor );
#ifdef MATCAP_BLENDING_MULTIPLY
outgoingLight *= matcapColor.rgb;
#elif defined( MATCAP_BLENDING_ADD )
outgoingLight += matcapColor.rgb;
#endif
#endif
`;
const MMDToonShader = {
defines: {
TOON: true,
MATCAP: true,
MATCAP_BLENDING_ADD: true
},
uniforms: THREE.UniformsUtils.merge( [ THREE.ShaderLib.toon.uniforms, THREE.ShaderLib.phong.uniforms, THREE.ShaderLib.matcap.uniforms ] ),
vertexShader: THREE.ShaderLib.phong.vertexShader,
fragmentShader: THREE.ShaderLib.phong.fragmentShader.replace( '#include <common>', `
#ifdef USE_MATCAP
uniform sampler2D matcap;
#endif
#include <common>
` ).replace( '#include <envmap_common_pars_fragment>', `
#include <gradientmap_pars_fragment>
#include <envmap_common_pars_fragment>
` ).replace( '#include <lights_phong_pars_fragment>', lights_mmd_toon_pars_fragment ).replace( '#include <envmap_fragment>', `
#include <envmap_fragment>
${mmd_toon_matcap_fragment}
` )
};
THREE.MMDToonShader = MMDToonShader;
} )();
......@@ -62,7 +62,7 @@
void main() {
float waveStrength = 0.1;
float waveStrength = 0.5;
float waveSpeed = 0.03;
// simple distortion (ripple) via dudv map (see https://www.youtube.com/watch?v=6B7IF6GOu7s)
......
......@@ -10,7 +10,7 @@ import {
const _plane = new Plane();
const _raycaster = new Raycaster();
const _mouse = new Vector2();
const _pointer = new Vector2();
const _offset = new Vector3();
const _intersection = new Vector3();
const _worldPosition = new Vector3();
......@@ -22,6 +22,8 @@ class DragControls extends EventDispatcher {
super();
_domElement.style.touchAction = 'none'; // disable touch scroll
let _selected = null, _hovered = null;
const _intersections = [];
......@@ -36,9 +38,6 @@ class DragControls extends EventDispatcher {
_domElement.addEventListener( 'pointerdown', onPointerDown );
_domElement.addEventListener( 'pointerup', onPointerCancel );
_domElement.addEventListener( 'pointerleave', onPointerCancel );
_domElement.addEventListener( 'touchmove', onTouchMove, { passive: false } );
_domElement.addEventListener( 'touchstart', onTouchStart, { passive: false } );
_domElement.addEventListener( 'touchend', onTouchEnd );
}
......@@ -48,9 +47,6 @@ class DragControls extends EventDispatcher {
_domElement.removeEventListener( 'pointerdown', onPointerDown );
_domElement.removeEventListener( 'pointerup', onPointerCancel );
_domElement.removeEventListener( 'pointerleave', onPointerCancel );
_domElement.removeEventListener( 'touchmove', onTouchMove );
_domElement.removeEventListener( 'touchstart', onTouchStart );
_domElement.removeEventListener( 'touchend', onTouchEnd );
_domElement.style.cursor = '';
......@@ -70,31 +66,13 @@ class DragControls extends EventDispatcher {
function onPointerMove( event ) {
event.preventDefault();
switch ( event.pointerType ) {
case 'mouse':
case 'pen':
onMouseMove( event );
break;
// TODO touch
}
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
const rect = _domElement.getBoundingClientRect();
_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
updatePointer( event );
_raycaster.setFromCamera( _mouse, _camera );
_raycaster.setFromCamera( _pointer, _camera );
if ( _selected && scope.enabled ) {
if ( _selected ) {
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
......@@ -108,80 +86,73 @@ class DragControls extends EventDispatcher {
}
_intersections.length = 0;
_raycaster.setFromCamera( _mouse, _camera );
_raycaster.intersectObjects( _objects, true, _intersections );
if ( _intersections.length > 0 ) {
const object = _intersections[ 0 ].object;
// hover support
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) {
if ( _hovered !== object && _hovered !== null ) {
_intersections.length = 0;
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
_raycaster.setFromCamera( _pointer, _camera );
_raycaster.intersectObjects( _objects, true, _intersections );
_domElement.style.cursor = 'auto';
_hovered = null;
if ( _intersections.length > 0 ) {
}
if ( _hovered !== object ) {
const object = _intersections[ 0 ].object;
scope.dispatchEvent( { type: 'hoveron', object: object } );
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
_domElement.style.cursor = 'pointer';
_hovered = object;
if ( _hovered !== object && _hovered !== null ) {
}
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
} else {
_domElement.style.cursor = 'auto';
_hovered = null;
if ( _hovered !== null ) {
}
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
if ( _hovered !== object ) {
_domElement.style.cursor = 'auto';
_hovered = null;
scope.dispatchEvent( { type: 'hoveron', object: object } );
}
_domElement.style.cursor = 'pointer';
_hovered = object;
}
}
}
} else {
function onPointerDown( event ) {
if ( _hovered !== null ) {
event.preventDefault();
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
switch ( event.pointerType ) {
_domElement.style.cursor = 'auto';
_hovered = null;
case 'mouse':
case 'pen':
onMouseDown( event );
break;
}
// TODO touch
}
}
}
function onMouseDown( event ) {
function onPointerDown() {
if ( scope.enabled === false ) return;
event.preventDefault();
updatePointer( event );
_intersections.length = 0;
_raycaster.setFromCamera( _mouse, _camera );
_raycaster.setFromCamera( _pointer, _camera );
_raycaster.intersectObjects( _objects, true, _intersections );
if ( _intersections.length > 0 ) {
_selected = ( scope.transformGroup === true ) ? _objects[ 0 ] : _intersections[ 0 ].object;
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
......@@ -198,26 +169,9 @@ class DragControls extends EventDispatcher {
}
function onPointerCancel( event ) {
event.preventDefault();
switch ( event.pointerType ) {
case 'mouse':
case 'pen':
onMouseCancel( event );
break;
// TODO touch
}
}
function onMouseCancel( event ) {
function onPointerCancel() {
event.preventDefault();
if ( scope.enabled === false ) return;
if ( _selected ) {
......@@ -231,84 +185,12 @@ class DragControls extends EventDispatcher {
}
function onTouchMove( event ) {
event.preventDefault();
event = event.changedTouches[ 0 ];
function updatePointer( event ) {
const rect = _domElement.getBoundingClientRect();
_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_raycaster.setFromCamera( _mouse, _camera );
if ( _selected && scope.enabled ) {
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
}
scope.dispatchEvent( { type: 'drag', object: _selected } );
return;
}
}
function onTouchStart( event ) {
event.preventDefault();
event = event.changedTouches[ 0 ];
const rect = _domElement.getBoundingClientRect();
_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_intersections.length = 0;
_raycaster.setFromCamera( _mouse, _camera );
_raycaster.intersectObjects( _objects, true, _intersections );
if ( _intersections.length > 0 ) {
_selected = ( scope.transformGroup === true ) ? _objects[ 0 ] : _intersections[ 0 ].object;
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
}
_domElement.style.cursor = 'move';
scope.dispatchEvent( { type: 'dragstart', object: _selected } );
}
}
function onTouchEnd( event ) {
event.preventDefault();
if ( _selected ) {
scope.dispatchEvent( { type: 'dragend', object: _selected } );
_selected = null;
}
_domElement.style.cursor = 'auto';
_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
}
......
......@@ -91,8 +91,6 @@ class FirstPersonControls {
}
event.preventDefault();
if ( this.activeLook ) {
switch ( event.button ) {
......@@ -110,8 +108,6 @@ class FirstPersonControls {
this.onMouseUp = function ( event ) {
event.preventDefault();
if ( this.activeLook ) {
switch ( event.button ) {
......@@ -145,8 +141,6 @@ class FirstPersonControls {
this.onKeyDown = function ( event ) {
//event.preventDefault();
switch ( event.code ) {
case 'ArrowUp':
......
......@@ -57,8 +57,6 @@ class FlyControls extends EventDispatcher {
}
//event.preventDefault();
switch ( event.code ) {
case 'ShiftLeft':
......@@ -123,14 +121,6 @@ class FlyControls extends EventDispatcher {
this.mousedown = function ( event ) {
if ( this.domElement !== document ) {
this.domElement.focus();
}
event.preventDefault();
if ( this.dragToLook ) {
this.mouseStatus ++;
......@@ -169,8 +159,6 @@ class FlyControls extends EventDispatcher {
this.mouseup = function ( event ) {
event.preventDefault();
if ( this.dragToLook ) {
this.mouseStatus --;
......
......@@ -30,6 +30,7 @@ class OrbitControls extends EventDispatcher {
this.object = object;
this.domElement = domElement;
this.domElement.style.touchAction = 'none'; // disable touch scroll
// Set to false to disable this control
this.enabled = true;
......@@ -290,12 +291,9 @@ class OrbitControls extends EventDispatcher {
scope.domElement.removeEventListener( 'contextmenu', onContextMenu );
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
scope.domElement.removeEventListener( 'wheel', onMouseWheel );
scope.domElement.removeEventListener( 'touchstart', onTouchStart );
scope.domElement.removeEventListener( 'touchend', onTouchEnd );
scope.domElement.removeEventListener( 'touchmove', onTouchMove );
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
......@@ -351,6 +349,9 @@ class OrbitControls extends EventDispatcher {
const dollyEnd = new Vector2();
const dollyDelta = new Vector2();
const pointers = [];
const pointerPositions = {};
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
......@@ -636,16 +637,16 @@ class OrbitControls extends EventDispatcher {
}
function handleTouchStartRotate( event ) {
function handleTouchStartRotate() {
if ( event.touches.length == 1 ) {
if ( pointers.length === 1 ) {
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
rotateStart.set( x, y );
......@@ -653,16 +654,16 @@ class OrbitControls extends EventDispatcher {
}
function handleTouchStartPan( event ) {
function handleTouchStartPan() {
if ( event.touches.length == 1 ) {
if ( pointers.length === 1 ) {
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
panStart.set( x, y );
......@@ -670,10 +671,10 @@ class OrbitControls extends EventDispatcher {
}
function handleTouchStartDolly( event ) {
function handleTouchStartDolly() {
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX;
const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY;
const distance = Math.sqrt( dx * dx + dy * dy );
......@@ -681,32 +682,34 @@ class OrbitControls extends EventDispatcher {
}
function handleTouchStartDollyPan( event ) {
function handleTouchStartDollyPan() {
if ( scope.enableZoom ) handleTouchStartDolly( event );
if ( scope.enableZoom ) handleTouchStartDolly();
if ( scope.enablePan ) handleTouchStartPan( event );
if ( scope.enablePan ) handleTouchStartPan();
}
function handleTouchStartDollyRotate( event ) {
function handleTouchStartDollyRotate() {
if ( scope.enableZoom ) handleTouchStartDolly( event );
if ( scope.enableZoom ) handleTouchStartDolly();
if ( scope.enableRotate ) handleTouchStartRotate( event );
if ( scope.enableRotate ) handleTouchStartRotate();
}
function handleTouchMoveRotate( event ) {
if ( event.touches.length == 1 ) {
if ( pointers.length == 1 ) {
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateEnd.set( event.pageX, event.pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const position = getSecondPointerPosition( event );
const x = 0.5 * ( event.pageX + position.x );
const y = 0.5 * ( event.pageY + position.y );
rotateEnd.set( x, y );
......@@ -726,14 +729,16 @@ class OrbitControls extends EventDispatcher {
function handleTouchMovePan( event ) {
if ( event.touches.length == 1 ) {
if ( pointers.length === 1 ) {
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panEnd.set( event.pageX, event.pageY );
} else {
const x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
const y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
const position = getSecondPointerPosition( event );
const x = 0.5 * ( event.pageX + position.x );
const y = 0.5 * ( event.pageY + position.y );
panEnd.set( x, y );
......@@ -749,8 +754,10 @@ class OrbitControls extends EventDispatcher {
function handleTouchMoveDolly( event ) {
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const position = getSecondPointerPosition( event );
const dx = event.pageX - position.x;
const dy = event.pageY - position.y;
const distance = Math.sqrt( dx * dx + dy * dy );
......@@ -794,14 +801,24 @@ class OrbitControls extends EventDispatcher {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( pointers.length === 0 ) {
case 'mouse':
case 'pen':
onMouseDown( event );
break;
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
}
// TODO touch
//
addPointer( event );
if ( event.pointerType === 'touch' ) {
onTouchStart( event );
} else {
onMouseDown( event );
}
......@@ -811,14 +828,13 @@ class OrbitControls extends EventDispatcher {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( event.pointerType === 'touch' ) {
case 'mouse':
case 'pen':
onMouseMove( event );
break;
onTouchMove( event );
// TODO touch
} else {
onMouseMove( event );
}
......@@ -826,28 +842,38 @@ class OrbitControls extends EventDispatcher {
function onPointerUp( event ) {
switch ( event.pointerType ) {
if ( scope.enabled === false ) return;
if ( event.pointerType === 'touch' ) {
case 'mouse':
case 'pen':
onMouseUp( event );
break;
onTouchEnd();
} else {
onMouseUp( event );
}
removePointer( event );
//
if ( pointers.length === 0 ) {
// TODO touch
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
}
}
function onMouseDown( event ) {
function onPointerCancel( event ) {
// Prevent the browser from scrolling.
event.preventDefault();
removePointer( event );
// Manually set the focus since calling preventDefault above
// prevents the browser from setting it automatically.
}
scope.domElement.focus ? scope.domElement.focus() : window.focus();
function onMouseDown( event ) {
let mouseAction;
......@@ -938,9 +964,6 @@ class OrbitControls extends EventDispatcher {
if ( state !== STATE.NONE ) {
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
scope.dispatchEvent( _startEvent );
}
......@@ -951,8 +974,6 @@ class OrbitControls extends EventDispatcher {
if ( scope.enabled === false ) return;
event.preventDefault();
switch ( state ) {
case STATE.ROTATE:
......@@ -985,11 +1006,6 @@ class OrbitControls extends EventDispatcher {
function onMouseUp( event ) {
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
if ( scope.enabled === false ) return;
handleMouseUp( event );
scope.dispatchEvent( _endEvent );
......@@ -1022,11 +1038,9 @@ class OrbitControls extends EventDispatcher {
function onTouchStart( event ) {
if ( scope.enabled === false ) return;
event.preventDefault(); // prevent scrolling
trackPointer( event );
switch ( event.touches.length ) {
switch ( pointers.length ) {
case 1:
......@@ -1036,7 +1050,7 @@ class OrbitControls extends EventDispatcher {
if ( scope.enableRotate === false ) return;
handleTouchStartRotate( event );
handleTouchStartRotate();
state = STATE.TOUCH_ROTATE;
......@@ -1046,7 +1060,7 @@ class OrbitControls extends EventDispatcher {
if ( scope.enablePan === false ) return;
handleTouchStartPan( event );
handleTouchStartPan();
state = STATE.TOUCH_PAN;
......@@ -1068,7 +1082,7 @@ class OrbitControls extends EventDispatcher {
if ( scope.enableZoom === false && scope.enablePan === false ) return;
handleTouchStartDollyPan( event );
handleTouchStartDollyPan();
state = STATE.TOUCH_DOLLY_PAN;
......@@ -1078,7 +1092,7 @@ class OrbitControls extends EventDispatcher {
if ( scope.enableZoom === false && scope.enableRotate === false ) return;
handleTouchStartDollyRotate( event );
handleTouchStartDollyRotate();
state = STATE.TOUCH_DOLLY_ROTATE;
......@@ -1108,9 +1122,7 @@ class OrbitControls extends EventDispatcher {
function onTouchMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault(); // prevent scrolling
trackPointer( event );
switch ( state ) {
......@@ -1164,8 +1176,6 @@ class OrbitControls extends EventDispatcher {
function onTouchEnd( event ) {
if ( scope.enabled === false ) return;
handleTouchEnd( event );
scope.dispatchEvent( _endEvent );
......@@ -1182,17 +1192,60 @@ class OrbitControls extends EventDispatcher {
}
function addPointer( event ) {
pointers.push( event );
}
function removePointer( event ) {
delete pointerPositions[ event.pointerId ];
for ( let i = 0; i < pointers.length; i ++ ) {
if ( pointers[ i ].pointerId == event.pointerId ) {
pointers.splice( i, 1 );
return;
}
}
}
function trackPointer( event ) {
let position = pointerPositions[ event.pointerId ];
if ( position === undefined ) {
position = new Vector2();
pointerPositions[ event.pointerId ] = position;
}
position.set( event.pageX, event.pageY );
}
function getSecondPointerPosition( event ) {
const pointer = ( event.pointerId === pointers[ 0 ].pointerId ) ? pointers[ 1 ] : pointers[ 0 ];
return pointerPositions[ pointer.pointerId ];
}
//
scope.domElement.addEventListener( 'contextmenu', onContextMenu );
scope.domElement.addEventListener( 'pointerdown', onPointerDown );
scope.domElement.addEventListener( 'pointercancel', onPointerCancel );
scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
scope.domElement.addEventListener( 'touchstart', onTouchStart, { passive: false } );
scope.domElement.addEventListener( 'touchend', onTouchEnd );
scope.domElement.addEventListener( 'touchmove', onTouchMove, { passive: false } );
// force an update at start
this.update();
......
......@@ -24,6 +24,7 @@ class TrackballControls extends EventDispatcher {
this.object = object;
this.domElement = domElement;
this.domElement.style.touchAction = 'none'; // disable touch scroll
// API
......@@ -77,7 +78,10 @@ class TrackballControls extends EventDispatcher {
_zoomEnd = new Vector2(),
_panStart = new Vector2(),
_panEnd = new Vector2();
_panEnd = new Vector2(),
_pointers = [],
_pointerPositions = {};
// for reset
......@@ -406,14 +410,24 @@ class TrackballControls extends EventDispatcher {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( _pointers.length === 0 ) {
case 'mouse':
case 'pen':
onMouseDown( event );
break;
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
}
//
addPointer( event );
// TODO touch
if ( event.pointerType === 'touch' ) {
onTouchStart( event );
} else {
onMouseDown( event );
}
......@@ -423,14 +437,13 @@ class TrackballControls extends EventDispatcher {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( event.pointerType === 'touch' ) {
case 'mouse':
case 'pen':
onMouseMove( event );
break;
onTouchMove( event );
// TODO touch
} else {
onMouseMove( event );
}
......@@ -440,17 +453,34 @@ class TrackballControls extends EventDispatcher {
if ( scope.enabled === false ) return;
switch ( event.pointerType ) {
if ( event.pointerType === 'touch' ) {
case 'mouse':
case 'pen':
onMouseUp( event );
break;
onTouchEnd( event );
} else {
// TODO touch
onMouseUp();
}
//
removePointer( event );
if ( _pointers.length === 0 ) {
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
}
}
function onPointerCancel( event ) {
removePointer( event );
}
function keydown( event ) {
......@@ -491,8 +521,6 @@ class TrackballControls extends EventDispatcher {
function onMouseDown( event ) {
event.preventDefault();
if ( _state === STATE.NONE ) {
switch ( event.button ) {
......@@ -544,10 +572,6 @@ class TrackballControls extends EventDispatcher {
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
const state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
if ( state === STATE.ROTATE && ! scope.noRotate ) {
......@@ -567,11 +591,7 @@ class TrackballControls extends EventDispatcher {
}
function onMouseUp( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
function onMouseUp() {
_state = STATE.NONE;
......@@ -582,7 +602,7 @@ class TrackballControls extends EventDispatcher {
}
function mousewheel( event ) {
function onMouseWheel( event ) {
if ( scope.enabled === false ) return;
......@@ -614,28 +634,26 @@ class TrackballControls extends EventDispatcher {
}
function touchstart( event ) {
function onTouchStart( event ) {
if ( scope.enabled === false ) return;
trackPointer( event );
event.preventDefault();
switch ( event.touches.length ) {
switch ( _pointers.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_moveCurr.copy( getMouseOnCircle( _pointers[ 0 ].pageX, _pointers[ 0 ].pageY ) );
_movePrev.copy( _moveCurr );
break;
default: // 2 or more
_state = STATE.TOUCH_ZOOM_PAN;
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const dx = _pointers[ 0 ].pageX - _pointers[ 1 ].pageX;
const dy = _pointers[ 0 ].pageY - _pointers[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
const x = ( _pointers[ 0 ].pageX + _pointers[ 1 ].pageX ) / 2;
const y = ( _pointers[ 0 ].pageY + _pointers[ 1 ].pageY ) / 2;
_panStart.copy( getMouseOnScreen( x, y ) );
_panEnd.copy( _panStart );
break;
......@@ -646,26 +664,27 @@ class TrackballControls extends EventDispatcher {
}
function touchmove( event ) {
function onTouchMove( event ) {
if ( scope.enabled === false ) return;
trackPointer( event );
event.preventDefault();
switch ( event.touches.length ) {
switch ( _pointers.length ) {
case 1:
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
break;
default: // 2 or more
const dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
const dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
const position = getSecondPointerPosition( event );
const dx = event.pageX - position.x;
const dy = event.pageY - position.y;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
const x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
const y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
const x = ( event.pageX + position.x ) / 2;
const y = ( event.pageY + position.y ) / 2;
_panEnd.copy( getMouseOnScreen( x, y ) );
break;
......@@ -673,11 +692,9 @@ class TrackballControls extends EventDispatcher {
}
function touchend( event ) {
if ( scope.enabled === false ) return;
function onTouchEnd( event ) {
switch ( event.touches.length ) {
switch ( _pointers.length ) {
case 0:
_state = STATE.NONE;
......@@ -685,7 +702,7 @@ class TrackballControls extends EventDispatcher {
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
_movePrev.copy( _moveCurr );
break;
......@@ -703,19 +720,59 @@ class TrackballControls extends EventDispatcher {
}
function addPointer( event ) {
_pointers.push( event );
}
function removePointer( event ) {
delete _pointerPositions[ event.pointerId ];
for ( let i = 0; i < _pointers.length; i ++ ) {
if ( _pointers[ i ].pointerId == event.pointerId ) {
_pointers.splice( i, 1 );
return;
}
}
}
function trackPointer( event ) {
let position = _pointerPositions[ event.pointerId ];
if ( position === undefined ) {
position = new Vector2();
_pointerPositions[ event.pointerId ] = position;
}
position.set( event.pageX, event.pageY );
}
function getSecondPointerPosition( event ) {
const pointer = ( event.pointerId === _pointers[ 0 ].pointerId ) ? _pointers[ 1 ] : _pointers[ 0 ];
return _pointerPositions[ pointer.pointerId ];
}
this.dispose = function () {
scope.domElement.removeEventListener( 'contextmenu', contextmenu );
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
scope.domElement.removeEventListener( 'wheel', mousewheel );
scope.domElement.removeEventListener( 'touchstart', touchstart );
scope.domElement.removeEventListener( 'touchend', touchend );
scope.domElement.removeEventListener( 'touchmove', touchmove );
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
scope.domElement.removeEventListener( 'wheel', onMouseWheel );
window.removeEventListener( 'keydown', keydown );
window.removeEventListener( 'keyup', keyup );
......@@ -725,14 +782,9 @@ class TrackballControls extends EventDispatcher {
this.domElement.addEventListener( 'contextmenu', contextmenu );
this.domElement.addEventListener( 'pointerdown', onPointerDown );
this.domElement.addEventListener( 'wheel', mousewheel, { passive: false } );
this.domElement.addEventListener( 'touchstart', touchstart, { passive: false } );
this.domElement.addEventListener( 'touchend', touchend );
this.domElement.addEventListener( 'touchmove', touchmove, { passive: false } );
this.domElement.addEventListener( 'pointercancel', onPointerCancel );
this.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
window.addEventListener( 'keydown', keydown );
window.addEventListener( 'keyup', keyup );
......
......@@ -34,7 +34,7 @@ class DRACOExporter {
if ( DracoEncoderModule === undefined ) {
throw new Error( 'THREE.DRACOExporter: required the draco_decoder to work.' );
throw new Error( 'THREE.DRACOExporter: required the draco_encoder to work.' );
}
......
......@@ -1185,12 +1185,21 @@ class GLTFWriter {
if ( material.emissive ) {
// note: `emissive` is not scaled by `material.emissiveIntensity` for now to accommodate glTF spec. see #21849.
const emissive = material.emissive.toArray();
// note: emissive components are limited to stay within the 0 - 1 range to accommodate glTF spec. see #21849 and #22000.
const emissive = material.emissive.clone().multiplyScalar( material.emissiveIntensity );
const maxEmissiveComponent = Math.max( emissive.r, emissive.g, emissive.b );
if ( ! equalArray( emissive, [ 0, 0, 0 ] ) ) {
if ( maxEmissiveComponent > 1 ) {
materialDef.emissiveFactor = emissive;
emissive.multiplyScalar( 1 / maxEmissiveComponent );
console.warn( 'THREE.GLTFExporter: Some emissive components exceed 1; emissive has been limited' );
}
if ( maxEmissiveComponent > 0 ) {
materialDef.emissiveFactor = emissive.toArray();
}
......
......@@ -426,6 +426,14 @@ function buildMaterial( material, textures ) {
inputs.push( `${ pad }float inputs:opacity = ${ material.opacity }` );
if ( material.isMeshPhysicalMaterial ) {
inputs.push( `${ pad }float inputs:clearcoat = ${ material.clearcoat }` );
inputs.push( `${ pad }float inputs:clearcoatRoughness = ${ material.clearcoatRoughness }` );
inputs.push( `${ pad }float inputs:ior = ${ material.ior }` );
}
return `
def Material "Material_${ material.id }"
{
......
......@@ -39,8 +39,7 @@ ShaderLib[ 'line' ] = {
UniformsLib.line
] ),
vertexShader:
`
vertexShader: /* glsl */`
#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
......@@ -183,11 +182,9 @@ ShaderLib[ 'line' ] = {
#include <clipping_planes_vertex>
#include <fog_vertex>
}
`,
}`,
fragmentShader:
`
fragmentShader: /* glsl */`
uniform vec3 diffuse;
uniform float opacity;
......@@ -263,8 +260,8 @@ ShaderLib[ 'line' ] = {
#include <fog_fragment>
#include <premultiplied_alpha_fragment>
}
`
}`
};
class LineMaterial extends ShaderMaterial {
......
......@@ -4,7 +4,6 @@ import {
Box3,
BufferAttribute,
BufferGeometry,
CanvasTexture,
ClampToEdgeWrapping,
Color,
DirectionalLight,
......@@ -54,6 +53,7 @@ import {
Sphere,
SpotLight,
TangentSpaceNormalMap,
Texture,
TextureLoader,
TriangleFanDrawMode,
TriangleStripDrawMode,
......@@ -2598,7 +2598,10 @@ class GLTFParser {
onLoad = function ( imageBitmap ) {
resolve( new CanvasTexture( imageBitmap ) );
const texture = new Texture( imageBitmap );
texture.needsUpdate = true;
resolve( texture );
};
......@@ -2640,6 +2643,11 @@ class GLTFParser {
return texture;
} ).catch( function () {
console.error( 'THREE.GLTFLoader: Couldn\'t load texture', sourceURI );
return null;
} );
this.textureCache[ cacheKey ] = promise;
......
//Example: https://github.com/tomvandig/web-ifc-three/tree/main/examples/jsm
//Docs: https://agviegas.github.io/ifcjs-docs/#/
import { IfcAPI } from './ifc/web-ifc-api.js';
import * as WebIFC from './ifc/web-ifc-api.js';
import {
FileLoader,
Loader,
Object3D,
Mesh,
Color,
MeshPhongMaterial,
MeshBasicMaterial,
MeshLambertMaterial,
DoubleSide,
Matrix4,
BufferGeometry,
InterleavedBuffer,
InterleavedBufferAttribute,
BufferAttribute,
} from '../../../build/three.module.js';
import {BufferGeometryUtils} from "../utils/BufferGeometryUtils.js"
const ifcAPI = new IfcAPI();
const ifcAPI = new WebIFC.IfcAPI();
class IFCLoader extends Loader {
constructor( manager ) {
super( manager );
this.modelID = 0;
this.mapFaceindexID = {};
this.mapIDGeometry = {};
this.selectedObjects = [];
this.highlightMaterial = new MeshBasicMaterial({ color: 0xff0000, depthTest: false, side: DoubleSide });
}
......@@ -66,8 +70,168 @@ class IFCLoader extends Loader {
}
setWasmPath( path ) {
ifcAPI.SetWasmPath( path );
}
getExpressId( faceIndex ) {
for (let index in this.mapFaceindexID) {
if (parseInt(index) >= faceIndex) return this.mapFaceindexID[index];
}
return -1;
}
highlightItems( expressIds, scene, material = this.highlightMaterial ) {
this.removePreviousSelection(scene);
expressIds.forEach((id) => {
if (!this.mapIDGeometry[id]) return;
var mesh = new Mesh(this.mapIDGeometry[id], material);
mesh.renderOrder = 1;
scene.add(mesh);
this.selectedObjects.push(mesh);
return;
});
}
removePreviousSelection( scene ) {
if (this.selectedObjects.length > 0){
this.selectedObjects.forEach((object) => scene.remove(object));
}
}
setItemsVisibility( expressIds, geometry, visible = false ) {
this.setupVisibility(geometry);
var previous = 0;
for (var current in this.mapFaceindexID) {
if (expressIds.includes(this.mapFaceindexID[current])) {
for (var i = previous; i <= current; i++) this.setVertexVisibility(geometry, i, visible);
}
previous = current;
}
geometry.attributes.visibility.needsUpdate = true;
}
setVertexVisibility( geometry, index, visible ) {
var isVisible = visible ? 0 : 1;
var geoIndex = geometry.index.array;
geometry.attributes.visibility.setX(geoIndex[3 * index], isVisible);
geometry.attributes.visibility.setX(geoIndex[3 * index + 1], isVisible);
geometry.attributes.visibility.setX(geoIndex[3 * index + 2], isVisible);
}
setupVisibility( geometry ) {
if (!geometry.attributes.visibility) {
var visible = new Float32Array(geometry.getAttribute('position').count);
geometry.setAttribute('visibility', new BufferAttribute(visible, 1));
}
}
getItemProperties( elementID, all = false ) {
const properties = ifcAPI.GetLine(this.modelID, elementID);
if (all) {
const propSetIds = this.getAllRelatedItemsOfType(elementID, WebIFC.IFCRELDEFINESBYPROPERTIES, "RelatedObjects", "RelatingPropertyDefinition");
properties.hasPropertySets = propSetIds.map((id) => ifcAPI.GetLine(this.modelID, id, true));
const typeId = this.getAllRelatedItemsOfType(elementID, WebIFC.IFCRELDEFINESBYTYPE, "RelatedObjects", "RelatingType");
properties.hasType = typeId.map((id) => ifcAPI.GetLine(this.modelID, id, true));
}
// properties.type = properties.constructor.name;
return properties;
}
getSpatialStructure() {
let lines = ifcAPI.GetLineIDsWithType(this.modelID, WebIFC.IFCPROJECT);
let ifcProjectId = lines.get(0);
let ifcProject = ifcAPI.GetLine(this.modelID, ifcProjectId);
this.getAllSpatialChildren(ifcProject);
return ifcProject;
}
getAllSpatialChildren( spatialElement ) {
const id = spatialElement.expressID;
const spatialChildrenID = this.getAllRelatedItemsOfType(id, WebIFC.IFCRELAGGREGATES, "RelatingObject", "RelatedObjects");
spatialElement.hasSpatialChildren = spatialChildrenID.map((id) => ifcAPI.GetLine(this.modelID, id, false));
spatialElement.hasChildren = this.getAllRelatedItemsOfType(id, WebIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE, "RelatingStructure", "RelatedElements");
spatialElement.hasSpatialChildren.forEach(child => this.getAllSpatialChildren(child));
}
getAllRelatedItemsOfType ( elementID, type, relation, relatedProperty ) {
const lines = ifcAPI.GetLineIDsWithType(this.modelID, type);
const IDs = [];
for (let i = 0; i < lines.size(); i++) {
const relID = lines.get(i);
const rel = ifcAPI.GetLine(this.modelID, relID);
const relatedItems = rel[relation];
let foundElement = false;
if (Array.isArray(relatedItems)){
relatedItems.forEach((relID) => {
if (relID.value === elementID) foundElement = true;
});
}
else foundElement = (relatedItems.value === elementID);
if (foundElement) {
var element = rel[relatedProperty];
if (!Array.isArray(element)) IDs.push(element.value);
else element.forEach(ele => IDs.push(ele.value))
}
}
return IDs;
}
async parse( buffer ) {
const geometryByMaterials = {};
const mapIDGeometry = this.mapIDGeometry;
const mapFaceindexID = this.mapFaceindexID;
if ( ifcAPI.wasmModule === undefined ) {
await ifcAPI.Init();
......@@ -75,108 +239,197 @@ class IFCLoader extends Loader {
}
const data = new Uint8Array( buffer );
const modelID = ifcAPI.OpenModel( 'example.ifc', data );
return loadAllGeometry( modelID );
this.modelID = ifcAPI.OpenModel( 'example.ifc', data );
return loadAllGeometry( this.modelID );
function loadAllGeometry(modelID) {
saveAllPlacedGeometriesByMaterial(modelID);
return generateAllGeometriesByMaterial();
}
function generateAllGeometriesByMaterial() {
const { materials, geometries } = getMaterialsAndGeometries();
const allGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries, true);
return new Mesh(allGeometry, materials);
}
function getMaterialsAndGeometries() {
const materials = [];
const geometries = [];
let totalFaceCount = 0;
for (let i in geometryByMaterials) {
materials.push(geometryByMaterials[i].material);
const currentGeometries = geometryByMaterials[i].geometry;
geometries.push(BufferGeometryUtils.mergeBufferGeometries(currentGeometries));
function loadAllGeometry( modelID ) {
for (let j in geometryByMaterials[i].indices) {
const flatMeshes = getFlatMeshes( modelID );
const mainObject = new Object3D();
for ( let i = 0; i < flatMeshes.size(); i ++ ) {
const globalIndex = parseInt(j, 10) + parseInt(totalFaceCount, 10);
mapFaceindexID[globalIndex] = geometryByMaterials[i].indices[j];
const placedGeometries = flatMeshes.get( i ).geometries;
for ( let j = 0; j < placedGeometries.size(); j ++ )
mainObject.add( getPlacedGeometry( modelID, placedGeometries.get( j ) ) );
}
totalFaceCount += geometryByMaterials[i].lastIndex;
}
return mainObject;
return { materials, geometries };
}
function saveAllPlacedGeometriesByMaterial(modelID) {
function getFlatMeshes( modelID ) {
const flatMeshes = ifcAPI.LoadAllGeometry(modelID);
const flatMeshes = ifcAPI.LoadAllGeometry( modelID );
return flatMeshes;
for (let i = 0; i < flatMeshes.size(); i++) {
}
const flatMesh = flatMeshes.get(i);
const productId = flatMesh.expressID;
const placedGeometries = flatMesh.geometries;
function getPlacedGeometry( modelID, placedGeometry ) {
for (let j = 0; j < placedGeometries.size(); j++) {
const geometry = getBufferGeometry( modelID, placedGeometry );
const material = getMeshMaterial( placedGeometry.color );
const mesh = new Mesh( geometry, material );
mesh.matrix = getMeshMatrix( placedGeometry.flatTransformation );
mesh.matrixAutoUpdate = false;
return mesh;
savePlacedGeometryByMaterial(modelID, placedGeometries.get(j), productId);
}
}
}
function getBufferGeometry( modelID, placedGeometry ) {
function savePlacedGeometryByMaterial(modelID, placedGeometry, productId) {
const geometry = ifcAPI.GetGeometry(
modelID,
placedGeometry.geometryExpressID
);
const verts = ifcAPI.GetVertexArray(
geometry.GetVertexData(),
geometry.GetVertexDataSize()
);
const indices = ifcAPI.GetIndexArray(
geometry.GetIndexData(),
geometry.GetIndexDataSize()
);
const bufferGeometry = ifcGeometryToBuffer( verts, indices );
return bufferGeometry;
const geometry = getBufferGeometry(modelID, placedGeometry);
geometry.computeVertexNormals();
const matrix = getMeshMatrix(placedGeometry.flatTransformation);
geometry.applyMatrix4(matrix);
storeGeometryForHighlight(productId, geometry);
saveGeometryByMaterial(geometry, placedGeometry, productId);
}
function getMeshMaterial( color ) {
function getBufferGeometry(modelID, placedGeometry) {
const col = new Color( color.x, color.y, color.z );
const material = new MeshPhongMaterial( { color: col, side: DoubleSide } );
material.transparent = color.w !== 1;
if ( material.transparent ) material.opacity = color.w;
return material;
const geometry = ifcAPI.GetGeometry(modelID, placedGeometry.geometryExpressID);
const verts = ifcAPI.GetVertexArray(geometry.GetVertexData(), geometry.GetVertexDataSize());
const indices = ifcAPI.GetIndexArray(geometry.GetIndexData(), geometry.GetIndexDataSize());
return ifcGeometryToBuffer(verts, indices);
}
function getMeshMatrix( matrix ) {
function getMeshMatrix(matrix) {
const mat = new Matrix4();
mat.fromArray( matrix );
// mat.elements[15 - 3] *= 0.001;
// mat.elements[15 - 2] *= 0.001;
// mat.elements[15 - 1] *= 0.001;
mat.fromArray(matrix);
return mat;
}
function storeGeometryForHighlight(productId, geometry) {
if (!mapIDGeometry[productId]) {
function ifcGeometryToBuffer( vertexData, indexData ) {
mapIDGeometry[productId] = geometry;
return;
}
const geometries = [mapIDGeometry[productId], geometry];
mapIDGeometry[productId] = BufferGeometryUtils.mergeBufferGeometries(geometries, true);
}
function ifcGeometryToBuffer(vertexData, indexData) {
const geometry = new BufferGeometry();
const buffer32 = new InterleavedBuffer( vertexData, 6 );
geometry.setAttribute(
'position',
new InterleavedBufferAttribute( buffer32, 3, 0 )
);
geometry.setAttribute(
'normal',
new InterleavedBufferAttribute( buffer32, 3, 3 )
);
geometry.setIndex( new BufferAttribute( indexData, 1 ) );
const { vertices, normals } = extractVertexData(vertexData);
geometry.setAttribute('position', new BufferAttribute(new Float32Array(vertices), 3));
geometry.setAttribute('normal', new BufferAttribute(new Float32Array(normals), 3));
geometry.setIndex(new BufferAttribute(indexData, 1));
return geometry;
}
function extractVertexData(vertexData) {
}
const vertices = [];
const normals = [];
let isNormalData = false;
setWasmPath( path ) {
for (let i = 0; i < vertexData.length; i++) {
ifcAPI.SetWasmPath( path );
isNormalData ? normals.push(vertexData[i]) : vertices.push(vertexData[i]);
if ((i + 1) % 3 == 0) isNormalData = !isNormalData;
}
}
return { vertices, normals };
}
function saveGeometryByMaterial(geometry, placedGeometry, productId) {
const color = placedGeometry.color;
const id = `${color.x}${color.y}${color.z}${color.w}`;
createMaterial(id, color);
const currentGeometry = geometryByMaterials[id];
currentGeometry.geometry.push(geometry);
currentGeometry.lastIndex += geometry.index.count / 3;
currentGeometry.indices[currentGeometry.lastIndex] = productId;
}
function createMaterial(id, color) {
if (!geometryByMaterials[id]){
const col = new Color(color.x, color.y, color.z);
const newMaterial = new MeshLambertMaterial({ color: col, side: DoubleSide });
newMaterial.onBeforeCompile = materialHider;
newMaterial.transparent = color.w !== 1;
if (newMaterial.transparent) newMaterial.opacity = color.w;
geometryByMaterials[id] = initializeGeometryByMaterial(newMaterial);
}
}
function initializeGeometryByMaterial(newMaterial) {
return {
material: newMaterial,
geometry: [],
indices: {},
lastIndex: 0
};
}
function materialHider(shader) {
shader.vertexShader = `
attribute float sizes;
attribute float visibility;
varying float vVisible;
${shader.vertexShader}`.replace(
`#include <fog_vertex>`,
`#include <fog_vertex>
vVisible = visibility;
`
);
shader.fragmentShader = `
varying float vVisible;
${shader.fragmentShader}`.replace(
`#include <clipping_planes_fragment>`,
`
if (vVisible > 0.5) discard;
#include <clipping_planes_fragment>`
);
}
}
}
export { IFCLoader };
此差异已折叠。
此差异已折叠。
......@@ -17,7 +17,7 @@ import { VolumeSlice } from '../misc/VolumeSlice.js';
* @param {string} type The type of data (uint8, uint16, ...)
* @param {ArrayBuffer} arrayBuffer The buffer with volume data
*/
var Volume = function ( xLength, yLength, zLength, type, arrayBuffer ) {
function Volume( xLength, yLength, zLength, type, arrayBuffer ) {
if ( arguments.length > 0 ) {
......@@ -33,7 +33,10 @@ var Volume = function ( xLength, yLength, zLength, type, arrayBuffer ) {
* @member {number} zLength Depth of the volume in the IJK coordinate system
*/
this.zLength = Number( zLength ) || 1;
/**
* @member {Array<string>} The order of the Axis dictated by the NRRD header
*/
this.axisOrder = [ 'x', 'y', 'z' ];
/**
* @member {TypedArray} data Data of the volume
*/
......@@ -193,7 +196,7 @@ var Volume = function ( xLength, yLength, zLength, type, arrayBuffer ) {
* @member {Array} RASDimensions This array holds the dimensions of the volume in the RAS space
*/
};
}
Volume.prototype = {
......@@ -301,8 +304,8 @@ Volume.prototype = {
axisInIJK.set( 1, 0, 0 );
firstDirection.set( 0, 0, - 1 );
secondDirection.set( 0, - 1, 0 );
firstSpacing = this.spacing[ 2 ];
secondSpacing = this.spacing[ 1 ];
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'z' ) ];
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'y' ) ];
IJKIndex = new Vector3( RASIndex, 0, 0 );
planeMatrix.multiply( ( new Matrix4() ).makeRotationY( Math.PI / 2 ) );
......@@ -313,8 +316,8 @@ Volume.prototype = {
axisInIJK.set( 0, 1, 0 );
firstDirection.set( 1, 0, 0 );
secondDirection.set( 0, 0, 1 );
firstSpacing = this.spacing[ 0 ];
secondSpacing = this.spacing[ 2 ];
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'x' ) ];
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'z' ) ];
IJKIndex = new Vector3( 0, RASIndex, 0 );
planeMatrix.multiply( ( new Matrix4() ).makeRotationX( - Math.PI / 2 ) );
......@@ -326,8 +329,8 @@ Volume.prototype = {
axisInIJK.set( 0, 0, 1 );
firstDirection.set( 1, 0, 0 );
secondDirection.set( 0, - 1, 0 );
firstSpacing = this.spacing[ 0 ];
secondSpacing = this.spacing[ 1 ];
firstSpacing = this.spacing[ this.axisOrder.indexOf( 'x' ) ];
secondSpacing = this.spacing[ this.axisOrder.indexOf( 'y' ) ];
IJKIndex = new Vector3( 0, 0, RASIndex );
positionOffset = ( volume.RASDimensions[ 2 ] - 1 ) / 2;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -65,7 +65,7 @@ const WaterRefractionShader = {
void main() {
float waveStrength = 0.1;
float waveStrength = 0.5;
float waveSpeed = 0.03;
// simple distortion (ripple) via dudv map (see https://www.youtube.com/watch?v=6B7IF6GOu7s)
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册