Commit | Line | Data |
---|---|---|
b311480e | 1 | // Created on: 2011-07-13 |
2 | // Created by: Sergey ZERCHANINOV | |
de75ed09 | 3 | // Copyright (c) 2011-2013 OPEN CASCADE SAS |
b311480e | 4 | // |
973c2be1 | 5 | // This file is part of Open CASCADE Technology software library. |
b311480e | 6 | // |
d5f74e42 | 7 | // This library is free software; you can redistribute it and/or modify it under |
8 | // the terms of the GNU Lesser General Public License version 2.1 as published | |
973c2be1 | 9 | // by the Free Software Foundation, with special exception defined in the file |
10 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT | |
11 | // distribution for complete text of the license and disclaimer of any warranty. | |
b311480e | 12 | // |
973c2be1 | 13 | // Alternatively, this file may be used under the terms of Open CASCADE |
14 | // commercial license or contractual agreement. | |
b311480e | 15 | |
2166f0fa | 16 | #include <OpenGl_AspectFace.hxx> |
30f0ad28 | 17 | #include <OpenGl_Context.hxx> |
2166f0fa | 18 | #include <OpenGl_GraphicDriver.hxx> |
30f0ad28 | 19 | #include <OpenGl_IndexBuffer.hxx> |
a577aaab | 20 | #include <OpenGl_PointSprite.hxx> |
30f0ad28 | 21 | #include <OpenGl_PrimitiveArray.hxx> |
22 | #include <OpenGl_ShaderManager.hxx> | |
23 | #include <OpenGl_ShaderProgram.hxx> | |
2166f0fa | 24 | #include <OpenGl_Structure.hxx> |
7d3e64ef | 25 | #include <OpenGl_VertexBufferCompat.hxx> |
bf75be98 | 26 | #include <OpenGl_Workspace.hxx> |
6c6aadb1 | 27 | #include <Graphic3d_TextureParams.hxx> |
2166f0fa | 28 | |
30f0ad28 | 29 | namespace |
30 | { | |
871fa103 | 31 | //! Convert index data type from size |
32 | inline GLenum toGlIndexType (const Standard_Integer theStride) | |
33 | { | |
34 | switch (theStride) | |
35 | { | |
36 | case 2: return GL_UNSIGNED_SHORT; | |
37 | case 4: return GL_UNSIGNED_INT; | |
38 | default: return GL_NONE; | |
39 | } | |
40 | } | |
41 | ||
42 | //! Convert data type to GL info | |
43 | inline GLenum toGlDataType (const Graphic3d_TypeOfData theType, | |
44 | GLint& theNbComp) | |
45 | { | |
46 | switch (theType) | |
47 | { | |
48 | case Graphic3d_TOD_USHORT: | |
49 | theNbComp = 1; | |
50 | return GL_UNSIGNED_SHORT; | |
51 | case Graphic3d_TOD_UINT: | |
52 | theNbComp = 1; | |
53 | return GL_UNSIGNED_INT; | |
54 | case Graphic3d_TOD_VEC2: | |
55 | theNbComp = 2; | |
56 | return GL_FLOAT; | |
57 | case Graphic3d_TOD_VEC3: | |
58 | theNbComp = 3; | |
59 | return GL_FLOAT; | |
60 | case Graphic3d_TOD_VEC4: | |
61 | theNbComp = 4; | |
62 | return GL_FLOAT; | |
63 | case Graphic3d_TOD_VEC4UB: | |
64 | theNbComp = 4; | |
65 | return GL_UNSIGNED_BYTE; | |
66 | } | |
67 | theNbComp = 0; | |
68 | return GL_NONE; | |
69 | } | |
70 | ||
30f0ad28 | 71 | } |
72 | ||
871fa103 | 73 | //! Auxiliary template for VBO with interleaved attributes. |
7d3e64ef | 74 | template<class TheBaseClass, int NbAttributes> |
75 | class OpenGl_VertexBufferT : public TheBaseClass | |
7fd59977 | 76 | { |
871fa103 | 77 | |
78 | public: | |
79 | ||
80 | //! Create uninitialized VBO. | |
81 | OpenGl_VertexBufferT (const Graphic3d_Attribute* theAttribs, | |
82 | const Standard_Integer theStride) | |
83 | : Stride (theStride) | |
84 | { | |
85 | memcpy (Attribs, theAttribs, sizeof(Graphic3d_Attribute) * NbAttributes); | |
86 | } | |
87 | ||
88 | //! Create uninitialized VBO. | |
89 | OpenGl_VertexBufferT (const Graphic3d_Buffer& theAttribs) | |
90 | : Stride (theAttribs.Stride) | |
91 | { | |
92 | memcpy (Attribs, theAttribs.AttributesArray(), sizeof(Graphic3d_Attribute) * NbAttributes); | |
93 | } | |
94 | ||
95 | virtual bool HasColorAttribute() const | |
96 | { | |
97 | for (Standard_Integer anAttribIter = 0; anAttribIter < NbAttributes; ++anAttribIter) | |
98 | { | |
99 | const Graphic3d_Attribute& anAttrib = Attribs[anAttribIter]; | |
100 | if (anAttrib.Id == Graphic3d_TOA_COLOR) | |
101 | { | |
102 | return true; | |
103 | } | |
104 | } | |
105 | return false; | |
106 | } | |
107 | ||
7d3e64ef | 108 | virtual bool HasNormalAttribute() const |
109 | { | |
110 | for (Standard_Integer anAttribIter = 0; anAttribIter < NbAttributes; ++anAttribIter) | |
111 | { | |
112 | const Graphic3d_Attribute& anAttrib = Attribs[anAttribIter]; | |
113 | if (anAttrib.Id == Graphic3d_TOA_NORM) | |
114 | { | |
115 | return true; | |
116 | } | |
117 | } | |
118 | return false; | |
119 | } | |
120 | ||
121 | virtual void BindPositionAttribute (const Handle(OpenGl_Context)& theGlCtx) const | |
871fa103 | 122 | { |
7d3e64ef | 123 | if (!TheBaseClass::IsValid()) |
871fa103 | 124 | { |
125 | return; | |
126 | } | |
127 | ||
7d3e64ef | 128 | TheBaseClass::Bind (theGlCtx); |
871fa103 | 129 | GLint aNbComp; |
7d3e64ef | 130 | const GLubyte* anOffset = TheBaseClass::myOffset; |
871fa103 | 131 | for (Standard_Integer anAttribIter = 0; anAttribIter < NbAttributes; ++anAttribIter) |
132 | { | |
133 | const Graphic3d_Attribute& anAttrib = Attribs[anAttribIter]; | |
134 | const GLenum aDataType = toGlDataType (anAttrib.DataType, aNbComp); | |
135 | if (aDataType == GL_NONE) | |
136 | { | |
137 | continue; | |
138 | } | |
139 | else if (anAttrib.Id == Graphic3d_TOA_POS) | |
140 | { | |
7d3e64ef | 141 | TheBaseClass::bindAttribute (theGlCtx, Graphic3d_TOA_POS, aNbComp, aDataType, Stride, anOffset); |
871fa103 | 142 | break; |
143 | } | |
144 | ||
145 | anOffset += Graphic3d_Attribute::Stride (anAttrib.DataType); | |
146 | } | |
147 | } | |
148 | ||
7d3e64ef | 149 | virtual void BindAllAttributes (const Handle(OpenGl_Context)& theGlCtx) const |
871fa103 | 150 | { |
7d3e64ef | 151 | if (!TheBaseClass::IsValid()) |
871fa103 | 152 | { |
153 | return; | |
154 | } | |
155 | ||
7d3e64ef | 156 | TheBaseClass::Bind (theGlCtx); |
871fa103 | 157 | GLint aNbComp; |
7d3e64ef | 158 | const GLubyte* anOffset = TheBaseClass::myOffset; |
871fa103 | 159 | for (Standard_Integer anAttribIter = 0; anAttribIter < NbAttributes; ++anAttribIter) |
160 | { | |
161 | const Graphic3d_Attribute& anAttrib = Attribs[anAttribIter]; | |
162 | const GLenum aDataType = toGlDataType (anAttrib.DataType, aNbComp); | |
163 | if (aDataType == GL_NONE) | |
164 | { | |
165 | continue; | |
166 | } | |
167 | ||
7d3e64ef | 168 | TheBaseClass::bindAttribute (theGlCtx, anAttrib.Id, aNbComp, aDataType, Stride, anOffset); |
871fa103 | 169 | anOffset += Graphic3d_Attribute::Stride (anAttrib.DataType); |
170 | } | |
171 | } | |
172 | ||
7d3e64ef | 173 | virtual void UnbindAllAttributes (const Handle(OpenGl_Context)& theGlCtx) const |
871fa103 | 174 | { |
7d3e64ef | 175 | if (!TheBaseClass::IsValid()) |
871fa103 | 176 | { |
177 | return; | |
178 | } | |
7d3e64ef | 179 | TheBaseClass::Unbind (theGlCtx); |
871fa103 | 180 | |
181 | for (Standard_Integer anAttribIter = 0; anAttribIter < NbAttributes; ++anAttribIter) | |
182 | { | |
183 | const Graphic3d_Attribute& anAttrib = Attribs[anAttribIter]; | |
7d3e64ef | 184 | TheBaseClass::unbindAttribute (theGlCtx, anAttrib.Id); |
871fa103 | 185 | } |
186 | } | |
187 | ||
188 | public: | |
189 | ||
190 | Graphic3d_Attribute Attribs[NbAttributes]; | |
191 | Standard_Integer Stride; | |
192 | ||
193 | }; | |
7fd59977 | 194 | |
2166f0fa SK |
195 | // ======================================================================= |
196 | // function : clearMemoryGL | |
197 | // purpose : | |
198 | // ======================================================================= | |
5e27df78 | 199 | void OpenGl_PrimitiveArray::clearMemoryGL (const Handle(OpenGl_Context)& theGlCtx) const |
7fd59977 | 200 | { |
871fa103 | 201 | if (!myVboIndices.IsNull()) |
2166f0fa | 202 | { |
871fa103 | 203 | myVboIndices->Release (theGlCtx.operator->()); |
204 | myVboIndices.Nullify(); | |
205 | } | |
206 | if (!myVboAttribs.IsNull()) | |
207 | { | |
208 | myVboAttribs->Release (theGlCtx.operator->()); | |
209 | myVboAttribs.Nullify(); | |
2166f0fa | 210 | } |
7fd59977 | 211 | } |
212 | ||
2166f0fa | 213 | // ======================================================================= |
7d3e64ef | 214 | // function : initNormalVbo |
2166f0fa SK |
215 | // purpose : |
216 | // ======================================================================= | |
7d3e64ef | 217 | Standard_Boolean OpenGl_PrimitiveArray::initNormalVbo (const Handle(OpenGl_Context)& theCtx) const |
7fd59977 | 218 | { |
7d3e64ef | 219 | switch (myAttribs->NbAttributes) |
2166f0fa | 220 | { |
7d3e64ef | 221 | case 1: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 1> (*myAttribs); break; |
222 | case 2: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 2> (*myAttribs); break; | |
223 | case 3: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 3> (*myAttribs); break; | |
224 | case 4: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 4> (*myAttribs); break; | |
225 | case 5: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 5> (*myAttribs); break; | |
226 | case 6: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 6> (*myAttribs); break; | |
227 | case 7: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 7> (*myAttribs); break; | |
228 | case 8: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 8> (*myAttribs); break; | |
229 | case 9: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 9> (*myAttribs); break; | |
230 | case 10: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 10>(*myAttribs); break; | |
231 | } | |
232 | ||
233 | if (!myVboAttribs->init (theCtx, 0, myAttribs->NbElements, myAttribs->Data(), GL_NONE, myAttribs->Stride)) | |
234 | { | |
235 | TCollection_ExtendedString aMsg; | |
236 | aMsg += "VBO creation for Primitive Array has failed for "; | |
237 | aMsg += myAttribs->NbElements; | |
238 | aMsg += " vertices. Out of memory?"; | |
239 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_PERFORMANCE_ARB, 0, GL_DEBUG_SEVERITY_LOW_ARB, aMsg); | |
240 | ||
241 | clearMemoryGL (theCtx); | |
5e27df78 | 242 | return Standard_False; |
2166f0fa | 243 | } |
7d3e64ef | 244 | else if (myIndices.IsNull()) |
245 | { | |
246 | return Standard_True; | |
247 | } | |
7fd59977 | 248 | |
7d3e64ef | 249 | myVboIndices = new OpenGl_IndexBuffer(); |
250 | bool isOk = false; | |
251 | switch (myIndices->Stride) | |
252 | { | |
253 | case 2: | |
254 | { | |
255 | isOk = myVboIndices->Init (theCtx, 1, myIndices->NbElements, reinterpret_cast<const GLushort*> (myIndices->Data())); | |
256 | break; | |
257 | } | |
258 | case 4: | |
259 | { | |
260 | isOk = myVboIndices->Init (theCtx, 1, myIndices->NbElements, reinterpret_cast<const GLuint*> (myIndices->Data())); | |
261 | break; | |
262 | } | |
263 | default: | |
264 | { | |
265 | clearMemoryGL (theCtx); | |
266 | return Standard_False; | |
267 | } | |
268 | } | |
269 | if (!isOk) | |
2166f0fa | 270 | { |
7d3e64ef | 271 | TCollection_ExtendedString aMsg; |
272 | aMsg += "VBO creation for Primitive Array has failed for "; | |
273 | aMsg += myIndices->NbElements; | |
274 | aMsg += " indices. Out of memory?"; | |
275 | theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_PERFORMANCE_ARB, 0, GL_DEBUG_SEVERITY_LOW_ARB, aMsg); | |
276 | clearMemoryGL (theCtx); | |
277 | return Standard_False; | |
7fd59977 | 278 | } |
7d3e64ef | 279 | return Standard_True; |
280 | } | |
871fa103 | 281 | |
7d3e64ef | 282 | // ======================================================================= |
283 | // function : buildVBO | |
284 | // purpose : | |
285 | // ======================================================================= | |
286 | Standard_Boolean OpenGl_PrimitiveArray::buildVBO (const Handle(OpenGl_Context)& theCtx, | |
287 | const Standard_Boolean theToKeepData) const | |
288 | { | |
289 | bool isNormalMode = theCtx->ToUseVbo(); | |
536d98e2 | 290 | clearMemoryGL (theCtx); |
7d3e64ef | 291 | if (myAttribs.IsNull() |
292 | || myAttribs->IsEmpty() | |
293 | || myAttribs->NbElements < 1 | |
294 | || myAttribs->NbAttributes < 1 | |
295 | || myAttribs->NbAttributes > 10) | |
2166f0fa | 296 | { |
7d3e64ef | 297 | // vertices should be always defined - others are optional |
871fa103 | 298 | return Standard_False; |
7fd59977 | 299 | } |
871fa103 | 300 | |
7d3e64ef | 301 | if (isNormalMode |
302 | && initNormalVbo (theCtx)) | |
303 | { | |
304 | if (!theCtx->caps->keepArrayData | |
305 | && !theToKeepData) | |
306 | { | |
307 | myIndices.Nullify(); | |
308 | myAttribs.Nullify(); | |
309 | } | |
310 | return Standard_True; | |
311 | } | |
312 | ||
313 | Handle(OpenGl_VertexBufferCompat) aVboAttribs; | |
314 | switch (myAttribs->NbAttributes) | |
315 | { | |
316 | case 1: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 1> (*myAttribs); break; | |
317 | case 2: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 2> (*myAttribs); break; | |
318 | case 3: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 3> (*myAttribs); break; | |
319 | case 4: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 4> (*myAttribs); break; | |
320 | case 5: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 5> (*myAttribs); break; | |
321 | case 6: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 6> (*myAttribs); break; | |
322 | case 7: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 7> (*myAttribs); break; | |
323 | case 8: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 8> (*myAttribs); break; | |
324 | case 9: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 9> (*myAttribs); break; | |
325 | case 10: aVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBufferCompat, 10>(*myAttribs); break; | |
326 | } | |
327 | aVboAttribs->initLink (myAttribs, 0, myAttribs->NbElements, GL_NONE); | |
871fa103 | 328 | if (!myIndices.IsNull()) |
2166f0fa | 329 | { |
7d3e64ef | 330 | Handle(OpenGl_VertexBufferCompat) aVboIndices = new OpenGl_VertexBufferCompat(); |
871fa103 | 331 | switch (myIndices->Stride) |
5e27df78 | 332 | { |
871fa103 | 333 | case 2: |
334 | { | |
7d3e64ef | 335 | aVboIndices->initLink (myIndices, 1, myIndices->NbElements, GL_UNSIGNED_SHORT); |
871fa103 | 336 | break; |
337 | } | |
338 | case 4: | |
339 | { | |
7d3e64ef | 340 | aVboIndices->initLink (myIndices, 1, myIndices->NbElements, GL_UNSIGNED_INT); |
871fa103 | 341 | break; |
342 | } | |
7d3e64ef | 343 | default: |
344 | { | |
345 | return Standard_False; | |
346 | } | |
5e27df78 | 347 | } |
7d3e64ef | 348 | myVboIndices = aVboIndices; |
2166f0fa | 349 | } |
7d3e64ef | 350 | myVboAttribs = aVboAttribs; |
351 | if (!theCtx->caps->keepArrayData | |
352 | && !theToKeepData) | |
e276548b | 353 | { |
7d3e64ef | 354 | // does not make sense for compatibility mode |
355 | //myIndices.Nullify(); | |
356 | //myAttribs.Nullify(); | |
e276548b | 357 | } |
7d3e64ef | 358 | |
2166f0fa | 359 | return Standard_True; |
7fd59977 | 360 | } |
361 | ||
2166f0fa | 362 | // ======================================================================= |
7d3e64ef | 363 | // function : drawArray |
2166f0fa SK |
364 | // purpose : |
365 | // ======================================================================= | |
7d3e64ef | 366 | void OpenGl_PrimitiveArray::drawArray (const Handle(OpenGl_Workspace)& theWorkspace, |
8625ef7e | 367 | const Graphic3d_Vec4* theFaceColors, |
368 | const Standard_Boolean theHasVertColor) const | |
7fd59977 | 369 | { |
871fa103 | 370 | const Handle(OpenGl_Context)& aGlContext = theWorkspace->GetGlContext(); |
871fa103 | 371 | const bool toHilight = (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT) != 0; |
8625ef7e | 372 | bool hasVColors = theHasVertColor && !toHilight; |
7d3e64ef | 373 | if (myVboAttribs.IsNull()) |
2166f0fa | 374 | { |
ca3c13d1 | 375 | #if !defined(GL_ES_VERSION_2_0) |
7d3e64ef | 376 | if (myDrawMode == GL_POINTS) |
377 | { | |
378 | // extreme compatibility mode - without sprites but with markers | |
379 | drawMarkers (theWorkspace); | |
380 | } | |
ca3c13d1 | 381 | #endif |
7d3e64ef | 382 | return; |
7fd59977 | 383 | } |
384 | ||
7d3e64ef | 385 | myVboAttribs->BindAllAttributes (aGlContext); |
8625ef7e | 386 | if (theHasVertColor && toHilight) |
2166f0fa | 387 | { |
8625ef7e | 388 | // disable per-vertex color |
389 | OpenGl_VertexBuffer::unbindAttribute (aGlContext, Graphic3d_TOA_COLOR); | |
7d3e64ef | 390 | } |
391 | if (!myVboIndices.IsNull()) | |
392 | { | |
393 | myVboIndices->Bind (aGlContext); | |
394 | GLubyte* anOffset = myVboIndices->GetDataOffset(); | |
395 | if (!myBounds.IsNull()) | |
2166f0fa | 396 | { |
7d3e64ef | 397 | // draw primitives by vertex count with the indices |
398 | const size_t aStride = myVboIndices->GetDataType() == GL_UNSIGNED_SHORT ? sizeof(unsigned short) : sizeof(unsigned int); | |
399 | for (Standard_Integer aGroupIter = 0; aGroupIter < myBounds->NbBounds; ++aGroupIter) | |
2166f0fa | 400 | { |
7d3e64ef | 401 | const GLint aNbElemsInGroup = myBounds->Bounds[aGroupIter]; |
8625ef7e | 402 | if (theFaceColors != NULL) aGlContext->SetColor4fv (theFaceColors[aGroupIter]); |
7d3e64ef | 403 | glDrawElements (myDrawMode, aNbElemsInGroup, myVboIndices->GetDataType(), anOffset); |
404 | anOffset += aStride * aNbElemsInGroup; | |
762aacae | 405 | } |
bf75be98 | 406 | } |
2166f0fa SK |
407 | else |
408 | { | |
7d3e64ef | 409 | // draw one (or sequential) primitive by the indices |
410 | glDrawElements (myDrawMode, myVboIndices->GetElemsNb(), myVboIndices->GetDataType(), anOffset); | |
5e27df78 | 411 | } |
7d3e64ef | 412 | myVboIndices->Unbind (aGlContext); |
7fd59977 | 413 | } |
7d3e64ef | 414 | else if (!myBounds.IsNull()) |
871fa103 | 415 | { |
7d3e64ef | 416 | GLint aFirstElem = 0; |
417 | for (Standard_Integer aGroupIter = 0; aGroupIter < myBounds->NbBounds; ++aGroupIter) | |
418 | { | |
419 | const GLint aNbElemsInGroup = myBounds->Bounds[aGroupIter]; | |
8625ef7e | 420 | if (theFaceColors != NULL) aGlContext->SetColor4fv (theFaceColors[aGroupIter]); |
7d3e64ef | 421 | glDrawArrays (myDrawMode, aFirstElem, aNbElemsInGroup); |
422 | aFirstElem += aNbElemsInGroup; | |
423 | } | |
871fa103 | 424 | } |
7d3e64ef | 425 | else |
2166f0fa | 426 | { |
7d3e64ef | 427 | if (myDrawMode == GL_POINTS) |
428 | { | |
429 | drawMarkers (theWorkspace); | |
430 | } | |
431 | else | |
432 | { | |
433 | glDrawArrays (myDrawMode, 0, myVboAttribs->GetElemsNb()); | |
434 | } | |
2166f0fa SK |
435 | } |
436 | ||
7d3e64ef | 437 | // bind with 0 |
438 | myVboAttribs->UnbindAllAttributes (aGlContext); | |
439 | ||
440 | if (hasVColors) | |
441 | { | |
442 | theWorkspace->NamedStatus |= OPENGL_NS_RESMAT; | |
443 | } | |
7fd59977 | 444 | } |
445 | ||
2166f0fa | 446 | // ======================================================================= |
7d3e64ef | 447 | // function : drawEdges |
2166f0fa SK |
448 | // purpose : |
449 | // ======================================================================= | |
7d3e64ef | 450 | void OpenGl_PrimitiveArray::drawEdges (const TEL_COLOUR* theEdgeColour, |
2166f0fa | 451 | const Handle(OpenGl_Workspace)& theWorkspace) const |
7fd59977 | 452 | { |
65360da3 | 453 | const Handle(OpenGl_Context)& aGlContext = theWorkspace->GetGlContext(); |
7d3e64ef | 454 | if (myVboAttribs.IsNull()) |
455 | { | |
456 | return; | |
457 | } | |
458 | ||
ca3c13d1 | 459 | #if !defined(GL_ES_VERSION_2_0) |
65360da3 | 460 | if (aGlContext->core11 != NULL) |
461 | { | |
462 | glDisable (GL_LIGHTING); | |
463 | } | |
ca3c13d1 | 464 | #endif |
7fd59977 | 465 | |
2166f0fa | 466 | const OpenGl_AspectLine* anAspectLineOld = NULL; |
7fd59977 | 467 | |
7d3e64ef | 468 | anAspectLineOld = theWorkspace->SetAspectLine (theWorkspace->AspectFace (Standard_True)->AspectEdge()); |
469 | const OpenGl_AspectLine* anAspect = theWorkspace->AspectLine (Standard_True); | |
30f0ad28 | 470 | |
ca3c13d1 | 471 | #if !defined(GL_ES_VERSION_2_0) |
7d3e64ef | 472 | glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); |
ca3c13d1 | 473 | #endif |
7d3e64ef | 474 | |
4e1523ef | 475 | if (aGlContext->core20fwd != NULL) |
7d3e64ef | 476 | { |
8625ef7e | 477 | aGlContext->ShaderManager()->BindProgram (anAspect, NULL, Standard_False, Standard_False, anAspect->ShaderProgramRes (aGlContext)); |
2166f0fa | 478 | } |
7fd59977 | 479 | |
5e27df78 | 480 | /// OCC22236 NOTE: draw edges for all situations: |
871fa103 | 481 | /// 1) draw elements with GL_LINE style as edges from myPArray->bufferVBO[VBOEdges] indices array |
482 | /// 2) draw elements from vertex array, when bounds defines count of primitive's vertices. | |
5e27df78 | 483 | /// 3) draw primitive's edges by vertexes if no edges and bounds array is specified |
7d3e64ef | 484 | myVboAttribs->BindPositionAttribute (aGlContext); |
8625ef7e | 485 | aGlContext->SetColor4fv (*(const OpenGl_Vec4* )theEdgeColour->rgb); |
7d3e64ef | 486 | if (!myVboIndices.IsNull()) |
bf75be98 | 487 | { |
7d3e64ef | 488 | myVboIndices->Bind (aGlContext); |
489 | GLubyte* anOffset = myVboIndices->GetDataOffset(); | |
2166f0fa | 490 | |
7d3e64ef | 491 | // draw primitives by vertex count with the indices |
492 | if (!myBounds.IsNull()) | |
2166f0fa | 493 | { |
7d3e64ef | 494 | const size_t aStride = myVboIndices->GetDataType() == GL_UNSIGNED_SHORT ? sizeof(unsigned short) : sizeof(unsigned int); |
871fa103 | 495 | for (Standard_Integer aGroupIter = 0; aGroupIter < myBounds->NbBounds; ++aGroupIter) |
2166f0fa | 496 | { |
871fa103 | 497 | const GLint aNbElemsInGroup = myBounds->Bounds[aGroupIter]; |
7d3e64ef | 498 | glDrawElements (myDrawMode, aNbElemsInGroup, myVboIndices->GetDataType(), anOffset); |
499 | anOffset += aStride * aNbElemsInGroup; | |
7fd59977 | 500 | } |
501 | } | |
7d3e64ef | 502 | // draw one (or sequential) primitive by the indices |
2166f0fa SK |
503 | else |
504 | { | |
7d3e64ef | 505 | glDrawElements (myDrawMode, myVboIndices->GetElemsNb(), myVboIndices->GetDataType(), anOffset); |
762aacae | 506 | } |
7d3e64ef | 507 | myVboIndices->Unbind (aGlContext); |
2166f0fa | 508 | } |
7d3e64ef | 509 | else if (!myBounds.IsNull()) |
2166f0fa | 510 | { |
7d3e64ef | 511 | GLint aFirstElem = 0; |
512 | for (Standard_Integer aGroupIter = 0; aGroupIter < myBounds->NbBounds; ++aGroupIter) | |
2166f0fa | 513 | { |
7d3e64ef | 514 | const GLint aNbElemsInGroup = myBounds->Bounds[aGroupIter]; |
515 | glDrawArrays (myDrawMode, aFirstElem, aNbElemsInGroup); | |
516 | aFirstElem += aNbElemsInGroup; | |
7fd59977 | 517 | } |
2166f0fa | 518 | } |
7d3e64ef | 519 | else |
520 | { | |
521 | glDrawArrays (myDrawMode, 0, myAttribs->NbElements); | |
522 | } | |
523 | ||
524 | // unbind buffers | |
525 | myVboAttribs->UnbindAttribute (aGlContext, Graphic3d_TOA_POS); | |
2166f0fa | 526 | |
8625ef7e | 527 | // restore line context |
8625ef7e | 528 | theWorkspace->SetAspectLine (anAspectLineOld); |
7fd59977 | 529 | } |
530 | ||
a577aaab | 531 | // ======================================================================= |
7d3e64ef | 532 | // function : drawMarkers |
a577aaab | 533 | // purpose : |
534 | // ======================================================================= | |
7d3e64ef | 535 | void OpenGl_PrimitiveArray::drawMarkers (const Handle(OpenGl_Workspace)& theWorkspace) const |
a577aaab | 536 | { |
537 | const OpenGl_AspectMarker* anAspectMarker = theWorkspace->AspectMarker (Standard_True); | |
538 | const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext(); | |
8625ef7e | 539 | const Handle(OpenGl_PointSprite)& aSpriteNorm = anAspectMarker->SpriteRes (aCtx); |
540 | if (!aSpriteNorm.IsNull() | |
541 | && !aSpriteNorm->IsDisplayList()) | |
a577aaab | 542 | { |
543 | // Textured markers will be drawn with the point sprites | |
8625ef7e | 544 | aCtx->SetPointSize (anAspectMarker->MarkerSize()); |
ca3c13d1 | 545 | #if !defined(GL_ES_VERSION_2_0) |
4e1523ef | 546 | if (aCtx->core11 != NULL) |
547 | { | |
548 | aCtx->core11fwd->glEnable (GL_ALPHA_TEST); | |
549 | aCtx->core11fwd->glAlphaFunc (GL_GEQUAL, 0.1f); | |
550 | } | |
ca3c13d1 | 551 | #endif |
a577aaab | 552 | |
8625ef7e | 553 | aCtx->core11fwd->glEnable (GL_BLEND); |
554 | aCtx->core11fwd->glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
a577aaab | 555 | |
8625ef7e | 556 | aCtx->core11fwd->glDrawArrays (myDrawMode, 0, !myVboAttribs.IsNull() ? myVboAttribs->GetElemsNb() : myAttribs->NbElements); |
a577aaab | 557 | |
8625ef7e | 558 | aCtx->core11fwd->glDisable (GL_BLEND); |
ca3c13d1 | 559 | #if !defined(GL_ES_VERSION_2_0) |
4e1523ef | 560 | if (aCtx->core11 != NULL) |
561 | { | |
562 | aCtx->core11fwd->glDisable (GL_ALPHA_TEST); | |
563 | } | |
ca3c13d1 | 564 | #endif |
8625ef7e | 565 | aCtx->SetPointSize (1.0f); |
a577aaab | 566 | return; |
567 | } | |
8625ef7e | 568 | else if (anAspectMarker->Type() == Aspect_TOM_POINT) |
a577aaab | 569 | { |
8625ef7e | 570 | aCtx->SetPointSize (anAspectMarker->MarkerSize()); |
571 | aCtx->core11fwd->glDrawArrays (myDrawMode, 0, !myVboAttribs.IsNull() ? myVboAttribs->GetElemsNb() : myAttribs->NbElements); | |
572 | aCtx->SetPointSize (1.0f); | |
a577aaab | 573 | } |
ca3c13d1 | 574 | #if !defined(GL_ES_VERSION_2_0) |
8625ef7e | 575 | // Textured markers will be drawn with the glBitmap |
576 | else if (anAspectMarker->Type() != Aspect_TOM_POINT | |
577 | && !aSpriteNorm.IsNull()) | |
a577aaab | 578 | { |
871fa103 | 579 | /**if (!isHilight && (myPArray->vcolours != NULL)) |
a577aaab | 580 | { |
871fa103 | 581 | for (Standard_Integer anIter = 0; anIter < myAttribs->NbElements; anIter++) |
a577aaab | 582 | { |
871fa103 | 583 | glColor4ubv (myPArray->vcolours[anIter].GetData()); |
584 | glRasterPos3fv (myAttribs->Value<Graphic3d_Vec3> (anIter).GetData()); | |
a577aaab | 585 | aSpriteNorm->DrawBitmap (theWorkspace->GetGlContext()); |
586 | } | |
587 | } | |
871fa103 | 588 | else*/ |
a577aaab | 589 | { |
871fa103 | 590 | for (Standard_Integer anIter = 0; anIter < myAttribs->NbElements; anIter++) |
a577aaab | 591 | { |
8625ef7e | 592 | aCtx->core11->glRasterPos3fv (myAttribs->Value<Graphic3d_Vec3> (anIter).GetData()); |
a577aaab | 593 | aSpriteNorm->DrawBitmap (theWorkspace->GetGlContext()); |
594 | } | |
595 | } | |
596 | } | |
ca3c13d1 | 597 | #endif |
a577aaab | 598 | } |
599 | ||
e1c659da | 600 | // ======================================================================= |
601 | // function : OpenGl_PrimitiveArray | |
602 | // purpose : | |
603 | // ======================================================================= | |
604 | OpenGl_PrimitiveArray::OpenGl_PrimitiveArray (const OpenGl_GraphicDriver* theDriver) | |
605 | ||
606 | : myDrawMode (DRAW_MODE_NONE), | |
607 | myIsVboInit (Standard_False) | |
608 | { | |
609 | if (theDriver != NULL) | |
610 | { | |
611 | myUID = theDriver->GetNextPrimitiveArrayUID(); | |
612 | } | |
613 | } | |
614 | ||
2166f0fa SK |
615 | // ======================================================================= |
616 | // function : OpenGl_PrimitiveArray | |
617 | // purpose : | |
618 | // ======================================================================= | |
8d3f219f | 619 | OpenGl_PrimitiveArray::OpenGl_PrimitiveArray (const OpenGl_GraphicDriver* theDriver, |
620 | const Graphic3d_TypeOfPrimitiveArray theType, | |
871fa103 | 621 | const Handle(Graphic3d_IndexBuffer)& theIndices, |
622 | const Handle(Graphic3d_Buffer)& theAttribs, | |
623 | const Handle(Graphic3d_BoundBuffer)& theBounds) | |
8d3f219f | 624 | |
871fa103 | 625 | : myIndices (theIndices), |
626 | myAttribs (theAttribs), | |
627 | myBounds (theBounds), | |
628 | myDrawMode (DRAW_MODE_NONE), | |
c827ea3a | 629 | myIsVboInit (Standard_False) |
2166f0fa | 630 | { |
c827ea3a | 631 | if (theDriver != NULL) |
632 | { | |
633 | myUID = theDriver->GetNextPrimitiveArrayUID(); | |
634 | } | |
635 | ||
871fa103 | 636 | if (!myIndices.IsNull() |
637 | && myIndices->NbElements < 1) | |
638 | { | |
639 | // dummy index buffer? | |
640 | myIndices.Nullify(); | |
641 | } | |
871fa103 | 642 | |
a79f67f8 | 643 | setDrawMode (theType); |
2166f0fa SK |
644 | } |
645 | ||
646 | // ======================================================================= | |
647 | // function : ~OpenGl_PrimitiveArray | |
648 | // purpose : | |
649 | // ======================================================================= | |
5e27df78 | 650 | OpenGl_PrimitiveArray::~OpenGl_PrimitiveArray() |
2166f0fa | 651 | { |
5e27df78 | 652 | // |
653 | } | |
2166f0fa | 654 | |
5e27df78 | 655 | // ======================================================================= |
656 | // function : Release | |
657 | // purpose : | |
658 | // ======================================================================= | |
10b9c7df | 659 | void OpenGl_PrimitiveArray::Release (OpenGl_Context* theContext) |
5e27df78 | 660 | { |
c827ea3a | 661 | myIsVboInit = Standard_False; |
871fa103 | 662 | if (!myVboIndices.IsNull()) |
2166f0fa | 663 | { |
10b9c7df | 664 | if (theContext) |
5e27df78 | 665 | { |
871fa103 | 666 | theContext->DelayedRelease (myVboIndices); |
667 | } | |
668 | myVboIndices.Nullify(); | |
669 | } | |
670 | if (!myVboAttribs.IsNull()) | |
671 | { | |
10b9c7df | 672 | if (theContext) |
871fa103 | 673 | { |
674 | theContext->DelayedRelease (myVboAttribs); | |
5e27df78 | 675 | } |
871fa103 | 676 | myVboAttribs.Nullify(); |
2166f0fa SK |
677 | } |
678 | } | |
679 | ||
680 | // ======================================================================= | |
681 | // function : Render | |
682 | // purpose : | |
683 | // ======================================================================= | |
684 | void OpenGl_PrimitiveArray::Render (const Handle(OpenGl_Workspace)& theWorkspace) const | |
685 | { | |
871fa103 | 686 | if (myDrawMode == DRAW_MODE_NONE) |
bf75be98 | 687 | { |
2166f0fa | 688 | return; |
bf75be98 | 689 | } |
2166f0fa | 690 | |
a577aaab | 691 | const OpenGl_AspectFace* anAspectFace = theWorkspace->AspectFace (Standard_True); |
692 | const OpenGl_AspectLine* anAspectLine = theWorkspace->AspectLine (Standard_True); | |
693 | const OpenGl_AspectMarker* anAspectMarker = theWorkspace->AspectMarker (myDrawMode == GL_POINTS); | |
694 | ||
2166f0fa | 695 | // create VBOs on first render call |
58655684 | 696 | const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext(); |
7d3e64ef | 697 | if (!myIsVboInit) |
2166f0fa | 698 | { |
7d3e64ef | 699 | // compatibility - keep data to draw markers using display lists |
700 | const Standard_Boolean toKeepData = myDrawMode == GL_POINTS | |
8625ef7e | 701 | && !anAspectMarker->SpriteRes (aCtx).IsNull() |
702 | && anAspectMarker->SpriteRes (aCtx)->IsDisplayList(); | |
7d3e64ef | 703 | buildVBO (aCtx, toKeepData); |
5e27df78 | 704 | myIsVboInit = Standard_True; |
2166f0fa SK |
705 | } |
706 | ||
fd4a6963 | 707 | Tint aFrontLightingModel = anAspectFace->IntFront().color_mask; |
708 | const TEL_COLOUR* anInteriorColor = &anAspectFace->IntFront().matcol; | |
2166f0fa | 709 | const TEL_COLOUR* anEdgeColor = &anAspectFace->AspectEdge()->Color(); |
871fa103 | 710 | const TEL_COLOUR* aLineColor = myDrawMode == GL_POINTS ? &anAspectMarker->Color() : &anAspectLine->Color(); |
2166f0fa SK |
711 | |
712 | // Use highlight colors | |
713 | if (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT) | |
bf75be98 | 714 | { |
2166f0fa SK |
715 | anEdgeColor = anInteriorColor = aLineColor = theWorkspace->HighlightColor; |
716 | aFrontLightingModel = 0; | |
717 | } | |
718 | ||
8625ef7e | 719 | const Standard_Boolean hasColorAttrib = !myVboAttribs.IsNull() |
720 | && myVboAttribs->HasColorAttribute(); | |
721 | const Standard_Boolean isLightOn = aFrontLightingModel != 0 | |
722 | && !myVboAttribs.IsNull() | |
723 | && myVboAttribs->HasNormalAttribute(); | |
ca3c13d1 | 724 | #if !defined(GL_ES_VERSION_2_0) |
7d3e64ef | 725 | // manage FFP lighting |
4e1523ef | 726 | if (aCtx->core11 != NULL) |
7d3e64ef | 727 | { |
4e1523ef | 728 | if (!isLightOn) |
729 | { | |
730 | glDisable (GL_LIGHTING); | |
731 | } | |
732 | else | |
733 | { | |
734 | glEnable (GL_LIGHTING); | |
735 | } | |
7d3e64ef | 736 | } |
ca3c13d1 | 737 | #endif |
8625ef7e | 738 | // Temporarily disable environment mapping |
739 | Handle(OpenGl_Texture) aTextureBack; | |
740 | if (myDrawMode <= GL_LINE_STRIP) | |
741 | { | |
742 | aTextureBack = theWorkspace->DisableTexture(); | |
743 | } | |
7d3e64ef | 744 | |
745 | if ((myDrawMode > GL_LINE_STRIP && anAspectFace->InteriorStyle() != Aspect_IS_EMPTY) || | |
746 | (myDrawMode <= GL_LINE_STRIP)) | |
747 | { | |
748 | const bool toHilight = (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT) != 0; | |
749 | const Graphic3d_Vec4* aFaceColors = !myBounds.IsNull() && !toHilight && anAspectFace->InteriorStyle() != Aspect_IS_HIDDENLINE | |
750 | ? myBounds->Colors | |
751 | : NULL; | |
8625ef7e | 752 | const Standard_Boolean hasVertColor = hasColorAttrib && !toHilight; |
4e1523ef | 753 | if (aCtx->core20fwd != NULL) |
8625ef7e | 754 | { |
755 | switch (myDrawMode) | |
756 | { | |
757 | case GL_POINTS: | |
758 | { | |
759 | const Handle(OpenGl_PointSprite)& aSpriteNorm = anAspectMarker->SpriteRes (aCtx); | |
760 | if (!aSpriteNorm.IsNull() | |
761 | && !aSpriteNorm->IsDisplayList()) | |
762 | { | |
763 | const Handle(OpenGl_PointSprite)& aSprite = (toHilight && anAspectMarker->SpriteHighlightRes (aCtx)->IsValid()) | |
764 | ? anAspectMarker->SpriteHighlightRes (aCtx) | |
765 | : aSpriteNorm; | |
766 | theWorkspace->EnableTexture (aSprite); | |
767 | aCtx->ShaderManager()->BindProgram (anAspectMarker, aSprite, isLightOn, hasVertColor, anAspectMarker->ShaderProgramRes (aCtx)); | |
768 | } | |
769 | else | |
770 | { | |
771 | aCtx->ShaderManager()->BindProgram (anAspectMarker, NULL, isLightOn, hasVertColor, anAspectMarker->ShaderProgramRes (aCtx)); | |
772 | } | |
773 | break; | |
774 | } | |
775 | case GL_LINES: | |
776 | case GL_LINE_STRIP: | |
777 | { | |
778 | aCtx->ShaderManager()->BindProgram (anAspectLine, NULL, isLightOn, hasVertColor, anAspectLine->ShaderProgramRes (aCtx)); | |
779 | break; | |
780 | } | |
781 | default: | |
782 | { | |
6c6aadb1 | 783 | const Standard_Boolean isLightOnFace = isLightOn |
784 | && (theWorkspace->ActiveTexture().IsNull() | |
785 | || theWorkspace->ActiveTexture()->GetParams()->IsModulate()); | |
786 | aCtx->ShaderManager()->BindProgram (anAspectFace, theWorkspace->ActiveTexture(), isLightOnFace, hasVertColor, anAspectFace->ShaderProgramRes (aCtx)); | |
8625ef7e | 787 | break; |
788 | } | |
789 | } | |
790 | } | |
791 | ||
792 | aCtx->SetColor4fv (*(const OpenGl_Vec4* )(myDrawMode <= GL_LINE_STRIP ? aLineColor->rgb : anInteriorColor->rgb)); | |
7d3e64ef | 793 | |
8625ef7e | 794 | drawArray (theWorkspace, aFaceColors, hasColorAttrib); |
7d3e64ef | 795 | } |
796 | ||
8625ef7e | 797 | if (myDrawMode <= GL_LINE_STRIP) |
798 | { | |
799 | theWorkspace->EnableTexture (aTextureBack); | |
800 | } | |
801 | else | |
7d3e64ef | 802 | { |
803 | if (anAspectFace->Edge() | |
804 | || anAspectFace->InteriorStyle() == Aspect_IS_HIDDENLINE) | |
30f0ad28 | 805 | { |
7d3e64ef | 806 | drawEdges (anEdgeColor, theWorkspace); |
b34efb62 | 807 | |
b34efb62 | 808 | // restore OpenGL polygon mode if needed |
65360da3 | 809 | #if !defined(GL_ES_VERSION_2_0) |
b34efb62 | 810 | if (anAspectFace->InteriorStyle() >= Aspect_IS_HATCH) |
811 | { | |
812 | glPolygonMode (GL_FRONT_AND_BACK, | |
813 | anAspectFace->InteriorStyle() == Aspect_IS_POINT ? GL_POINT : GL_FILL); | |
814 | } | |
815 | #endif | |
30f0ad28 | 816 | } |
817 | } | |
818 | ||
7d3e64ef | 819 | aCtx->BindProgram (NULL); |
2166f0fa | 820 | } |
a79f67f8 | 821 | |
822 | // ======================================================================= | |
823 | // function : setDrawMode | |
824 | // purpose : | |
825 | // ======================================================================= | |
826 | void OpenGl_PrimitiveArray::setDrawMode (const Graphic3d_TypeOfPrimitiveArray theType) | |
827 | { | |
828 | if (myAttribs.IsNull()) | |
829 | { | |
830 | myDrawMode = DRAW_MODE_NONE; | |
831 | return; | |
832 | } | |
833 | ||
834 | switch (theType) | |
835 | { | |
836 | case Graphic3d_TOPA_POINTS: | |
837 | myDrawMode = GL_POINTS; | |
838 | break; | |
839 | case Graphic3d_TOPA_POLYLINES: | |
840 | myDrawMode = GL_LINE_STRIP; | |
841 | break; | |
842 | case Graphic3d_TOPA_SEGMENTS: | |
843 | myDrawMode = GL_LINES; | |
844 | break; | |
845 | case Graphic3d_TOPA_TRIANGLES: | |
846 | myDrawMode = GL_TRIANGLES; | |
847 | break; | |
848 | case Graphic3d_TOPA_TRIANGLESTRIPS: | |
849 | myDrawMode = GL_TRIANGLE_STRIP; | |
850 | break; | |
851 | case Graphic3d_TOPA_TRIANGLEFANS: | |
852 | myDrawMode = GL_TRIANGLE_FAN; | |
853 | break; | |
854 | #if !defined(GL_ES_VERSION_2_0) | |
855 | case Graphic3d_TOPA_POLYGONS: | |
856 | myDrawMode = GL_POLYGON; | |
857 | break; | |
858 | case Graphic3d_TOPA_QUADRANGLES: | |
859 | myDrawMode = GL_QUADS; | |
860 | break; | |
861 | case Graphic3d_TOPA_QUADRANGLESTRIPS: | |
862 | myDrawMode = GL_QUAD_STRIP; | |
863 | break; | |
864 | #else | |
865 | case Graphic3d_TOPA_POLYGONS: | |
866 | case Graphic3d_TOPA_QUADRANGLES: | |
867 | case Graphic3d_TOPA_QUADRANGLESTRIPS: | |
868 | #endif | |
869 | case Graphic3d_TOPA_UNDEFINED: | |
870 | break; | |
871 | } | |
872 | } | |
873 | ||
874 | // ======================================================================= | |
875 | // function : InitBuffers | |
876 | // purpose : | |
877 | // ======================================================================= | |
878 | void OpenGl_PrimitiveArray::InitBuffers (const Handle(OpenGl_Context)& theContext, | |
879 | const Graphic3d_TypeOfPrimitiveArray theType, | |
880 | const Handle(Graphic3d_IndexBuffer)& theIndices, | |
881 | const Handle(Graphic3d_Buffer)& theAttribs, | |
882 | const Handle(Graphic3d_BoundBuffer)& theBounds) | |
883 | { | |
884 | // Release old graphic resources | |
885 | Release (theContext.operator->()); | |
886 | ||
887 | myIndices = theIndices; | |
888 | myAttribs = theAttribs; | |
889 | myBounds = theBounds; | |
890 | ||
891 | setDrawMode (theType); | |
892 | } |