Advertisement
Guest User

Untitled

a guest
Jul 22nd, 2019
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import * as http from "http";
  2. import httpRequest = require("request");
  3. import {Box3, Material, Object3D, ObjectLoader, Texture} from "three";
  4. import * as THREE from "three";
  5. import {IModel3DStreamItem, IUserData} from "../../../../helpers/interfaces";
  6. import {IstreamObjectItem} from "../../../../models/interfaces";
  7. import {model3DModel} from "../../../../models/model3D/model3D";
  8. import {IModel3DDocument} from "../../../../models/model3D/schema";
  9. import {model3DStreamModel} from "../../../../models/model3D_stream/model3D_stream";
  10. import {fo, Logger, SkyviewerContext} from "../../../../services/logger";
  11. import {getUploadsPath} from "./helper";
  12. import {
  13.     IModel3DBoundingBox,
  14.     IModel3DCategorySortedObjects,
  15.     IModel3DFile,
  16.     IModel3DFileImage,
  17.     IModel3DGhostObject,
  18.     IModel3DMaterial,
  19.     IModel3DMesh,
  20.     IModel3DPreProcessDataItem,
  21.     IModel3DStreamingClientMetaData,
  22. } from "./interfaces";
  23.  
  24. const logger = new Logger(SkyviewerContext.SERVER);
  25.  
  26. export async function preProcessModel(model3dGuid: string, userData: IUserData, token: string | string[]): Promise<IModel3DDocument> {
  27.     try {
  28.         const loader: ObjectLoader = new THREE.ObjectLoader();
  29.         const model3D: IModel3DDocument = await model3DModel.getModel3DByGuid(userData.companyId, model3dGuid);
  30.  
  31.         const fileLocation: string = getUploadsPath() + model3D.stats.model3D.fileName;
  32.         logger.log("File should be at: " + fileLocation);
  33.  
  34.         const file: string = await retrieveFile(fileLocation, token as string);
  35.         const objectModel: IModel3DFile = JSON.parse(file);
  36.  
  37.         const model = await parseModel(objectModel, loader);
  38.         return await processModel(model, model3dGuid, userData);
  39.     } catch (error) {
  40.         logger.error(`preProcessModel::${fo(error)}`);
  41.         return error;
  42.     }
  43. }
  44.  
  45. export async function retrieveFile(fileLocation: string, token: string): Promise<string> {
  46.     try {
  47.         return new Promise((resolve: (data) => void, reject: (data) => void) => {
  48.             try {
  49.                 httpRequest({
  50.                     uri: fileLocation,
  51.                     method: "GET",
  52.                     timeout: 1000000,
  53.                     headers: {
  54.                         "Authorization": token,
  55.                         "Accept": "*/*",
  56.                         "Accept-language": "en-US,en;q=0.8",
  57.                     },
  58.                     agent: new http.Agent({ keepAlive: false }),
  59.                 }, function(err: Error, res) {
  60.                     if (err) {
  61.                         console.log(err);
  62.                         reject("error on getting file data from Services3D");
  63.                     }
  64.                     resolve(res.body);
  65.                 });
  66.             } catch (e) {
  67.                 logger.log(`error on getting file data from Services3D:: ${e}`);
  68.             }
  69.         });
  70.     } catch (error) {
  71.         logger.log(`error on getting file data from Services3DFunction::${fo(error)}`);
  72.         return error;
  73.     }
  74. }
  75.  
  76. export async function parseModel(json: IModel3DFile, loader: ObjectLoader): Promise<Object3D> {
  77.     try {
  78.         const geometries: Object3D[] = loader.parseGeometries( json.geometries );
  79.         const images: Array<{ url: string }> = await parseImages(json.images);
  80.         const textures: Texture[] = loader.parseTextures( json.textures, images );
  81.         const materials: Material[] = loader.parseMaterials( json.materials, textures );
  82.  
  83.         return loader.parseObject( json.object, geometries, materials );
  84.     } catch (error) {
  85.         logger.error(`ParseModel::${fo(error)}`);
  86.         return error;
  87.     }
  88. }
  89.  
  90. export async function parseImages(images: IModel3DFileImage[]): Promise<Array<{ url: string }>> {
  91.     try {
  92.         const out: Array<{ url: string }> = [];
  93.         if (images !== undefined) {
  94.             images.forEach((value: { uuid: string, url: string}) => {
  95.                 out[value.uuid] = value.url;
  96.             });
  97.         }
  98.         return out;
  99.     } catch (error) {
  100.         logger.error(`parseImages::${fo(error)}`);
  101.         return error;
  102.     }
  103. }
  104.  
  105. export async function processModel(model: Object3D, modelGuid: string, userData: IUserData): Promise<IModel3DDocument> {
  106.     try {
  107.         model.scale.set(0.001, 0.001, 0.001);
  108.         model.updateMatrixWorld(true);
  109.  
  110.         const ghostObjects: IModel3DGhostObject[] = await createGhostObjects(model);
  111.         const streamingObjects: IstreamObjectItem[] = await createStreamingObjects(model, modelGuid);
  112.         const materials: IModel3DMaterial[] = await createMaterialDefinitions(model);
  113.         const boundingBox: IModel3DBoundingBox = await getModelBoundingBox(model);
  114.         const categorySortedObjectList: string[] = await getCategorySortedObjectList(model);
  115.         const streamingObjectList: string[] = await getStreamingObjectIds(model);
  116.  
  117.         const preProcessDataArray: IModel3DPreProcessDataItem[] = [];
  118.         const clientMetaData: { modelBoundingBox: IModel3DBoundingBox } = {
  119.             modelBoundingBox: boundingBox,
  120.         };
  121.  
  122.         const preProcessData: IModel3DPreProcessDataItem = {
  123.             type: "Generic",
  124.             metaData: clientMetaData,
  125.             listOfIdsToStreamFirst: categorySortedObjectList,
  126.         };
  127.  
  128.         const q = model.quaternion;
  129.         const p = model.position;
  130.         model.children = [];
  131.         const streamingClientMetaData: IModel3DStreamingClientMetaData = {
  132.             modelBoundingBox: boundingBox,
  133.             model: {
  134.                 quaternion: {x: q.x, y: q.y, z: q.z, w: q.w},
  135.                 position: {x: p.x, y: p.y, z: p.z},
  136.             },
  137.             materials,
  138.             uuid: modelGuid,
  139.             ghostObjects,
  140.         };
  141.         // TODO: Find the right type here
  142.         const streamingData: IModel3DPreProcessDataItem = {
  143.             type: "streaming",
  144.             metaData: streamingClientMetaData,
  145.             listOfIdsToStreamFirst: streamingObjectList,
  146.         };
  147.         preProcessDataArray.push(preProcessData);
  148.         preProcessDataArray.push(streamingData);
  149.  
  150.         await model3DModel.updateModel3DPreProcessedDataArray(modelGuid, preProcessDataArray, userData);
  151.         await model3DStreamModel.setModel3DStreamObjectItems(streamingObjects);
  152.         await model3DModel.updateModel3DPreProcessedStatus(modelGuid, "done", userData);
  153.  
  154.         return await model3DModel.getModel3DByGuid(userData.companyId, modelGuid);
  155.     } catch (error) {
  156.         logger.error(`ProcessModel::${fo(error)}`);
  157.         return error;
  158.     }
  159. }
  160.  
  161. export async function createGhostObjects(model: Object3D): Promise<IModel3DGhostObject[]> {
  162.     try {
  163.         const ghostObjects: IModel3DGhostObject[] = [];
  164.  
  165.         for (let i = 0; i < model.children.length; i++) {
  166.  
  167.             const child: Object3D = model.children[i];
  168.  
  169.             if (!child) { return; }
  170.  
  171.             const category = child.userData.category;
  172.  
  173.             let transparent = false;
  174.             const meshes: IModel3DMesh[] = [];
  175.  
  176.             for (const mesh of child.children) {
  177.                 // @ts-ignore
  178.                 if (mesh && mesh.material) {
  179.                     // @ts-ignore
  180.                     if (mesh.material.transparent) {
  181.                         transparent = true;
  182.                         continue;
  183.                     }
  184.                 }
  185.                 const box: Box3 = new THREE.Box3().setFromObject(mesh);
  186.  
  187.                 const boundingBox: IModel3DBoundingBox = {
  188.                     max: { x: box.max.x, y: box.max.y, z: box.max.z },
  189.                     min: { x: box.min.x, y: box.min.y, z: box.min.z },
  190.                 };
  191.  
  192.                 meshes.push({
  193.                     uuid: mesh.uuid,
  194.                     boundingBox,
  195.                     visible: mesh.visible,
  196.                 });
  197.             }
  198.  
  199.             let staticObject = false;
  200.  
  201.             // These objects are not removed when they are no longer visible on client side
  202.             if (category === "Walls" || transparent) {
  203.                 staticObject = true;
  204.             }
  205.  
  206.             const key = i;
  207.             const uuid =  child.uuid;
  208.  
  209.             const ghostObject: IModel3DGhostObject = {
  210.                 uuid,
  211.                 name: child.name,
  212.                 id: child.id,
  213.                 key,
  214.                 static: staticObject,
  215.                 userData: child.userData,
  216.                 meshes,
  217.             };
  218.  
  219.             ghostObjects.push(ghostObject);
  220.  
  221.         }
  222.         return ghostObjects;
  223.     } catch (error) {
  224.         logger.error(`CreateGhostObjects::${fo(error)}`);
  225.         return error;
  226.     }
  227. }
  228.  
  229. export async function createStreamingObjects(model: Object3D, model3DGuid: string): Promise<IstreamObjectItem[]> {
  230.     try {
  231.         const streamingObjects: IstreamObjectItem[] = [];
  232.  
  233.         for (const child of model.children) {
  234.             if (!child) { return; }
  235.  
  236.             const meshes: IModel3DMesh[] = [];
  237.  
  238.             for (const mesh of child.children) {
  239.                 // @ts-ignore
  240.                 if (mesh && mesh.material && mesh.geometry) {
  241.                     // @ts-ignore
  242.                     let geometry = mesh.geometry;
  243.                     // @ts-ignore
  244.                     if (mesh.geometry.type !== "BufferGeometry") {
  245.                         geometry = new THREE.BufferGeometry();
  246.                         // @ts-ignore
  247.                         geometry = geometry.fromGeometry(mesh.geometry);
  248.                     }
  249.  
  250.                     const geometryCloned = geometry.clone();
  251.                     geometryCloned.applyMatrix(mesh.parent.matrix);
  252.  
  253.                     meshes.push({
  254.                         uuid: mesh.uuid,
  255.                         // @ts-ignore
  256.                         materialId: mesh.material.uuid,
  257.                         position: geometryCloned.attributes.position.array,
  258.                         visible: mesh.visible,
  259.                     });
  260.                 }
  261.             }
  262.             const object: IModel3DStreamItem = {
  263.                 uuid: child.uuid,
  264.                 name: child.name,
  265.                 model3D: model3DGuid,
  266.                 meshes,
  267.             };
  268.             streamingObjects.push(object);
  269.         }
  270.  
  271.         return streamingObjects;
  272.     } catch (error) {
  273.         logger.error(`CreateStreamingObjects::${fo(error)}`);
  274.         return error;
  275.     }
  276. }
  277.  
  278. export async function createMaterialDefinitions(model: Object3D): Promise<IModel3DMaterial[]> {
  279.     try {
  280.         const materialMapping: IModel3DMaterial[] = [];
  281.  
  282.         for (const child of model.children) {
  283.             if (!child) { return; }
  284.  
  285.             for (const mesh of child.children) {
  286.                 if (mesh && mesh.material && materialMapping[mesh.material.uuid] == null) {
  287.  
  288.                     const material: IModel3DMaterial = {
  289.                         uuid: mesh.material.uuid,
  290.                         json: mesh.material.toJSON(),
  291.                     };
  292.                     materialMapping[material.uuid] = material;
  293.                 }
  294.             }
  295.         }
  296.         return materialMapping;
  297.     } catch (error) {
  298.         logger.error(`CreateMaterialDefinitions::${fo(error)}`);
  299.         return error;
  300.     }
  301. }
  302.  
  303. export async function getStreamingObjectIds(model: Object3D): Promise<string[]> {
  304.     try {
  305.         // Add categories that should be loaded last under OtherCategories
  306.         const categorySortedObjects: IModel3DCategorySortedObjects = {
  307.             Walls: [],
  308.             Floors: [],
  309.             Roofs: [],
  310.             Topographies: [],
  311.             OtherCategories: [],
  312.         };
  313.  
  314.         // Sort each object into category array container
  315.         for (const child of model.children) {
  316.             if (child.userData && child.userData.category && categorySortedObjects[child.userData.category]) {
  317.  
  318.                 categorySortedObjects[child.userData.category].push(child.uuid);
  319.  
  320.             } else {
  321.                 // categorySortedObjects["OtherCategories"].push(child.uuid);
  322.             }
  323.         }
  324.  
  325.         const categorySortedList: string[] = [];
  326.  
  327.         // Merge all the category object arrays in the order of the categorySortedObjects
  328.         Object.keys(categorySortedObjects).forEach(function(key) {
  329.             categorySortedObjects[key].forEach(function(id) {
  330.                 categorySortedList.push(id);
  331.             });
  332.         });
  333.  
  334.         return categorySortedList;
  335.     } catch (error) {
  336.         logger.error(`getStreamingObjectIds::${fo(error)}`);
  337.         return error;
  338.     }
  339. }
  340.  
  341. export async function getCategorySortedObjectList(model: Object3D): Promise<string[]> {
  342.     try {
  343.         if (model.type === "Object3D") {
  344.             // Add categories that should be loaded last under OtherCategories
  345.             const categorySortedObjects: IModel3DCategorySortedObjects = {
  346.                 Walls: [],
  347.                 Floors: [],
  348.                 Roofs: [],
  349.                 Topographies: [],
  350.                 OtherCategories: [],
  351.             };
  352.  
  353.             // Sort each object into category array container
  354.             for (const child of model.children) {
  355.                 if (child.userData && child.userData.category && categorySortedObjects[child.userData.category]) {
  356.  
  357.                     categorySortedObjects[child.userData.category].push(child.uuid);
  358.  
  359.                 } else {
  360.                     categorySortedObjects.OtherCategories.push(child.uuid);
  361.                 }
  362.             }
  363.  
  364.             const categorySortedList: string[] = [];
  365.  
  366.             // Merge all the category object arrays in the order of the categorySortedObjects
  367.             Object.keys(categorySortedObjects).forEach(function(key) {
  368.                 categorySortedObjects[key].forEach(function(id) {
  369.                     categorySortedList.push(id);
  370.                 });
  371.             });
  372.  
  373.             return categorySortedList;
  374.         }
  375.     } catch (error) {
  376.         logger.error(`getCategorySortedObjectList::${fo(error)}`);
  377.         return error;
  378.     }
  379. }
  380.  
  381. export async function getModelBoundingBox(object: Object3D, box: Box3 = new THREE.Box3()): Promise<IModel3DBoundingBox> {
  382.     try {
  383.         if (object.type === "Object3D") {
  384.             box.setFromObject(object);
  385.             return {
  386.                 max: { x: box.max.x, y: box.max.y, z: box.max.z },
  387.                 min: { x: box.min.x, y: box.min.y, z: box.min.z },
  388.             };
  389.         }
  390.     } catch (error) {
  391.         logger.error(`getModelBoudingBox::${fo(error)}`);
  392.         return error;
  393.     }
  394. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement