From 12e1c2b9c0e764f02f684887e3d3578a72c83bc7 Mon Sep 17 00:00:00 2001 From: "Angel X. Chang" Date: Sun, 16 Dec 2018 15:52:21 -0800 Subject: [PATCH] Have multiple grid cell ids for room goal --- client/js/lib/STK-core.js | 3 +- client/js/lib/materials/Materials.js | 4 +- client/js/lib/nav/Graph.js | 24 +++++++++++- client/js/lib/nav/NavScene.js | 39 ++++++++++++++++++- client/js/lib/scene/SceneUtil.js | 10 ++++- .../js/lib/sim/CollisionProcessorNavGrid.js | 12 +++++- client/js/lib/sim/CollisionProcessorNull.js | 2 +- client/js/lib/sim/SimState.js | 23 +++++++++-- client/js/lib/sim/Simulator.js | 21 +++++++--- client/package.json | 1 - ssc/render.js | 3 +- ssc/scripts/export-meshes.sh | 2 +- 12 files changed, 120 insertions(+), 24 deletions(-) diff --git a/client/js/lib/STK-core.js b/client/js/lib/STK-core.js index 35cdaef..6f0f810 100644 --- a/client/js/lib/STK-core.js +++ b/client/js/lib/STK-core.js @@ -77,6 +77,7 @@ module.exports = { /** @namespace gfx */ gfx: { Camera: require('./gfx/Camera'), + ClippingBox: require('./gfx/ClippingBox'), EDLPass: require('./gfx/EDLPass'), Lights: require('./gfx/Lights'), Renderer: require('./gfx/Renderer'), @@ -94,7 +95,7 @@ module.exports = { /** @namespace search */ search: { BasicSearchController: require('./search/BasicSearchController'), - SolrQuerier: require("./search/SolrQuerier") + SolrQuerier: require('./search/SolrQuerier') }, /** @namespace sim */ sim: { diff --git a/client/js/lib/materials/Materials.js b/client/js/lib/materials/Materials.js index c1a3501..a34ddd5 100644 --- a/client/js/lib/materials/Materials.js +++ b/client/js/lib/materials/Materials.js @@ -131,7 +131,7 @@ Materials.getBasicMaterial = function (color, alpha) { if (color instanceof THREE.Material || color instanceof THREE.MultiMaterial) { return color; } - var mat = new THREE.MeshBasicMaterial({side: THREE.DoubleSide, color: color}); + var mat = new THREE.MeshBasicMaterial({side: Materials.DefaultMaterialSide, color: color}); if (alpha != undefined) { Materials.setMaterialOpacity(mat, alpha); } @@ -183,7 +183,7 @@ Materials.getStandardMaterial = function (color, alpha, materialType) { //ambient: a, specular: s, shininess: 64, - side: THREE.DoubleSide + side: Materials.DefaultMaterialSide })); if (alpha != undefined) { Materials.setMaterialOpacity(mat, alpha); diff --git a/client/js/lib/nav/Graph.js b/client/js/lib/nav/Graph.js index ed85927..c4e1555 100644 --- a/client/js/lib/nav/Graph.js +++ b/client/js/lib/nav/Graph.js @@ -60,6 +60,9 @@ Graph.prototype.createCellAttributes = function(key, cellAttr, force) { this._tileAttributes[key] = new type(this.numNodes); } }; +Graph.prototype.hasCellAttribute = function(key) { + return this._tileAttributes[key] != null; +}; Graph.prototype.setCellAttribute = function(id, key, value) { this._tileAttributes[key][id] = value; }; @@ -329,7 +332,26 @@ Graph.prototype.getCellIdsWithUserData = function(key, valuefilter) { } }); return ids; -} +}; + +Graph.prototype.getCellIdsWithCellAttribute = function(key, valuefilter) { + var ids = []; + var filter; + if (valuefilter == undefined) { + filter = function(v, k) { return _.isFinite(v); }; + } else if (_.isFunction(valuefilter)) { + filter = valuefilter; + } else { + filter = function(v, k) { return v === valuefilter; }; + } + _.each(this._tileAttributes[key], function (v, id) { + if (filter(v, key)) { + ids.push(id); + } + }); + return ids; +}; + /** * A grid of squares, to be used as a graph. diff --git a/client/js/lib/nav/NavScene.js b/client/js/lib/nav/NavScene.js index 0294cdc..bc6d12a 100644 --- a/client/js/lib/nav/NavScene.js +++ b/client/js/lib/nav/NavScene.js @@ -102,7 +102,7 @@ SceneGrid2D.prototype.idToCell = function(id) { }; SceneGrid2D.prototype.idToPosition = function(id) { var ij = this.fromId(id); - var cellHeight = this.getCellAttribute('floorHeight', id); + var cellHeight = this.getCellAttribute(id, 'floorHeight'); if (cellHeight == undefined || !isFinite(cellHeight)) { cellHeight = this.min[1]; } @@ -455,6 +455,15 @@ MultiLevelGrid.prototype.createCellAttributes = function(key, cellAttr, force) { levelGrid.createCellAttributes(key, cellAttr, force); } }; +MultiLevelGrid.prototype.hasCellAttribute = function(key) { + for (var i = 0; i < this.levelGrids.length; i++) { + var levelGrid = this.levelGrids[i]; + if (levelGrid.hasCellAttribute(key)) { + return true; + } + } + return false; +}; MultiLevelGrid.prototype.setCellAttribute = function(id, key, value) { var levelGrid = this.idToLevelGrid(id); if (levelGrid) { @@ -607,6 +616,18 @@ MultiLevelGrid.prototype.getCellIdsWithUserData = function(key, valuefilter) { } return cellIds; }; +MultiLevelGrid.prototype.getCellIdsWithCellAttribute = function(key, valuefilter) { + var cellIds = []; + for (var i = 0; i < this.levelGrids.length; i++) { + var levelGrid = this.levelGrids[i]; + var ids = levelGrid.getCellIdsWithCellAttribute(key, valuefilter); + for (var j = 0; j < ids.length; j++) { + var id = ids[j]; + cellIds.push(levelGrid.nodeIdOffset + id); + } + } + return cellIds; +}; MultiLevelGrid.prototype.checkCellsTraversable = function(cellIds, filter) { var scope = this; var cellIdsByLevelGrid = _.groupBy(cellIds, function(id) { @@ -639,6 +660,7 @@ MultiLevelGrid.prototype.checkCellsTraversable = function(cellIds, filter) { * @param [opts.autoUpdate=true] {boolean} Whether to automatically update the position of the agent and the shortest path * @param [opts.refineGrid] {{radius: number}} Options for how to refine loaded grid * @param [opts.mapName=navmap] {string} Default name of navigation map to use + * @param [opts.metadata] {Object} Additional metadata to be stored away * @constructor * @memberOf nav */ @@ -1051,6 +1073,10 @@ NavScene.prototype.getCellAttribute = function(pos, key) { return value; }; +NavScene.prototype.hasCellAttribute = function(key) { + return this.cellAttributes[key] != null; +}; + NavScene.prototype.getRoom = function(pos) { var roomIndex = this.getCellAttribute(pos, 'roomIndex'); return this.sceneState.getRoomByIndex1(roomIndex); @@ -1198,6 +1224,14 @@ NavScene.prototype.getGoalCells = function(goals, opts) { scope.getCellId(goal, opts.saveCell); return objectCellIds; } + } else if (goal.type === 'room') { + // var roomIndex = _.map(goal.room, function(x) { return scope.sceneState.getRoomIndex().indexOf(x); }); + var roomIndex = scope.sceneState.getRoomIndex().indexOf(goal.room[0]); + var roomCellIds = scope.grid.getCellIdsWithCellAttribute('roomIndex', roomIndex); + if (roomCellIds.length) { + scope.getCellId(goal, opts.saveCell); + return roomCellIds; + } } return [scope.getCellId(goal, opts.saveCell)]; }); @@ -1402,6 +1436,7 @@ NavScene.prototype.visualizeTileWeight = function() { NavScene.prototype.visualizeTraversable = function(color) { //console.log('visualizing navigation tiles'); this.__visualizeTiles({ + type: 'nav', getColor: function(w) { if (w === Infinity) { return NavScene.defaultTileColors.obstacle; @@ -1415,7 +1450,7 @@ NavScene.prototype.visualizeTraversable = function(color) { NavScene.prototype.visualizePathCost = function(opts) { opts = opts || {}; var showPathOnly = opts.showPathOnly; - //console.log('visualizing navigation map'); + // console.log('visualizing navigation map', showPathOnly); var colorFn = Colors.getColorFunction({ type: 'interpolate', space: 'hsl', diff --git a/client/js/lib/scene/SceneUtil.js b/client/js/lib/scene/SceneUtil.js index bb4d71a..5ed050c 100644 --- a/client/js/lib/scene/SceneUtil.js +++ b/client/js/lib/scene/SceneUtil.js @@ -316,8 +316,14 @@ SceneUtil.__remapSceneModelMaterials = function (sceneState, opts) { }; for (var i = 0; i < sceneState.modelInstances.length; i++) { var modelInstance = sceneState.modelInstances[i]; - var getMeshMaterial = function (m) { return getMeshMaterialForModel(modelInstance.model.info, m); }; - colorObj(modelInstance.object3D, getMeshMaterial, opts); + var modelInfo = modelInstance.model.info; + if (modelInfo[remapMaterialsKey].data != null) { + var getMeshMaterial = function (m) { return getMeshMaterialForModel(modelInfo, m); }; + colorObj(modelInstance.object3D, getMeshMaterial, opts); + } else { + console.error('Cannot find remapMaterial ' + remapMaterialsKey + ' for model ' + modelInfo.fullId); + colorObj(modelInstance.object3D, Object3DUtil.BlackMat, opts); + } } }; diff --git a/client/js/lib/sim/CollisionProcessorNavGrid.js b/client/js/lib/sim/CollisionProcessorNavGrid.js index 3906be2..c4345e3 100644 --- a/client/js/lib/sim/CollisionProcessorNavGrid.js +++ b/client/js/lib/sim/CollisionProcessorNavGrid.js @@ -49,9 +49,17 @@ CollisionProcessorNavGrid.prototype.computeSensorForces = function (sceneState, CollisionProcessorNavGrid.prototype.isPositionInsideScene = function(sceneState, pFeet) { var navscene = this.__ensureNavScene(sceneState); - //TODO implement more precise check var sceneBBox = sceneState.getBBox(); - return sceneBBox.contains(pFeet); + if (sceneBBox.contains(pFeet)) { + // TODO: decide if this is the correct check to use + if (navscene.hasCellAttribute('floorHeight')) { + return _.isFinite(navscene.getCellAttribute(pFeet, 'floorHeight')); + } else { + return true; + } + } else { + return false; + } }; CollisionProcessorNavGrid.prototype.isPositionAgentCanStandAt = function (sceneState, agent, pFeet, opts) { diff --git a/client/js/lib/sim/CollisionProcessorNull.js b/client/js/lib/sim/CollisionProcessorNull.js index 8a03377..114c604 100644 --- a/client/js/lib/sim/CollisionProcessorNull.js +++ b/client/js/lib/sim/CollisionProcessorNull.js @@ -6,7 +6,7 @@ var _ = require('util/util'); * @constructor * @memberOf sim * @param opts Configuration parameters for collision processing - * @param [opts.traversableHeight=0.25] {number} Ignore obstacles lower than this height + * @param [opts.traversableFloorHeight=0.25] {number} Ignore obstacles lower than this height **/ function CollisionProcessorNull(opts) { opts = _.defaultsDeep(Object.create(null), opts, { diff --git a/client/js/lib/sim/SimState.js b/client/js/lib/sim/SimState.js index 024a519..1aa3e79 100644 --- a/client/js/lib/sim/SimState.js +++ b/client/js/lib/sim/SimState.js @@ -315,7 +315,12 @@ SimState.prototype.getSerializableGoals = function() { return this.__serializableGoals; }; -// Convert a model instance to a goal +/** + * Convert a model instance to a goal + * @param mi {model.ModelInstance} + * @returns {sim.ObjectGoal} + * @private + */ SimState.prototype.__getGoalForModelInstance = function (mi) { const bbox = mi.getBBox(); const objectCentroid = bbox.centroid(); @@ -346,7 +351,12 @@ SimState.prototype.__getGoalForModelInstance = function (mi) { }; }; -// Convert a room object to a goal +/** + * Convert a room object to a goal + * @param room {THREE.Object3D} + * @returns {sim.RoomGoal} + * @private + */ SimState.prototype.__getGoalInRoom = function (room) { const bbox = Object3DUtil.getBoundingBox(room); const sample = this.__sampleFloor(room, this.opts.sampleFloor); @@ -365,7 +375,12 @@ SimState.prototype.__getGoalInRoom = function (room) { }; }; -// Convert a position in the scene to a goal +/** + * Convert a position in the scene to a goal + * @param position {THREE.Vector3} + * @returns {sim.PositionGoal} + * @private + */ SimState.prototype.__getGoalForPosition = function (position) { const room = this.sceneState.getIntersectedRoomAt(position); const roomInfo = this.sceneState.getRoomInfo(room.object); @@ -380,7 +395,7 @@ SimState.prototype.__getGoalForPosition = function (position) { /** * Computes actual goals from goal specification - * @returns {Array<{position: THREE.Vector3, room: ?string, angle: ?number, bbox: ?geo.BBox, objectId: ?string, modelInstance: ?model.ModelInstance, initialOffsetFromAgent: ?THREE.Vector3, audioFile: ?string}>} + * @returns {Array} */ SimState.prototype.computeGoals = function () { let goalsSpec = this.opts.goal; diff --git a/client/js/lib/sim/Simulator.js b/client/js/lib/sim/Simulator.js index eede6fd..962df7c 100644 --- a/client/js/lib/sim/Simulator.js +++ b/client/js/lib/sim/Simulator.js @@ -34,15 +34,17 @@ var __optsToIgnore = ['renderer', 'simpleRenderer', 'assetManager', 'net', 'wav' * @param [opts.assetManager] {AssetManager} For loading assets (will be created if not specified). * @param [opts.assetCacheSize=100] {int} Size of asset cache * @param [opts.outputDir] {string} Output directory for debug output (e.g. screenshots and audio output) - * @param [opts.agent] {Object} Agent configuration + * @param [opts.agent] {Object} Agent configuration. See {@link sim.Agent} for options. * @param [opts.sensors] {Object} Sensor configuration * @param [opts.scene] {Object} Options for loading a scene - * @param [opts.start] {Object|string} Start specification (use `random` for random start) - * @param [opts.goal] {Object|string} Goal specification (use `random` for random goal) - * @param [opts.audio] {Object} Options for audio simulator - * @param [opts.navmap] {Object} Options for navigation map (use `{ recompute=true }` for force recomputation of navigation maps + * @param [opts.start] {Object|string} Start specification (use `random` for random start, and `{ position: number[], angle: number}` to specify a position) + * @param [opts.goal] {sim.GoalSpec|string} Goal specification (use `random` for random goal) + * @param [opts.audio] {Object} Options for audio simulator. See {@link sim.AudioSimulator} for options. + * @param [opts.navmap] {Object} Options for navigation map (use `{ recompute=true }` for force recomputation of navigation maps). See {@link nav.NavScene} for more options. * @param [opts.collisionDetection] {Object} Parameters for collision detection + * @param [opts.modifications] {sim.ModificationCmd[]} Modifications to scene * @param [opts.seed] {int} Seed to use for random number generator + * @param [opts.rng] {math.RNG} Random number generator to use * @param [opts.useSky=true] {boolean} Whether to use grass and sky for outside environment. * @param [opts.actionTraceLog] {string|boolean} True to use log action traces to file. If string, used to specify filename of action trace. * By default, action trace is saved to `outputDir/action_trace.csv`. @@ -66,6 +68,13 @@ function Simulator(opts) { useSky: true }); opts = _.mapKeysDeep(opts, function(v,k) { return _.camelCase(k); }); + if (opts.scene) { + if (opts.scene.level != null) { + if (_.isString(opts.scene.level)) { + opts.scene.level = parseInt(opts.scene.level); + } + } + } console.log('Creating Simulator with options', _.omit(opts, __optsToIgnore)); this.__init(opts); } @@ -703,7 +712,7 @@ Simulator.prototype.prepareTopDownMapProjection = function() { /** * Set the simulator to this the specified scene state. If there * @param sceneState {scene.SceneState} Scene state for the simulator - * @param modifications {Array[]} Array of modifications to apply to the scene state + * @param modifications {sim.ModificationCmd[]} Array of modifications to apply to the scene state * @param callback {callback} Error first callback returning modifications to the scene if scene loaded successfully * @private */ diff --git a/client/package.json b/client/package.json index eada37d..b98ab34 100644 --- a/client/package.json +++ b/client/package.json @@ -49,7 +49,6 @@ "jsdom": "^9.12.0", "jshint": "^2.9.5", "json-loader": "^0.5.4", - "minimist": "^1.2.0", "mocha": "^5.2.0", "optimist": "^0.6.1", "pngjs": "^3.3.3", diff --git a/ssc/render.js b/ssc/render.js index 40da7f2..5edc42d 100755 --- a/ssc/render.js +++ b/ssc/render.js @@ -337,7 +337,7 @@ function processIds(assetsDb) { console.warn('No wall for scene ' + fullId); } } else if (extraInfo.assetType === 'navmap') { - var collisionProcessor = STK.sim.CollisionProcessorFactory.createCollisionProcessor(); + var collisionProcessor = STK.sim.CollisionProcessorFactory.createCollisionProcessor({mode: 'navgrid'}); if (extraInfo.data) { var navscene = new STK.nav.NavScene({ sceneState: sceneState, @@ -347,6 +347,7 @@ function processIds(assetsDb) { return collisionProcessor.isPositionInsideScene(sceneState, position); } }); + sceneState.navscene = navscene; //navscene.visualizeTileWeight(); navscene.visualizeTraversable(new THREE.Color('orange')); } else { diff --git a/ssc/scripts/export-meshes.sh b/ssc/scripts/export-meshes.sh index e7948e5..3a21be0 100755 --- a/ssc/scripts/export-meshes.sh +++ b/ssc/scripts/export-meshes.sh @@ -9,7 +9,7 @@ input_format=kmz output_format=obj n=4 -opts="--input_type {input_type} --input_format ${input_format} --output_format ${output_format} --assetType model --export_textures copy --texture_path images --require_faces --use_search_controller --include_group --normalize_size diagonal --center --handle_material_side" +opts="--input_type ${input_type} --input_format ${input_format} --output_format ${output_format} --assetType model --export_textures copy --texture_path images --require_faces --use_search_controller --include_group --normalize_size diagonal --center --handle_material_side" mkdir -p $output_dir mkdir -p $output_dir/meshes