ModelOutlineLoader.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. import defined from "../Core/defined.js";
  2. import PixelFormat from "../Core/PixelFormat.js";
  3. import ContextLimits from "../Renderer/ContextLimits.js";
  4. import Sampler from "../Renderer/Sampler.js";
  5. import Texture from "../Renderer/Texture.js";
  6. import TextureMagnificationFilter from "../Renderer/TextureMagnificationFilter.js";
  7. import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
  8. import TextureWrap from "../Renderer/TextureWrap.js";
  9. import ForEach from "./GltfPipeline/ForEach.js";
  10. // glTF does not allow an index value of 65535 because this is the primitive
  11. // restart value in some APIs.
  12. const MAX_GLTF_UINT16_INDEX = 65534;
  13. /**
  14. * Creates face outlines for glTF primitives with the `CESIUM_primitive_outline` extension.
  15. * @private
  16. */
  17. function ModelOutlineLoader() {}
  18. /**
  19. * Returns true if the model uses or requires CESIUM_primitive_outline.
  20. * @private
  21. */
  22. ModelOutlineLoader.hasExtension = function (model) {
  23. return (
  24. defined(model.extensionsRequired.CESIUM_primitive_outline) ||
  25. defined(model.extensionsUsed.CESIUM_primitive_outline)
  26. );
  27. };
  28. /**
  29. * Arranges to outline any primitives with the CESIUM_primitive_outline extension.
  30. * It is expected that all buffer data is loaded and available in
  31. * `extras._pipeline.source` before this function is called, and that vertex
  32. * and index WebGL buffers are not yet created.
  33. * @private
  34. */
  35. ModelOutlineLoader.outlinePrimitives = function (model) {
  36. if (!ModelOutlineLoader.hasExtension(model)) {
  37. return;
  38. }
  39. const gltf = model.gltf;
  40. // Assumption: A single bufferView contains a single zero-indexed range of vertices.
  41. // No trickery with using large accessor byteOffsets to store multiple zero-based
  42. // ranges of vertices in a single bufferView. Use separate bufferViews for that,
  43. // you monster.
  44. // Note that interleaved vertex attributes (e.g. position0, normal0, uv0,
  45. // position1, normal1, uv1, ...) _are_ supported and should not be confused with
  46. // the above.
  47. const vertexNumberingScopes = [];
  48. ForEach.mesh(gltf, function (mesh, meshId) {
  49. ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {
  50. if (!defined(primitive.extensions)) {
  51. return;
  52. }
  53. const outlineData = primitive.extensions.CESIUM_primitive_outline;
  54. if (!defined(outlineData)) {
  55. return;
  56. }
  57. const vertexNumberingScope = getVertexNumberingScope(model, primitive);
  58. if (vertexNumberingScope === undefined) {
  59. return;
  60. }
  61. if (vertexNumberingScopes.indexOf(vertexNumberingScope) < 0) {
  62. vertexNumberingScopes.push(vertexNumberingScope);
  63. }
  64. // Add the outline to this primitive
  65. addOutline(
  66. model,
  67. meshId,
  68. primitiveId,
  69. outlineData.indices,
  70. vertexNumberingScope
  71. );
  72. });
  73. });
  74. // Update all relevant bufferViews to include the duplicate vertices that are
  75. // needed for outlining.
  76. for (let i = 0; i < vertexNumberingScopes.length; ++i) {
  77. updateBufferViewsWithNewVertices(
  78. model,
  79. vertexNumberingScopes[i].bufferViews
  80. );
  81. }
  82. // Remove data not referenced by any bufferViews anymore.
  83. compactBuffers(model);
  84. };
  85. ModelOutlineLoader.createTexture = function (model, context) {
  86. let cache = context.cache.modelOutliningCache;
  87. if (!defined(cache)) {
  88. cache = context.cache.modelOutliningCache = {};
  89. }
  90. if (defined(cache.outlineTexture)) {
  91. return cache.outlineTexture;
  92. }
  93. const maxSize = Math.min(4096, ContextLimits.maximumTextureSize);
  94. let size = maxSize;
  95. const levelZero = createTexture(size);
  96. const mipLevels = [];
  97. while (size > 1) {
  98. size >>= 1;
  99. mipLevels.push(createTexture(size));
  100. }
  101. const texture = new Texture({
  102. context: context,
  103. source: {
  104. arrayBufferView: levelZero,
  105. mipLevels: mipLevels,
  106. },
  107. width: maxSize,
  108. height: 1,
  109. pixelFormat: PixelFormat.LUMINANCE,
  110. sampler: new Sampler({
  111. wrapS: TextureWrap.CLAMP_TO_EDGE,
  112. wrapT: TextureWrap.CLAMP_TO_EDGE,
  113. minificationFilter: TextureMinificationFilter.LINEAR_MIPMAP_LINEAR,
  114. magnificationFilter: TextureMagnificationFilter.LINEAR,
  115. }),
  116. });
  117. cache.outlineTexture = texture;
  118. return texture;
  119. };
  120. function addOutline(
  121. model,
  122. meshId,
  123. primitiveId,
  124. edgeIndicesAccessorId,
  125. vertexNumberingScope
  126. ) {
  127. const vertexCopies = vertexNumberingScope.vertexCopies;
  128. const extraVertices = vertexNumberingScope.extraVertices;
  129. const outlineCoordinates = vertexNumberingScope.outlineCoordinates;
  130. const gltf = model.gltf;
  131. const mesh = gltf.meshes[meshId];
  132. const primitive = mesh.primitives[primitiveId];
  133. const accessors = gltf.accessors;
  134. const bufferViews = gltf.bufferViews;
  135. // Find the number of vertices in this primitive by looking at
  136. // the first attribute. Others are required to be the same.
  137. let numVertices;
  138. for (const semantic in primitive.attributes) {
  139. if (primitive.attributes.hasOwnProperty(semantic)) {
  140. const attributeId = primitive.attributes[semantic];
  141. const accessor = accessors[attributeId];
  142. if (defined(accessor)) {
  143. numVertices = accessor.count;
  144. break;
  145. }
  146. }
  147. }
  148. if (!defined(numVertices)) {
  149. return undefined;
  150. }
  151. const triangleIndexAccessorGltf = accessors[primitive.indices];
  152. const triangleIndexBufferViewGltf =
  153. bufferViews[triangleIndexAccessorGltf.bufferView];
  154. const edgeIndexAccessorGltf = accessors[edgeIndicesAccessorId];
  155. const edgeIndexBufferViewGltf = bufferViews[edgeIndexAccessorGltf.bufferView];
  156. const loadResources = model._loadResources;
  157. const triangleIndexBufferView = loadResources.getBuffer(
  158. triangleIndexBufferViewGltf
  159. );
  160. const edgeIndexBufferView = loadResources.getBuffer(edgeIndexBufferViewGltf);
  161. let triangleIndices =
  162. triangleIndexAccessorGltf.componentType === 5123
  163. ? new Uint16Array(
  164. triangleIndexBufferView.buffer,
  165. triangleIndexBufferView.byteOffset +
  166. triangleIndexAccessorGltf.byteOffset,
  167. triangleIndexAccessorGltf.count
  168. )
  169. : new Uint32Array(
  170. triangleIndexBufferView.buffer,
  171. triangleIndexBufferView.byteOffset +
  172. triangleIndexAccessorGltf.byteOffset,
  173. triangleIndexAccessorGltf.count
  174. );
  175. const edgeIndices =
  176. edgeIndexAccessorGltf.componentType === 5123
  177. ? new Uint16Array(
  178. edgeIndexBufferView.buffer,
  179. edgeIndexBufferView.byteOffset + edgeIndexAccessorGltf.byteOffset,
  180. edgeIndexAccessorGltf.count
  181. )
  182. : new Uint32Array(
  183. edgeIndexBufferView.buffer,
  184. edgeIndexBufferView.byteOffset + edgeIndexAccessorGltf.byteOffset,
  185. edgeIndexAccessorGltf.count
  186. );
  187. // Make a hash table for quick lookups of whether an edge exists between two
  188. // vertices. The hash is a sparse array indexed by
  189. // `smallerVertexIndex * totalNumberOfVertices + biggerVertexIndex`
  190. // A value of 1 indicates an edge exists between the two vertex indices; any
  191. // other value indicates that it does not. We store the
  192. // `edgeSmallMultipler` - that is, the number of vertices in the equation
  193. // above - at index 0 for easy access to it later.
  194. const edgeSmallMultiplier = numVertices;
  195. const edges = [edgeSmallMultiplier];
  196. let i;
  197. for (i = 0; i < edgeIndices.length; i += 2) {
  198. const a = edgeIndices[i];
  199. const b = edgeIndices[i + 1];
  200. const small = Math.min(a, b);
  201. const big = Math.max(a, b);
  202. edges[small * edgeSmallMultiplier + big] = 1;
  203. }
  204. // For each triangle, adjust vertex data so that the correct edges are outlined.
  205. for (i = 0; i < triangleIndices.length; i += 3) {
  206. let i0 = triangleIndices[i];
  207. let i1 = triangleIndices[i + 1];
  208. let i2 = triangleIndices[i + 2];
  209. const all = false; // set this to true to draw a full wireframe.
  210. const has01 = all || isHighlighted(edges, i0, i1);
  211. const has12 = all || isHighlighted(edges, i1, i2);
  212. const has20 = all || isHighlighted(edges, i2, i0);
  213. let unmatchableVertexIndex = matchAndStoreCoordinates(
  214. outlineCoordinates,
  215. i0,
  216. i1,
  217. i2,
  218. has01,
  219. has12,
  220. has20
  221. );
  222. while (unmatchableVertexIndex >= 0) {
  223. // Copy the unmatchable index and try again.
  224. let copy;
  225. if (unmatchableVertexIndex === i0) {
  226. copy = vertexCopies[i0];
  227. } else if (unmatchableVertexIndex === i1) {
  228. copy = vertexCopies[i1];
  229. } else {
  230. copy = vertexCopies[i2];
  231. }
  232. if (copy === undefined) {
  233. copy = numVertices + extraVertices.length;
  234. let original = unmatchableVertexIndex;
  235. while (original >= numVertices) {
  236. original = extraVertices[original - numVertices];
  237. }
  238. extraVertices.push(original);
  239. vertexCopies[unmatchableVertexIndex] = copy;
  240. }
  241. if (
  242. copy > MAX_GLTF_UINT16_INDEX &&
  243. triangleIndices instanceof Uint16Array
  244. ) {
  245. // We outgrew a 16-bit index buffer, switch to 32-bit.
  246. triangleIndices = new Uint32Array(triangleIndices);
  247. triangleIndexAccessorGltf.componentType = 5125; // UNSIGNED_INT
  248. triangleIndexBufferViewGltf.buffer =
  249. gltf.buffers.push({
  250. byteLength: triangleIndices.byteLength,
  251. extras: {
  252. _pipeline: {
  253. source: triangleIndices.buffer,
  254. },
  255. },
  256. }) - 1;
  257. triangleIndexBufferViewGltf.byteLength = triangleIndices.byteLength;
  258. triangleIndexBufferViewGltf.byteOffset = 0;
  259. model._loadResources.buffers[
  260. triangleIndexBufferViewGltf.buffer
  261. ] = new Uint8Array(
  262. triangleIndices.buffer,
  263. 0,
  264. triangleIndices.byteLength
  265. );
  266. // The index componentType is also squirreled away in ModelLoadResources.
  267. // Hackily update it, or else we'll end up creating the wrong type
  268. // of index buffer later.
  269. loadResources.indexBuffersToCreate._array.forEach(function (toCreate) {
  270. if (toCreate.id === triangleIndexAccessorGltf.bufferView) {
  271. toCreate.componentType = triangleIndexAccessorGltf.componentType;
  272. }
  273. });
  274. }
  275. if (unmatchableVertexIndex === i0) {
  276. i0 = copy;
  277. triangleIndices[i] = copy;
  278. } else if (unmatchableVertexIndex === i1) {
  279. i1 = copy;
  280. triangleIndices[i + 1] = copy;
  281. } else {
  282. i2 = copy;
  283. triangleIndices[i + 2] = copy;
  284. }
  285. if (defined(triangleIndexAccessorGltf.max)) {
  286. triangleIndexAccessorGltf.max[0] = Math.max(
  287. triangleIndexAccessorGltf.max[0],
  288. copy
  289. );
  290. }
  291. unmatchableVertexIndex = matchAndStoreCoordinates(
  292. outlineCoordinates,
  293. i0,
  294. i1,
  295. i2,
  296. has01,
  297. has12,
  298. has20
  299. );
  300. }
  301. }
  302. }
  303. // Each vertex has three coordinates, a, b, and c.
  304. // a is the coordinate that applies to edge 2-0 for the vertex.
  305. // b is the coordinate that applies to edge 0-1 for the vertex.
  306. // c is the coordinate that applies to edge 1-2 for the vertex.
  307. // A single triangle with all edges highlighted:
  308. //
  309. // | a | b | c |
  310. // | 1 | 1 | 0 |
  311. // 0
  312. // / \
  313. // / \
  314. // edge 0-1 / \ edge 2-0
  315. // / \
  316. // / \
  317. // | a | b | c | 1-----------2 | a | b | c |
  318. // | 0 | 1 | 1 | edge 1-2 | 1 | 0 | 1 |
  319. //
  320. // There are 6 possible orderings of coordinates a, b, and c:
  321. // 0 - abc
  322. // 1 - acb
  323. // 2 - bac
  324. // 3 - bca
  325. // 4 - cab
  326. // 5 - cba
  327. // All vertices must use the _same ordering_ for the edges to be rendered
  328. // correctly. So we compute a bitmask for each vertex, where the bit at
  329. // each position indicates whether that ordering works (i.e. doesn't
  330. // conflict with already-assigned coordinates) for that vertex.
  331. // Then we can find an ordering that works for all three vertices with a
  332. // bitwise AND.
  333. function computeOrderMask(outlineCoordinates, vertexIndex, a, b, c) {
  334. const startIndex = vertexIndex * 3;
  335. const first = outlineCoordinates[startIndex];
  336. const second = outlineCoordinates[startIndex + 1];
  337. const third = outlineCoordinates[startIndex + 2];
  338. if (first === undefined) {
  339. // If one coordinate is undefined, they all are, and all orderings are fine.
  340. return 63; // 0b111111;
  341. }
  342. return (
  343. ((first === a && second === b && third === c) << 0) +
  344. ((first === a && second === c && third === b) << 1) +
  345. ((first === b && second === a && third === c) << 2) +
  346. ((first === b && second === c && third === a) << 3) +
  347. ((first === c && second === a && third === b) << 4) +
  348. ((first === c && second === b && third === a) << 5)
  349. );
  350. }
  351. // popcount for integers 0-63, inclusive.
  352. // i.e. how many 1s are in the binary representation of the integer.
  353. function popcount0to63(value) {
  354. return (
  355. (value & 1) +
  356. ((value >> 1) & 1) +
  357. ((value >> 2) & 1) +
  358. ((value >> 3) & 1) +
  359. ((value >> 4) & 1) +
  360. ((value >> 5) & 1)
  361. );
  362. }
  363. function matchAndStoreCoordinates(
  364. outlineCoordinates,
  365. i0,
  366. i1,
  367. i2,
  368. has01,
  369. has12,
  370. has20
  371. ) {
  372. const a0 = has20 ? 1.0 : 0.0;
  373. const b0 = has01 ? 1.0 : 0.0;
  374. const c0 = 0.0;
  375. const i0Mask = computeOrderMask(outlineCoordinates, i0, a0, b0, c0);
  376. if (i0Mask === 0) {
  377. return i0;
  378. }
  379. const a1 = 0.0;
  380. const b1 = has01 ? 1.0 : 0.0;
  381. const c1 = has12 ? 1.0 : 0.0;
  382. const i1Mask = computeOrderMask(outlineCoordinates, i1, a1, b1, c1);
  383. if (i1Mask === 0) {
  384. return i1;
  385. }
  386. const a2 = has20 ? 1.0 : 0.0;
  387. const b2 = 0.0;
  388. const c2 = has12 ? 1.0 : 0.0;
  389. const i2Mask = computeOrderMask(outlineCoordinates, i2, a2, b2, c2);
  390. if (i2Mask === 0) {
  391. return i2;
  392. }
  393. const workingOrders = i0Mask & i1Mask & i2Mask;
  394. let a, b, c;
  395. if (workingOrders & (1 << 0)) {
  396. // 0 - abc
  397. a = 0;
  398. b = 1;
  399. c = 2;
  400. } else if (workingOrders & (1 << 1)) {
  401. // 1 - acb
  402. a = 0;
  403. c = 1;
  404. b = 2;
  405. } else if (workingOrders & (1 << 2)) {
  406. // 2 - bac
  407. b = 0;
  408. a = 1;
  409. c = 2;
  410. } else if (workingOrders & (1 << 3)) {
  411. // 3 - bca
  412. b = 0;
  413. c = 1;
  414. a = 2;
  415. } else if (workingOrders & (1 << 4)) {
  416. // 4 - cab
  417. c = 0;
  418. a = 1;
  419. b = 2;
  420. } else if (workingOrders & (1 << 5)) {
  421. // 5 - cba
  422. c = 0;
  423. b = 1;
  424. a = 2;
  425. } else {
  426. // No ordering works.
  427. // Report the most constrained vertex as unmatched so we copy that one.
  428. const i0Popcount = popcount0to63(i0Mask);
  429. const i1Popcount = popcount0to63(i1Mask);
  430. const i2Popcount = popcount0to63(i2Mask);
  431. if (i0Popcount < i1Popcount && i0Popcount < i2Popcount) {
  432. return i0;
  433. } else if (i1Popcount < i2Popcount) {
  434. return i1;
  435. }
  436. return i2;
  437. }
  438. const i0Start = i0 * 3;
  439. outlineCoordinates[i0Start + a] = a0;
  440. outlineCoordinates[i0Start + b] = b0;
  441. outlineCoordinates[i0Start + c] = c0;
  442. const i1Start = i1 * 3;
  443. outlineCoordinates[i1Start + a] = a1;
  444. outlineCoordinates[i1Start + b] = b1;
  445. outlineCoordinates[i1Start + c] = c1;
  446. const i2Start = i2 * 3;
  447. outlineCoordinates[i2Start + a] = a2;
  448. outlineCoordinates[i2Start + b] = b2;
  449. outlineCoordinates[i2Start + c] = c2;
  450. return -1;
  451. }
  452. function isHighlighted(edges, i0, i1) {
  453. const edgeSmallMultiplier = edges[0];
  454. const index = Math.min(i0, i1) * edgeSmallMultiplier + Math.max(i0, i1);
  455. // If i0 and i1 are both 0, then our index will be 0 and we'll end up
  456. // accessing the edgeSmallMultiplier that we've sneakily squirreled away
  457. // in index 0. But it makes no sense to have an edge between vertex 0 and
  458. // itself, so for any edgeSmallMultiplier other than 1 we'll return the
  459. // correct answer: false. If edgeSmallMultiplier is 1, that means there is
  460. // only a single vertex, so no danger of forming a meaningful triangle
  461. // with that.
  462. return edges[index] === 1;
  463. }
  464. function createTexture(size) {
  465. const texture = new Uint8Array(size);
  466. texture[size - 1] = 192;
  467. if (size === 8) {
  468. texture[size - 1] = 96;
  469. } else if (size === 4) {
  470. texture[size - 1] = 48;
  471. } else if (size === 2) {
  472. texture[size - 1] = 24;
  473. } else if (size === 1) {
  474. texture[size - 1] = 12;
  475. }
  476. return texture;
  477. }
  478. function updateBufferViewsWithNewVertices(model, bufferViews) {
  479. const gltf = model.gltf;
  480. const loadResources = model._loadResources;
  481. let i, j;
  482. for (i = 0; i < bufferViews.length; ++i) {
  483. const bufferView = bufferViews[i];
  484. const vertexNumberingScope =
  485. bufferView.extras._pipeline.vertexNumberingScope;
  486. // Let the temporary data be garbage collected.
  487. bufferView.extras._pipeline.vertexNumberingScope = undefined;
  488. const newVertices = vertexNumberingScope.extraVertices;
  489. const sourceData = loadResources.getBuffer(bufferView);
  490. const byteStride = bufferView.byteStride || 4;
  491. const newVerticesLength = newVertices.length;
  492. const destData = new Uint8Array(
  493. sourceData.byteLength + newVerticesLength * byteStride
  494. );
  495. // Copy the original vertices
  496. destData.set(sourceData);
  497. // Copy the vertices added for outlining
  498. for (j = 0; j < newVerticesLength; ++j) {
  499. const sourceIndex = newVertices[j] * byteStride;
  500. const destIndex = sourceData.length + j * byteStride;
  501. for (let k = 0; k < byteStride; ++k) {
  502. destData[destIndex + k] = destData[sourceIndex + k];
  503. }
  504. }
  505. // This bufferView is an independent buffer now. Update the model accordingly.
  506. bufferView.byteOffset = 0;
  507. bufferView.byteLength = destData.byteLength;
  508. const bufferId =
  509. gltf.buffers.push({
  510. byteLength: destData.byteLength,
  511. extras: {
  512. _pipeline: {
  513. source: destData.buffer,
  514. },
  515. },
  516. }) - 1;
  517. bufferView.buffer = bufferId;
  518. loadResources.buffers[bufferId] = destData;
  519. // Update the accessors to reflect the added vertices.
  520. const accessors = vertexNumberingScope.accessors;
  521. for (j = 0; j < accessors.length; ++j) {
  522. const accessorId = accessors[j];
  523. gltf.accessors[accessorId].count += newVerticesLength;
  524. }
  525. if (!vertexNumberingScope.createdOutlines) {
  526. // Create the buffers, views, and accessors for the outline texture coordinates.
  527. const outlineCoordinates = vertexNumberingScope.outlineCoordinates;
  528. const outlineCoordinateBuffer = new Float32Array(outlineCoordinates);
  529. const bufferIndex =
  530. model.gltf.buffers.push({
  531. byteLength: outlineCoordinateBuffer.byteLength,
  532. extras: {
  533. _pipeline: {
  534. source: outlineCoordinateBuffer.buffer,
  535. },
  536. },
  537. }) - 1;
  538. loadResources.buffers[bufferIndex] = new Uint8Array(
  539. outlineCoordinateBuffer.buffer,
  540. 0,
  541. outlineCoordinateBuffer.byteLength
  542. );
  543. const bufferViewIndex =
  544. model.gltf.bufferViews.push({
  545. buffer: bufferIndex,
  546. byteLength: outlineCoordinateBuffer.byteLength,
  547. byteOffset: 0,
  548. byteStride: 3 * Float32Array.BYTES_PER_ELEMENT,
  549. target: 34962,
  550. }) - 1;
  551. const accessorIndex =
  552. model.gltf.accessors.push({
  553. bufferView: bufferViewIndex,
  554. byteOffset: 0,
  555. componentType: 5126,
  556. count: outlineCoordinateBuffer.length / 3,
  557. type: "VEC3",
  558. min: [0.0, 0.0, 0.0],
  559. max: [1.0, 1.0, 1.0],
  560. }) - 1;
  561. const primitives = vertexNumberingScope.primitives;
  562. for (j = 0; j < primitives.length; ++j) {
  563. primitives[j].attributes._OUTLINE_COORDINATES = accessorIndex;
  564. }
  565. loadResources.vertexBuffersToCreate.enqueue(bufferViewIndex);
  566. vertexNumberingScope.createdOutlines = true;
  567. }
  568. }
  569. }
  570. function compactBuffers(model) {
  571. const gltf = model.gltf;
  572. const loadResources = model._loadResources;
  573. let i;
  574. for (i = 0; i < gltf.buffers.length; ++i) {
  575. const buffer = gltf.buffers[i];
  576. const bufferViewsUsingThisBuffer = gltf.bufferViews.filter(
  577. usesBuffer.bind(undefined, i)
  578. );
  579. const newLength = bufferViewsUsingThisBuffer.reduce(function (
  580. previous,
  581. current
  582. ) {
  583. return previous + current.byteLength;
  584. },
  585. 0);
  586. if (newLength === buffer.byteLength) {
  587. continue;
  588. }
  589. const newBuffer = new Uint8Array(newLength);
  590. let offset = 0;
  591. for (let j = 0; j < bufferViewsUsingThisBuffer.length; ++j) {
  592. const bufferView = bufferViewsUsingThisBuffer[j];
  593. const sourceData = loadResources.getBuffer(bufferView);
  594. newBuffer.set(sourceData, offset);
  595. bufferView.byteOffset = offset;
  596. offset += sourceData.byteLength;
  597. }
  598. loadResources.buffers[i] = newBuffer;
  599. buffer.extras._pipeline.source = newBuffer.buffer;
  600. buffer.byteLength = newLength;
  601. }
  602. }
  603. function usesBuffer(bufferId, bufferView) {
  604. return bufferView.buffer === bufferId;
  605. }
  606. function getVertexNumberingScope(model, primitive) {
  607. const attributes = primitive.attributes;
  608. if (attributes === undefined) {
  609. return undefined;
  610. }
  611. const gltf = model.gltf;
  612. let vertexNumberingScope;
  613. // Initialize common details for all bufferViews used by this primitive's vertices.
  614. // All bufferViews used by this primitive must use a common vertex numbering scheme.
  615. for (const semantic in attributes) {
  616. if (!attributes.hasOwnProperty(semantic)) {
  617. continue;
  618. }
  619. const accessorId = attributes[semantic];
  620. const accessor = gltf.accessors[accessorId];
  621. const bufferViewId = accessor.bufferView;
  622. const bufferView = gltf.bufferViews[bufferViewId];
  623. if (!defined(bufferView.extras)) {
  624. bufferView.extras = {};
  625. }
  626. if (!defined(bufferView.extras._pipeline)) {
  627. bufferView.extras._pipeline = {};
  628. }
  629. if (!defined(bufferView.extras._pipeline.vertexNumberingScope)) {
  630. bufferView.extras._pipeline.vertexNumberingScope = vertexNumberingScope || {
  631. // Each element in this array is:
  632. // a) undefined, if the vertex at this index has no copies
  633. // b) the index of the copy.
  634. vertexCopies: [],
  635. // Extra vertices appended after the ones originally included in the model.
  636. // Each element is the index of the vertex that this one is a copy of.
  637. extraVertices: [],
  638. // The texture coordinates used for outlining, three floats per vertex.
  639. outlineCoordinates: [],
  640. // The IDs of accessors that use this vertex numbering.
  641. accessors: [],
  642. // The IDs of bufferViews that use this vertex numbering.
  643. bufferViews: [],
  644. // The primitives that use this vertex numbering.
  645. primitives: [],
  646. // True if the buffer for the outlines has already been created.
  647. createdOutlines: false,
  648. };
  649. } else if (
  650. vertexNumberingScope !== undefined &&
  651. bufferView.extras._pipeline.vertexNumberingScope !== vertexNumberingScope
  652. ) {
  653. // Conflicting vertex numbering, let's give up.
  654. return undefined;
  655. }
  656. vertexNumberingScope = bufferView.extras._pipeline.vertexNumberingScope;
  657. if (vertexNumberingScope.bufferViews.indexOf(bufferView) < 0) {
  658. vertexNumberingScope.bufferViews.push(bufferView);
  659. }
  660. if (vertexNumberingScope.accessors.indexOf(accessorId) < 0) {
  661. vertexNumberingScope.accessors.push(accessorId);
  662. }
  663. }
  664. vertexNumberingScope.primitives.push(primitive);
  665. return vertexNumberingScope;
  666. }
  667. export default ModelOutlineLoader;