【第五回】OCCT渲染数据的结构定义解析

        OCCT渲染数据的结构CALL_DEF_PARRAY定义如下所示(在文件<InterfaceGraphic_PrimitiveArray.hxx>中):

// Copyright (c) 1991-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and / or modify it
// under the terms of the GNU Lesser General Public version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifndef _InterfaceGraphic_PrimitiveArray_header_file_
#define _InterfaceGraphic_PrimitiveArray_header_file_

/*
FILE: InterfaceGraphic_PrimitiveArray.hxx

Created 16/06/2000 : ATS,SPK : G005

This file contains definitios of internal structures for 
PARRAY and DARRAY primitives, used in OpenGl package for presentation

*/

#include <InterfaceGraphic_telem.hxx>

#define MVERTICE 1
#define MVNORMAL 2
#define MVCOLOR 4
#define MVTEXEL 8

typedef enum {
  TelUnknownArrayType,
  TelPointsArrayType,
  TelPolylinesArrayType,
  TelSegmentsArrayType,
  TelPolygonsArrayType,
  TelTrianglesArrayType,
  TelQuadranglesArrayType,
  TelTriangleStripsArrayType,
  TelQuadrangleStripsArrayType,
  TelTriangleFansArrayType
} TelPrimitivesArrayType;

typedef struct {
  TelPrimitivesArrayType type;                    /* Array type */
  Tint                   format;                  /* Array datas format */
  Tint                   num_vertexs;             /* Number of vertexs */
  Tint                   num_bounds;              /* Number of bounds */
  Tint                   num_edges;               /* Number of edges */
  Tint                  *bounds;                  /* Bounds array */
  Tint                  *edges;                   /* Edges array vertex index */
  tel_colour             fcolours;                /* Facet colour values */
  tel_point              vertices;                /* Vertices */
  Tint                  *vcolours;                /* Vertex colour values */
  tel_point              vnormals;                /* Vertex normals */
  tel_texture_coord      vtexels;                 /* Texture Coordinates */
  Tchar                 *edge_vis;                /* Edge visibility flag*/
  Tchar                 *keys;                    /* Vertex keys*/
} CALL_DEF_PARRAY;

#endif /* _InterfaceGraphic_PrimitiveArray_header_file_ */
在OCCT中,不管是在哪种渲染模式(实体、线框和消隐)下,它们的渲染数据都是组装在这个结构体中,所以这个结构体是渲染这三种模式下所需数据的载体。那么下面就来分析下这个结构体里面的指针变量:vertices, bounds, edges, edge_vis。(关于这个结构体里面用到的数据类型tel_point等可参见: InterfaceGraphic_telem.hxx)

        vertices中存储的是顶点数据,num_vertexs存储顶点的个数;edges中存储的是顶点索引数据,num_edges存储顶点索引的个数;edges_vis中存储的是可见边,数组的大小为num_edges;bounds[i]存储的是进行一次性绘制时所使用的顶点个数,num_bounds存储绘制的次数

        OCCT使用的渲染引擎是OpenGL,渲染数据都是存储在缓冲区中的。其中指针vertices中的数据存储在顶点缓冲区VBO中,edges中的数据存储在顶点索引缓冲区VAO中。通过分析OpenGL_PrimitiveArray::DrawArray方法:

// =======================================================================
// function : DrawArray
// purpose  :
// =======================================================================
void OpenGl_PrimitiveArray::DrawArray (Tint theLightingModel,
                                       const Aspect_InteriorStyle theInteriorStyle,
                                       Tint theEdgeFlag,
                                       const TEL_COLOUR* theInteriorColour,
                                       const TEL_COLOUR* theLineColour,
                                       const TEL_COLOUR* theEdgeColour,
                                       const OPENGL_SURF_PROP* theFaceProp,
                                       const Handle(OpenGl_Workspace)& theWorkspace) const
{
  const Handle(OpenGl_Context)& aGlContext = theWorkspace->GetGlContext();

  Tint i,n;
  Tint transp = 0;
  // Following pointers have been provided for performance improvement
  tel_colour pfc = myPArray->fcolours;
  Tint* pvc = myPArray->vcolours;
  if (pvc != NULL)
  {
    for (i = 0; i < myPArray->num_vertexs; ++i)
    {
      transp = int(theFaceProp->trans * 255.0f);
    #if defined (sparc) || defined (__sparc__) || defined (__sparc)
      pvc[i] = (pvc[i] & 0xffffff00);
      pvc[i] += transp;
    #else
      pvc[i] = (pvc[i] & 0x00ffffff);
      pvc[i] += transp << 24;
    #endif
    }
  }

  switch (myPArray->type)
  {
    case TelPointsArrayType:
    case TelPolylinesArrayType:
    case TelSegmentsArrayType:
      glColor3fv (theLineColour->rgb);
      break;
    case TelPolygonsArrayType:
    case TelTrianglesArrayType:
    case TelQuadranglesArrayType:
    case TelTriangleStripsArrayType:
    case TelQuadrangleStripsArrayType:
    case TelTriangleFansArrayType:
      glColor3fv (theInteriorColour->rgb);
      break;
    case TelUnknownArrayType:
      break;
  }

  // Temporarily disable environment mapping
  if (myDrawMode <= GL_LINE_STRIP)
  {
    glPushAttrib (GL_ENABLE_BIT);
    glDisable (GL_TEXTURE_1D);
    glDisable (GL_TEXTURE_2D);
  }

  if ((myDrawMode >  GL_LINE_STRIP && theInteriorStyle != Aspect_IS_EMPTY) ||
      (myDrawMode <= GL_LINE_STRIP))
  {
    if (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT)
    {
      pfc = NULL;
      pvc = NULL;
    }

    if (theInteriorStyle == Aspect_IS_HIDDENLINE)
    {
      theEdgeFlag = 1;
      pfc = NULL;
      pvc = NULL;
    }

    // Sometimes the GL_LIGHTING mode is activated here
    // without glEnable(GL_LIGHTING) call for an unknown reason, so it is necessary
    // to call glEnable(GL_LIGHTING) to synchronize Light On/Off mechanism*
    if (theLightingModel == 0 || myDrawMode <= GL_LINE_STRIP)
      glDisable (GL_LIGHTING);
    else
      glEnable (GL_LIGHTING);

    if (!toDrawVbo())
    {
      if (myPArray->vertices != NULL)
      {
        glVertexPointer (3, GL_FLOAT, 0, myPArray->vertices); // array of vertices
        glEnableClientState (GL_VERTEX_ARRAY);
      }
      if (myPArray->vnormals != NULL)
      {
        glNormalPointer (GL_FLOAT, 0, myPArray->vnormals); // array of normals
        glEnableClientState (GL_NORMAL_ARRAY);
      }
      if (myPArray->vtexels != NULL)
      {
        glTexCoordPointer (2, GL_FLOAT, 0, myPArray->vtexels); // array of texture coordinates
        glEnableClientState (GL_TEXTURE_COORD_ARRAY);
      }

      if ((pvc != NULL) && (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT) == 0)
      {
        glColorPointer (4, GL_UNSIGNED_BYTE, 0, pvc);  // array of colors
        glEnableClientState (GL_COLOR_ARRAY);
        glColorMaterial (GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
        glEnable (GL_COLOR_MATERIAL);
      }
    }
    else
    {
      // Bindings concrete pointer in accordance with VBO buffer
      myVbos[VBOVertices]->BindFixed (aGlContext, GL_VERTEX_ARRAY);
      if (!myVbos[VBOVnormals].IsNull())
      {
        myVbos[VBOVnormals]->BindFixed (aGlContext, GL_NORMAL_ARRAY);
      }
      if (!myVbos[VBOVtexels].IsNull() && (theWorkspace->NamedStatus & OPENGL_NS_FORBIDSETTEX) == 0)
      {
        myVbos[VBOVtexels]->BindFixed (aGlContext, GL_TEXTURE_COORD_ARRAY);
      }
      if (!myVbos[VBOVcolours].IsNull() && (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT) == 0)
      {
        myVbos[VBOVcolours]->BindFixed (aGlContext, GL_COLOR_ARRAY);
        glColorMaterial (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
        glEnable (GL_COLOR_MATERIAL);
      }
    }

    /// OCC22236 NOTE: draw for all situations:
    /// 1) draw elements from myPArray->bufferVBO[VBOEdges] indicies array
    /// 2) draw elements from vertice array, when bounds defines count of primitive's verts.
    /// 3) draw primitive by vertexes if no edges and bounds array is specified
    if (toDrawVbo())
    {
      if (!myVbos[VBOEdges].IsNull())
      {
        myVbos[VBOEdges]->Bind (aGlContext);
        if (myPArray->num_bounds > 0)
        {
          // draw primitives by vertex count with the indicies
          Tint* anOffset = NULL;
          for (i = 0; i < myPArray->num_bounds; ++i)
          {
            if (pfc != NULL) glColor3fv (pfc[i].rgb);
            glDrawElements (myDrawMode, myPArray->bounds[i], myVbos[VBOEdges]->GetDataType(), anOffset);
            anOffset += myPArray->bounds[i];
          }
        }
        else
        {
          // draw one (or sequential) primitive by the indicies
          glDrawElements (myDrawMode, myPArray->num_edges, myVbos[VBOEdges]->GetDataType(), NULL);
        }
        myVbos[VBOEdges]->Unbind (aGlContext);
      }
      else if (myPArray->num_bounds > 0)
      {
        for (i = n = 0; i < myPArray->num_bounds; ++i)
        {
          if (pfc != NULL) glColor3fv (pfc[i].rgb);
          glDrawArrays (myDrawMode, n, myPArray->bounds[i]);
          n += myPArray->bounds[i];
        }
      }
      else
      {
        if (myDrawMode == GL_POINTS)
        {
          DrawMarkers (theWorkspace);
        }
        else
        {
          glDrawArrays (myDrawMode, 0, myVbos[VBOVertices]->GetElemsNb());
        }
      }

      // bind with 0
      myVbos[VBOVertices]->UnbindFixed (aGlContext, GL_VERTEX_ARRAY);
      if (!myVbos[VBOVnormals].IsNull())
      {
        myVbos[VBOVnormals]->UnbindFixed (aGlContext, GL_NORMAL_ARRAY);
      }
      if (!myVbos[VBOVtexels].IsNull() && (theWorkspace->NamedStatus & OPENGL_NS_FORBIDSETTEX) == 0)
      {
        myVbos[VBOVtexels]->UnbindFixed (aGlContext, GL_TEXTURE_COORD_ARRAY);
      }
      if (!myVbos[VBOVcolours].IsNull())
      {
        myVbos[VBOVcolours]->UnbindFixed (aGlContext, GL_COLOR_ARRAY);
        glDisable (GL_COLOR_MATERIAL);
        theWorkspace->NamedStatus |= OPENGL_NS_RESMAT; // Reset material
      }
    }
    else
    {
      if (myPArray->num_bounds > 0)
      {
        if (myPArray->num_edges > 0)
        {
          for (i = n = 0; i < myPArray->num_bounds; ++i)
          {
            if (pfc != NULL) glColor3fv (pfc[i].rgb);
            glDrawElements (myDrawMode, myPArray->bounds[i], GL_UNSIGNED_INT, (GLenum* )&myPArray->edges[n]);
            n += myPArray->bounds[i];
          }
        }
        else
        {
          for (i = n = 0; i < myPArray->num_bounds; ++i)
          {
            if (pfc != NULL) glColor3fv (pfc[i].rgb);
            glDrawArrays (myDrawMode, n, myPArray->bounds[i]);
            n += myPArray->bounds[i];
          }
        }
      }
      else if (myPArray->num_edges > 0)
      {
        glDrawElements (myDrawMode, myPArray->num_edges, GL_UNSIGNED_INT, (GLenum* )myPArray->edges);
      }
      else
      {
        if (myDrawMode == GL_POINTS)
        {
          DrawMarkers (theWorkspace);
        }
        else
        {
          glDrawArrays (myDrawMode, 0, myPArray->num_vertexs);
        }
      }

      if (pvc != NULL)
      {
        glDisable (GL_COLOR_MATERIAL);
        theWorkspace->NamedStatus |= OPENGL_NS_RESMAT; // Reset material
      }

      glDisableClientState (GL_VERTEX_ARRAY);
      if (myPArray->vcolours != NULL)
        glDisableClientState (GL_COLOR_ARRAY);
      if (myPArray->vnormals != NULL)
        glDisableClientState (GL_NORMAL_ARRAY);
      if (myPArray->vtexels != NULL)
        glDisableClientState (GL_TEXTURE_COORD_ARRAY);
    }
  }

  // On some NVIDIA graphic cards, using glEdgeFlagPointer() in
  // combination with VBO (edge flag data put into a VBO buffer)
  // leads to a crash in a driver. Therefore, edge flags are simply
  // igonored when VBOs are enabled, so all the edges are drawn if
  // edge visibility is turned on. In order to draw edges selectively,
  // either disable VBO or turn off edge visibilty in the current
  // primitive array and create a separate primitive array (segments)
  // and put edges to be drawn into it.
  if (theEdgeFlag && myDrawMode > GL_LINE_STRIP)
  {
    DrawEdges (theEdgeColour, theWorkspace);
  }

  if (myDrawMode <= GL_LINE_STRIP)
    glPopAttrib();
}
方法toDrawVBO判断顶点数组缓冲区是否为非空,若为非空,返回true,否则返回false。当返回true时则判断顶点索引数组缓冲区是否为空,若为空,则通过glDrawArray方法来直接使用顶点数组缓冲区中的数据进行绘制,其中bounds[i]作为count参数传入;若为非空,则通过glDrawElements方法来使用顶点索引数组缓冲区中的数据进行绘制。那么什么时候用到指针edge_vis呢?用到指针edge_vis的方法是DrawEdges,这个方法是绘制显示可见边,去掉不可见边。可看到上面这个方法的倒数第二个判断控制代码,可分析出当体元的内部类型指定为Aspect_IS_HIDDENLINE,并且当前体元类型为GL_TRIANGLES及以上时,根据TopoDS_Shape网格细分的特征可知,是在显示面以上的模型时,在这两个条件同时成立的条件下会使用edge_vis进行可见边的绘制。
        OCCT在绘制面或者体时,在三种渲染模式下会分别为其提供一套CALL_DEF_PARRAY数据。在实体渲染模式下,vertices为对面或体进行网格细分后所有三角面片的顶点数据,edges为这些顶点的索引数据,而bounds为NULL;在线框模式下,vertices为对面或体提取出来的轮廓线的顶点数据,若轮廓线有n根,则num_bounds为n,bounds[i]存储的是第i根轮廓线用到的vertices中的顶点的个数,取vertices的起始位置为bounds[0]+...+bounds[i-1],edges为NULL;在消隐模式下,vertices为对面或体进行消隐处理后的轮廓线的顶点数据,若轮廓线有n跟,则num_bounds为n,bounds[i]存储的是第i根轮廓线用到的vertices中的顶点的个数,取vertices的起始位置为bounds[0]+...+bounds[i-1],edges为NULL。

        OCCT在绘制线时,没有为线提供在消隐模式下的渲染数据,也就是OCCT并不对线做消隐处理。在实体渲染和线框渲染模式下,vertices为线的顶点数据,num_bounds为1,bounds[0]存储的是vertices中的顶点的个数,edges为NULL。

        那么现在可以总结一下,bounds是用作显示轮廓线(边界信息)和线消隐信息的,edges是用作显示面和体信息的。在线框和消隐模式下,edges为NULL;在实体渲染模式下,绘制面或体时,bounds为NULL;在实体渲染模式下,绘制线时,edges仍为NULL,bounds不为NULL。

        如果想通过调试代码来验证的话可通过调试OCCT中提供的Modeling例子来解析这几个变量。Modeling例子的界面框架如下图所示,在图中已经标出如何切换这三种渲染模式。默认情况下,渲染模式为实体模式;消隐开关打开的时候渲染模式为消隐模式;在关闭消隐的情况下,需要选中模型后才能对线框模式和实体模式进行切换。调试时将断点设置在OpenGL_PrimitiveArray::BuildVBO方法上,在此处查看渲染数据CALL_DEF_PARRAY。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值