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。