def __init__(self, scene, suncgDatasetRoot=None, debug=False, objectMode='box', agentRadius=0.1, agentHeight=1.6, agentMass=60.0, agentMode='capsule'): super(Panda3dBulletPhysics, self).__init__() self.__dict__.update(scene=scene, suncgDatasetRoot=suncgDatasetRoot, debug=debug, objectMode=objectMode, agentRadius=agentRadius, agentHeight=agentHeight, agentMass=agentMass, agentMode=agentMode) if suncgDatasetRoot is not None: self.modelCatMapping = ModelCategoryMapping(os.path.join( suncgDatasetRoot, "metadata", "ModelCategoryMapping.csv")) else: self.modelCatMapping = None self.bulletWorld = BulletWorld() self.bulletWorld.setGravity(Vec3(0, 0, -9.81)) if debug: debugNode = BulletDebugNode('physic-debug') debugNode.showWireframe(True) debugNode.showConstraints(False) debugNode.showBoundingBoxes(True) debugNode.showNormals(False) self.debugNodePath = self.scene.scene.attachNewNode(debugNode) self.debugNodePath.show() self.bulletWorld.setDebugNode(debugNode) else: self.debugNodePath = None self._initLayoutModels() self._initAgents() self._initObjects() self.scene.worlds['physics'] = self
def __init__(self, scene, suncgDatasetRoot, size=(512, 512), mode='offscreen', zNear=0.1, zFar=1000.0, fov=40.0, cameraTransform=None): # Off-screen buffers are not supported in OSX if sys.platform == 'darwin': mode = 'onscreen' super(Panda3dSemanticsRenderer, self).__init__() self.__dict__.update(scene=scene, suncgDatasetRoot=suncgDatasetRoot, size=size, mode=mode, zNear=zNear, zFar=zFar, fov=fov, cameraTransform=cameraTransform) self.categoryMapping = ModelCategoryMapping( os.path.join(self.suncgDatasetRoot, 'metadata', 'ModelCategoryMapping.csv')) self.cameraMask = BitMask32.bit(1) self.graphicsEngine = GraphicsEngine.getGlobalPtr() self.loader = Loader.getGlobalPtr() self.graphicsEngine.setDefaultLoader(self.loader) self._initModels() selection = GraphicsPipeSelection.getGlobalPtr() self.pipe = selection.makeDefaultPipe() logger.debug('Using %s' % (self.pipe.getInterfaceName())) # Attach a camera to every agent in the scene self.cameras = [] for agentNp in self.scene.scene.findAllMatches('**/agents/agent*'): camera = agentNp.attachNewNode(ModelNode('camera-semantics')) if self.cameraTransform is not None: camera.setTransform(cameraTransform) camera.node().setPreserveTransform(ModelNode.PTLocal) self.cameras.append(camera) # Reparent node below the existing physic node (if any) physicsNp = agentNp.find('**/physics') if not physicsNp.isEmpty(): camera.reparentTo(physicsNp) self.rgbBuffers = dict() self.rgbTextures = dict() self._initRgbCapture() self.scene.worlds['render-semantics'] = self
def getDimensionsFromModelId(modelId, modelInfoFilename, modelCatFilename): modelInfo = ModelInformation(modelInfoFilename) modelCat = ModelCategoryMapping(modelCatFilename) refModelDimensions = None otherSimilarDimensions = [] refModelId = str(modelId) refCategory = modelCat.getCoarseGrainedCategoryForModelId(refModelId) for modelId in modelInfo.model_info.keys(): category = modelCat.getCoarseGrainedCategoryForModelId(modelId) if refCategory == category: info = modelInfo.getModelInfo(modelId) # FIXME: handle the general case where for the front vector, do not ignore # NOTE: SUNCG is using the Y-up coordinate system frontVec = info['front'] if np.count_nonzero(frontVec) > 1 or not np.array_equal( frontVec, [0, 0, 1]): continue width, height, depth = info['aligned_dims'] / 100.0 # cm to m otherSimilarDimensions.append([width, height, depth]) if refModelId == modelId: refModelDimensions = np.array([width, height, depth]) otherSimilarDimensions = np.array(otherSimilarDimensions) logger.debug('Number of similar objects found in dataset: %d' % (otherSimilarDimensions.shape[0])) # Volume statistics (assume a gaussian distribution) # XXX: use a more general histogram method to define the categories, # rather than simply comparing the deviation to the mean refVolume = np.prod(refModelDimensions) otherVolumes = np.prod(otherSimilarDimensions, axis=-1) mean = np.mean(otherVolumes) std = np.std(otherVolumes) # Compare the deviation to the mean overallSizeTag = None diff = refVolume - mean for tag, threshold in DimensionTable.overallSizeTable: if threshold >= 0.0: if diff > threshold * std: overallSizeTag = tag else: if diff < threshold * std: overallSizeTag = tag if overallSizeTag is None: overallSizeTag = 'normal' return overallSizeTag
def __init__(self, scene, suncgDatasetRoot): super(SuncgSemantics, self).__init__() self.__dict__.update(scene=scene, suncgDatasetRoot=suncgDatasetRoot) self.categoryMapping = ModelCategoryMapping( os.path.join(self.suncgDatasetRoot, 'metadata', 'ModelCategoryMapping.csv')) self._initLayoutModels() self._initAgents() self._initObjects() self.scene.worlds['semantics'] = self
def __init__(self, scene, suncgDatasetRoot): self.scene = scene self.suncgDatasetRoot = suncgDatasetRoot if suncgDatasetRoot is not None: self.categoryMapping = ModelCategoryMapping( os.path.join(self.suncgDatasetRoot, 'metadata', 'ModelCategoryMapping.csv')) else: self.categoryMapping = None self._initLayoutModels() self._initAgents() self._initObjects() self.scene.worlds['semantics'] = self
def testInit(self): _ = ModelCategoryMapping( os.path.join(TEST_SUNCG_DATA_DIR, "metadata", "ModelCategoryMapping.csv"))
class SuncgSemantics(World): # XXX: is not a complete list of movable objects, and the list is redundant with the one for physics movableObjectCategories = ['table', 'dressing_table', 'sofa', 'trash_can', 'chair', 'ottoman', 'bed'] def __init__(self, scene, suncgDatasetRoot): super(SuncgSemantics, self).__init__() self.__dict__.update(scene=scene, suncgDatasetRoot=suncgDatasetRoot) self.categoryMapping = ModelCategoryMapping( os.path.join( self.suncgDatasetRoot, 'metadata', 'ModelCategoryMapping.csv') ) self._initLayoutModels() self._initAgents() self._initObjects() self.scene.worlds['semantics'] = self def _initLayoutModels(self): # Load layout objects as meshes for _ in self.scene.scene.findAllMatches('**/layouts/object*/model*'): # Nothing to do pass def _initAgents(self): # Load agents for _ in self.scene.scene.findAllMatches('**/agents/agent*'): # Nothing to do pass def _initObjects(self): # Load objects for model in self.scene.scene.findAllMatches('**/objects/object*/model*'): modelId = model.getParent().getTag('model-id') objNp = model.getParent() semanticsNp = objNp.attachNewNode('semantics') # Categories coarseCategory = self.categoryMapping.getCoarseGrainedCategoryForModelId(modelId) semanticsNp.setTag('coarse-category', coarseCategory) fineCategory = self.categoryMapping.getFineGrainedCategoryForModelId(modelId) semanticsNp.setTag('fine-category', fineCategory) # Estimate mass of object based on volumetric data and default material density objVoxFilename = os.path.join(self.suncgDatasetRoot, 'object_vox', 'object_vox_data', modelId, modelId + '.binvox') voxelData = ObjectVoxelData.fromFile(objVoxFilename) volume = voxelData.getFilledVolume() semanticsNp.setTag('volume', str(volume)) #XXX: we could get information below from physic node (if any) if coarseCategory in self.movableObjectCategories: semanticsNp.setTag('movable', 'true') else: semanticsNp.setTag('movable', 'false') #TODO: add mass information # Color information basicColors = MaterialColorTable.getColorsFromObject(objNp, mode='basic') semanticsNp.setTag('basic-colors', ",".join(basicColors)) advancedColors = MaterialColorTable.getColorsFromObject(objNp, mode='advanced') semanticsNp.setTag('advanced-colors', ",".join(advancedColors)) # Material information materials = MaterialTable.getMaterialNameFromObject(objNp) semanticsNp.setTag('materials', ",".join(materials)) # Object size information modelInfoFilename = os.path.join(self.suncgDatasetRoot, 'metadata', 'models.csv') modelCatFilename = os.path.join(self.suncgDatasetRoot, 'metadata', 'ModelCategoryMapping.csv') overallSize = DimensionTable.getDimensionsFromModelId(modelId, modelInfoFilename, modelCatFilename) semanticsNp.setTag('overall-size', str(overallSize)) def describeObject(self, obj): semanticsNp = obj.find('**/semantics') if not semanticsNp.isEmpty(): items = [] sizeDescription = semanticsNp.getTag('overall-size') if sizeDescription == 'normal': sizeDescription = '' items.append(sizeDescription) colorDescription = semanticsNp.getTag('advanced-colors') items.append(colorDescription) categoryDescription = semanticsNp.getTag('fine-category') categoryDescription = categoryDescription.replace("_", " ") items.append(categoryDescription) materialDescription = semanticsNp.getTag('materials') materialDescription = 'made of ' + materialDescription items.append(materialDescription) desc = " ".join(items) else: desc = "" return desc def step(self, dt): #Nothing to do yet here pass
class Panda3dBulletPhysics(World): # NOTE: the model ids of objects that correspond to opened doors. They will be ignored for collisions. openedDoorModelIds = [ '122', '133', '214', '246', '247', '361', '73', '756', '757', '758', '759', '760', '761', '762', '763', '764', '765', '768', '769', '770', '771', '778', '779', '780', 's__1762', 's__1763', 's__1764', 's__1765', 's__1766', 's__1767', 's__1768', 's__1769', 's__1770', 's__1771', 's__1772', 's__1773', ] # FIXME: is not a complete list of movable objects movableObjectCategories = [ 'table', 'dressing_table', 'sofa', 'trash_can', 'chair', 'ottoman', 'bed' ] # For more material, see table: http://www.ambrsoft.com/CalcPhysics/Density/Table_2.htm defaultDensity = 1000.0 # kg/m^3 # For more coefficients, see table: https://www.thoughtspike.com/friction-coefficients-for-bullet-physics/ defaultMaterialFriction = 0.7 defaultMaterialRestitution = 0.5 def __init__(self, scene, suncgDatasetRoot=None, debug=False, objectMode='box', agentRadius=0.1, agentHeight=1.6, agentMass=60.0, agentMode='capsule'): super(Panda3dBulletPhysics, self).__init__() self.__dict__.update(scene=scene, suncgDatasetRoot=suncgDatasetRoot, debug=debug, objectMode=objectMode, agentRadius=agentRadius, agentHeight=agentHeight, agentMass=agentMass, agentMode=agentMode) if suncgDatasetRoot is not None: self.modelCatMapping = ModelCategoryMapping( os.path.join(suncgDatasetRoot, "metadata", "ModelCategoryMapping.csv")) else: self.modelCatMapping = None self.bulletWorld = BulletWorld() self.bulletWorld.setGravity(Vec3(0, 0, -9.81)) if debug: debugNode = BulletDebugNode('physic-debug') debugNode.showWireframe(True) debugNode.showConstraints(False) debugNode.showBoundingBoxes(True) debugNode.showNormals(False) self.debugNodePath = self.scene.scene.attachNewNode(debugNode) self.debugNodePath.show() self.bulletWorld.setDebugNode(debugNode) else: self.debugNodePath = None self._initLayoutModels() self._initAgents() self._initObjects() self.scene.worlds['physics'] = self def destroy(self): # Nothing to do pass def _initLayoutModels(self): # Load layout objects as meshes for model in self.scene.scene.findAllMatches( '**/layouts/object*/model*'): shape, transform = getCollisionShapeFromModel( model, mode='mesh', defaultCentered=False) node = BulletRigidBodyNode('physics') node.setMass(0.0) node.setFriction(self.defaultMaterialFriction) node.setRestitution(self.defaultMaterialRestitution) node.setStatic(True) node.addShape(shape) node.setDeactivationEnabled(True) node.setIntoCollideMask(BitMask32.allOn()) self.bulletWorld.attach(node) # Attach the physic-related node to the scene graph physicsNp = model.getParent().attachNewNode(node) physicsNp.setTransform(transform) if node.isStatic(): model.getParent().setTag('physics-mode', 'static') else: model.getParent().setTag('physics-mode', 'dynamic') # Reparent render and acoustics nodes (if any) below the new physic node #XXX: should be less error prone to just reparent all children (except the hidden model) renderNp = model.getParent().find('**/render') if not renderNp.isEmpty(): renderNp.reparentTo(physicsNp) acousticsNp = model.getParent().find('**/acoustics') if not acousticsNp.isEmpty(): acousticsNp.reparentTo(physicsNp) # NOTE: we need this to update the transform state of the internal bullet node physicsNp.node().setTransformDirty() # Validation assert np.allclose( mat4ToNumpyArray(physicsNp.getNetTransform().getMat()), mat4ToNumpyArray(model.getNetTransform().getMat()), atol=1e-6) def _initAgents(self): # Load agents for agent in self.scene.scene.findAllMatches('**/agents/agent*'): transform = TransformState.makeIdentity() if self.agentMode == 'capsule': shape = BulletCapsuleShape( self.agentRadius, self.agentHeight - 2 * self.agentRadius) elif self.agentMode == 'sphere': shape = BulletCapsuleShape(self.agentRadius, 2 * self.agentRadius) # XXX: use BulletCharacterControllerNode class, which already handles local transform? node = BulletRigidBodyNode('physics') node.setMass(self.agentMass) node.setStatic(False) node.setFriction(self.defaultMaterialFriction) node.setRestitution(self.defaultMaterialRestitution) node.addShape(shape) self.bulletWorld.attach(node) # Constrain the agent to have fixed position on the Z-axis node.setLinearFactor(Vec3(1.0, 1.0, 0.0)) # Constrain the agent not to be affected by rotations node.setAngularFactor(Vec3(0.0, 0.0, 0.0)) node.setIntoCollideMask(BitMask32.allOn()) node.setDeactivationEnabled(True) # Enable continuous collision detection (CCD) node.setCcdMotionThreshold(1e-7) node.setCcdSweptSphereRadius(0.50) if node.isStatic(): agent.setTag('physics-mode', 'static') else: agent.setTag('physics-mode', 'dynamic') # Attach the physic-related node to the scene graph physicsNp = NodePath(node) physicsNp.setTransform(transform) # Reparent all child nodes below the new physic node for child in agent.getChildren(): child.reparentTo(physicsNp) physicsNp.reparentTo(agent) # NOTE: we need this to update the transform state of the internal bullet node physicsNp.node().setTransformDirty() # Validation assert np.allclose( mat4ToNumpyArray(physicsNp.getNetTransform().getMat()), mat4ToNumpyArray(agent.getNetTransform().getMat()), atol=1e-6) def _initObjects(self): # Load objects for model in self.scene.scene.findAllMatches( '**/objects/object*/model*'): modelId = model.getParent().getTag('model-id') # XXX: we could create BulletGhostNode instance for non-collidable objects, but we would need to filter out the collisions later on if not modelId in self.openedDoorModelIds: shape, transform = getCollisionShapeFromModel( model, self.objectMode, defaultCentered=True) node = BulletRigidBodyNode('physics') node.addShape(shape) node.setFriction(self.defaultMaterialFriction) node.setRestitution(self.defaultMaterialRestitution) node.setIntoCollideMask(BitMask32.allOn()) node.setDeactivationEnabled(True) if self.suncgDatasetRoot is not None: # Check if it is a movable object category = self.modelCatMapping.getCoarseGrainedCategoryForModelId( modelId) if category in self.movableObjectCategories: # Estimate mass of object based on volumetric data and default material density objVoxFilename = os.path.join(self.suncgDatasetRoot, 'object_vox', 'object_vox_data', modelId, modelId + '.binvox') voxelData = ObjectVoxelData.fromFile(objVoxFilename) mass = Panda3dBulletPhysics.defaultDensity * voxelData.getFilledVolume( ) node.setMass(mass) else: node.setMass(0.0) node.setStatic(True) else: node.setMass(0.0) node.setStatic(True) if node.isStatic(): model.getParent().setTag('physics-mode', 'static') else: model.getParent().setTag('physics-mode', 'dynamic') # Attach the physic-related node to the scene graph physicsNp = model.getParent().attachNewNode(node) physicsNp.setTransform(transform) # Reparent render and acoustics nodes (if any) below the new physic node #XXX: should be less error prone to just reparent all children (except the hidden model) renderNp = model.getParent().find('**/render') if not renderNp.isEmpty(): renderNp.reparentTo(physicsNp) acousticsNp = model.getParent().find('**/acoustics') if not acousticsNp.isEmpty(): acousticsNp.reparentTo(physicsNp) # NOTE: we need this to update the transform state of the internal bullet node node.setTransformDirty() # NOTE: we need to add the node to the bullet engine only after setting all attributes self.bulletWorld.attach(node) # Validation assert np.allclose( mat4ToNumpyArray(physicsNp.getNetTransform().getMat()), mat4ToNumpyArray( model.getParent().getNetTransform().getMat()), atol=1e-6) else: logger.debug('Object %s ignored from physics' % (modelId)) def step(self, dt): self.bulletWorld.doPhysics(dt) def isCollision(self, root): isCollisionDetected = False if isinstance(root.node(), BulletRigidBodyNode): result = self.bulletWorld.contactTest(root.node()) if result.getNumContacts() > 0: isCollisionDetected = True else: for nodePath in root.findAllMatches('**/+BulletBodyNode'): isCollisionDetected |= self.isCollision(nodePath) return isCollisionDetected def getCollisionInfo(self, root, dt): result = self.bulletWorld.contactTest(root.node()) force = 0.0 relativePosition = LVecBase3f(0.0, 0.0, 0.0) isCollisionDetected = False for _ in result.getContacts(): # Iterate over all manifolds of the world # NOTE: it seems like the contact manifold doesn't hold the information # to calculate contact force. We need the persistent manifolds for that. for manifold in self.bulletWorld.getManifolds(): # Check if the root node is part of that manifold, by checking positions # TODO: there is surely a better way to compare the two nodes here #if (manifold.getNode0().getTransform().getPos() == root.getNetTransform().getPos()): if manifold.getNode0().getTag('model-id') == root.getTag( 'model-id'): # Calculate the to totalImpulse = 0.0 maxImpulse = 0.0 for pt in manifold.getManifoldPoints(): impulse = pt.getAppliedImpulse() totalImpulse += impulse if impulse > maxImpulse: maxImpulse = impulse relativePosition = pt.getLocalPointA() force = totalImpulse / dt isCollisionDetected = True return force, relativePosition, isCollisionDetected def calculate2dNavigationMap(self, agent, z=0.1, precision=0.1, yup=True): agentRbNp = agent.find('**/+BulletRigidBodyNode') # Calculate the bounding box of the scene bounds = [] for nodePath in self.scene.scene.findAllMatches( '**/object*/+BulletRigidBodyNode'): node = nodePath.node() #NOTE: the bounding sphere doesn't seem to take into account the transform, so apply it manually (translation only) bsphere = node.getShapeBounds() center = nodePath.getNetTransform().getPos() bounds.extend( [center + bsphere.getMin(), center + bsphere.getMax()]) minBounds, maxBounds = np.min(bounds, axis=0), np.max(bounds, axis=0) # Using the X and Y dimensions of the bounding box, discretize the 2D plan into a uniform grid with given precision X = np.arange(minBounds[0], maxBounds[0], step=precision) Y = np.arange(minBounds[1], maxBounds[1], step=precision) nbTotalCells = len(X) * len(Y) threshold10Perc = int(nbTotalCells / 10) # XXX: the simulation needs to be run a little before moving the agent, not sure why self.bulletWorld.doPhysics(0.1) # Sweep the position of the agent across the grid, checking if collision/contacts occurs with objects or walls in the scene. occupancyMap = np.zeros((len(X), len(Y))) occupancyMapCoord = np.zeros((len(X), len(Y), 2)) n = 0 for i, x in enumerate(X): for j, y in enumerate(Y): agentRbNp.setPos(LVecBase3f(x, y, z)) if self.isCollision(agentRbNp): occupancyMap[i, j] = 1.0 occupancyMapCoord[i, j, 0] = x occupancyMapCoord[i, j, 1] = y n += 1 if n % threshold10Perc == 0: logger.debug('Collision test no.%d (out of %d total)' % (n, nbTotalCells)) if yup: # Convert to image format (y,x) occupancyMap = np.flipud(occupancyMap.T) occupancyMapCoord = np.flipud( np.transpose(occupancyMapCoord, axes=(1, 0, 2))) return occupancyMap, occupancyMapCoord
class Panda3dSemanticsRenderer(World): def __init__(self, scene, suncgDatasetRoot, size=(512,512), mode='offscreen', zNear=0.1, zFar=1000.0, fov=40.0, cameraTransform=None): super(Panda3dSemanticsRenderer, self).__init__() self.__dict__.update(scene=scene, suncgDatasetRoot=suncgDatasetRoot, size=size, mode=mode, zNear=zNear, zFar=zFar, fov=fov, cameraTransform=cameraTransform) self.categoryMapping = ModelCategoryMapping( os.path.join( self.suncgDatasetRoot, 'metadata', 'ModelCategoryMapping.csv') ) self.cameraMask = BitMask32.bit(4) self.graphicsEngine = GraphicsEngine.getGlobalPtr() self.loader = Loader.getGlobalPtr() self.graphicsEngine.setDefaultLoader(self.loader) self._initModels() selection = GraphicsPipeSelection.getGlobalPtr() self.pipe = selection.makeDefaultPipe() logger.debug('Using %s' % (self.pipe.getInterfaceName())) # Attach a camera to every agent in the scene self.cameras = [] for agentNp in self.scene.scene.findAllMatches('**/agents/agent*'): camera = agentNp.attachNewNode(ModelNode('camera-semantics')) if self.cameraTransform is not None: camera.setTransform(cameraTransform) camera.node().setPreserveTransform(ModelNode.PTLocal) self.cameras.append(camera) self.rgbBuffers = dict() self.rgbTextures = dict() self._initRgbCapture() self.scene.worlds['render-semantics'] = self def _initCategoryColors(self): catNames = self.categoryMapping.getFineGrainedClassList() size = int(np.ceil(np.cbrt(len(catNames)) - 1e-6)) # Uniform sampling of colors colors = np.zeros((size**3, 3)) i = 0 for r in np.linspace(0.0, 1.0, size): for g in np.linspace(0.0, 1.0, size): for b in np.linspace(0.0, 1.0, size): colors[i] = [r,g,b] i += 1 # Shuffle indices = np.arange(len(colors)) np.random.shuffle(indices) colors = colors[indices] self.catColors = dict() for i, name in enumerate(catNames): self.catColors[name] = colors[i] print '\'%s\': [%d, %d, %d],' % (name, int(colors[i][0]*255), int(colors[i][1]*255), int(colors[i][2]*255)) def _initModels(self): models = [] for model in self.scene.scene.findAllMatches('**/objects/**/+ModelNode'): models.append(model) for model in self.scene.scene.findAllMatches('**/layouts/**/+ModelNode'): models.append(model) for model in models: objectNp = model.getParent() rendererNp = objectNp.attachNewNode('render-semantics') model = model.copyTo(rendererNp) # Set the model to be visible only to this camera model.node().adjustDrawMask(self.cameraMask, self.cameraMask, self.cameraMask) model.show() # Get semantic-related color of model modelId = model.getNetTag('model-id') if 'fr_' in modelId: if modelId.endswith('c'): catName = 'ceiling' elif modelId.endswith('f'): catName = 'floor' elif modelId.endswith('w'): catName = 'wall' else: pass catName = self.categoryMapping.getFineGrainedCategoryForModelId(modelId) color = MODEL_CATEGORY_COLOR_MAPPING[catName] # Clear all GeomNode render attributes and set a specified flat color for nodePath in model.findAllMatches('**/+GeomNode'): geomNode = nodePath.node() for n in range(geomNode.getNumGeoms()): geomNode.setGeomState(n, RenderState.make(ColorAttrib.makeFlat(LColor(color[0]/255.0, color[1]/255.0, color[2]/255.0, 1.0)), 1)) # Disable lights for this model model.setLightOff(1) # Enable antialiasing model.setAntialias(AntialiasAttrib.MAuto) # Reparent render node below the existing physic node (if any) physicsNp = objectNp.find('**/physics') if not physicsNp.isEmpty(): rendererNp.reparentTo(physicsNp) def _initRgbCapture(self): for camera in self.cameras: camNode = Camera('Semantic camera') camNode.setCameraMask(self.cameraMask) lens = PerspectiveLens() lens.setFov(self.fov) lens.setAspectRatio(float(self.size[0]) / float(self.size[1])) lens.setNear(self.zNear) lens.setFar(self.zFar) camNode.setLens(lens) camNode.setScene(self.scene.scene) cam = camera.attachNewNode(camNode) winprops = WindowProperties.size(self.size[0], self.size[1]) fbprops = FrameBufferProperties.getDefault() fbprops = FrameBufferProperties(fbprops) fbprops.setRgbaBits(8, 8, 8, 8) flags = GraphicsPipe.BFFbPropsOptional if self.mode == 'onscreen': flags = flags | GraphicsPipe.BFRequireWindow elif self.mode == 'offscreen': flags = flags | GraphicsPipe.BFRefuseWindow else: raise Exception('Unsupported rendering mode: %s' % (self.mode)) buf = self.graphicsEngine.makeOutput(self.pipe, 'RGB buffer', 0, fbprops, winprops, flags) if buf is None: raise Exception('Unable to create RGB buffer') # Set to render at the end buf.setSort(10000) dr = buf.makeDisplayRegion() dr.setSort(0) dr.setCamera(cam) dr = camNode.getDisplayRegion(0) tex = Texture() tex.setFormat(Texture.FRgba8) tex.setComponentType(Texture.TUnsignedByte) buf.addRenderTexture(tex, GraphicsOutput.RTMCopyRam, GraphicsOutput.RTPColor) #XXX: should use tex.setMatchFramebufferFormat(True)? agent = camera.getParent() self.rgbBuffers[agent.getName()] = buf self.rgbTextures[agent.getName()] = tex def showRoomLayout(self, showCeilings=True, showWalls=True, showFloors=True): for np in self.scene.scene.findAllMatches('**/layouts/**/render-semantics/*c'): if showCeilings: np.show() else: np.hide() for np in self.scene.scene.findAllMatches('**/layouts/**/render-semantics/*w'): if showWalls: np.show() else: np.hide() for np in self.scene.scene.findAllMatches('**/layouts/**/render-semantics/*f'): if showFloors: np.show() else: np.hide() def destroy(self): self.graphicsEngine.removeAllWindows() del self.pipe def getRgbaImages(self, channelOrder="RGBA"): images = dict() for name, tex in self.rgbTextures.iteritems(): data = tex.getRamImageAs(channelOrder) image = np.frombuffer(data.get_data(), np.uint8) # Must match Texture.TUnsignedByte image.shape = (tex.getYSize(), tex.getXSize(), 4) image = np.flipud(image) images[name] = image return images def step(self, dt): self.graphicsEngine.renderFrame() #NOTE: we need to call frame rendering twice in onscreen mode because of double-buffering if self.mode == 'onscreen': self.graphicsEngine.renderFrame()