Mercurial > hg > orthanc-stl
changeset 64:5e11f5880e6d
simplified the embedding of Nexus viewer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sat, 15 Jun 2024 16:35:21 +0200 |
parents | 1fd6d0f8fdc9 |
children | 6a891818bad5 |
files | CMakeLists.txt Resources/CMake/NexusViewer.cmake Resources/CMake/nexus-viewer-4.2.patch Resources/Nexus.txt Resources/Nexus/js/TrackballControls.js Resources/Nexus/js/meco.js Resources/Nexus/js/nexus.js Resources/Nexus/js/nexus_three.js Resources/Nexus/threejs.html Sources/Plugin.cpp |
diffstat | 10 files changed, 65 insertions(+), 3105 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Sat Jun 15 16:17:41 2024 +0200 +++ b/CMakeLists.txt Sat Jun 15 16:35:21 2024 +0200 @@ -198,26 +198,10 @@ list(APPEND STATIC_ASSETS_CONTENT "${CMAKE_SOURCE_DIR}/JavaScriptLibraries/dist-three") if (ENABLE_NEXUS) - set(NEXUS_ASSETS_DIR ${AUTOGENERATED_DIR}/nexus) - file(MAKE_DIRECTORY ${NEXUS_ASSETS_DIR}) - - DownloadCompressedFile( - "df21a4a192c0952a1189125609cc76f9" - "https://orthanc.uclouvain.be/downloads/third-party-downloads/STL/three-84.js.gz" - "${NEXUS_ASSETS_DIR}/three-84.js") + include(${CMAKE_SOURCE_DIR}/Resources/CMake/NexusViewer.cmake) list(APPEND STATIC_ASSETS_PREFIXES "nexus") - list(APPEND STATIC_ASSETS_CONTENT - ${NEXUS_ASSETS_DIR} # This adds "three-84.js" that is needed by the Nexus viewer - ) - - list(APPEND EMBEDDED_RESOURCES - NEXUS_HTML ${CMAKE_SOURCE_DIR}/Resources/Nexus/threejs.html - NEXUS_JS ${CMAKE_SOURCE_DIR}/Resources/Nexus/js/nexus.js - NEXUS_MECO_JS ${CMAKE_SOURCE_DIR}/Resources/Nexus/js/meco.js - NEXUS_THREE_JS ${CMAKE_SOURCE_DIR}/Resources/Nexus/js/nexus_three.js - NEXUS_TRACKBALL_JS ${CMAKE_SOURCE_DIR}/Resources/Nexus/js/TrackballControls.js - ) + list(APPEND STATIC_ASSETS_CONTENT ${NEXUS_VIEWER_DIR}) add_definitions(-DORTHANC_ENABLE_NEXUS=1)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/NexusViewer.cmake Sat Jun 15 16:35:21 2024 +0200 @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: 2023-2024 Sebastien Jodogne, UCLouvain, Belgium +# SPDX-License-Identifier: GPL-3.0-or-later + + +# STL plugin for Orthanc +# Copyright (C) 2023-2024 Sebastien Jodogne, UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +DownloadPackage( + "6069b141edb7ce1d543b53ddaa4b41d1" + "https://orthanc.uclouvain.be/downloads/third-party-downloads/STL/nexus-4.2.zip" + "${CMAKE_BINARY_DIR}/nexus-4.2") + +set(NEXUS_VIEWER_DIR ${CMAKE_CURRENT_BINARY_DIR}/nexus) +file(MAKE_DIRECTORY ${NEXUS_VIEWER_DIR}) + +DownloadCompressedFile( + "df21a4a192c0952a1189125609cc76f9" + "https://orthanc.uclouvain.be/downloads/third-party-downloads/STL/three-84.js.gz" + "${NEXUS_VIEWER_DIR}/three-84.js") + +file(COPY + ${CMAKE_BINARY_DIR}/nexus-4.2/html/js + ${CMAKE_BINARY_DIR}/nexus-4.2/html/threejs.html + DESTINATION + ${NEXUS_VIEWER_DIR} + ) + +execute_process( + COMMAND ${PATCH_EXECUTABLE} -p0 -N -i + ${CMAKE_CURRENT_LIST_DIR}/nexus-viewer-4.2.patch + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE Failure + ) + +list(APPEND STATIC_ASSETS_PREFIXES "nexus") +list(APPEND STATIC_ASSETS_CONTENT ${NEXUS_VIEWER_DIR})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/nexus-viewer-4.2.patch Sat Jun 15 16:35:21 2024 +0200 @@ -0,0 +1,12 @@ +diff -urEb nexus.orig/threejs.html nexus/threejs.html +--- nexus.orig/threejs.html 2024-06-15 16:27:10.763574203 +0200 ++++ nexus/threejs.html 2024-06-15 16:28:03.807136118 +0200 +@@ -5,7 +5,7 @@ + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> + <style>body { margin: 0px; overflow: hidden; }</style> +-<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js"></script> ++<script src="three-84.js"></script> + <script src="js/TrackballControls.js"></script> + <script src="js/nexus.js"></script> + <script src="js/nexus_three.js"></script>
--- a/Resources/Nexus.txt Sat Jun 15 16:17:41 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -Homepage: https://vcg.isti.cnr.it/nexus/ - -GitHub: https://github.com/cnr-isti-vclab/nexus - - -The files in folder "./Nexus" come from folder "html" in release 4.2 -(Nexus 2018), where the line: - -<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.js"></script> - -is replaced by the line: - -<script src="three-84.js"></script> - - -WARNING: Releases 4.2.1, 4.2.2, and 4.3 do not seem to work anymore.
--- a/Resources/Nexus/js/TrackballControls.js Sat Jun 15 16:17:41 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,604 +0,0 @@ -/** - * @author Eberhard Graether / http://egraether.com/ - * @author Mark Lundin / http://mark-lundin.com - * @author Simone Manini / http://daron1337.github.io - * @author Luca Antiga / http://lantiga.github.io - */ - -THREE.TrackballControls = function ( object, domElement ) { - - var _this = this; - var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; - - this.object = object; - this.domElement = ( domElement !== undefined ) ? domElement : document; - - // API - - this.enabled = true; - - this.screen = { left: 0, top: 0, width: 0, height: 0 }; - - this.rotateSpeed = 1.0; - this.zoomSpeed = 1.2; - this.panSpeed = 0.3; - - this.noRotate = false; - this.noZoom = false; - this.noPan = false; - - this.staticMoving = false; - this.dynamicDampingFactor = 0.2; - - this.minDistance = 0; - this.maxDistance = Infinity; - - this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; - - // internals - - this.target = new THREE.Vector3(); - - var EPS = 0.000001; - - var lastPosition = new THREE.Vector3(); - - var _state = STATE.NONE, - _prevState = STATE.NONE, - - _eye = new THREE.Vector3(), - - _movePrev = new THREE.Vector2(), - _moveCurr = new THREE.Vector2(), - - _lastAxis = new THREE.Vector3(), - _lastAngle = 0, - - _zoomStart = new THREE.Vector2(), - _zoomEnd = new THREE.Vector2(), - - _touchZoomDistanceStart = 0, - _touchZoomDistanceEnd = 0, - - _panStart = new THREE.Vector2(), - _panEnd = new THREE.Vector2(); - - // for reset - - this.target0 = this.target.clone(); - this.position0 = this.object.position.clone(); - this.up0 = this.object.up.clone(); - - // events - - var changeEvent = { type: 'change' }; - var startEvent = { type: 'start' }; - var endEvent = { type: 'end' }; - - - // methods - - this.handleResize = function () { - - if ( this.domElement === document ) { - - this.screen.left = 0; - this.screen.top = 0; - this.screen.width = window.innerWidth; - this.screen.height = window.innerHeight; - - } else { - - var box = this.domElement.getBoundingClientRect(); - // adjustments come from similar code in the jquery offset() function - var d = this.domElement.ownerDocument.documentElement; - this.screen.left = box.left + window.pageXOffset - d.clientLeft; - this.screen.top = box.top + window.pageYOffset - d.clientTop; - this.screen.width = box.width; - this.screen.height = box.height; - - } - - }; - - this.handleEvent = function ( event ) { - - if ( typeof this[ event.type ] == 'function' ) { - - this[ event.type ]( event ); - - } - - }; - - var getMouseOnScreen = ( function () { - - var vector = new THREE.Vector2(); - - return function getMouseOnScreen( pageX, pageY ) { - - vector.set( - ( pageX - _this.screen.left ) / _this.screen.width, - ( pageY - _this.screen.top ) / _this.screen.height - ); - - return vector; - - }; - - }() ); - - var getMouseOnCircle = ( function () { - - var vector = new THREE.Vector2(); - - return function getMouseOnCircle( pageX, pageY ) { - - vector.set( - ( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / ( _this.screen.width * 0.5 ) ), - ( ( _this.screen.height + 2 * ( _this.screen.top - pageY ) ) / _this.screen.width ) // screen.width intentional - ); - - return vector; - - }; - - }() ); - - this.rotateCamera = ( function() { - - var axis = new THREE.Vector3(), - quaternion = new THREE.Quaternion(), - eyeDirection = new THREE.Vector3(), - objectUpDirection = new THREE.Vector3(), - objectSidewaysDirection = new THREE.Vector3(), - moveDirection = new THREE.Vector3(), - angle; - - return function rotateCamera() { - - moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 ); - angle = moveDirection.length(); - - if ( angle ) { - - _eye.copy( _this.object.position ).sub( _this.target ); - - eyeDirection.copy( _eye ).normalize(); - objectUpDirection.copy( _this.object.up ).normalize(); - objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize(); - - objectUpDirection.setLength( _moveCurr.y - _movePrev.y ); - objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x ); - - moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) ); - - axis.crossVectors( moveDirection, _eye ).normalize(); - - angle *= _this.rotateSpeed; - quaternion.setFromAxisAngle( axis, angle ); - - _eye.applyQuaternion( quaternion ); - _this.object.up.applyQuaternion( quaternion ); - - _lastAxis.copy( axis ); - _lastAngle = angle; - - } else if ( ! _this.staticMoving && _lastAngle ) { - - _lastAngle *= Math.sqrt( 1.0 - _this.dynamicDampingFactor ); - _eye.copy( _this.object.position ).sub( _this.target ); - quaternion.setFromAxisAngle( _lastAxis, _lastAngle ); - _eye.applyQuaternion( quaternion ); - _this.object.up.applyQuaternion( quaternion ); - - } - - _movePrev.copy( _moveCurr ); - - }; - - }() ); - - - this.zoomCamera = function () { - - var factor; - - if ( _state === STATE.TOUCH_ZOOM_PAN ) { - - factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; - _touchZoomDistanceStart = _touchZoomDistanceEnd; - _eye.multiplyScalar( factor ); - - } else { - - factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; - - if ( factor !== 1.0 && factor > 0.0 ) { - - _eye.multiplyScalar( factor ); - - if ( _this.staticMoving ) { - - _zoomStart.copy( _zoomEnd ); - - } else { - - _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; - - } - - } - - } - - }; - - this.panCamera = ( function() { - - var mouseChange = new THREE.Vector2(), - objectUp = new THREE.Vector3(), - pan = new THREE.Vector3(); - - return function panCamera() { - - mouseChange.copy( _panEnd ).sub( _panStart ); - - if ( mouseChange.lengthSq() ) { - - mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); - - pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); - pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); - - _this.object.position.add( pan ); - _this.target.add( pan ); - - if ( _this.staticMoving ) { - - _panStart.copy( _panEnd ); - - } else { - - _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); - - } - - } - - }; - - }() ); - - this.checkDistances = function () { - - if ( ! _this.noZoom || ! _this.noPan ) { - - if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) { - - _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) ); - _zoomStart.copy( _zoomEnd ); - - } - - if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) { - - _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) ); - _zoomStart.copy( _zoomEnd ); - - } - - } - - }; - - this.update = function () { - - _eye.subVectors( _this.object.position, _this.target ); - - if ( ! _this.noRotate ) { - - _this.rotateCamera(); - - } - - if ( ! _this.noZoom ) { - - _this.zoomCamera(); - - } - - if ( ! _this.noPan ) { - - _this.panCamera(); - - } - - _this.object.position.addVectors( _this.target, _eye ); - - _this.checkDistances(); - - _this.object.lookAt( _this.target ); - - if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { - - _this.dispatchEvent( changeEvent ); - - lastPosition.copy( _this.object.position ); - - } - - }; - - this.reset = function () { - - _state = STATE.NONE; - _prevState = STATE.NONE; - - _this.target.copy( _this.target0 ); - _this.object.position.copy( _this.position0 ); - _this.object.up.copy( _this.up0 ); - - _eye.subVectors( _this.object.position, _this.target ); - - _this.object.lookAt( _this.target ); - - _this.dispatchEvent( changeEvent ); - - lastPosition.copy( _this.object.position ); - - }; - - // listeners - - function keydown( event ) { - - if ( _this.enabled === false ) return; - - _prevState = _state; - - if ( _state !== STATE.NONE ) { - - return; - - } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ) { - - _state = STATE.ROTATE; - - } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && ! _this.noZoom ) { - - _state = STATE.ZOOM; - - } else if ( event.keyCode === _this.keys[ STATE.PAN ] && ! _this.noPan ) { - - _state = STATE.PAN; - - } - - } - - function keyup( event ) { - - if ( _this.enabled === false ) return; - - _state = _prevState; - - } - - function mousedown( event ) { - - if ( _this.enabled === false ) return; - - if ( _state === STATE.NONE ) { - - _state = event.button; - - } - - if ( _state === STATE.ROTATE && ! _this.noRotate ) { - - _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) ); - _movePrev.copy( _moveCurr ); - - } else if ( _state === STATE.ZOOM && ! _this.noZoom ) { - - _zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); - _zoomEnd.copy( _zoomStart ); - - } else if ( _state === STATE.PAN && ! _this.noPan ) { - - _panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); - _panEnd.copy( _panStart ); - - } - - document.addEventListener( 'mousemove', mousemove, false ); - document.addEventListener( 'mouseup', mouseup, false ); - - _this.dispatchEvent( startEvent ); - - } - - function mousemove( event ) { - - if ( _this.enabled === false ) return; - - if ( _state === STATE.ROTATE && ! _this.noRotate ) { - - _movePrev.copy( _moveCurr ); - _moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) ); - - } else if ( _state === STATE.ZOOM && ! _this.noZoom ) { - - _zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); - - } else if ( _state === STATE.PAN && ! _this.noPan ) { - - _panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); - - } - - } - - function mouseup( event ) { - - if ( _this.enabled === false ) return; - - _state = STATE.NONE; - - document.removeEventListener( 'mousemove', mousemove ); - document.removeEventListener( 'mouseup', mouseup ); - _this.dispatchEvent( endEvent ); - - } - - function mousewheel( event ) { - - if ( _this.enabled === false ) return; - - var delta = 0; - - if ( event.wheelDelta ) { - - // WebKit / Opera / Explorer 9 - - delta = event.wheelDelta / 40; - - } else if ( event.detail ) { - - // Firefox - - delta = - event.detail / 3; - - } - - _zoomStart.y += delta * 0.01; - _this.dispatchEvent( startEvent ); - _this.dispatchEvent( endEvent ); - - } - - function touchstart( event ) { - - if ( _this.enabled === false ) return; - - switch ( event.touches.length ) { - - case 1: - _state = STATE.TOUCH_ROTATE; - _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); - _movePrev.copy( _moveCurr ); - break; - - default: // 2 or more - _state = STATE.TOUCH_ZOOM_PAN; - var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; - var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; - _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); - - var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; - var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; - _panStart.copy( getMouseOnScreen( x, y ) ); - _panEnd.copy( _panStart ); - break; - - } - - _this.dispatchEvent( startEvent ); - - } - - function touchmove( event ) { - - if ( _this.enabled === false ) return; - - switch ( event.touches.length ) { - - case 1: - _movePrev.copy( _moveCurr ); - _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); - break; - - default: // 2 or more - var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; - var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; - _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); - - var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; - var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; - _panEnd.copy( getMouseOnScreen( x, y ) ); - break; - - } - - } - - function touchend( event ) { - - if ( _this.enabled === false ) return; - - switch ( event.touches.length ) { - - case 0: - _state = STATE.NONE; - break; - - case 1: - _state = STATE.TOUCH_ROTATE; - _moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); - _movePrev.copy( _moveCurr ); - break; - - } - - _this.dispatchEvent( endEvent ); - - } - - function contextmenu( event ) { - - event.preventDefault(); - - } - - this.dispose = function() { - - this.domElement.removeEventListener( 'contextmenu', contextmenu, false ); - this.domElement.removeEventListener( 'mousedown', mousedown, false ); - this.domElement.removeEventListener( 'mousewheel', mousewheel, false ); - this.domElement.removeEventListener( 'MozMousePixelScroll', mousewheel, false ); // firefox - - this.domElement.removeEventListener( 'touchstart', touchstart, false ); - this.domElement.removeEventListener( 'touchend', touchend, false ); - this.domElement.removeEventListener( 'touchmove', touchmove, false ); - - document.removeEventListener( 'mousemove', mousemove, false ); - document.removeEventListener( 'mouseup', mouseup, false ); - - window.removeEventListener( 'keydown', keydown, false ); - window.removeEventListener( 'keyup', keyup, false ); - - }; - - this.domElement.addEventListener( 'contextmenu', contextmenu, false ); - this.domElement.addEventListener( 'mousedown', mousedown, false ); - this.domElement.addEventListener( 'mousewheel', mousewheel, false ); - this.domElement.addEventListener( 'MozMousePixelScroll', mousewheel, false ); // firefox - - this.domElement.addEventListener( 'touchstart', touchstart, false ); - this.domElement.addEventListener( 'touchend', touchend, false ); - this.domElement.addEventListener( 'touchmove', touchmove, false ); - - window.addEventListener( 'keydown', keydown, false ); - window.addEventListener( 'keyup', keyup, false ); - - this.handleResize(); - - // force an update at start - this.update(); - -}; - -THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); -THREE.TrackballControls.prototype.constructor = THREE.TrackballControls;
--- a/Resources/Nexus/js/meco.js Sat Jun 15 16:17:41 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,954 +0,0 @@ -/* -Nexus -Copyright (c) 2012-2018, Visual Computing Lab, ISTI - CNR -All rights reserved. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -onmessage = function(job) { - if(typeof(job.data) == "string") return; - var node = job.data.node; - var signature = job.data.signature; - var patches = job.data.patches; -// var now =new Date().getTime(); - - var size; - if(!node.buffer) return; - else size = node.buffer.byteLength; - var buffer; - for(var i =0 ; i < 1; i++) { - var coder = new MeshCoder(signature, node, patches); - buffer = coder.decode(node.buffer); - } - node.buffer = buffer; - node.owner = job.owner; -// var elapsed = new Date().getTime() - now; -// var t = node.nface; -// console.log("Z Time: " + elapsed + " Size: " + size + " KT/s " + (t/(elapsed)) + " Mbps " + (8*1000*node.buffer.byteLength/elapsed)/(1<<20)); - postMessage(node); -} - -// actually bitstreams expects a little endian uin64 type. convert it to 2 uint32 - -BitStream = function(array) { - this.a = array; - for(var i = 0; i < array.length; i += 2) { - var s = array[i]; - array[i] = array[i+1]; - array[i+1] = s; - } - this.position = 0; - this.bitsPending = 0; -}; - -BitStream.prototype = { - read: function(bits) { - var bitBuffer = 0; - while(bits > 0) { - var partial; - var bitsConsumed; - if (this.bitsPending > 0) { - var byte = (this.a[this.position - 1] & (0xffffffff >>> (32 - this.bitsPending)))>>>0; - bitsConsumed = Math.min(this.bitsPending, bits); - this.bitsPending -= bitsConsumed; - partial = byte >>> this.bitsPending; - } else { - bitsConsumed = Math.min(32, bits); - this.bitsPending = 32 - bitsConsumed; - partial = this.a[this.position++] >>> this.bitsPending; - } - bits -= bitsConsumed; - bitBuffer = ((bitBuffer << bitsConsumed) | partial)>>>0; - } - return bitBuffer; - }, - replace: function(bits, value) { - //zero last part - value = (value & (0xffffffff >>> 32 - bits)) >>> 0; - value = (value | read(bits)) >>> 0; - return value; - } -}; - -Stream = function(buffer) { - this.data = buffer; - this.buffer = new Uint8Array(buffer); - this.pos = 0; -} - -Stream.prototype = { - readChar: function() { - var c = this.buffer[this.pos++]; - if(c > 127) c -= 256; - return c; - }, - readUChar: function() { - return this.buffer[this.pos++]; - }, - readInt: function() { - var c = this.buffer[this.pos + 3] - c <<= 8; - c |= this.buffer[this.pos + 2]; - c <<= 8; - c |= this.buffer[this.pos + 1]; - c <<= 8; - c |= this.buffer[this.pos + 0]; - this.pos += 4; - return c; - }, - readArray: function(n) { - var a = this.buffer.subarray(this.pos, this.pos+n); - this.pos += n; - return a; - }, - readBitStream:function() { - var n = this.readInt(); - var pad = this.pos & 0x3; - if(pad != 0) - this.pos += 4 - pad; - var b = new BitStream(new Uint32Array(this.data, this.pos, n*2)); - this.pos += n*8; - return b; - } -}; - -function Tunstall(wordsize, lookup_size) { - this.wordsize = wordsize? wordsize : 8; - this.lookup_size = lookup_size? lookup_size : 8; -} - -Tunstall.prototype = { - decompress: function(stream) { - var nsymbols = stream.readUChar(); - this.probabilities = stream.readArray(nsymbols*2); - this.createDecodingTables(); - var size = stream.readInt(); - var data = new Uint8Array(size); - var compressed_size = stream.readInt(); - var compressed_data = stream.readArray(compressed_size); - if(size) - this._decompress(compressed_data, compressed_size, data, size); - return data; - }, - - createDecodingTables: function() { - //read symbol,prob,symbol,prob as uchar. - //Here probabilities will range from 0 to 0xffff for better precision - - var n_symbols = this.probabilities.length/2; - if(n_symbols <= 1) return; - - var queues = []; //array of arrays - var buffer = []; - - //initialize adding all symbols to queues - for(var i = 0; i < n_symbols; i++) { - var symbol = this.probabilities[i*2]; - var s = [(this.probabilities[i*2+1])<<8, buffer.length, 1]; //probability, position in the buffer, length - queues[i] = [s]; - buffer.push(this.probabilities[i*2]); //symbol - } - var dictionary_size = 1<<this.wordsize; - var n_words = n_symbols; - var table_length = n_symbols; - - //at each step we grow all queues using the most probable sequence - while(n_words < dictionary_size - n_symbols +1) { - //Should use a stack or something to be faster, but we have few symbols - //find highest probability word - var best = 0; - var max_prob = 0; - for(var i = 0; i < n_symbols; i++) { - var p = queues[i][0][0]; //front of queue probability. - if(p > max_prob) { - best = i; - max_prob = p; - } - } - var symbol = queues[best][0]; - var pos = buffer.length; - - for(var i = 0; i < n_symbols; i++) { - var sym = this.probabilities[i*2]; - var prob = this.probabilities[i*2+1]<<8; - var s = [((prob*symbol[0])>>>16), pos, symbol[2]+1]; //combine probabilities, keep track of buffer, keep length of queue - - for(var k = 0; k < symbol[2]; k++) - buffer[pos+k] = buffer[symbol[1] + k]; //copy sequence of symbols - - pos += symbol[2]; - buffer[pos++] = sym; //append symbol - queues[i].push(s); - } - table_length += (n_symbols-1)*(symbol[2] + 1) +1; - n_words += n_symbols -1; - queues[best].shift(); //remove first thing - } - - this.index = new Uint32Array(n_words); - this.lengths = new Uint32Array(n_words); - this.table = new Uint8Array(table_length); - var word = 0; - var pos = 0; - for(i = 0; i < queues.length; i++) { - var queue = queues[i]; - for(var k = 0; k < queue.length; k++) { - var s = queue[k]; - this.index[word] = pos; - this.lengths[word] = s[2]; //length - word++; - - for(var j = 0; j < s[2]; j++) - this.table[pos + j] = buffer[s[1] + j]; //buffer of offset - pos += s[2]; //length - } - } - }, - _decompress: function(input, input_size, output, output_size) { - var input_pos = 0; - var output_pos = 0; - if(this.probabilities.length == 2) { - var symbol = this.probabilities[0]; - for(var i = 0; i < output_size; i++) - output[i] = symbol; - return; - } - - while(input_pos < input_size-1) { - var symbol = input[input_pos++]; - var start = this.index[symbol]; - var end = start + this.lengths[symbol]; - for(var i = start; i < end; i++) - output[output_pos++] = this.table[i]; - } - - //last symbol might override so we check. - var symbol = input[input_pos]; - var start = this.index[symbol]; - var end = start + output_size - output_pos; - var length = output_size - output_pos; - for(var i = start; i < end; i++) - output[output_pos++] = this.table[i]; - - return output; - } -} - -ZPoint = function(h, l) { - this.lo = l; - this.hi = h; -} - -ZPoint.prototype = { - copy: function(z) { - this.lo = z.lo; - this.hi = z.hi; - }, - setBit: function(d) { - if(d < 32) - this.lo = (this.lo | (1<<d))>>>0; - else - this.hi = (this.hi | (1<<(d-32)))>>>0; - }, - toPoint: function(min, step, buffer, pos) { - var x = this.morton3(this.lo, this.hi>>>1); - var y = this.morton3(this.lo>>>1, this.hi>>>2); - var z = this.morton3((this.lo>>>2 | (this.hi & 0x1)<<30 )>>>0, this.hi>>>3); //first hi bit needs to go into low. - - buffer[pos+0] = (x + min[0])*step; - buffer[pos+1] = (y + min[1])*step; - buffer[pos+2] = (z + min[2])*step; - }, - morton3: function(lo, hi) { - lo = ( lo & 0x49249249)>>>0; - lo = ((lo | (lo >>> 2 )) & 0xc30c30c3)>>>0; - lo = ((lo | (lo >>> 4 )) & 0x0f00f00f)>>>0; - lo = ((lo | (lo >>> 8 )) & 0xff0000ff)>>>0; - lo = ((lo | (lo >>> 16)) & 0x0000ffff)>>>0; - - hi = ( hi & 0x49249249)>>>0; - hi = ((hi | (hi >> 2 )) & 0xc30c30c3)>>>0; - hi = ((hi | (hi >> 4 )) & 0x0f00f00f)>>>0; - hi = ((hi | (hi >> 8 )) & 0xff0000ff)>>>0; - hi = ((hi | (hi >> 16)) & 0x0000ffff)>>>0; - - return ((hi<<11) | lo)>>>0; - } -}; - -//node is an object with nvert, nface -//patches is an array of offsets in the index, triangle are grouped by those offsets -//signature tells wether mesh has indices, normals, colors, etc. {'colors': true, 'normals':true, 'indices': true } - -function MeshCoder(signature, node, patches) { - this.sig = signature; - this.node = node; - this.patches = patches; - - this.last = new Int32Array(this.node.nvert); - this.last_count = 0; -} - -MeshCoder.prototype = { - //assumes input is an ArrayBuffer -decode: function(input) { - var t = this; - - t.buffer = new ArrayBuffer(t.node.nvert*(12 + t.sig.texcoords*8 + t.sig.normals*6 + t.sig.colors*4) + t.node.nface*t.sig.indices*6); - - var size = t.node.nvert*12; //float - t.coords = new Float32Array(t.buffer, 0, t.node.nvert*3); - - if(t.sig.texcoords) { - t.texcoords = new Float32Array(t.buffer, size, t.node.nvert*2); - size += t.node.nvert*8; //float - } - if(t.sig.normals) { - t.normals = new Int16Array(t.buffer, size, t.node.nvert*3); - size += t.node.nvert*6; //short - } - if(t.sig.colors) { - t.colors = new Uint8ClampedArray(t.buffer, size, t.node.nvert*4); - size += t.node.nvert*4; //chars - } - if(t.sig.indices) { - t.faces = new Uint16Array(t.buffer, size, t.node.nface*3); - size += t.node.nface*6; //short - } - - t.stream = new Stream(input); - - t.stack = new Float32Array(12); //min0, min1, min2, step, tmin0, tmin1, tstep - - t.stack[3] = t.stream.readInt(); - t.stack[4] = t.stream.readInt(); - t.stack[5] = t.stream.readInt(); - - t.coord_q = t.stream.readChar(); - t.coord_bits = t.stream.readChar()*3; - - t.stack[6] = Math.pow(2.0, t.coord_q); - - if(t.sig.texcoords) { - t.stack[9] = t.stream.readInt(); - t.stack[10] = t.stream.readInt(); - - t.texcoord_q = t.stream.readChar(); - t.texcoord_bits = t.stream.readChar()*2; - t.stack[11] = Math.pow(2.0, t.texcoord_q); - } - - if(t.sig.indices) { - t.decodeFaces(); - -// var faces = window.performance.now() - start; -// start += faces; - } else { - t.decodeCoordinates(); - -// var coords = window.performance.now() - start; -// start += coords; - } - - if(t.sig.normals) - t.decodeNormals(); -// var normals = window.performance.now() - start; -// start += normals; - if(t.sig.colors) - t.decodeColors(); -// var colors = window.performance.now() - start; -// start += colors; -// console.log("Decode " + (faces + coords + normals + colors) + "ms. C: " + coords + " F: " + faces + " N: " + normals + " C: " + colors); - - return t.buffer; -}, - -decodeCoordinates: function() { - var t = this; - t.min = [t.stack[3], t.stack[4], t.stack[5]]; - - var step = Math.pow(2.0, t.coord_q); - - var hi_bits = Math.max(t.coord_bits - 32, 0); - var lo_bits = Math.min(t.coord_bits, 32); - - var bitstream = t.stream.readBitStream(); - - var tunstall = new Tunstall; - var diffs = tunstall.decompress(t.stream); - - var hi = bitstream.read(hi_bits); - var lo = bitstream.read(lo_bits); - var p = new ZPoint(hi, lo); - var count = 0; - p.toPoint(t.min, step, t.coords, count); - count += 3; - for(var i = 1; i < t.node.nvert; i++) { - var d = diffs[i-1]; - p.setBit(d, 1); - if(d > 32) { - p.hi = (p.hi & ~((1<<(d-32))-1))>>>0; - var e = bitstream.read(d - 32); - p.hi = (p.hi | e)>>>0; - p.lo = bitstream.read(32); - } else { - - if(d == 32) { - p.lo = bitstream.read(d); - } else { - var e = bitstream.read(d); - p.lo = (p.lo & ~((1<<d) -1))>>>0; - p.lo = (p.lo | e)>>>0; - } - } - p.toPoint(t.min, step, t.coords, count); - count += 3; - } -}, - -decodeFaces: function() { - if(!this.node.nface) return; - - this.vertex_count = 0; - var start = 0; - for(var p = 0; p < this.patches.length; p++) { - var end = this.patches[p]; - this.decodeConnectivity(end - start, start*3); - start = end; - } - //dequantize positions - var tot = this.node.nvert*3; - var coords = this.coords; - var stack = this.stack; - for(var i = 0; i < tot; ) { - coords[i] = (coords[i] + stack[3])*stack[6]; i++; - coords[i] = (coords[i] + stack[4])*stack[6]; i++; - coords[i] = (coords[i] + stack[5])*stack[6]; i++; - } - if(this.sig.texcoords) { - var t_tot = this.node.nvert*2; - var t_coords = this.texcoords; - for(var i = 0; i < tot; ) { - t_coords[i] = (t_coords[i] + stack[9])*stack[11]; i++; - t_coords[i] = (t_coords[i] + stack[10])*stack[11]; i++; - } - } -}, - -decodeNormals: function() { - var norm_q = this.stream.readChar(); - - var dtunstall = new Tunstall; - var diffs = dtunstall.decompress(this.stream); - - var stunstall = new Tunstall; - var signs = stunstall.decompress(this.stream); - var bitstream = this.stream.readBitStream(); - - var side = (1<<(16 - norm_q))>>>0; - var diffcount = 0; - var signcount = 0; - - if(!this.sig.indices) { - for(var k = 0; k < 2; k++) { - var on = 0; - for(var i = 0; i < this.node.nvert; i++) { - var d = this.decodeDiff(diffs[diffcount++], bitstream); - on = on + d; - this.normals[3*i + k] = on*side; - } - } - for(var i = 0; i < this.node.nvert; i++) { - var offset = i*3; - var x = this.normals[offset + 0]; - var y = this.normals[offset + 1]; - var z = 32767.0*32767.0 - x*x - y*y; - - if(z < 0) z = 0; - z = Math.sqrt(z); - if(z > 32767) z = 32767; - if(signs[i] == 0) - z = -z; - this.normals[offset + 2] = z; - } - return; - } - - var boundary = this.markBoundary(); - this.computeNormals(); - - if(this.sig.texcoords) //hack, fixing normals makes it worse actually - return; - - var stat = 0; - //get difference between original and predicted - for(var i = 0; i < this.node.nvert; i++) { - if(!boundary[i]) continue; - var offset = i*3; - var x = (this.normals[offset + 0]/side); - var y = (this.normals[offset + 1]/side); - var dx = this.decodeDiff(diffs[diffcount++], bitstream); - var dy = this.decodeDiff(diffs[diffcount++], bitstream); - x = (x + dx)*side; - y = (y + dy)*side; - - var z = 32767.0*32767.0 - x*x - y*y; - - if(z < 0) z = 0; - z = Math.sqrt(z); - //sign - if(z > 32767.0) z = 32767.0; - var signbit = signs[signcount++]; -// if(this.normals[offset+2] < 0 != signbit) - if((this.normals[offset+2] < 0 && signbit == 0) || (this.normals[offset+2] > 0 && signbit == 1)) - z = -z; - this.normals[offset + 0] = x; - this.normals[offset + 1] = y; - this.normals[offset + 2] = z; - } -}, - -decodeColors: function() { - var color_q = []; - for(var k = 0; k < 4; k++) - color_q[k] = this.stream.readChar(); - - var diffs = []; - for(var k = 0; k < 4; k++) { - var tunstall = new Tunstall;; - diffs[k] = tunstall.decompress(this.stream); - } - var bitstream = this.stream.readBitStream(); - - var count = 0; - if(this.sig.indices) { - for(var i = 0; i < this.node.nvert; i++) { - var last = this.last[i]*4; - var offset = i*4; - - for(var k = 0; k < 4; k++) { - var c = this.decodeDiff(diffs[k][count], bitstream); - - if(last >= 0) - c += this.colors[last + k]; - this.colors[offset] = c; - offset++; - } - count++; - } - } else { - for(var k = 0; k < 4; k++) - this.colors[k] = this.decodeDiff(diffs[k][count], bitstream); - count++; - - var offset = 4; - for(var i = 1; i < this.node.nvert; i++) { - for(var k = 0; k < 4; k++) { - var d = this.decodeDiff(diffs[k][count], bitstream); - this.colors[offset] = this.colors[offset-4] + d; - offset ++; - } - count++; - } - } - - var steps = []; - for(var k = 0; k < 4; k++) - steps[k] = (1<<(8 - color_q[k])); - - //convert to rgb - for(var i = 0; i < this.node.nvert; i++) { - var offset = i*4; - - var e0 = this.colors[offset + 0] * steps[0]; - var e1 = this.colors[offset + 1] * steps[1]; - var e2 = this.colors[offset + 2] * steps[2]; - - this.colors[offset + 0] = (e2 + e0)&0xff; - this.colors[offset + 1] = e0; - this.colors[offset + 2] = (e1 + e0)&0xff; - } -}, - -//how to determine if a vertex is a boundary without topology: -//for each edge a vertex is in, add or subtract the id of the other vertex depending on order -//for internal vertices sum is zero. -//unless we have strange configurations and a lot of sfiga, zero wont happen. //TODO think about this -markBoundary: function() { -// var boundary = new Uint8Array(this.node.nvert); - var count = new Uint32Array(this.node.nvert); - - var offset = 0; - for(var i = 0; i < this.node.nface; i++) { - count[this.faces[offset + 0]] += this.faces[offset + 1] - this.faces[offset + 2]; - count[this.faces[offset + 1]] += this.faces[offset + 2] - this.faces[offset + 0]; - count[this.faces[offset + 2]] += this.faces[offset + 0] - this.faces[offset + 1]; - offset += 3; - } - return count; -// for(var i = 0; i < this.node.nvert; i++) -// if(count[i] != 0) -// boundary[i] = true; -// return boundary; -}, - -norm: function(buffer, a, b, c) { //a b c offsets in the buffer - var ba0 = buffer[b+0] - buffer[a+0]; - var ba1 = buffer[b+1] - buffer[a+1]; - var ba2 = buffer[b+2] - buffer[a+2]; - - var ca0 = buffer[c+0] - buffer[a+0]; - var ca1 = buffer[c+1] - buffer[a+1]; - var ca2 = buffer[c+2] - buffer[a+2]; - - var p = []; - p[0] = ba1*ca2 - ba2*ca1; - p[1] = ba2*ca0 - ba0*ca2; - p[2] = ba0*ca1 - ba1*ca0; - return p; -}, - -normalize: function(buffer, offset) { - var x = buffer[offset + 0]; - var y = buffer[offset + 1]; - var z = buffer[offset + 2]; - var n = Math.sqrt(x*x + y*y + z*z); - if(n > 0) { - buffer[offset + 0] = x/n; - buffer[offset + 1] = y/n; - buffer[offset + 2] = z/n; - } -}, - -computeNormals:function() { - var tmp_normals = new Float32Array(this.node.nvert*3); - - var offset = 0; - for(var i = 0; i < this.node.nface; i++) { - var a = 3*this.faces[offset + 0]; - var b = 3*this.faces[offset + 1]; - var c = 3*this.faces[offset + 2]; - - var buffer = this.coords; - var ba0 = buffer[b+0] - buffer[a+0]; - var ba1 = buffer[b+1] - buffer[a+1]; - var ba2 = buffer[b+2] - buffer[a+2]; - - var ca0 = buffer[c+0] - buffer[a+0]; - var ca1 = buffer[c+1] - buffer[a+1]; - var ca2 = buffer[c+2] - buffer[a+2]; - - var n0 = ba1*ca2 - ba2*ca1; - var n1 = ba2*ca0 - ba0*ca2; - var n2 = ba0*ca1 - ba1*ca0; - - tmp_normals[a + 0] += n0; - tmp_normals[a + 1] += n1; - tmp_normals[a + 2] += n2; - tmp_normals[b + 0] += n0; - tmp_normals[b + 1] += n1; - tmp_normals[b + 2] += n2; - tmp_normals[c + 0] += n0; - tmp_normals[c + 1] += n1; - tmp_normals[c + 2] += n2; - offset += 3; - } - - //normalize - var offset = 0; - for(var i = 0; i < this.node.nvert; i++) { - var x = tmp_normals[offset + 0]; - var y = tmp_normals[offset + 1]; - var z = tmp_normals[offset + 2]; - var n = Math.sqrt(x*x + y*y + z*z); - if(n > 0) { - tmp_normals[offset + 0] = x/n; - tmp_normals[offset + 1] = y/n; - tmp_normals[offset + 2] = z/n; - } - this.normals[offset + 0] = tmp_normals[offset + 0]*32767; - this.normals[offset + 1] = tmp_normals[offset + 1]*32767; - this.normals[offset + 2] = tmp_normals[offset + 2]*32767; - offset += 3; - } -}, - -decodeDiff: function(diff, bitstream) { - var val; - if(diff == 0) { - val = 1; - } else { - val = 1<<(diff); - val |= bitstream.read(diff); - }; - val--; //vall is always >= 1 - if(val & 0x1) - val = -((val+1)>>1); - else - val = val>>1; - return val; -}, - -/* an edge is: uint16_t face, uint16_t side, uint32_t prev, next, bool deleted -I do not want to create millions of small objects, I will use aUint32Array. -Problem is how long, sqrt(nface) we will over blow using nface. -*/ - -decodeConnectivity: function(length, start) { - - var t = this; - var ctunstall = new Tunstall; - var clers = ctunstall.decompress(this.stream); - var cler_count = 0; - - var dtunstall = new Tunstall; - var diffs = dtunstall.decompress(this.stream); - var diff_count = 0; - - var tdiffs; - var tdiff_count = 0; - if(t.sig.texcoords) { - var ttunstall = new Tunstall; - tdiffs = ttunstall.decompress(this.stream); - } - - var bitstream = this.stream.readBitStream(bitstream); - - var current_face = 0; //keep track of connected component start - //t.vertex_count = 0; - var front = new Uint32Array(this.node.nface*18); - var front_count = 0; //count each integer so it's front_back*5 - function addFront(_v0, _v1, _v2, _prev, _next) { - front[front_count++] = _v0; - front[front_count++] = _v1; - front[front_count++] = _v2; - front[front_count++] = _prev; - front[front_count++] = _next; - front[front_count++] = 0; //deleted - } - function _next(t) { - t++; - if(t == 3) t = 0; - return t; - } - function _prev(t) { - t--; - if(t == -1) t = 2; - return t; - } - - var delayed = []; - var faceorder = []; - - var faces_count = start; //count indices in this.faces array - var totfaces = length; -// var estimated = [0, 0, 0]; //no! use stack. - var stack = this.stack; - var coords = this.coords; - var texcoords = this.texcoords; - var hasTexCoords = t.sig.texcoords; - - while(totfaces > 0) { - if(!faceorder.length && !delayed.length) { - if(current_face == this.node.nface) break; //no more faces to encode exiting - - stack[0] = stack[1] = stack[2] = 0; - stack[7] = stack[8] = 0; //texcoords - var last_index = -1; - var index = []; - for(var k = 0; k < 3; k++) { - this.last[this.last_count++] = last_index; - var diff = diffs[diff_count++]; - var tdiff = diff && hasTexCoords? tdiffs[tdiff_count++] : 0; - var v = this.decodeVertex(bitstream, diff, tdiff); - index[k] = v; - this.faces[faces_count++] = v; - stack[0] = coords[v*3]; - stack[1] = coords[v*3+1]; - stack[2] = coords[v*3+2]; - if(t.sig.texcoords) { - stack[7] = texcoords[v*2]; - stack[8] = texcoords[v*2+1]; - } - last_index = v; - } - var current_edge = front_count; - for(var k = 0; k < 3; k++) { - faceorder.push(front_count); - front[front_count++] = index[_next(k)]; - front[front_count++] = index[_prev(k)]; - front[front_count++] = index[k]; - front[front_count++] = current_edge + _prev(k)*6; - front[front_count++] = current_edge + _next(k)*6; - front_count++; -// addFront(index[_next(k)], index[_prev(k)], index[k], current_edge + _prev(k)*6, current_edge + _next(k)*6); - } - current_face++; - totfaces--; - continue; - } - var f; - if(faceorder.length) - f = faceorder.shift(); - else - f = delayed.pop(); - - var edge_start = f; - - if(front[edge_start + 5]) continue; //deleted - front[edge_start + 5] = 1; //set edge as deleted anyway - - var c = clers[cler_count++]; - if(c == 4) continue; //BOUNDARY - - var v0 = front[edge_start + 0]; - var v1 = front[edge_start + 1]; - var v2 = front[edge_start + 2]; - var prev = front[edge_start + 3]; - var next = front[edge_start + 4]; - - var first_edge = front_count; //points to new edge to be inserted - var opposite = -1; - if(c == 0) { //VERTEX - //predict position based on v0, v1 and v2 - for(var k = 0; k < 3; k++) - stack[k] = coords[v0*3 + k] + coords[v1*3 + k] - coords[v2*3 + k]; - - if(hasTexCoords) - for(var k = 0; k < 2; k++) - stack[7+k] = texcoords[v0*2 + k] + texcoords[v1*2 + k] - texcoords[v2*2 + k]; - - var diff = diffs[diff_count++]; - var tdiff = diff && hasTexCoords? tdiffs[tdiff_count++] : 0; - opposite = this.decodeVertex(bitstream, diff, tdiff); - if(diff != 0) - this.last[this.last_count++] = v1; - - front[prev + 4] = first_edge; - front[next + 3] = first_edge + 6; - faceorder.unshift(front_count); - - front[front_count++] = v0; - front[front_count++] = opposite; - front[front_count++] = v1; - front[front_count++] = prev; - front[front_count++] = first_edge+6; - front_count++; -// addFront(v0, opposite, v1, prev, first_edge + 6); - - faceorder.push(front_count); - - front[front_count++] = opposite; - front[front_count++] = v1; - front[front_count++] = v0; - front[front_count++] = first_edge; - front[front_count++] = next; - front_count++; -// addFront(opposite, v1, v0, first_edge, next); - - } else if(c == 3) { //END - front[prev + 5] = 1; - front[next + 5] = 1; - front[front[prev + 3] + 4] = front[next + 4]; - front[front[next + 4] + 3] = front[prev + 3]; - opposite = front[prev + 0]; - - } else if(c == 1) { //LEFT - front[prev + 5] = 1; //deleted - front[front[prev + 3] + 4] = first_edge; - front[next + 3] = first_edge; - opposite = front[prev + 0]; - - faceorder.unshift(front_count); - - front[front_count++] = opposite; - front[front_count++] = v1; - front[front_count++] = v0; - front[front_count++] = front[prev +3]; - front[front_count++] = next; - front_count++; -// addFront(opposite, v1, v0, front[prev + 3], next); - - } else if(c == 2) { //RIGHT - front[next + 5] = 1; - front[front[next + 4] + 3] = first_edge; - front[prev + 4] = first_edge; - opposite = front[next + 1]; - - - faceorder.unshift(front_count); - - front[front_count++] = v0; - front[front_count++] = opposite; - front[front_count++] = v1; - front[front_count++] = prev; - front[front_count++] = front[next+4]; - front_count++; -// addFront(v0, opposite, v1, prev, front[next + 4]); - - } else if(c == 5) { //DELAY - front[edge_start + 5] = 0; - delayed.push(edge_start); - continue; - } - this.faces[faces_count++] = v1; - this.faces[faces_count++] = v0; - this.faces[faces_count++] = opposite; - totfaces--; - } -}, - -decodeVertex: function(bitstream, diff, tdiff) { - if(diff == 0) - return bitstream.read(16); - - var v = this.vertex_count++; - - var max = 1<<(diff-1); - - for(var k = 0; k < 3; k++) { - var d = bitstream.read(diff) - max; - this.coords[v*3+k] = this.stack[k] + d; //stack 0-3 is used as extimated - } - if(this.sig.texcoords) { - var tmax = 1<<(tdiff-1); - for(var k = 0; k < 2; k++) { - var d = bitstream.read(tdiff) - tmax; - this.texcoords[v*2+k] = this.stack[7+k] + d; //stack 7-9 is used as extimated - } - } - return v; -}, - -decodeDiff: function(diff, bitstream) { - var val; - if(diff == 0) { - return 0; - } - val = 1<<diff; - val += bitstream.read(diff); - - - if(val & 0x1) - val >>>= 1; - else - val = -(val>>>1); - - return val; -} - -}; - -var tot = 0;
--- a/Resources/Nexus/js/nexus.js Sat Jun 15 16:17:41 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1257 +0,0 @@ -/* -Nexus -Copyright (c) 2012-2018, Visual Computing Lab, ISTI - CNR -All rights reserved. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -Nexus = function() { - -/* WORKER INITIALIZED ONCE */ - -var meco; -var corto; - -var scripts = document.getElementsByTagName('script'); -var i, j, k; -var path; -for(i = 0; i < scripts.length; i++) { - var attrs = scripts[i].attributes; - for(j = 0; j < attrs.length; j++) { - var a = attrs[j]; - if(a.name != 'src') continue; - if(!a.value) continue; - if(a.value.search('nexus.js') >= 0) { - path = a.value; - break; - } - } -} -var meco = null; -function loadMeco() { - meco = new Worker(path.replace('nexus.js', 'meco.js')); - - meco.onerror = function(e) { console.log(e); } - meco.requests = {}; - meco.count = 0; - meco.postRequest = function(sig, node, patches) { - var signature = { - texcoords: sig.texcoords ? 1 : 0, - colors : sig.colors ? 1 : 0, - normals : sig.normals ? 1 : 0, - indices : sig.indices ? 1 : 0 - }; - meco.postMessage({ - signature:signature, - node:{ nface: node.nface, nvert: node.nvert, buffer:node.buffer, request:this.count}, - patches:patches - }); - this.requests[this.count++] = node; - }; - meco.onmessage = function(e) { - var node = this.requests[e.data.request]; - node.buffer = e.data.buffer; - readyNode(node); - }; -} - -var corto = null; - -function loadCorto() { - corto = new Worker(path.replace('nexus.js', 'corto.js')); - corto.requests = {}; - corto.count = 0; - corto.postRequest = function(node) { - corto.postMessage({ buffer: node.buffer, request:this.count, rgba_colors: true, short_normals: true }); - this.requests[this.count++] = node; - } - corto.onmessage = function(e) { - var node = this.requests[e.data.request]; - node.buffer = e.data.buffer; - node.model = e.data.model; - readyNode(node); - }; -} - -/* UTILITIES */ - -function getUint64(view) { - var s = 0; - var lo = view.getUint32(view.offset, true); - var hi = view.getUint32(view.offset + 4, true); - view.offset += 8; - return ((hi * (1 << 32)) + lo); -} - -function getUint32(view) { - var s = view.getUint32(view.offset, true); - view.offset += 4; - return s; -} - -function getUint16(view) { - var s = view.getUint16(view.offset, true); - view.offset += 2; - return s; -} - -function getFloat32(view) { - var s = view.getFloat32(view.offset, true); - view.offset += 4; - return s; -} - -/* MATRIX STUFF */ - -function vecMul(m, v, r) { - var w = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]; - - r[0] = (m[0]*v[0] + m[4]*v[1] + m[8 ]*v[2] + m[12 ])/w; - r[1] = (m[1]*v[0] + m[5]*v[1] + m[9 ]*v[2] + m[13 ])/w; - r[2] = (m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14])/w; -} - - -function matMul(a, b, r) { - r[ 0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; - r[ 1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; - r[ 2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; - r[ 3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; - - r[ 4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; - r[ 5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; - r[ 6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; - r[ 7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; - - r[ 8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; - r[ 9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; - r[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; - r[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; - - r[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; - r[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; - r[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; - r[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; -} - -function matInv(m, t) { - var s = 1.0/( - m[12]* m[9]*m[6]*m[3]-m[8]*m[13]*m[6]*m[3]-m[12]*m[5]*m[10]*m[3]+m[4]*m[13]*m[10]*m[3]+ - m[8]*m[5]*m[14]*m[3]-m[4]*m[9]*m[14]*m[3]-m[12]*m[9]*m[2]*m[7]+m[8]*m[13]*m[2]*m[7]+ - m[12]*m[1]*m[10]*m[7]-m[0]*m[13]*m[10]*m[7]-m[8]*m[1]*m[14]*m[7]+m[0]*m[9]*m[14]*m[7]+ - m[12]*m[5]*m[2]*m[11]-m[4]*m[13]*m[2]*m[11]-m[12]*m[1]*m[6]*m[11]+m[0]*m[13]*m[6]*m[11]+ - m[4]*m[1]*m[14]*m[11]-m[0]*m[5]*m[14]*m[11]-m[8]*m[5]*m[2]*m[15]+m[4]*m[9]*m[2]*m[15]+ - m[8]*m[1]*m[6]*m[15]-m[0]*m[9]*m[6]*m[15]-m[4]*m[1]*m[10]*m[15]+m[0]*m[5]*m[10]*m[15] - ); - - t[ 0] = (m[9]*m[14]*m[7]-m[13]*m[10]*m[7]+m[13]*m[6]*m[11]-m[5]*m[14]*m[11]-m[9]*m[6]*m[15]+m[5]*m[10]*m[15])*s; - t[ 1] = (m[13]*m[10]*m[3]-m[9]*m[14]*m[3]-m[13]*m[2]*m[11]+m[1]*m[14]*m[11]+m[9]*m[2]*m[15]-m[1]*m[10]*m[15])*s; - t[ 2] = (m[5]*m[14]*m[3]-m[13]*m[6]*m[3]+m[13]*m[2]*m[7]-m[1]*m[14]*m[7]-m[5]*m[2]*m[15]+m[1]*m[6]*m[15])*s; - t[ 3] = (m[9]*m[6]*m[3]-m[5]*m[10]*m[3]-m[9]*m[2]*m[7]+m[1]*m[10]*m[7]+m[5]*m[2]*m[11]-m[1]*m[6]*m[11])*s; - - t[ 4] = (m[12]*m[10]*m[7]-m[8]*m[14]*m[7]-m[12]*m[6]*m[11]+m[4]*m[14]*m[11]+m[8]*m[6]*m[15]-m[4]*m[10]*m[15])*s; - t[ 5] = (m[8]*m[14]*m[3]-m[12]*m[10]*m[3]+m[12]*m[2]*m[11]-m[0]*m[14]*m[11]-m[8]*m[2]*m[15]+m[0]*m[10]*m[15])*s; - t[ 6] = (m[12]*m[6]*m[3]-m[4]*m[14]*m[3]-m[12]*m[2]*m[7]+m[0]*m[14]*m[7]+m[4]*m[2]*m[15]-m[0]*m[6]*m[15])*s; - t[ 7] = (m[4]*m[10]*m[3]-m[8]*m[6]*m[3]+m[8]*m[2]*m[7]-m[0]*m[10]*m[7]-m[4]*m[2]*m[11]+m[0]*m[6]*m[11])*s; - - t[ 8] = (m[8]*m[13]*m[7]-m[12]*m[9]*m[7]+m[12]*m[5]*m[11]-m[4]*m[13]*m[11]-m[8]*m[5]*m[15]+m[4]*m[9]*m[15])*s; - t[ 9] = (m[12]*m[9]*m[3]-m[8]*m[13]*m[3]-m[12]*m[1]*m[11]+m[0]*m[13]*m[11]+m[8]*m[1]*m[15]-m[0]*m[9]*m[15])*s; - t[10] = (m[4]*m[13]*m[3]-m[12]*m[5]*m[3]+m[12]*m[1]*m[7]-m[0]*m[13]*m[7]-m[4]*m[1]*m[15]+m[0]*m[5]*m[15])*s; - t[11] = (m[8]*m[5]*m[3]-m[4]*m[9]*m[3]-m[8]*m[1]*m[7]+m[0]*m[9]*m[7]+m[4]*m[1]*m[11]-m[0]*m[5]*m[11])*s; - - t[12] = (m[12]*m[9]*m[6]-m[8]*m[13]*m[6]-m[12]*m[5]*m[10]+m[4]*m[13]*m[10]+m[8]*m[5]*m[14]-m[4]*m[9]*m[14])*s; - t[13] = (m[8]*m[13]*m[2]-m[12]*m[9]*m[2]+m[12]*m[1]*m[10]-m[0]*m[13]*m[10]-m[8]*m[1]*m[14]+m[0]*m[9]*m[14])*s; - t[14] = (m[12]*m[5]*m[2]-m[4]*m[13]*m[2]-m[12]*m[1]*m[6]+m[0]*m[13]*m[6]+m[4]*m[1]*m[14]-m[0]*m[5]*m[14])*s; - t[15] = (m[4]*m[9]*m[2]-m[8]*m[5]*m[2]+m[8]*m[1]*m[6]-m[0]*m[9]*m[6]-m[4]*m[1]*m[10]+m[0]*m[5]*m[10])*s; -} - -/* PRIORITY QUEUE */ - -PriorityQueue = function(max_length) { - this.error = new Float32Array(max_length); - this.data = new Int32Array(max_length); - this.size = 0; -} - -PriorityQueue.prototype = { - push: function(data, error) { - this.data[this.size] = data; - this.error[this.size] = error; - this.bubbleUp(this.size); - this.size++; - }, - - pop: function() { - var result = this.data[0]; - this.size--; - if(this.size > 0) { - this.data[0] = this.data[this.size]; - this.error[0] = this.error[this.size]; - this.sinkDown(0); - } - return result; - }, - - bubbleUp: function(n) { - var data = this.data[n]; - var error = this.error[n]; - while (n > 0) { - var pN = ((n+1)>>1) -1; - var pError = this.error[pN]; - if(pError > error) - break; - //swap - this.data[n] = this.data[pN]; - this.error[n] = pError; - this.data[pN] = data; - this.error[pN] = error; - n = pN; - } - }, - - sinkDown: function(n) { - var data = this.data[n]; - var error = this.error[n]; - - while(true) { - var child2N = (n + 1) * 2; - var child1N = child2N - 1; - var swap = -1; - if (child1N < this.size) { - var child1Error = this.error[child1N]; - if(child1Error > error) - swap = child1N; - } - if (child2N < this.size) { - var child2Error = this.error[child2N]; - if (child2Error > (swap == -1 ? error : child1Error)) - swap = child2N; - } - - if (swap == -1) break; - - this.data[n] = this.data[swap]; - this.error[n] = this.error[swap]; - this.data[swap] = data; - this.error[swap] = error; - n = swap; - } - } -}; - - -/* HEADER AND PARSING */ - -var padding = 256; -var Debug = { - nodes : false, //color each node - culling : false, //visibility culling disabled - draw : false, //final rendering call disabled - extract : false, //no extraction - request : false, //no network requests - worker : false //no web workers -}; - - -var glP = WebGLRenderingContext.prototype; -var attrGlMap = [glP.NONE, glP.BYTE, glP.UNSIGNED_BYTE, glP.SHORT, glP.UNSIGNED_SHORT, glP.INT, glP.UNSIGNED_INT, glP.FLOAT, glP.DOUBLE]; -var attrSizeMap = [0, 1, 1, 2, 2, 4, 4, 4, 8]; - - -var targetError = 2.0; -var targetFps = 15; -var maxPending = 3; -var maxBlocked = 3; -var maxReqAttempt = 2; -var maxCacheSize = 512*(1<<20); //TODO DEBUG -var drawBudget = 5*(1<<20); - - -/* MESH DEFINITION */ - -Mesh = function() { - var t = this; - t.onLoad = null; - t.reqAttempt = 0; -} - -Mesh.prototype = { - open: function(url) { - var mesh = this; - mesh.url = url; - mesh.httpRequest( - 0, - 88, - function() { -// console.log("Loading header for " + mesh.url); - var view = new DataView(this.response); - view.offset = 0; - mesh.reqAttempt++; - var header = mesh.importHeader(view); - if(!header) { - console.log("Empty header!"); - if(mesh.reqAttempt < maxReqAttempt) mesh.open(mesh.url + '?' + Math.random()); // BLINK ENGINE CACHE BUG PATCH - return null; - } - mesh.reqAttempt = 0; - for(i in header) - mesh[i] = header[i]; - mesh.vertex = mesh.signature.vertex; - mesh.face = mesh.signature.face; - mesh.renderMode = mesh.face.index?["FILL", "POINT"]:["POINT"]; - mesh.compressed = (mesh.signature.flags & (2 | 4)); //meco or corto - mesh.meco = (mesh.signature.flags & 2); - mesh.corto = (mesh.signature.flags & 4); - mesh.requestIndex(); - }, - function() { console.log("Open request error!");}, - function() { console.log("Open request abort!");} - ); - }, - - httpRequest: function(start, end, load, error, abort, type) { - if(!type) type = 'arraybuffer'; - var r = new XMLHttpRequest(); - r.open('GET', this.url, true); - r.responseType = type; - r.setRequestHeader("Range", "bytes=" + start + "-" + (end -1)); - r.onload = function(){ - switch (this.status){ - case 0: -// console.log("0 response: server unreachable.");//returned in chrome for local files - case 206: -// console.log("206 response: partial content loaded."); - load.bind(this)(); - break; - case 200: -// console.log("200 response: server does not support byte range requests."); - } - }; - r.onerror = error; - r.onabort = abort; - r.send(); - return r; - }, - - requestIndex: function() { - var mesh = this; - var end = 88 + mesh.nodesCount*44 + mesh.patchesCount*12 + mesh.texturesCount*68; - mesh.httpRequest( - 88, - end, - function() { mesh.handleIndex(this.response); }, - function() { console.log("Index request error!");}, - function() { console.log("Index request abort!");} - ); - }, - - handleIndex: function(buffer) { - var t = this; - var view = new DataView(buffer); - view.offset = 0; - - var n = t.nodesCount; - - t.noffsets = new Uint32Array(n); - t.nvertices = new Uint32Array(n); - t.nfaces = new Uint32Array(n); - t.nerrors = new Float32Array(n); - t.nspheres = new Float32Array(n*5); - t.nsize = new Float32Array(n); - t.nfirstpatch = new Uint32Array(n); - - for(i = 0; i < n; i++) { - t.noffsets[i] = padding*getUint32(view); //offset - t.nvertices[i] = getUint16(view); //verticesCount - t.nfaces[i] = getUint16(view); //facesCount - t.nerrors[i] = getFloat32(view); - view.offset += 8; //skip cone - for(k = 0; k < 5; k++) - t.nspheres[i*5+k] = getFloat32(view); //sphere + tight - t.nfirstpatch[i] = getUint32(view); //first patch - } - t.sink = n -1; - - t.patches = new Uint32Array(view.buffer, view.offset, t.patchesCount*3); //noded, lastTriangle, texture - t.nroots = t.nodesCount; - for(j = 0; j < t.nroots; j++) { - for(i = t.nfirstpatch[j]; i < t.nfirstpatch[j+1]; i++) { - if(t.patches[i*3] < t.nroots) - t.nroots = t.patches[i*3]; - } - } - - view.offset += t.patchesCount*12; - - t.textures = new Uint32Array(t.texturesCount); - t.texref = new Uint32Array(t.texturesCount); - for(i = 0; i < t.texturesCount; i++) { - t.textures[i] = padding*getUint32(view); - view.offset += 16*4; //skip proj matrix - } - - t.vsize = 12 + (t.vertex.normal?6:0) + (t.vertex.color?4:0) + (t.vertex.texCoord?8:0); - t.fsize = 6; - //problem: I have no idea how much space a texture is needed in GPU. 10x factor assumed. - - var tmptexsize = new Uint32Array(n-1); - var tmptexcount = new Uint32Array(n-1); - for(var i = 0; i < n-1; i++) { - for(var p = t.nfirstpatch[i]; p != t.nfirstpatch[i+1]; p++) { - var tex = t.patches[p*3+2]; - tmptexsize[i] += t.textures[tex+1] - t.textures[tex]; - tmptexcount[i]++; - } - t.nsize[i] = t.vsize*t.nvertices[i] + t.fsize*t.nfaces[i]; - } - for(var i = 0; i < n-1; i++) { - t.nsize[i] += 10*tmptexsize[i]/tmptexcount[i]; - } - - t.status = new Uint8Array(n); //0 for none, 1 for ready, 2+ for waiting data - t.frames = new Uint32Array(n); - t.errors = new Float32Array(n); //biggest error of instances - t.ibo = new Array(n); - t.vbo = new Array(n); - t.texids = new Array(n); - - t.isReady = true; - if(t.onLoad) t.onLoad(); - }, - - importAttribute: function(view) { - var a = {}; - a.type = view.getUint8(view.offset++, true); - a.size = view.getUint8(view.offset++, true); - a.glType = attrGlMap[a.type]; - a.normalized = a.type < 7; - a.stride = attrSizeMap[a.type]*a.size; - if(a.size == 0) return null; - return a; - }, - - importElement: function(view) { - var e = []; - for(i = 0; i < 8; i++) - e[i] = this.importAttribute(view); - return e; - }, - - importVertex: function(view) { //enum POSITION, NORMAL, COLOR, TEXCOORD, DATA0 - var e = this.importElement(view); - var color = e[2]; - if(color) { - color.type = 2; //unsigned byte - color.glType = attrGlMap[2]; - } - return { position: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; - }, - - //enum INDEX, NORMAL, COLOR, TEXCOORD, DATA0 - importFace: function(view) { - var e = this.importElement(view); - var color = e[2]; - if(color) { - color.type = 2; //unsigned byte - color.glType = attrGlMap[2]; - } - return { index: e[0], normal: e[1], color: e[2], texCoord: e[3], data: e[4] }; - }, - - importSignature: function(view) { - var s = {}; - s.vertex = this.importVertex(view); - s.face = this.importFace(view); - s.flags = getUint32(view); - return s; - }, - - importHeader: function(view) { - var magic = getUint32(view); - if(magic != 0x4E787320) return null; - var h = {}; - h.version = getUint32(view); - h.verticesCount = getUint64(view); - h.facesCount = getUint64(view); - h.signature = this.importSignature(view); - h.nodesCount = getUint32(view); - h.patchesCount = getUint32(view); - h.texturesCount = getUint32(view); - h.sphere = { - center: [getFloat32(view), getFloat32(view), getFloat32(view)], - radius: getFloat32(view) - }; - return h; - } -}; - -Instance = function(gl) { - this.gl = gl; - this.onLoad = function() {}; - this.onUpdate = function() {}; - this.drawBudget = drawBudget; - this.attributes = { 'position':0, 'normal':1, 'color':2, 'uv':3 }; -} - -Instance.prototype = { - open: function(url) { - var t = this; - t.context = getContext(t.gl); - - t.modelMatrix = new Float32Array(16); - t.viewMatrix = new Float32Array(16); - t.projectionMatrix = new Float32Array(16); - t.modelView = new Float32Array(16); - t.modelViewInv = new Float32Array(16); - t.modelViewProj = new Float32Array(16); - t.modelViewProjInv = new Float32Array(16); - t.planes = new Float32Array(24); - t.viewport = new Float32Array(4); - t.viewpoint = new Float32Array(4); - - t.context.meshes.forEach(function(m) { - if(m.url == url){ - t.mesh = m; - t.renderMode = t.mesh.renderMode; - t.mode = t.renderMode[0]; - t.onLoad(); - } - }); - - if(!t.mesh) { - t.mesh = new Mesh(); - t.mesh.onLoad = function() { t.renderMode = t.mesh.renderMode; t.mode = t.renderMode[0]; t.onLoad(); } - t.mesh.open(url); - t.context.meshes.push(t.mesh); - } - }, - - close: function() { - //remove instance from mesh. - }, - - get isReady() { return this.mesh.isReady; }, - setPrimitiveMode : function (mode) { this.mode = mode; }, - get datasetRadius() { if(!this.isReady) return 1.0; return this.mesh.sphere.radius; }, - get datasetCenter() { if(!this.isReady) return [0, 0, 0]; return this.mesh.sphere.center; }, - - updateView: function(viewport, projection, modelView) { - var t = this; - - for(var i = 0; i < 16; i++) { - t.projectionMatrix[i] = projection[i]; - t.modelView[i] = modelView[i]; - } - for(var i = 0; i < 4; i++) - t.viewport[i] = viewport[i]; - - matMul(t.projectionMatrix, t.modelView, t.modelViewProj); - matInv(t.modelViewProj, t.modelViewProjInv); - - matInv(t.modelView, t.modelViewInv); - t.viewpoint[0] = t.modelViewInv[12]; - t.viewpoint[1] = t.modelViewInv[13]; - t.viewpoint[2] = t.modelViewInv[14]; - t.viewpoint[3] = 1.0; - - - var m = t.modelViewProj; - var mi = t.modelViewProjInv; - var p = t.planes; - - //left, right, bottom, top as Ax + By + Cz + D = 0; - p[0] = m[0] + m[3]; p[1] = m[4] + m[7]; p[2] = m[8] + m[11]; p[3] = m[12] + m[15]; - p[4] = -m[0] + m[3]; p[5] = -m[4] + m[7]; p[6] = -m[8] + m[11]; p[7] = -m[12] + m[15]; - p[8] = m[1] + m[3]; p[9] = m[5] + m[7]; p[10] = m[9] + m[11]; p[11] = m[13] + m[15]; - p[12] = -m[1] + m[3]; p[13] = -m[5] + m[7]; p[14] = -m[9] + m[11]; p[15] = -m[13] + m[15]; - p[16] = -m[2] + m[3]; p[17] = -m[6] + m[7]; p[18] = -m[10] + m[11]; p[19] = -m[14] + m[15]; - p[20] = -m[2] + m[3]; p[21] = -m[6] + m[7]; p[22] = -m[10] + m[11]; p[23] = -m[14] + m[15]; - - for(var i = 0; i < 16; i+= 4) { - var l = Math.sqrt(p[i]*p[i] + p[i+1]*p[i+1] + p[i+2]*p[i+2]); - p[i] /= l; p[i+1] /= l; p[i+2] /= l; p[i+3] /= l; - } - //side is M'(1,0,0,1) - M'(-1,0,0,1) and they lie on the planes - var r3 = mi[3] + mi[15]; - var r0 = (mi[0] + mi[12 ])/r3; - var r1 = (mi[1] + mi[13 ])/r3; - var r2 = (mi[2] + mi[14 ])/r3; - - var l3 = -mi[3] + mi[15]; - var l0 = (-mi[0] + mi[12 ])/l3 - r0; - var l1 = (-mi[1] + mi[13 ])/l3 - r1; - var l2 = (-mi[2] + mi[14 ])/l3 - r2; - - var side = Math.sqrt(l0*l0 + l1*l1 + l2*l2); - - //center of the scene is M'*(0, 0, 0, 1) - var c0 = mi[12]/mi[15] - t.viewpoint[0]; - var c1 = mi[13]/mi[15] - t.viewpoint[1]; - var c2 = mi[14]/mi[15] - t.viewpoint[2]; - var dist = Math.sqrt(c0*c0 + c1*c1 + c2*c2); - - t.resolution = (2*side/dist)/ t.viewport[2]; - }, - - traversal : function () { - var t = this; - if(Debug.extract == true) - return; - - if(!t.isReady) return; - - var n = t.mesh.nodesCount; - t.visited = new Uint8Array(n); - t.blocked = new Uint8Array(n); - t.selected = new Uint8Array(n); - - t.visitQueue = new PriorityQueue(n); - for(var i = 0; i < t.mesh.nroots; i++) - t.insertNode(i); - - var candidatesCount = 0; - t.targetError = t.context.currentError; - t.currentError = 1e20; - t.drawSize = 0; - - var nblocked = 0; - var requested = 0; - while(t.visitQueue.size && nblocked < maxBlocked) { - var error = t.visitQueue.error[0]; - var node = t.visitQueue.pop(); - if ((requested < maxPending) && (t.mesh.status[node] == 0)) { - t.context.candidates.push({id: node, instance:t, mesh:t.mesh, frame:t.context.frame, error:error}); - requested++; - } - - var blocked = t.blocked[node] || !t.expandNode(node, error); - if (blocked) - nblocked++; - else { - t.selected[node] = 1; - t.currentError = error; - } - t.insertChildren(node, blocked); - } - }, - - insertNode: function (node) { - var t = this; - t.visited[node] = 1; - - var error = t.nodeError(node); - if(node > 0 && error < t.targetError) return; //2% speed TODO check if needed - - var errors = t.mesh.errors; - var frames = t.mesh.frames; - if(frames[node] != t.context.frame || errors[node] < error) { - errors[node] = error; - frames[node] = t.context.frame; - } - t.visitQueue.push(node, error); - }, - - insertChildren : function (node, block) { - var t = this; - for(var i = t.mesh.nfirstpatch[node]; i < t.mesh.nfirstpatch[node+1]; ++i) { - var child = t.mesh.patches[i*3]; - if (child == t.mesh.sink) return; - if (block) t.blocked[child] = 1; - if (!t.visited[child]) - t.insertNode(child); - } - }, - - expandNode : function (node, error) { - var t = this; - if(node > 0 && error < t.targetError) { -// console.log("Reached error", error, t.targetError); - return false; - } - - if(t.drawSize > t.drawBudget) { -// console.log("Reached drawsize", t.drawSize, t.drawBudget); - return false; - } - - if(t.mesh.status[node] != 1) { //not ready -// console.log("Node " + node + " still not loaded (cache?)"); - return false; - } - - var sp = t.mesh.nspheres; - var off = node*5; - if(t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+3])) //expanded radius - t.drawSize += t.mesh.nvertices[node]*0.8; - //we are adding half of the new faces. (but we are using the vertices so *2) - - return true; - }, - - nodeError : function (n, tight) { - var t = this; - var spheres = t.mesh.nspheres; - var b = t.viewpoint; - var off = n*5; - var cx = spheres[off+0]; - var cy = spheres[off+1]; - var cz = spheres[off+2]; - var r = spheres[off+3]; - if(tight) - r = spheres[off+4]; - var d0 = b[0] - cx; - var d1 = b[1] - cy; - var d2 = b[2] - cz; - var dist = Math.sqrt(d0*d0 + d1*d1 + d2*d2) - r; - if (dist < 0.1) - dist = 0.1; - - //resolution is how long is a pixel at distance 1. - var error = t.mesh.nerrors[n]/(t.resolution*dist); //in pixels - - if (!t.isVisible(cx, cy. cz, spheres[off+4])) - error /= 1000.0; - return error; - }, - - isVisible : function (x, y, z, r) { - var p = this.planes; - for (i = 0; i < 24; i +=4) { - if(p[i]*x + p[i+1]*y + p[i+2]*z +p[i+3] + r < 0) //ax+by+cz+w = 0; - return false; - } - return true; - }, - - renderNodes: function() { - var t = this; - var m = t.mesh; - var gl = t.gl; - var attr = t.attributes; - - var vertexEnabled = gl.getVertexAttrib(attr.position, gl.VERTEX_ATTRIB_ARRAY_ENABLED); - var normalEnabled = gl.getVertexAttrib(attr.normal, gl.VERTEX_ATTRIB_ARRAY_ENABLED); - var colorEnabled = attr.color >= 0? gl.getVertexAttrib(attr.color, gl.VERTEX_ATTRIB_ARRAY_ENABLED): false; - var uvEnabled = attr.uv >= 0? gl.getVertexAttrib(attr.uv, gl.VERTEX_ATTRIB_ARRAY_ENABLED): false; - - gl.enableVertexAttribArray(attr.position); - if(m.vertex.texCoord && attr.uv >= 0) gl.enableVertexAttribArray(attr.uv); - if(m.vertex.normal && attr.normal >= 0) gl.enableVertexAttribArray(attr.normal); - if(m.vertex.color && attr.color >= 0) gl.enableVertexAttribArray(attr.color); - gl.vertexAttrib4fv(2, [0.8, 0.8, 0.8, 1.0]); - - if(Debug.nodes) { - gl.disableVertexAttribArray(2); - gl.disableVertexAttribArray(3); - } - - var rendered = 0; - var last_texture = -1; - for(var n = 0; n < m.nodesCount; n++) { - if(!t.selected[n]) continue; - - if(t.mode != "POINT") { - var skip = true; - for(var p = m.nfirstpatch[n]; p < m.nfirstpatch[n+1]; p++) { - var child = m.patches[p*3]; - if(!t.selected[child]) { - skip = false; - break; - } - } - if(skip) continue; - } - - - var sp = t.mesh.nspheres; - var off = n*5; - if(!t.isVisible(sp[off], sp[off+1], sp[off+2], sp[off+4])) //tight radius - continue; - - gl.bindBuffer(gl.ARRAY_BUFFER, m.vbo[n]); - if(t.mode != "POINT") - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ibo[n]); - - var nv = m.nvertices[n]; - - gl.vertexAttribPointer(attr.position, 3, gl.FLOAT, false, 12, 0); - var offset = nv*12; - if(m.vertex.texCoord && attr.uv >= 0) - gl.vertexAttribPointer(attr.uv, 2, gl.FLOAT, false, 8, offset), offset += nv*8; - if(m.vertex.normal && attr.normal >= 0) - gl.vertexAttribPointer(attr.normal, 3, gl.SHORT, true, 6, offset), offset += nv*6; - if(m.vertex.color && attr.color >= 0) - gl.vertexAttribPointer(attr.color, 4, gl.UNSIGNED_BYTE, true, 4, offset); - - if (Debug.nodes) { - var error = t.nodeError(n, true); - var palette = { - 1: [1, 1, 1, 1], - 2: [0.5, 1, 1, 1], - 4: [0, 1, 1, 1], - 8: [0, 1, 0.5, 1], - 12: [0, 1, 0, 1], - 16: [0, 1, 0, 1], - 20: [1, 1, 0, 1], - 30: [1, 0.5, 0, 1], - 1e20: [1, 0, 0, 1] }; - - for(i in palette) - if(i > error) { - gl.vertexAttrib4fv(attr.color, palette[i]); - break; - } -// gl.vertexAttrib4fv(2, [(n*200 %255)/255.0, (n*140 %255)/255.0,(n*90 %255)/255.0, 1]); - } - - if (Debug.draw) continue; - - if(t.mode == "POINT") { - var pointsize = Math.ceil(0.30*t.currentError); - if(pointsize > 2) pointsize = 2; - gl.vertexAttrib1fv(4, [pointsize]); - - var error = t.nodeError(n); - var fraction = (error/t.currentError - 1); - if(fraction > 1) fraction = 1; - - var count = fraction * nv; - if(count != 0) { - if(m.vertex.texCoord) { - var texid = m.patches[m.nfirstpatch[n]*3+2]; - if(texid != -1 && texid != last_texture) { //bind texture - var tex = m.texids[texid]; - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, tex); - } - } - gl.drawArrays(gl.POINTS, 0, count); - rendered += count; - } - continue; - } - - //concatenate renderings to remove useless calls. except we have textures. - var offset = 0; - var end = 0; - var last = m.nfirstpatch[n+1]-1; - for (var p = m.nfirstpatch[n]; p < m.nfirstpatch[n+1]; ++p) { - var child = m.patches[p*3]; - - if(!t.selected[child]) { - end = m.patches[p*3+1]; - if(p < last) //if textures we do not join. TODO: should actually check for same texture of last one. - continue; - } - if(end > offset) { - if(m.vertex.texCoord) { - var texid = m.patches[p*3+2]; - if(texid != -1 && texid != last_texture) { //bind texture - var tex = m.texids[texid]; - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, tex); - last_texture = texid; - } - } - gl.drawElements(gl.TRIANGLES, (end - offset) * 3, gl.UNSIGNED_SHORT, offset * 6); - rendered += end - offset; - } - offset = m.patches[p*3+1]; - } - } - - t.context.rendered += rendered; - if(!vertexEnabled) gl.disableVertexAttribArray(attr.position); - if(!normalEnabled && attr.normal >= 0) gl.disableVertexAttribArray(attr.normal); - if(!colorEnabled && attr.color >= 0) gl.disableVertexAttribArray(attr.color); - if(!uvEnabled && attr.uv >= 0) gl.disableVertexAttribArray(attr.uv); - - gl.bindBuffer(gl.ARRAY_BUFFER, null); - if(t.mode != "POINT") - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); - }, - - render: function() { - this.traversal(); - this.renderNodes(); - } -}; - - -//keep track of meshes and which GL they belong to. (no sharing between contexts) -var contexts = []; - -function getContext(gl) { - var c = null; - if(!gl.isTexture) throw "Something wrong"; - contexts.forEach(function(g) { - if(g.gl == gl) c = g; - }); - if(c) return c; - c = { gl:gl, meshes:[], frame:0, cacheSize:0, candidates:[], pending:0, - targetFps: targetFps, targetError: targetError, currentError: targetError }; - contexts.push(c); - return c; -} - - - -function beginFrame(gl, fps) { //each context has a separate frame count. - var c = getContext(gl); - - c.frame++; - c.candidates = []; - if(fps && c.targetFps) { - var r = c.targetFps/fps; - if(r > 1.1) - c.currentError *= 1.05; - if(r < 0.9) - c.currentError *= 0.95; - - if(c.currentError < c.targetError) - c.currentError = c.targetError; - if(c.currentError > 10) c.currentError = 10; - } -// console.log("current", c.currentError, "fps", fps, "targetFps", c.targetFps, "rendered", c.rendered); - c.rendered = 0; -} - -function endFrame(gl) { - updateCache(gl); -} - -function removeNode(context, node) { - var n = node.id; - var m = node.mesh; - if(m.status[n] == 0) return; - -// console.log("Removing " + m.url + " node: " + n); - m.status[n] = 0; - - if (m.georeq.readyState != 4) m.georeq.abort(); - if (m.texreq.readyState != 4) m.texreq.abort(); - - context.cacheSize -= m.nsize[n]; - context.gl.deleteBuffer(m.vbo[n]); - context.gl.deleteBuffer(m.ibo[n]); - m.vbo[n] = m.ibo[n] = null; - - if(!m.vertex.texCoord) return; - var tex = m.patches[m.nfirstpatch[n]*3+2]; //TODO assuming one texture per node - m.texref[tex]--; - - if(m.texref[tex] == 0 && m.texids[tex]) { - context.gl.deleteTexture(m.texids[tex]); - m.texids[tex] = null; - } -} - -function requestNode(context, node) { - var n = node.id; - var m = node.mesh; - - m.status[n] = 2; //pending - - context.pending++; - context.cacheSize += m.nsize[n]; - - node.reqAttempt = 0; - node.context = context; - node.nvert = m.nvertices[n]; - node.nface = m.nfaces[n]; - -// console.log("Requesting " + m.url + " node: " + n); - requestNodeGeometry(context, node); - requestNodeTexture(context, node); -} - -function requestNodeGeometry(context, node) { - var n = node.id; - var m = node.mesh; - - m.status[n]++; //pending - m.georeq = m.httpRequest( - m.noffsets[n], - m.noffsets[n+1], - function() { loadNodeGeometry(this, context, node); }, - function() { -// console.log("Geometry request error!"); - recoverNode(context, node, 0); - }, - function() { -// console.log("Geometry request abort!"); - removeNode(context, node); - }, - 'arraybuffer' - ); -} - -function requestNodeTexture(context, node) { - var n = node.id; - var m = node.mesh; - - if(!m.vertex.texCoord) return; - - var tex = m.patches[m.nfirstpatch[n]*3+2]; - m.texref[tex]++; - if(m.texids[tex]) - return; - - m.status[n]++; //pending - m.texreq = m.httpRequest( - m.textures[tex], - m.textures[tex+1], - function() { loadNodeTexture(this, context, node, tex); }, - function() { -// console.log("Texture request error!"); - recoverNode(context, node, 1); - }, - function() { -// console.log("Texture request abort!"); - removeNode(context, node); - }, - 'blob' - ); -} - -function recoverNode(context, node, id) { - var n = node.id; - var m = node.mesh; - if(m.status[n] == 0) return; - - m.status[n]--; - - if(node.reqAttempt > maxReqAttempt) { -// console.log("Max request limit for " + m.url + " node: " + n); - removeNode(context, node); - return; - } - - node.reqAttempt++; - - switch (id){ - case 0: - requestNodeGeometry(context, node); - console.log("Recovering geometry for " + m.url + " node: " + n); - break; - case 1: - requestNodeTexture(context, node); - console.log("Recovering texture for " + m.url + " node: " + n); - break; - } -} - -function loadNodeGeometry(request, context, node) { - var n = node.id; - var m = node.mesh; - if(m.status[n] == 0) return; - - node.buffer = request.response; - - if(!m.compressed) - readyNode(node); - else if(m.meco) { - var sig = { texcoords: m.vertex.texCoord, normals:m.vertex.normal, colors:m.vertex.color, indices: m.face.index } - var patches = []; - for(var k = m.nfirstpatch[n]; k < m.nfirstpatch[n+1]; k++) - patches.push(m.patches[k*3+1]); - if(!meco) loadMeco(); - meco.postRequest(sig, node, patches); - } else { - if(!corto) loadCorto(); - corto.postRequest(node); - } -} - -function loadNodeTexture(request, context, node, texid) { - var n = node.id; - var m = node.mesh; - if(m.status[n] == 0) return; - - var blob = request.response; - - var urlCreator = window.URL || window.webkitURL; - var img = document.createElement('img'); - img.onerror = function(e) { console.log("Texture loading error!"); }; - img.src = urlCreator.createObjectURL(blob); - - var gl = context.gl; - img.onload = function() { - urlCreator.revokeObjectURL(img.src); - - var flip = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); - var tex = m.texids[texid] = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, tex); - var s = gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flip); - - m.status[n]--; - - if(m.status[n] == 2) { - m.status[n]--; //ready - node.reqAttempt = 0; - node.context.pending--; - node.instance.onUpdate(); - updateCache(gl); - } - } -} - -function scramble(n, coords, normals, colors) { - while (n > 0) { - var i = Math.floor(Math.random() * n); - n--; - for(var k =0; k < 3; k++) { - var v = coords[n*3+k]; - coords[n*3+k] = coords[i*3+k]; - coords[i*3+k] = v; - - if(normals) { - var v = normals[n*3+k]; - normals[n*3+k] = normals[i*3+k]; - normals[i*3+k] = v; - } - if(colors) { - var v = colors[n*4+k]; - colors[n*4+k] = colors[i*4+k]; - colors[i*4+k] = v; - } - } - } -} - -function readyNode(node) { - var m = node.mesh; - var n = node.id; - var nv = m.nvertices[n]; - var nf = m.nfaces[n]; - var model = node.model; - - var vertices; - var indices; - - if(!m.corto) { - vertices = new Uint8Array(node.buffer, 0, nv*m.vsize); - //dangerous alignment 16 bits if we had 1b attribute - indices = new Uint8Array(node.buffer, nv*m.vsize, nf*m.fsize); - if(nf == 0) { - var off = nv*12; - var v = new Float32Array(node.buffer, 0, nv*3); - if(m.vertex.normal) { - var no = new Int16Array(node.buffer, off, nv*3); off += nv*6; - } - if(m.vertex.color) { - var co = new Uint8Array(node.buffer, off, nv*4); - } - scramble(nv, v, no, co); - } - } else { - indices = node.model.index; - vertices = new ArrayBuffer(nv*m.vsize); - var v = new Float32Array(vertices, 0, nv*3); - v.set(model.position, 0, nv*3); - var off = nv*12; - if(model.uv) { - var uv = new Float32Array(vertices, off, nv*2); - uv.set(model.uv); off += nv*8; - } - if(model.normal) { - var no = new Int16Array(vertices, off, nv*3); - no.set(model.normal); off += nv*6; - } - if(model.color) { - var co = new Uint8Array(vertices, off, nv*4); - co.set(model.color); off += nv*4; - } - if(nf == 0) - scramble(nv, v, no, co); - } - - var gl = node.context.gl; - var vbo = m.vbo[n] = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, vbo); - gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); - var ibo = m.ibo[n] = gl.createBuffer(); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); - - m.status[n]--; - - if(m.status[n] == 2) { - m.status[n]--; //ready - node.reqAttempt = 0; - node.context.pending--; - node.instance.onUpdate(); - updateCache(gl); - } -} - -function updateCache(gl) { - var context = getContext(gl); - - var best = null; - context.candidates.forEach(function(e) { - if(e.mesh.status[e.id] == 0 && (!best || e.error > best.error)) best = e; - }); - context.candidates = []; - if(!best) return; - - while(context.cacheSize > maxCacheSize) { - var worst = null; - //find worst in cache - context.meshes.forEach(function(m) { - var n = m.nodesCount; - for(i = 0; i < n; i++) - if(!worst || (m.status[i] == 1 && m.errors[i] < worst.error)) - worst = {error: m.errors[i], frame: m.frames[i], mesh:m, id:i}; - }); - - if(!worst || (worst.error >= best.error && worst.frame == best.frame)) - return; - removeNode(context, worst); - } - - requestNode(context, best); - - if(context.pending < maxPending) - updateCache(gl); -} - -//nodes are loaded asincronously, just update mesh content (VBO) cache size is kept globally. -//but this could be messy. - -function setTargetError(gl, error) { - var context = getContext(gl); - context.targetError = error; -} -function setTargetFps(gl, fps) { - var context = getContext(gl); - context.targetFps = fps; -} -function setMaxCacheSize(gl, size) { - var context = getContext(gl); - context.maxCacheSize = size; -} - -return { Mesh: Mesh, Renderer: Instance, Renderable: Instance, Instance:Instance, - Debug: Debug, contexts: contexts, beginFrame:beginFrame, endFrame:endFrame, - setTargetError:setTargetError, setTargetFps:setTargetFps, setMaxCacheSize:setMaxCacheSize }; - -}();
--- a/Resources/Nexus/js/nexus_three.js Sat Jun 15 16:17:41 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -function NexusObject(url, renderer, render, material) { - var gl = renderer.context; - var geometry = new THREE.BufferGeometry(); - var positions = new Float32Array(3); - - - geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3)); - - - if(!material) - this.autoMaterial = true; - - THREE.Mesh.call( this, geometry, material); - this.frustumCulled = false; - - var mesh = this; - var instance = this.instance = new Nexus.Instance(gl); - instance.open(url); - instance.onLoad = function() { - var s = 1/instance.mesh.sphere.radius; - var pos = instance.mesh.sphere.center; - mesh.position.set(-pos[0]*s, -pos[1]*s, -pos[2]*s); - mesh.scale.set(s, s, s); - if(mesh.autoMaterial) - mesh.material = new THREE.MeshLambertMaterial( { color: 0xffffff } ); - - if(this.mesh.vertex.normal) { - var normals = new Float32Array(3); - geometry.addAttribute( 'normal', new THREE.BufferAttribute(normals, 3)); - } - if(this.mesh.vertex.color) { - var colors = new Float32Array(4); - geometry.addAttribute( 'color', new THREE.BufferAttribute(colors, 4)); - if(mesh.autoMaterial) - mesh.material = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors }); - } - - if(this.mesh.vertex.texCoord) { - var uv = new Float32Array(2); - geometry.addAttribute( 'uv', new THREE.BufferAttribute(uv, 2)); - if(mesh.autoMaterial) { - var texture = new THREE.DataTexture( new Uint8Array([1, 1, 1]), 1, 1, THREE.RGBFormat ); - texture.needsUpdate = true; - mesh.material = new THREE.MeshLambertMaterial( { color: 0xffffff, map: texture } ); - } - } - - if(this.mesh.face.index) { - var indices = new Uint32Array(3); - geometry.setIndex(new THREE.BufferAttribute( indices, 3) ); - } - render(); - }; - instance.onUpdate = function() { render(); } - - this.onAfterRender = function(renderer, scene, camera, geometry, material, group) { - if(!instance.isReady) return; - var s = renderer.getSize(); - instance.updateView([0, 0, s.width, s.height], - camera.projectionMatrix.elements, - mesh.modelViewMatrix.elements); - var program = renderer.context.getParameter(gl.CURRENT_PROGRAM); - instance.attributes['position'] = renderer.context.getAttribLocation(program, "position"); - instance.attributes['normal'] = renderer.context.getAttribLocation(program, "normal"); - instance.attributes['color'] = renderer.context.getAttribLocation(program, "color"); - instance.attributes['uv'] = renderer.context.getAttribLocation(program, "uv"); - - instance.render(); - } -} - -NexusObject.prototype = Object.create(THREE.Mesh.prototype); - -NexusObject.prototype.raycast = function(raycaster, intersects) { - var instance = this.instance; - var nexus = instance.mesh; - if(!nexus.sphere) return; - var sp = nexus.sphere; - var c = sp.center; - var center = new THREE.Vector3(c[0], c[1], c[2]); - var sphere = new THREE.Sphere(center, sp.radius); - sphere.applyMatrix4( this.matrixWorld ); - - if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - //just check the last level spheres. - if(!nexus.sink) return; - - var distance = -1.0; - for(var i = 0; i < nexus.sink; i++) { - var patch = nexus.nfirstpatch[i]; - if(nexus.patches[patch*3] != nexus.sink) - continue; - var x = nexus.nspheres[i*5]; - var y = nexus.nspheres[i*5+1]; - var z = nexus.nspheres[i*5+2]; - var r = nexus.nspheres[i*5+4]; //tight radius - var sphere = new THREE.Sphere(new THREE.Vector3(x, y, z), r); - sphere.applyMatrix4( this.matrixWorld ); - if ( raycaster.ray.intersectsSphere( sphere ) != false ) { - var d = sphere.center.lengthSq(); - if(distance == -1.0 || d < distance) - distance = d; - } - } - if(distance == -1.0) return; - - intersects.push({ distance: distance, object: this} ); -}
--- a/Resources/Nexus/threejs.html Sat Jun 15 16:17:41 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> -<title>Nexus threejs</title> -<meta charset="utf-8"> -<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> -<style>body { margin: 0px; overflow: hidden; }</style> -<script src="three-84.js"></script> -<script src="js/TrackballControls.js"></script> -<script src="js/nexus.js"></script> -<script src="js/nexus_three.js"></script> -</head> - -<body> - <div id="container"></div> -</body> - -<script> - -var camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.1, 100 ); -camera.position.z = 4; - -var controls = new THREE.TrackballControls( camera ); -controls.rotateSpeed = 10.0; -controls.zoomSpeed = 1.5; -controls.panSpeed = 0.8; -controls.noZoom = false; -controls.noPan = false; -controls.staticMoving = true; -controls.dynamicDampingFactor = 0.3; -controls.keys = [ 65, 83, 68 ]; -controls.addEventListener( 'change', render ); - -var scene = new THREE.Scene(); -scene.fog = new THREE.Fog( 0x050505, 2000, 3500 ); -scene.add( new THREE.AmbientLight( 0x444444 ) ); - -var light1 = new THREE.DirectionalLight( 0xffffff, 1.0 ); -light1.position.set( 1, 1, -1 ); -scene.add( light1 ); - -var light2 = new THREE.DirectionalLight( 0xffffff, 1.0 ); -light2.position.set( -1, -1, 1 ); -scene.add( light2 ); - -var renderer = new THREE.WebGLRenderer( { antialias: false } ); -renderer.setClearColor( scene.fog.color ); -renderer.setPixelRatio( window.devicePixelRatio ); -renderer.setSize( window.innerWidth, window.innerHeight); - -var container = document.getElementById( 'container'); -container.appendChild( renderer.domElement ); - -/* An appropriate material can be used as a fourth arg for the NexusObject constructor - -var texture = new THREE.DataTexture( new Uint8Array([1, 1, 1]), 1, 1, THREE.RGBFormat ); -texture.needsUpdate = true; -var material = new THREE.MeshLambertMaterial( { color: 0xffffff, map: texture } ); -*/ - -function getURLParameter(name) { - return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null; -} - -var model = getURLParameter('model') || "models/gargo.nxz"; - -var nexus_obj = new NexusObject(model, renderer, render); -scene.add(nexus_obj); - -window.addEventListener( 'resize', onWindowResize, false ); -render(); - - -function onWindowResize() { - - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - - renderer.setSize( window.innerWidth, window.innerHeight ); - - controls.handleResize(); - controls.update(); - render(); -} - -function animate() { - requestAnimationFrame( animate ); - controls.update(); -} - -function render() { - Nexus.beginFrame(renderer.context); - renderer.render( scene, camera ); - Nexus.endFrame(renderer.context); -} - -animate(); - -</script> - - -</html> -
--- a/Sources/Plugin.cpp Sat Jun 15 16:17:41 2024 +0200 +++ b/Sources/Plugin.cpp Sat Jun 15 16:35:21 2024 +0200 @@ -771,51 +771,7 @@ } const std::string file = request->groups[0]; - - if (file == "three-84.js") - { - cache_.Answer(output, "nexus/three-84.js"); - } - else - { - Orthanc::EmbeddedResources::FileResourceId resourceId; - Orthanc::MimeType mimeType; - - if (file == "threejs.html") - { - resourceId = Orthanc::EmbeddedResources::NEXUS_HTML; - mimeType = Orthanc::MimeType_Html; - } - else if (file == "js/meco.js") - { - resourceId = Orthanc::EmbeddedResources::NEXUS_MECO_JS; - mimeType = Orthanc::MimeType_JavaScript; - } - else if (file == "js/nexus.js") - { - resourceId = Orthanc::EmbeddedResources::NEXUS_JS; - mimeType = Orthanc::MimeType_JavaScript; - } - else if (file == "js/nexus_three.js") - { - resourceId = Orthanc::EmbeddedResources::NEXUS_THREE_JS; - mimeType = Orthanc::MimeType_JavaScript; - } - else if (file == "js/TrackballControls.js") - { - resourceId = Orthanc::EmbeddedResources::NEXUS_TRACKBALL_JS; - mimeType = Orthanc::MimeType_JavaScript; - } - else - { - OrthancPluginSendHttpStatusCode(OrthancPlugins::GetGlobalContext(), output, 404); - return; - } - - std::string s; - Orthanc::EmbeddedResources::GetFileResource(s, resourceId); - OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::EnumerationToString(mimeType)); - } + cache_.Answer(output, "nexus/" + file); }