Reference Source

src/plugins/XML3DLoaderPlugin/XML3DSceneGraphLoader.js

  1. import {Node} from "../../viewer/scene/nodes/Node.js";
  2. import {Mesh} from "../../viewer/scene/mesh/Mesh.js";
  3. import {ReadableGeometry} from "../../viewer/scene/geometry/ReadableGeometry.js";
  4. import {PhongMaterial} from "../../viewer/scene/materials/PhongMaterial.js";
  5. import {MetallicMaterial} from "../../viewer/scene/materials/MetallicMaterial.js";
  6. import {SpecularMaterial} from "../../viewer/scene/materials/SpecularMaterial.js";
  7. import {LambertMaterial} from "../../viewer/scene/materials/LambertMaterial.js";
  8. import {math} from "../../viewer/scene/math/math.js";
  9.  
  10. import {zipLib} from "./zipjs/zip.js";
  11. import {zipExt} from "./zipjs/zip-ext.js";
  12.  
  13. const zip = zipLib.zip;
  14. zipExt(zip);
  15.  
  16. const supportedSchemas = ["4.2"];
  17.  
  18. /**
  19. * @private
  20. */
  21. class XML3DSceneGraphLoader {
  22.  
  23. constructor(plugin, cfg = {}) {
  24.  
  25. /**
  26. * Supported 3DXML schema versions
  27. * @property supportedSchemas
  28. * @type {string[]}
  29. */
  30. this.supportedSchemas = supportedSchemas;
  31.  
  32. this._xrayOpacity = 0.7;
  33. this._src = null;
  34. this._options = cfg;
  35.  
  36. /**
  37. * Default viewpoint, containing eye, look and up vectors.
  38. * Only defined if found in the 3DXML file.
  39. * @property viewpoint
  40. * @type {Number[]}
  41. */
  42. this.viewpoint = null;
  43.  
  44. if (!cfg.workerScriptsPath) {
  45. plugin.error("Config expected: workerScriptsPath");
  46. return
  47. }
  48. zip.workerScriptsPath = cfg.workerScriptsPath;
  49.  
  50. this.src = cfg.src;
  51. this.xrayOpacity = 0.7;
  52. this.displayEffect = cfg.displayEffect;
  53. this.createMetaModel = cfg.createMetaModel;
  54. }
  55.  
  56. load(plugin, modelNode, src, options, ok, error) {
  57.  
  58. switch (options.materialType) {
  59. case "MetallicMaterial":
  60. modelNode._defaultMaterial = new MetallicMaterial(modelNode, {
  61. baseColor: [1, 1, 1],
  62. metallic: 0.6,
  63. roughness: 0.6
  64. });
  65. break;
  66.  
  67. case "SpecularMaterial":
  68. modelNode._defaultMaterial = new SpecularMaterial(modelNode, {
  69. diffuse: [1, 1, 1],
  70. specular: math.vec3([1.0, 1.0, 1.0]),
  71. glossiness: 0.5
  72. });
  73. break;
  74.  
  75. default:
  76. modelNode._defaultMaterial = new PhongMaterial(modelNode, {
  77. reflectivity: 0.75,
  78. shiness: 100,
  79. diffuse: [1, 1, 1]
  80. });
  81. }
  82.  
  83. modelNode._wireframeMaterial = new LambertMaterial(modelNode, {
  84. color: [0, 0, 0],
  85. lineWidth: 2
  86. });
  87.  
  88. var spinner = modelNode.scene.canvas.spinner;
  89. spinner.processes++;
  90.  
  91. load3DXML(plugin, modelNode, src, options, function () {
  92. spinner.processes--;
  93. if (ok) {
  94. ok();
  95. }
  96. modelNode.fire("loaded", true, false);
  97. },
  98. function (msg) {
  99. spinner.processes--;
  100. modelNode.error(msg);
  101. if (error) {
  102. error(msg);
  103. }
  104. /**
  105. Fired whenever this XML3D fails to load the 3DXML file
  106. specified by {@link XML3D/src}.
  107. @event error
  108. @param msg {String} Description of the error
  109. */
  110. modelNode.fire("error", msg);
  111. },
  112. function (err) {
  113. console.log("Error, Will Robinson: " + err);
  114. });
  115. }
  116. }
  117.  
  118. var load3DXML = (function () {
  119. return function (plugin, modelNode, src, options, ok, error) {
  120. loadZIP(src, function (zip) { // OK
  121. parse3DXML(plugin, zip, options, modelNode, ok, error);
  122. },
  123. error);
  124. };
  125. })();
  126.  
  127. var parse3DXML = (function () {
  128. return function (plugin, zip, options, modelNode, ok) {
  129. var ctx = {
  130. plugin: plugin,
  131. zip: zip,
  132. edgeThreshold: 30, // Guess at degrees of normal deviation between adjacent tris below which we remove edge between them
  133. materialType: options.materialType,
  134. scene: modelNode.scene,
  135. modelNode: modelNode,
  136. info: {
  137. references: {}
  138. },
  139. materials: {}
  140. };
  141.  
  142. if (options.createMetaModel) {
  143. ctx.metaModelData = {
  144. modelId: modelNode.id,
  145. metaObjects: [{
  146. name: modelNode.id,
  147. type: "Default",
  148. id: modelNode.id
  149. }]
  150. };
  151. }
  152. modelNode.scene.loading++; // Disables (re)compilation
  153.  
  154. parseDocument(ctx, function () {
  155. if (ctx.metaModelData) {
  156. plugin.viewer.metaScene.createMetaModel(modelNode.id, ctx.metaModelData);
  157. }
  158. modelNode.scene.loading--; // Re-enables (re)compilation
  159. ok();
  160. });
  161. };
  162.  
  163. function parseDocument(ctx, ok) {
  164. ctx.zip.getFile("Manifest.xml", function (xmlDoc, json) {
  165. var node = json;
  166. var children = node.children;
  167. for (var i = 0, len = children.length; i < len; i++) {
  168. var child = children[i];
  169. switch (child.type) {
  170. case "Manifest":
  171. parseManifest(ctx, child, ok);
  172. break;
  173. }
  174. }
  175. });
  176. }
  177.  
  178. function parseManifest(ctx, manifest, ok) {
  179. var children = manifest.children;
  180. for (var i = 0, len = children.length; i < len; i++) {
  181. var child = children[i];
  182. switch (child.type) {
  183. case "Root":
  184. var rootFileSrc = child.children[0];
  185. ctx.zip.getFile(rootFileSrc, function (xmlDoc, json) {
  186. parseRoot(ctx, json, ok);
  187. });
  188. break;
  189. }
  190. }
  191. }
  192.  
  193. function parseRoot(ctx, node, ok) {
  194. var children = node.children;
  195. for (var i = 0, len = children.length; i < len; i++) {
  196. var child = children[i];
  197. switch (child.type) {
  198. case "Model_3dxml":
  199. parseModel(ctx, child, ok);
  200. break;
  201. }
  202. }
  203. }
  204.  
  205. function parseModel(ctx, node, ok) {
  206. var children = node.children;
  207. for (var i = 0, len = children.length; i < len; i++) {
  208. var child = children[i];
  209. switch (child.type) {
  210. case "Header":
  211. parseHeader(ctx, child);
  212. break;
  213. case "ProductStructure":
  214. parseProductStructure(ctx, child, ok);
  215. break;
  216. case "DefaultView":
  217. parseDefaultView(ctx, child);
  218. break;
  219. }
  220. }
  221. }
  222.  
  223. function parseHeader(ctx, node) {
  224. var children = node.children;
  225. var metaData = {};
  226. for (var i = 0, len = children.length; i < len; i++) {
  227. var child = children[i];
  228. switch (child.type) {
  229. case "SchemaVersion":
  230. metaData.schemaVersion = child.children[0];
  231. if (!isSchemaVersionSupported(ctx, metaData.schemaVersion)) {
  232. ctx.plugin.error("Schema version not supported: " + metaData.schemaVersion + " - supported versions are: " + supportedSchemas.join(","));
  233. } else {
  234. //ctx.plugin.log("Parsing schema version: " + metaData.schemaVersion);
  235. }
  236. break;
  237. case "Title":
  238. metaData.title = child.children[0];
  239. break;
  240. case "Author":
  241. metaData.author = child.children[0];
  242. break;
  243. case "Created":
  244. metaData.created = child.children[0];
  245. break;
  246. }
  247. }
  248. ctx.modelNode.meta = metaData;
  249. }
  250.  
  251. function isSchemaVersionSupported(ctx, schemaVersion) {
  252. for (var i = 0, len = supportedSchemas.length; i < len; i++) {
  253. if (schemaVersion === supportedSchemas[i]) {
  254. return true;
  255. }
  256. }
  257. return false;
  258. }
  259.  
  260. function parseProductStructure(ctx, productStructureNode, ok) {
  261.  
  262. parseReferenceReps(ctx, productStructureNode, function (referenceReps) {
  263.  
  264. // Parse out an intermediate scene DAG representation, that we can then
  265. // recursive descend through to build a xeokit Object hierarchy.
  266.  
  267. var children = productStructureNode.children;
  268.  
  269. var reference3Ds = {};
  270. var instanceReps = {};
  271. var instance3Ds = {};
  272.  
  273. var rootNode;
  274. var nodes = {};
  275.  
  276. // Map all the elements
  277.  
  278. for (var i = 0, len = children.length; i < len; i++) {
  279. var child = children[i];
  280. switch (child.type) {
  281.  
  282. case "Reference3D":
  283. reference3Ds[child.id] = {
  284. type: "Reference3D",
  285. id: child.id,
  286. name: child.name,
  287. instance3Ds: {},
  288. instanceReps: {}
  289. };
  290. break;
  291.  
  292. case "InstanceRep":
  293. var isAggregatedBy;
  294. var isInstanceOf;
  295. var relativeMatrix;
  296. for (var j = 0, lenj = child.children.length; j < lenj; j++) {
  297. var child2 = child.children[j];
  298. switch (child2.type) {
  299. case "IsAggregatedBy":
  300. isAggregatedBy = child2.children[0];
  301. break;
  302. case "IsInstanceOf":
  303. isInstanceOf = child2.children[0];
  304. break;
  305. }
  306. }
  307. instanceReps[child.id] = {
  308. type: "InstanceRep",
  309. id: child.id,
  310. name: child.name,
  311. isAggregatedBy: isAggregatedBy,
  312. isInstanceOf: isInstanceOf,
  313. referenceReps: {}
  314. };
  315. break;
  316.  
  317. case "Instance3D":
  318. var isAggregatedBy;
  319. var isInstanceOf;
  320. var relativeMatrix;
  321. for (var j = 0, lenj = child.children.length; j < lenj; j++) {
  322. var child2 = child.children[j];
  323. switch (child2.type) {
  324. case "IsAggregatedBy":
  325. isAggregatedBy = child2.children[0];
  326. break;
  327. case "IsInstanceOf":
  328. isInstanceOf = child2.children[0];
  329. break;
  330. case "RelativeMatrix":
  331. relativeMatrix = child2.children[0];
  332. break;
  333. }
  334. }
  335. instance3Ds[child.id] = {
  336. type: "Instance3D",
  337. id: child.id,
  338. name: child.name,
  339. isAggregatedBy: isAggregatedBy,
  340. isInstanceOf: isInstanceOf,
  341. relativeMatrix: relativeMatrix,
  342. reference3Ds: {}
  343. };
  344. break;
  345. }
  346. }
  347.  
  348. // Connect Reference3Ds to the Instance3Ds they aggregate
  349.  
  350. for (var id in instance3Ds) {
  351. var instance3D = instance3Ds[id];
  352. var reference3D = reference3Ds[instance3D.isAggregatedBy];
  353. if (reference3D) {
  354. reference3D.instance3Ds[instance3D.id] = instance3D;
  355. } else {
  356. alert("foo")
  357. }
  358. }
  359.  
  360. // Connect Instance3Ds to the Reference3Ds they instantiate
  361.  
  362. for (var id in instance3Ds) {
  363. var instance3D = instance3Ds[id];
  364. var reference3D = reference3Ds[instance3D.isInstanceOf];
  365. instance3D.reference3Ds[reference3D.id] = reference3D;
  366. reference3D.instance3D = instance3D;
  367. }
  368.  
  369. // Connect InstanceReps to the ReferenceReps they instantiate
  370.  
  371. for (var id in instanceReps) {
  372. var instanceRep = instanceReps[id];
  373. var referenceRep = referenceReps[instanceRep.isInstanceOf];
  374. if (referenceRep) {
  375. instanceRep.referenceReps[referenceRep.id] = referenceRep;
  376. }
  377. }
  378.  
  379. // Connect Reference3Ds to the InstanceReps they aggregate
  380.  
  381. for (var id in instanceReps) {
  382. var instanceRep = instanceReps[id];
  383. var reference3D = reference3Ds[instanceRep.isAggregatedBy];
  384. if (reference3D) {
  385. reference3D.instanceReps[instanceRep.id] = instanceRep;
  386. }
  387. }
  388.  
  389. function parseReference3D(ctx, reference3D, group) {
  390. //ctx.plugin.log("parseReference3D( " + reference3D.id + " )");
  391. for (var id in reference3D.instance3Ds) {
  392. parseInstance3D(ctx, reference3D.instance3Ds[id], group);
  393. }
  394. for (var id in reference3D.instanceReps) {
  395. parseInstanceRep(ctx, reference3D.instanceReps[id], group);
  396. }
  397. }
  398.  
  399. function parseInstance3D(ctx, instance3D, group) {
  400. //ctx.plugin.log("parseInstance3D( " + instance3D.id + " )");
  401.  
  402. if (instance3D.relativeMatrix) {
  403. var matrix = parseFloatArray(instance3D.relativeMatrix, 12);
  404. var translate = [matrix[9], matrix[10], matrix[11]];
  405. var mat3 = matrix.slice(0, 9); // Rotation matrix
  406. var mat4 = math.mat3ToMat4(mat3, math.identityMat4()); // Convert rotation matrix to 4x4
  407. var childGroup = new Node(ctx.modelNode, {
  408. position: translate
  409. });
  410. if (ctx.metaModelData) {
  411. ctx.metaModelData.metaObjects.push({
  412. id: childGroup.id,
  413. type: "Default",
  414. name: instance3D.name,
  415. parent: group ? group.id : ctx.modelNode.id
  416. });
  417. }
  418. if (group) {
  419. group.addChild(childGroup, true);
  420. } else {
  421. ctx.modelNode.addChild(childGroup, true);
  422. }
  423. group = childGroup;
  424. childGroup = new Node(ctx.modelNode, {
  425. matrix: mat4
  426. });
  427. if (ctx.metaModelData) {
  428. ctx.metaModelData.metaObjects.push({
  429. id: childGroup.id,
  430. type: "Default",
  431. name: instance3D.name,
  432. parent: group ? group.id : ctx.modelNode.id
  433. });
  434. }
  435. group.addChild(childGroup, true);
  436. group = childGroup;
  437. } else {
  438. var childGroup = new Node(ctx.modelNode, {});
  439. if (ctx.metaModelData) {
  440. ctx.metaModelData.metaObjects.push({
  441. id: childGroup.id,
  442. type: "Default",
  443. name: instance3D.name,
  444. parent: group ? group.id : ctx.modelNode.id
  445. });
  446. }
  447. if (group) {
  448. group.addChild(childGroup, true);
  449. } else {
  450. ctx.modelNode.addChild(childGroup, true);
  451. }
  452. group = childGroup;
  453. }
  454. for (var id in instance3D.reference3Ds) {
  455. parseReference3D(ctx, instance3D.reference3Ds[id], group);
  456. }
  457. }
  458.  
  459. function parseInstanceRep(ctx, instanceRep, group) {
  460. //ctx.plugin.log("parseInstanceRep( " + instanceRep.id + " )");
  461. if (instanceRep.referenceReps) {
  462. for (var id in instanceRep.referenceReps) {
  463. var referenceRep = instanceRep.referenceReps[id];
  464. for (var id2 in referenceRep) {
  465. if (id2 === "id") {
  466. continue; // HACK
  467. }
  468. var meshCfg = referenceRep[id2];
  469. var lines = meshCfg.geometry.primitive === "lines";
  470. var material = lines ? ctx.modelNode._wireframeMaterial : (meshCfg.materialId ? ctx.materials[meshCfg.materialId] : null);
  471. var colorize = meshCfg.color;
  472. var mesh = new Mesh(ctx.modelNode, {
  473. isObject: true,
  474. geometry: meshCfg.geometry,
  475. material: material || ctx.modelNode._defaultMaterial,
  476. colorize: colorize,
  477. backfaces: false
  478. });
  479. if (ctx.metaModelData) {
  480. ctx.metaModelData.metaObjects.push({
  481. id: mesh.id,
  482. type: "Default",
  483. name: instanceRep.name,
  484. parent: group ? group.id : ctx.modelNode.id
  485. });
  486. }
  487. if (group) {
  488. group.addChild(mesh, true);
  489. } else {
  490. ctx.modelNode.addChild(mesh, true);
  491. }
  492. mesh.colorize = colorize; // HACK: Mesh has inherited modelNode's colorize state, so we need to restore it (we'd better not modify colorize on the modelNode).
  493. }
  494. }
  495. }
  496. }
  497.  
  498. // Find the root Reference3D
  499.  
  500. for (var id in reference3Ds) {
  501. var reference3D = reference3Ds[id];
  502. if (!reference3D.instance3D) {
  503. parseReference3D(ctx, reference3D, null); // HACK: Assuming that root has id == "1"
  504. ok();
  505. return;
  506. }
  507. }
  508.  
  509. alert("No root Reference3D element found in this modelNode - can't load.");
  510.  
  511. ok();
  512. });
  513. }
  514.  
  515. function parseIntArray(str) {
  516. var parts = str.trim().split(" ");
  517. var result = new Int32Array(parts.length);
  518. for (var i = 0; i < parts.length; i++) {
  519. result[i] = parseInt(parts[i]);
  520. }
  521. return result;
  522. }
  523.  
  524. function parseReferenceReps(ctx, node, ok) {
  525. var referenceReps = {};
  526. var children = node.children;
  527. var numToLoad = 0;
  528. for (var i = 0, len = children.length; i < len; i++) {
  529. var child = children[i];
  530. if (child.type === "ReferenceRep") {
  531. numToLoad++;
  532. }
  533. }
  534. for (var i = 0, len = children.length; i < len; i++) {
  535. var child = children[i];
  536. switch (child.type) {
  537. case "ReferenceRep":
  538. if (child.associatedFile) {
  539. var src = stripURN(child.associatedFile);
  540. (function () {
  541. var childId = child.id;
  542. ctx.zip.getFile(src, function (xmlDoc, json) {
  543.  
  544. var materialIds = xmlDoc.getElementsByTagName("MaterialId");
  545.  
  546. loadCATMaterialRefDocuments(ctx, materialIds, function () {
  547.  
  548. // ctx.plugin.log("reference loaded: " + src);
  549. var referenceRep = {
  550. id: childId
  551. };
  552. parse3DRepDocument(ctx, json, referenceRep);
  553. referenceReps[childId] = referenceRep;
  554. if (--numToLoad === 0) {
  555. ok(referenceReps);
  556. }
  557. });
  558. },
  559. function (error) {
  560. // TODO:
  561. });
  562. })();
  563. }
  564. break;
  565. }
  566. }
  567. }
  568.  
  569.  
  570. function parseDefaultView(ctx, node) {
  571. // ctx.plugin.log("parseDefaultView");
  572. var children = node.children;
  573. for (var i = 0, len = children.length; i < len; i++) {
  574. var child = children[i];
  575. switch (child.type) {
  576. case "Viewpoint":
  577. var children2 = child.children;
  578. ctx.modelNode.viewpoint = {};
  579. for (var i2 = 0, len2 = children2.length; i2 < len2; i2++) {
  580. var child2 = children2[i];
  581. switch (child2.type) {
  582. case "Position":
  583. ctx.modelNode.viewpoint.eye = parseFloatArray(child2.children[0], 3);
  584. break;
  585. case "Sight":
  586. ctx.modelNode.viewpoint.look = parseFloatArray(child2.children[0], 3);
  587. break;
  588. case "Up":
  589. ctx.modelNode.viewpoint.up = parseFloatArray(child2.children[0], 3);
  590. break;
  591. }
  592. }
  593. break;
  594. case "DefaultViewProperty":
  595. break;
  596. }
  597. }
  598. }
  599.  
  600. function parse3DRepDocument(ctx, node, result) {
  601. var children = node.children;
  602. for (var i = 0, len = children.length; i < len; i++) {
  603. var child = children[i];
  604. switch (child.type) {
  605. case "XMLRepresentation":
  606. parseXMLRepresentation(ctx, child, result);
  607. break;
  608. }
  609. }
  610. }
  611.  
  612. function parseXMLRepresentation(ctx, node, result) {
  613. var children = node.children;
  614. for (var i = 0, len = children.length; i < len; i++) {
  615. var child = children[i];
  616. switch (child.type) {
  617. case "Root":
  618. parse3DRepRoot(ctx, child, result);
  619. break;
  620. }
  621. }
  622. }
  623.  
  624. function parse3DRepRoot(ctx, node, result) {
  625. switch (node["xsi:type"]) {
  626. case "BagRepType":
  627. break;
  628. case "PolygonalRepType":
  629. break;
  630. }
  631. var children = node.children;
  632. for (var i = 0, len = children.length; i < len; i++) {
  633. var child = children[i];
  634. switch (child.type) {
  635. case "Rep":
  636. parse3DRepRep(ctx, child, result);
  637. break;
  638. }
  639. }
  640. }
  641.  
  642. function parse3DRepRep(ctx, node, result) {
  643. switch (node["xsi:type"]) {
  644. case "BagRepType":
  645. break;
  646. case "PolygonalRepType":
  647. break;
  648. }
  649. var meshesResult = {
  650. edgeThreshold: ctx.edgeThreshold || 30,
  651. compressGeometry: true
  652. };
  653. var children = node.children;
  654. for (var i = 0, len = children.length; i < len; i++) {
  655. var child = children[i];
  656. switch (child.type) {
  657. case "Rep":
  658. parse3DRepRep(ctx, child, result);
  659. break;
  660. case "Edges":
  661. // Ignoring edges because we auto-generate our own using xeokit
  662. break;
  663. case "Faces":
  664. meshesResult.primitive = "triangles";
  665. parseFaces(ctx, child, meshesResult);
  666. break;
  667. case "VertexBuffer":
  668. parseVertexBuffer(ctx, child, meshesResult);
  669. break;
  670. case "SurfaceAttributes":
  671. parseSurfaceAttributes(ctx, child, meshesResult);
  672. break;
  673. }
  674. }
  675. if (meshesResult.positions) {
  676. var geometry = new ReadableGeometry(ctx.modelNode, meshesResult);
  677. result[geometry.id] = {
  678. geometry: geometry,
  679. color: meshesResult.color || [1.0, 1.0, 1.0, 1.0],
  680. materialId: meshesResult.materialId
  681. };
  682. }
  683. }
  684.  
  685. function parseEdges(ctx, node, result) {
  686. result.positions = [];
  687. result.indices = [];
  688. var children = node.children;
  689. for (var i = 0, len = children.length; i < len; i++) {
  690. var child = children[i];
  691. switch (child.type) {
  692. case "Polyline":
  693. parsePolyline(ctx, child, result);
  694. break;
  695. }
  696. }
  697. }
  698.  
  699. function parsePolyline(ctx, node, result) {
  700. var vertices = node.vertices;
  701. if (vertices) {
  702. var positions = parseFloatArray(vertices, 3);
  703. if (positions.length > 0) {
  704. var positionsOffset = result.positions.length / 3;
  705. for (var i = 0, len = positions.length; i < len; i++) {
  706. result.positions.push(positions[i]);
  707. }
  708. for (var i = 0, len = (positions.length / 3) - 1; i < len; i++) {
  709. result.indices.push(positionsOffset + i);
  710. result.indices.push(positionsOffset + i + 1);
  711. }
  712. }
  713. }
  714. }
  715.  
  716. function parseFaces(ctx, node, result) {
  717. var children = node.children;
  718. for (var i = 0, len = children.length; i < len; i++) {
  719. var child = children[i];
  720. switch (child.type) {
  721. case "Face":
  722. parseFace(ctx, child, result);
  723. break;
  724. }
  725. }
  726. }
  727.  
  728. function parseFace(ctx, node, result) {
  729. var strips = node.strips;
  730. if (strips) {
  731. // Triangle strips
  732. var arrays = parseIntArrays(strips);
  733. if (arrays.length > 0) {
  734. result.primitive = "triangles";
  735. var indices = [];
  736. for (var i = 0, len = arrays.length; i < len; i++) {
  737. var array = convertTriangleStrip(arrays[i]);
  738. for (var j = 0, lenj = array.length; j < lenj; j++) {
  739. indices.push(array[j]);
  740. }
  741. }
  742. result.indices = indices; // TODO
  743. }
  744. } else {
  745. // Triangle meshes
  746. var triangles = node.triangles;
  747. if (triangles) {
  748. result.primitive = "triangles";
  749. result.indices = parseIntArray(triangles);
  750. }
  751. }
  752. // Material
  753. var children = node.children;
  754. for (var i = 0, len = children.length; i < len; i++) {
  755. var child = children[i];
  756. switch (child.type) {
  757. case "SurfaceAttributes":
  758. parseSurfaceAttributes(ctx, child, result);
  759. break;
  760. }
  761. }
  762. }
  763.  
  764. function convertTriangleStrip(indices) {
  765. var ccw = false;
  766. var indices2 = [];
  767. for (var i = 0, len = indices.length; i < len - 2; i++) {
  768. if (ccw) {
  769. if (i & 1) { //
  770. indices2.push(indices[i]);
  771. indices2.push(indices[i + 1]);
  772. indices2.push(indices[i + 2]);
  773. } else {
  774. indices2.push(indices[i]);
  775. indices2.push(indices[i + 2]);
  776. indices2.push(indices[i + 1]);
  777. }
  778. } else {
  779. if (i & 1) { //
  780. indices2.push(indices[i]);
  781. indices2.push(indices[i + 2]);
  782. indices2.push(indices[i + 1]);
  783. } else {
  784. indices2.push(indices[i]);
  785. indices2.push(indices[i + 1]);
  786. indices2.push(indices[i + 2]);
  787. }
  788. }
  789. }
  790. return indices2;
  791. }
  792.  
  793. function parseVertexBuffer(ctx, node, result) {
  794. var children = node.children;
  795. for (var i = 0, len = children.length; i < len; i++) {
  796. var child = children[i];
  797. switch (child.type) {
  798. case "Positions":
  799. result.positions = parseFloatArray(child.children[0], 3);
  800. break;
  801. case "Normals":
  802. result.normals = parseFloatArray(child.children[0], 3);
  803. break;
  804. case "TextureCoordinates": // TODO: Support dimension and channel?
  805. result.uv = parseFloatArray(child.children[0], 2);
  806. break;
  807. }
  808. }
  809. }
  810.  
  811. function parseIntArrays(str) {
  812. var coordStrings = str.split(",");
  813. var array = [];
  814. for (var i = 0, len = coordStrings.length; i < len; i++) {
  815. var coordStr = coordStrings[i].trim();
  816. if (coordStr.length > 0) {
  817. var elemStrings = coordStr.trim().split(" ");
  818. var arr = new Int16Array(elemStrings.length);
  819. var arrIdx = 0;
  820. for (var j = 0, lenj = elemStrings.length; j < lenj; j++) {
  821. if (elemStrings[j] !== "") {
  822. arr[arrIdx++] = parseInt(elemStrings[j]);
  823. }
  824. }
  825. array.push(arr);
  826. }
  827. }
  828. return array;
  829. }
  830.  
  831. function parseFloatArray(str, numElems) {
  832. str = str.split(",");
  833. var arr = new Float32Array(str.length * numElems);
  834. var arrIdx = 0;
  835. for (var i = 0, len = str.length; i < len; i++) {
  836. var value = str[i];
  837. value = value.split(" ");
  838. for (var j = 0, lenj = value.length; j < lenj; j++) {
  839. if (value[j] !== "") {
  840. arr[arrIdx++] = parseFloat(value[j]);
  841. }
  842. }
  843. }
  844. return arr;
  845. }
  846.  
  847. function parseIntArray(str) {
  848. str = str.trim().split(" ");
  849. var arr = new Int32Array(str.length);
  850. var arrIdx = 0;
  851. for (var i = 0, len = str.length; i < len; i++) {
  852. var value = str[i];
  853. arr[i] = parseInt(value);
  854. }
  855. return arr;
  856. }
  857.  
  858. function parseSurfaceAttributes(ctx, node, result) {
  859. result.color = [1, 1, 1, 1];
  860. var children = node.children;
  861. for (var i = 0, len = children.length; i < len; i++) {
  862. var child = children[i];
  863. switch (child.type) {
  864. case "Color":
  865. result.color[0] = child.red;
  866. result.color[1] = child.green;
  867. result.color[2] = child.blue;
  868. result.color[3] = child.alpha;
  869. break;
  870. case "MaterialApplication":
  871. var children2 = child.children;
  872. for (var j = 0, lenj = children2.length; j < lenj; j++) {
  873. var child2 = children2[j];
  874. switch (child2.type) {
  875. case "MaterialId":
  876. var materialId = getIDFromURI(child2.id);
  877. var material = ctx.materials[materialId];
  878. if (!material) {
  879. ctx.plugin.error("material not found: " + materialId);
  880. }
  881. result.materialId = materialId;
  882. break;
  883. }
  884. }
  885. break;
  886. }
  887. }
  888. }
  889. })();
  890.  
  891. function loadCATMaterialRefDocuments(ctx, materialIds, ok) {
  892. var loaded = {};
  893.  
  894. function load(i, done) {
  895. if (i >= materialIds.length) {
  896. ok();
  897. return;
  898. }
  899. var materialId = materialIds[i];
  900. var src = materialId.id;
  901. var colonIdx = src.lastIndexOf(":");
  902. if (colonIdx > 0) {
  903. src = src.substring(colonIdx + 1);
  904. }
  905. var hashIdx = src.lastIndexOf("#");
  906. if (hashIdx > 0) {
  907. src = src.substring(0, hashIdx);
  908. }
  909. if (!loaded[src]) {
  910. loadCATMaterialRefDocument(ctx, src, function () {
  911. loaded[src] = true;
  912. load(i + 1, done);
  913. });
  914. } else {
  915. load(i + 1, done);
  916. }
  917. }
  918.  
  919. load(0, ok);
  920. }
  921.  
  922. function loadCATMaterialRefDocument(ctx, src, ok) { // Loads CATMaterialRef.3dxml
  923. ctx.zip.getFile(src, function (xmlDoc, json) {
  924. parseCATMaterialRefDocument(ctx, json, ok);
  925. });
  926. }
  927.  
  928. function parseCATMaterialRefDocument(ctx, node, ok) { // Parse CATMaterialRef.3dxml
  929. // ctx.plugin.log("parseCATMaterialRefDocument");
  930. var children = node.children;
  931. var child;
  932. for (var i = 0, len = children.length; i < len; i++) {
  933. child = children[i];
  934. if (child.type === "Model_3dxml") {
  935. parseModel_3dxml(ctx, child, ok);
  936. }
  937. }
  938. }
  939.  
  940. function parseModel_3dxml(ctx, node, ok) { // Parse CATMaterialRef.3dxml
  941. // ctx.plugin.log("parseModel_3dxml");
  942. var children = node.children;
  943. var child;
  944. for (var i = 0, len = children.length; i < len; i++) {
  945. child = children[i];
  946. if (child.type === "CATMaterialRef") {
  947. parseCATMaterialRef(ctx, child, ok);
  948. }
  949. }
  950. }
  951.  
  952. function parseCATMaterialRef(ctx, node, ok) {
  953. var domainToReferenceMap = {};
  954. var materials = {};
  955. var result = {};
  956. var children = node.children;
  957. var child;
  958. var numToLoad = 0;
  959. for (var j = 0, lenj = children.length; j < lenj; j++) {
  960. var child2 = children[j];
  961. switch (child2.type) {
  962. case "MaterialDomainInstance":
  963. var isAggregatedBy;
  964. var isInstanceOf;
  965. for (var k = 0, lenk = child2.children.length; k < lenk; k++) {
  966. var child3 = child2.children[k];
  967. switch (child3.type) {
  968. case "IsAggregatedBy":
  969. isAggregatedBy = child3.children[0];
  970. break;
  971. case "IsInstanceOf":
  972. isInstanceOf = child3.children[0];
  973. break;
  974. }
  975. }
  976. domainToReferenceMap[isInstanceOf] = isAggregatedBy;
  977. break;
  978. }
  979. }
  980. for (var j = 0, lenj = children.length; j < lenj; j++) {
  981. var child2 = children[j];
  982. switch (child2.type) {
  983. case "MaterialDomain":
  984. numToLoad++;
  985. break;
  986. }
  987. }
  988. // Now load them
  989. for (var j = 0, lenj = children.length; j < lenj; j++) {
  990. var child2 = children[j];
  991. switch (child2.type) {
  992. case "MaterialDomain":
  993. if (child2.associatedFile) {
  994. (function () {
  995. var childId = child2.id;
  996. var src = stripURN(child2.associatedFile);
  997. ctx.zip.getFile(src, function (xmlDoc, json) {
  998. // ctx.plugin.log("Material def loaded: " + src);
  999. ctx.materials[domainToReferenceMap[childId]] = parseMaterialDefDocument(ctx, json);
  1000.  
  1001. if (--numToLoad === 0) {
  1002. // console.log("All ReferenceReps loaded.");
  1003. ok();
  1004. }
  1005. },
  1006. function (error) {
  1007. // TODO:
  1008. });
  1009. })();
  1010. }
  1011. break;
  1012. }
  1013. }
  1014. }
  1015.  
  1016. function parseMaterialDefDocument(ctx, node) {
  1017. var children = node.children;
  1018. for (var i = 0, len = children.length; i < len; i++) {
  1019. var child = children[i];
  1020. switch (child.type) {
  1021. case "Osm":
  1022. return parseMaterialDefDocumentOsm(ctx, child);
  1023. break;
  1024. }
  1025. }
  1026. }
  1027.  
  1028. function parseMaterialDefDocumentOsm(ctx, node) {
  1029. var children = node.children;
  1030. for (var i = 0, len = children.length; i < len; i++) {
  1031. var child = children[i];
  1032. switch (child.type) {
  1033. case "RenderingRootFeature":
  1034. //..
  1035. break;
  1036. case "Feature":
  1037.  
  1038. if (child.Alias === "RenderingFeature") {
  1039. // Parse the coefficients, then parse the colors, scaling those by their coefficients.
  1040. var coeffs = {};
  1041. var materialCfg = {};
  1042. var children2 = child.children;
  1043. var j;
  1044. var lenj;
  1045. var child2;
  1046. for (j = 0, lenj = children2.length; j < lenj; j++) {
  1047. child2 = children2[j];
  1048. switch (child2.Name) {
  1049. case "AmbientCoef":
  1050. coeffs.ambient = parseFloat(child2.Value);
  1051. break;
  1052. case "DiffuseCoef":
  1053. coeffs.diffuse = parseFloat(child2.Value);
  1054. break;
  1055. case "EmissiveCoef":
  1056. coeffs.emissive = parseFloat(child2.Value);
  1057. break;
  1058. case "SpecularExponent":
  1059. coeffs.specular = parseFloat(child2.Value);
  1060. break;
  1061. }
  1062. }
  1063. for (j = 0, lenj = children2.length; j < lenj; j++) {
  1064. child2 = children2[j];
  1065. switch (child2.Name) {
  1066. case "AmbientColor":
  1067. materialCfg.ambient = parseRGB(child2.Value, coeffs.ambient);
  1068. break;
  1069. case "DiffuseColor":
  1070. materialCfg.diffuse = parseRGB(child2.Value, coeffs.diffuse);
  1071. break;
  1072. case "EmissiveColor":
  1073. materialCfg.emissive = parseRGB(child2.Value, coeffs.emissive);
  1074. break;
  1075. case "SpecularColor":
  1076. materialCfg.specular = parseRGB(child2.Value, coeffs.specular);
  1077. break;
  1078. case "Transparency":
  1079. var alpha = 1.0 - parseFloat(child2.Value); // GOTCHA: Degree of transparency, not degree of opacity
  1080. if (alpha < 1.0) {
  1081. materialCfg.alpha = alpha;
  1082. materialCfg.alphaMode = "blend";
  1083. }
  1084. break;
  1085. }
  1086. }
  1087.  
  1088. var material;
  1089.  
  1090. switch (ctx.materialType) {
  1091. case "MetallicMaterial":
  1092. material = new MetallicMaterial(ctx.modelNode, {
  1093. baseColor: materialCfg.diffuse,
  1094. metallic: 0.7,
  1095. roughness: 0.5,
  1096. emissive: materialCfg.emissive,
  1097. alpha: materialCfg.alpha,
  1098. alphaMode: materialCfg.alphaMode
  1099. });
  1100. break;
  1101.  
  1102. case "SpecularMaterial":
  1103. material = new SpecularMaterial(ctx.modelNode, {
  1104. diffuse: materialCfg.diffuse,
  1105. specular: materialCfg.specular,
  1106. glossiness: 0.5,
  1107. emissive: materialCfg.emissive,
  1108. alpha: materialCfg.alpha,
  1109. alphaMode: materialCfg.alphaMode
  1110. });
  1111. break;
  1112.  
  1113. default:
  1114. material = new PhongMaterial(ctx.modelNode, {
  1115. reflectivity: 0.5,
  1116. ambient: materialCfg.ambient,
  1117. diffuse: materialCfg.diffuse,
  1118. specular: materialCfg.specular,
  1119. // shininess: node.shine,
  1120. emissive: materialCfg.emissive,
  1121. alphaMode: materialCfg.alphaMode,
  1122. alpha: node.alpha
  1123. });
  1124. }
  1125. return material;
  1126. }
  1127. break;
  1128. }
  1129. }
  1130. }
  1131.  
  1132. function parseRGB(str, coeff) {
  1133. coeff = (coeff !== undefined) ? coeff : 0.5;
  1134. var openBracketIndex = str.indexOf("[");
  1135. var closeBracketIndex = str.indexOf("]");
  1136. str = str.substring(openBracketIndex + 1, closeBracketIndex - openBracketIndex);
  1137. str = str.split(",");
  1138. var arr = new Float32Array(str.length);
  1139. var arrIdx = 0;
  1140. for (var i = 0, len = str.length; i < len; i++) {
  1141. var value = str[i];
  1142. value = value.trim().split(" ");
  1143. for (var j = 0, lenj = value.length; j < lenj; j++) {
  1144. if (value[j] !== "") {
  1145. arr[arrIdx++] = parseFloat(value[j]) * coeff;
  1146. }
  1147. }
  1148. }
  1149. return arr;
  1150. }
  1151.  
  1152.  
  1153. //----------------------------------------------------------------------------------------------------
  1154.  
  1155. /**
  1156. * Wraps zip.js to provide an in-memory ZIP archive representing the 3DXML file bundle.
  1157. *
  1158. * Allows us to pluck each file from it as XML and JSON.
  1159. *
  1160. * @constructor
  1161. */
  1162. var ZIP = function () {
  1163.  
  1164. var reader;
  1165. var files = {};
  1166.  
  1167. /**
  1168. Loads this ZIP
  1169.  
  1170. @param src
  1171. @param ok
  1172. @param error
  1173. */
  1174. this.load = function (src, ok, error) {
  1175. var self = this;
  1176. zip.createReader(new zip.HttpReader(src), function (reader) {
  1177. reader.getEntries(function (entries) {
  1178. if (entries.length > 0) {
  1179. for (var i = 0, len = entries.length; i < len; i++) {
  1180. var entry = entries[i];
  1181. files[entry.filename] = entry;
  1182. }
  1183. }
  1184. ok();
  1185. });
  1186. }, error);
  1187. };
  1188.  
  1189. /**
  1190. Gets a file as XML and JSON from this ZIP
  1191. @param src
  1192. @param ok
  1193. @param error
  1194. */
  1195. this.getFile = function (src, ok, error) {
  1196. var entry = files[src];
  1197. if (!entry) {
  1198. var errMsg = "ZIP entry not found: " + src;
  1199. console.error(errMsg);
  1200. if (error) {
  1201. error(errMsg);
  1202. }
  1203. return;
  1204. }
  1205. entry.getData(new zip.TextWriter(), function (text) {
  1206.  
  1207. // Parse to XML
  1208. var parser = new DOMParser();
  1209. var xmlDoc = parser.parseFromString(text, "text/xml");
  1210.  
  1211. // Parse to JSON
  1212. var json = xmlToJSON(xmlDoc, {});
  1213.  
  1214. ok(xmlDoc, json);
  1215. });
  1216. };
  1217.  
  1218. function xmlToJSON(node, attributeRenamer) {
  1219. if (node.nodeType === node.TEXT_NODE) {
  1220. var v = node.nodeValue;
  1221. if (v.match(/^\s+$/) === null) {
  1222. return v;
  1223. }
  1224. } else if (node.nodeType === node.ELEMENT_NODE ||
  1225. node.nodeType === node.DOCUMENT_NODE) {
  1226. var json = {type: node.nodeName, children: []};
  1227. if (node.nodeType === node.ELEMENT_NODE) {
  1228. for (var j = 0; j < node.attributes.length; j++) {
  1229. var attribute = node.attributes[j];
  1230. var nm = attributeRenamer[attribute.nodeName] || attribute.nodeName;
  1231. json[nm] = attribute.nodeValue;
  1232. }
  1233. }
  1234. for (var i = 0; i < node.childNodes.length; i++) {
  1235. var item = node.childNodes[i];
  1236. var j = xmlToJSON(item, attributeRenamer);
  1237. if (j) json.children.push(j);
  1238. }
  1239. return json;
  1240. }
  1241. }
  1242.  
  1243. /**
  1244. Disposes of this ZIP
  1245. */
  1246. this.destroy = function () {
  1247. reader.close(function () {
  1248. // onclose callback
  1249. });
  1250. };
  1251. };
  1252.  
  1253. function
  1254.  
  1255. loadZIP(src, ok, err) {
  1256. var zip = new ZIP();
  1257. zip.load(src, function () {
  1258. ok(zip);
  1259. }, function (errMsg) {
  1260. err("Error loading ZIP archive: " + errMsg);
  1261. })
  1262. }
  1263.  
  1264. function
  1265.  
  1266. stripURN(str) {
  1267. var subStr = "urn:3DXML:";
  1268. return (str.indexOf(subStr) === 0) ? str.substring(subStr.length) : str;
  1269. }
  1270.  
  1271.  
  1272. function
  1273.  
  1274. getIDFromURI(str) {
  1275. var hashIdx = str.lastIndexOf("#");
  1276. return hashIdx !== -1 ? str.substring(hashIdx + 1) : str;
  1277. }
  1278.  
  1279. export {XML3DSceneGraphLoader};