1+ /*
2+ * This file is part of BlueMap, licensed under the MIT License (MIT).
3+ *
4+ * Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
5+ * Copyright (c) Kevin Chapelier <https://github.com/kchapelier>
6+ * Copyright (c) contributors
7+ *
8+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9+ * of this software and associated documentation files (the "Software"), to deal
10+ * in the Software without restriction, including without limitation the rights
11+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+ * copies of the Software, and to permit persons to whom the Software is
13+ * furnished to do so, subject to the following conditions:
14+ *
15+ * The above copyright notice and this permission notice shall be included in
16+ * all copies or substantial portions of the Software.
17+ *
18+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+ * THE SOFTWARE.
25+ *
26+ * Adapted version of PRWM by Kevin Chapelier
27+ * See https://github.com/kchapelier/PRWM for more informations about this file format
28+ */
29+
30+ import {
31+ DefaultLoadingManager ,
32+ BufferGeometry ,
33+ BufferAttribute ,
34+ FloatType
35+ } from "three"
36+
37+ "use strict" ;
38+
39+ let bigEndianPlatform = null ;
40+
41+ /**
42+ * Check if the endianness of the platform is big-endian (most significant bit first)
43+ * @returns {boolean } True if big-endian, false if little-endian
44+ */
45+ function isBigEndianPlatform ( ) {
46+ if ( bigEndianPlatform === null ) {
47+ let buffer = new ArrayBuffer ( 2 ) ,
48+ uint8Array = new Uint8Array ( buffer ) ,
49+ uint16Array = new Uint16Array ( buffer ) ;
50+
51+ uint8Array [ 0 ] = 0xAA ; // set first byte
52+ uint8Array [ 1 ] = 0xBB ; // set second byte
53+ bigEndianPlatform = ( uint16Array [ 0 ] === 0xAABB ) ;
54+ }
55+
56+ return bigEndianPlatform ;
57+ }
58+
59+ // match the values defined in the spec to the TypedArray types
60+ let InvertedEncodingTypes = [
61+ null ,
62+ Float32Array ,
63+ null ,
64+ Int8Array ,
65+ Int16Array ,
66+ null ,
67+ Int32Array ,
68+ Uint8Array ,
69+ Uint16Array ,
70+ null ,
71+ Uint32Array
72+ ] ;
73+
74+ // define the method to use on a DataView, corresponding the TypedArray type
75+ let getMethods = {
76+ Uint16Array : 'getUint16' ,
77+ Uint32Array : 'getUint32' ,
78+ Int16Array : 'getInt16' ,
79+ Int32Array : 'getInt32' ,
80+ Float32Array : 'getFloat32' ,
81+ Float64Array : 'getFloat64'
82+ } ;
83+
84+ function copyFromBuffer ( sourceArrayBuffer , viewType , position , length , fromBigEndian ) {
85+ let bytesPerElement = viewType . BYTES_PER_ELEMENT ,
86+ result ;
87+
88+ if ( fromBigEndian === isBigEndianPlatform ( ) || bytesPerElement === 1 ) {
89+ result = new viewType ( sourceArrayBuffer , position , length ) ;
90+ } else {
91+ console . debug ( "PRWM file has opposite encoding, loading will be slow..." ) ;
92+
93+ let readView = new DataView ( sourceArrayBuffer , position , length * bytesPerElement ) ,
94+ getMethod = getMethods [ viewType . name ] ,
95+ littleEndian = ! fromBigEndian ,
96+ i = 0 ;
97+
98+ result = new viewType ( length ) ;
99+
100+ for ( ; i < length ; i ++ ) {
101+ result [ i ] = readView [ getMethod ] ( i * bytesPerElement , littleEndian ) ;
102+ }
103+ }
104+
105+ return result ;
106+ }
107+
108+ /**
109+ * @param buffer {ArrayBuffer}
110+ * @param offset {number}
111+ */
112+ function decodePrwm ( buffer , offset ) {
113+ offset = offset || 0 ;
114+
115+ let array = new Uint8Array ( buffer , offset ) ,
116+ version = array [ 0 ] ,
117+ flags = array [ 1 ] ,
118+ indexedGeometry = ! ! ( flags >> 7 & 0x01 ) ,
119+ indicesType = flags >> 6 & 0x01 ,
120+ bigEndian = ( flags >> 5 & 0x01 ) === 1 ,
121+ attributesNumber = flags & 0x1F ,
122+ valuesNumber = 0 ,
123+ indicesNumber = 0 ;
124+
125+ if ( bigEndian ) {
126+ valuesNumber = ( array [ 2 ] << 16 ) + ( array [ 3 ] << 8 ) + array [ 4 ] ;
127+ indicesNumber = ( array [ 5 ] << 16 ) + ( array [ 6 ] << 8 ) + array [ 7 ] ;
128+ } else {
129+ valuesNumber = array [ 2 ] + ( array [ 3 ] << 8 ) + ( array [ 4 ] << 16 ) ;
130+ indicesNumber = array [ 5 ] + ( array [ 6 ] << 8 ) + ( array [ 7 ] << 16 ) ;
131+ }
132+
133+ /** PRELIMINARY CHECKS **/
134+
135+ if ( offset / 4 % 1 !== 0 ) {
136+ throw new Error ( 'PRWM decoder: Offset should be a multiple of 4, received ' + offset ) ;
137+ }
138+
139+ if ( version === 0 ) {
140+ throw new Error ( 'PRWM decoder: Invalid format version: 0' ) ;
141+ } else if ( version !== 1 ) {
142+ throw new Error ( 'PRWM decoder: Unsupported format version: ' + version ) ;
143+ }
144+
145+ if ( ! indexedGeometry ) {
146+ if ( indicesType !== 0 ) {
147+ throw new Error ( 'PRWM decoder: Indices type must be set to 0 for non-indexed geometries' ) ;
148+ } else if ( indicesNumber !== 0 ) {
149+ throw new Error ( 'PRWM decoder: Number of indices must be set to 0 for non-indexed geometries' ) ;
150+ }
151+ }
152+
153+ /** PARSING **/
154+
155+ let pos = 8 ;
156+
157+ let attributes = { } ,
158+ attributeName ,
159+ char ,
160+ attributeType ,
161+ cardinality ,
162+ encodingType ,
163+ normalized ,
164+ arrayType ,
165+ values ,
166+ indices ,
167+ groups ,
168+ next ,
169+ i ;
170+
171+ for ( i = 0 ; i < attributesNumber ; i ++ ) {
172+ attributeName = '' ;
173+
174+ while ( pos < array . length ) {
175+ char = array [ pos ] ;
176+ pos ++ ;
177+
178+ if ( char === 0 ) {
179+ break ;
180+ } else {
181+ attributeName += String . fromCharCode ( char ) ;
182+ }
183+ }
184+
185+ flags = array [ pos ] ;
186+
187+ attributeType = flags >> 7 & 0x01 ;
188+ normalized = flags >> 6 & 0x01 ;
189+ cardinality = ( flags >> 4 & 0x03 ) + 1 ;
190+ encodingType = flags & 0x0F ;
191+ arrayType = InvertedEncodingTypes [ encodingType ] ;
192+
193+ pos ++ ;
194+
195+ // padding to next multiple of 4
196+ pos = Math . ceil ( pos / 4 ) * 4 ;
197+
198+ values = copyFromBuffer ( buffer , arrayType , pos + offset , cardinality * valuesNumber , bigEndian ) ;
199+
200+ pos += arrayType . BYTES_PER_ELEMENT * cardinality * valuesNumber ;
201+
202+ attributes [ attributeName ] = {
203+ type : attributeType ,
204+ cardinality : cardinality ,
205+ values : values ,
206+ normalized : normalized === 1
207+ } ;
208+ }
209+
210+ indices = null ;
211+ if ( indexedGeometry ) {
212+ pos = Math . ceil ( pos / 4 ) * 4 ;
213+ indices = copyFromBuffer (
214+ buffer ,
215+ indicesType === 1 ? Uint32Array : Uint16Array ,
216+ pos + offset ,
217+ indicesNumber ,
218+ bigEndian
219+ ) ;
220+ }
221+
222+ // read groups
223+ groups = [ ] ;
224+ pos = Math . ceil ( pos / 4 ) * 4 ;
225+ while ( pos < array . length ) {
226+ next = read4ByteInt ( array , pos ) ;
227+ if ( next === - 1 ) {
228+ pos += 4 ;
229+ break ;
230+ }
231+ groups . push ( {
232+ materialIndex : next ,
233+ start : read4ByteInt ( array , pos + 4 ) ,
234+ count : read4ByteInt ( array , pos + 8 )
235+ } ) ;
236+ pos += 12 ;
237+ }
238+
239+ return {
240+ version : version ,
241+ attributes : attributes ,
242+ indices : indices ,
243+ groups : groups
244+ } ;
245+ }
246+
247+ function read4ByteInt ( array , pos ) {
248+ return array [ pos ] |
249+ array [ pos + 1 ] << 8 |
250+ array [ pos + 2 ] << 16 |
251+ array [ pos + 3 ] << 24 ;
252+ }
253+
254+ export class PRBMLoader {
255+
256+ constructor ( manager ) {
257+ this . manager = ( manager !== undefined ) ? manager : DefaultLoadingManager ;
258+ }
259+
260+ load ( url , onLoad , onProgress , onError ) {
261+ let scope = this ;
262+
263+ url = url . replace ( / \* / g, isBigEndianPlatform ( ) ? 'be' : 'le' ) ;
264+
265+ let loader = new FileLoader ( scope . manager ) ;
266+ loader . setPath ( scope . path ) ;
267+ loader . setResponseType ( 'arraybuffer' ) ;
268+
269+ loader . load ( url , function ( arrayBuffer ) {
270+ onLoad ( scope . parse ( arrayBuffer ) ) ;
271+ } , onProgress , onError ) ;
272+ }
273+
274+ setPath ( value ) {
275+ this . path = value ;
276+ return this ;
277+ }
278+
279+ parse ( arrayBuffer , offset ) {
280+ let data = decodePrwm ( arrayBuffer , offset ) ,
281+ attributesKey = Object . keys ( data . attributes ) ,
282+ bufferGeometry = new BufferGeometry ( ) ,
283+ attribute ,
284+ bufferAttribute ,
285+ i ;
286+
287+ for ( i = 0 ; i < attributesKey . length ; i ++ ) {
288+ attribute = data . attributes [ attributesKey [ i ] ] ;
289+ bufferAttribute = new BufferAttribute ( attribute . values , attribute . cardinality , attribute . normalized ) ;
290+ bufferAttribute . gpuType = FloatType ;
291+ bufferGeometry . setAttribute ( attributesKey [ i ] , bufferAttribute ) ;
292+ }
293+
294+ if ( data . indices !== null ) {
295+ bufferGeometry . setIndex ( new BufferAttribute ( data . indices , 1 ) ) ;
296+ }
297+
298+ bufferGeometry . groups = data . groups ;
299+
300+ return bufferGeometry ;
301+ }
302+
303+ isBigEndianPlatform ( ) {
304+ return isBigEndianPlatform ( ) ;
305+ }
306+
307+ }
0 commit comments