Extract Data from .nwd (.nwc) files

Vadim Asandiy

February 11, 2022

This article describes the procedure of extracting the following data from an .nwd (.nwc) file:

  • Geometry objects
  • Materials
  • Parameters of a scene

The procedure is based on the OdSimpleLoader sample application and includes the following key steps:

  1. Initialize the ODA BimNv SDK libraries.
  2. Load an .nwd (.nwc) file.
  3. Extract geometry data.
  4. Extract scene parameters.
  5. Uninitialize the ODA BimNv SDK libraries.

Initialize ODA BimNv SDK Libraries

Before using BimNv SDK functionality, the libraries should be initialized:

  ::odrxDynamicLinker()->loadModule(sNwDbModuleName, false);

Load an .nwd (.nwc) File

  1. If there is a folder containing textures for the .nwd (.nwc) file, use the OdNwHostAppServices::setTextureDirectoryPath() method to specify the folder path before loading the file:
    void OdNwHostAppServices::setTextureDirectoryPath(const OdString& path, bool rename_path);
  2. To load an .nwd (.nwc) file, create an OdNwDatabase object and read the file:
    //read file by path from szSource
    OdNwDatabasePtr pNwDb = svcs.readFile(szSource);
  3. An .nwd (.nwc) file database contains a scene graph for only one model. To make sure that the created OdNwDatabase object contains .nwd or .nwc data, use the OdNwDatabase::isComposite() method. This method returns true if the OdNwDatabase object is a composite and contains more than one model (.nwf file), or false otherwise (for .nwd or .nwc data). Check that the database contains only one model:
    if (pNwDb->isComposite())
        // .nwd (.nwc) data extraction routine

Now that the .nwd (.nwc) file is loaded into the database object, you can proceed to extracting the data.

Extract Geometry Data

Within the model hierarchy, each item (node) is represented by an OdNwModelItem object. The model hierarchy contains the root node and its children which can be layers, composite objects, or single geometry objects. The end nodes of the model graph are geometry objects.

To find the nodes that contain geometry data, get the root item in the model hierarchy and recursively iterate the child nodes while checking whether they contain geometry data:

  1. Get the root node of the model hierarchy using OdNwDatabase::getModelItemRootId():
  2. OdNwObjectId modelItemRootId = pNwDb->getModelItemRootId();
    if (!modelItemRootId.isNull())
      OdNwModelItemPtr pModelItemRoot = modelItemRootId.safeOpenObject();
  3. To get ancestors, descendants, children, and parents of a specific node, use the getAncestors(), getDescendants(), getChildren() and getParent() methods respectively. To check whether the node contains geometry data, the OdNwModelItem::hasGeometry() method is used. The following method from the OdSimpleLoader sample application recursively seeks the nodes that contain geometry data by the root node and writes them to an array:
OdResult getGeometryNodesByRoot(OdNwModelItemPtr pRoot, OdArray& aHasGeomNodes)
  OdNwObjectIdArray aRootChildren;
  OdResult res = pRoot->getChildren(aRootChildren);
  if (res!= eOk)
    return res;
  if (pRoot->hasGeometry())
  for (OdNwObjectIdArray::const_iterator itRootChildren = aRootChildren.begin(); itRootChildren != aRootChildren.end(); ++itRootChildren)
    if (!itRootChildren->isNull())
      OdNwModelItemPtr pRootChildren = itRootChildren->safeOpenObject();
      res = getGeometryNodesByRoot(pRootChildren, aHasGeomNodes);
      if (res != eOk)
        return res;
  return eOk;

Once you have an array of geometry nodes, you can get geometry components for each of them. A geometry component is represented by an OdNwComponent object that contains geometry fragments (with geometry objects themselves) and material and transparency applied to the component.

for (OdArray<OdNwModelItemPtr>::iterator itHasGeomNode = aHasGeometryNodes.begin(); itHasGeomNode != aHasGeometryNodes.end(); ++itHasGeomNode)
  OdNwObjectId compId = (*itHasGeomNode)->getGeometryComponentId();
  OdNwComponentPtr pComp = compId.safeOpenObject();
  // Actions with geometry components, for example, getting materials, geometry fragments etc.
Extract Geometry Fragments of Geometry Components

For each OdNwComponent object, you can get an array of geometry fragments:

OdNwObjectIdArray aCompFragIds;

For each fragment, you can get the transformation matrix, geometry type, and geometry object itself:

OdGeMatrix3d trMat = pFrag->getTransformation();
OdNwGeometryPtr pGeometry = pFrag->getGeometryId().safeOpenObject();
if (pFrag->isLineSet())
  OdNwGeometryLineSetPtr pGeomertyLineSet = pGeometry;
else if (pFrag->isEllipse())
  OdNwGeometryEllipticalShapePtr pGeometryEllipticalShape = pGeometry;
else if (pFrag->isMesh())
  OdNwGeometryMeshPtr pGeometrMesh = pGeometry;
else if (pFrag->isPointSet())
  OdNwGeometryPointSetPtr pGeometryPointSet = pGeometry;
else if (pFrag->isText())
  OdNwGeometryTextPtr pGeometrText = pGeometry;
else if (pFrag->isTube())
  OdNwGeometryTubePtr pGeometrTube = pGeometry;

Each of the geometric objects has its own set of attributes that can be obtained using a corresponding "get" method. The following code illustrates getting attributes of line set, ellipse, mesh, point set, text, and tube objects:

// Line set
  OdNwVerticesDataPtr pVerticesData = pGeomertyLineSet->getVerticesData();
  OdGePoint3dArray aVertexes = pVerticesData->getVertices();
  OdUInt32Array aColors = pVerticesData->getColors();
  OdUInt16Array aVertexPerLine = pGeomertyLineSet->getVerticesCountPerLine();
  // Ellipse
  OdGePoint3d origin = pGeometryEllipticalShape->getOrigin();
  OdGeVector3d xVector = pGeometryEllipticalShape->getXVector();
  double Radius = pGeometryEllipticalShape->getRadius();
  OdGeVector3d yVector = pGeometryEllipticalShape->getYVector();
  // Mesh
  OdNwVerticesDataPtr pVerticesData = pGeometrMesh->getVerticesData();
  OdGePoint3dArray aVertexes = pVerticesData->getVertices();
  OdUInt32Array aColors = pVerticesData->getColors();
  OdGeVector3dArray aNormales = pVerticesData->getNormals();
  OdGePoint2dArray aUvParams = pVerticesData->getTexCoords();
  OdUInt16Array aVertexPerLine = pGeometrMesh->getVerticesPerFace();
  OdUInt16Array aIndexes = pGeometrMesh->getIndices();
  OdArray aFaces = pGeometrMesh->getTriangles();
  // Point set
  OdNwVerticesDataPtr pVerticesData = pGeometryPointSet->getVerticesData();
  OdGePoint3dArray aVertexes = pVerticesData->getVertices();
  OdUInt32Array aColors = pVerticesData->getColors();
  // Text
  OdGePoint3d lefPoint = pGeometrText->getLeftPoint();
  OdGePoint3d rightPoint = pGeometrText->getRightPoint();
  OdGeVector3d normal = pGeometrText->getNormal();
  OdString text = pGeometrText->getText();
  float rotate = pGeometrText->getRotation();
  OdNwTextFontInfo textFont = pGeometrText->getFont();
  OdString fontName = textFont.getName();
  bool isFontItalic = textFont.isItalic();
  float scale = pGeometrText->getScale();
  // Tube
  double radius = pGeometrTube->getRadius();
  OdGePoint3d topCenter = pGeometrTube->getTopCenter();
  OdGePoint3d bottomCenter = pGeometrTube->getBottomCenter();
  OdGeVector3d xAxis = pGeometrTube->getXAxis();
  OdGeVector3d yAxis = pGeometrTube->getYAxis();
Extract Materials of Geometry Components

To extract materials of geometry components, use the OdNwComponent::getMaterialId() method. For a material, you can get its attributes such as ambient, diffuse, specular, emissive colors, and values of transparency and shininess:

OdNwObjectId materilId = pComp->getMaterialId();
OdNwMaterialPtr pMaterial = materilId.safeOpenObject();
OdNwColor diffuse = pMaterial->getDiffuse();
OdNwColor ambient = pMaterial->getAmbient();
OdNwColor specular = pMaterial->getSpecular();
OdNwColor emissive = pMaterial->getEmissive();
float transparency = pMaterial->getTransparency();
float shininess = pMaterial->getShininess();

If a texture is applied to the material, you can get additional information about the texture:

  • Tint and transmitted colors
  • Diffusion, specularity, and reflection intensity values
  • Bitmap files for embedded textures
  • Data from texture mappers: bump, diffusion, opacity, and pattern

The following code illustrates getting data of a texture applied to a material:

OdNwTexturePtr pTextureMaterial = pMaterial;
  double diffuseIntensity = pTextureMaterial->getDiffuseIntensity();
  double specularIntensity = pTextureMaterial->getSpecularIntensity();
  double reflectIntensity = pTextureMaterial->getReflectIntensity();
  if (pTextureMaterial->isHaveBitmap())
    OdGiRasterImagePtr pRasterImg = pTextureMaterial->getBitmap();

The following code illustrates getting texture mappers data:

OdString patternPath = pTextureMaterial->getStringValue(NwTextureValueType::pattern_path);
  double patternScaleXValue = pTextureMaterial->getDoubleValue(NwTextureValueType::pattern_scale_x_value);
  NwModelUnits::Enum patternScaleXUnit = pTextureMaterial->getUnitValue(NwTextureValueType::pattern_scale_x_unit);
  double patternScaleYValue = pTextureMaterial->getDoubleValue(NwTextureValueType::pattern_scale_y_value);
  NwModelUnits::Enum patternScaleYUnit = pTextureMaterial->getUnitValue(NwTextureValueType::pattern_scale_y_unit);
  double patternOffsetXValue = pTextureMaterial->getDoubleValue(NwTextureValueType::pattern_offset_x_value);
  NwModelUnits::Enum patternOffsetXUnit = pTextureMaterial->getUnitValue(NwTextureValueType::pattern_offset_x_unit);
  double patternOffsetYValue = pTextureMaterial->getDoubleValue(NwTextureValueType::pattern_offset_y_value);
  NwModelUnits::Enum patternOffsetYUnit = pTextureMaterial->getUnitValue(NwTextureValueType::pattern_offset_y_unit);

Extract Scene Parameters

In addition to geometry data, you can also extract the parameters of a scene from the .nwd (.nwc) file database.

The following code demonstrates getting the scene background color from the database:

OdNwObjectId bgId = pNwDb->getBackgroundElementId();
OdNwBackgroundElementPtr pBackGround = bgId.safeOpenObject();
switch (pBackGround->getBackgroundType())
  case NwBackgroundType::PLAIN:
    OdNwColor cPlain;
  case NwBackgroundType::GRADUATED:
    OdNwColor cTop, cBottom;
    pBackGround->getGraduatedColor(cTop, cBottom);
  case NwBackgroundType::HORIZON:
    OdNwColor cSkyColor, cHorizonSkyColor, cHorionGround, cGround;
    pBackGround->getHorizonColor(cSkyColor, cHorizonSkyColor, cHorionGround, cGround);

The following code demonstrates getting the settings of the current viewpoint: the world up vector, camera parameters (position, rotation, focal length, angular speed, linear speed, etc.), viewpoint name, projection type, lighting type, and others:

OdNwObjectId curViewId = pNwDb->getCurrentViewId();
OdNwViewpointPtr pCurView = curViewId.safeOpenObject();
OdGePoint3d position = pCurView->getPosition();
OdGeQuaternion rotate = pCurView->getRotation();
if (pCurView->hasFocalDistance())
  double focalDistance = pCurView->getFocalDistance();
if (pCurView->hasWorldUpVector())
  OdGeVector3d worldUpVector = pCurView->getWorldUpVector();
NwViewType::Enum projection = pCurView->getProjection();
if (pCurView->hasAngularSpeed())
  double angularSpeed = pCurView->getAngularSpeed();
double nearDistance = pCurView->getNearDistance();
double farDistance = pCurView->getFarDistance();
if (pCurView->hasLighting())
  NwLightType::Enum lighting = pCurView->getLighting();
if (pCurView->hasLinearSpeed())
  double linearSpeed = pCurView->getLinearSpeed();
if (pCurView->hasRenderStyle())
  NwModeType::Enum renderStyle = pCurView->getRenderStyle();
double heightField = pCurView->getHeightField();
double horizontalScale = pCurView->getHorizontalScale();
OdString avatar = pCurView->getAvatar();
NwCameraMode::Enum viewerCamerMode = pCurView->getViewerCameraMode();

Uninitialize ODA BimNv SDK Libraries

When the extraction routine is done, uninitialize the BimNv SDK libraries: