1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SoText3 SoText3.h Inventor/nodes/SoText3.h
35   \brief The SoText3 class renders extruded 3D text.
36 
37   \ingroup nodes
38 
39   Render text as 3D geometry.
40 
41   The size of the textual geometry representation is decided from the
42   SoFont::size field of a preceding SoFont-node in the scene graph,
43   which specifies the size in unit coordinates. This value sets the
44   approximate vertical size of the letters.  The default value if no
45   SoFont-nodes are used, is 10.
46 
47   This node will create 3D geometry from a specified font defined by a
48   preceding SoFont node. The complexity of the glyphs is controlled by
49   a preceding SoComplexity node with \e Type set to OBJECT_SPACE.
50   Please note that the default builtin 3D font will not be affected by
51   the SoComplexity node.
52 
53   This is a simple example of an extruded SoText3 string:
54 
55   \verbatim
56    #Inventor V2.1 ascii
57 
58    Separator {
59      renderCaching ON
60      Font {
61         name "Arial"
62         size 2
63      }
64      ProfileCoordinate2 {
65        point [ 0 0,
66                0.05 0.05,
67                0.25 0.05,
68                0.3 0 ]
69      }
70      LinearProfile {
71        index [ 0, 1, 2, 3 ]
72      }
73      Complexity {
74        type OBJECT_SPACE
75        value 1
76      }
77      ShapeHints {
78        creaseAngle 1.5
79        shapeType SOLID
80        vertexOrdering COUNTERCLOCKWISE
81      }
82      Material {
83        diffuseColor 0.6 0.6 0.8
84        specularColor 1 1 1
85      }
86      Text3 {
87        string ["Coin3D"]
88        parts ALL
89      }
90    }
91   \endverbatim
92 
93   <center>
94   \image html text3.png "Rendering of Example File"
95   </center>
96 
97 
98   if SoText3::Part is set to SIDES or ALL and no profile is provided, a
99   flat, one unit long profile will be created.
100 
101   Separate colors can be assigned to the front, sides and back of the
102   glyphs by adding a preceding SoMaterialBinding node.  Set the \e value
103   field to PER_PART (default is OVERALL). The front, side and back of
104   the glyphs will then be colored according to diffuse color 0, 1 and 2
105   found on the stack.
106 
107   Beware that using a lot of SoText3 text characters in a scene will
108   usually have severe impact on the rendering performance, as each and
109   every character of the text increases the polygon-count a lot. This
110   makes SoText3 nodes most suitable in situations where you just need
111   a few characters to be placed in your scene, rather than to
112   visualize complete sentences.
113 
114   <b>FILE FORMAT/DEFAULTS:</b>
115   \code
116     Text3 {
117         string ""
118         spacing 1
119         justification LEFT
120         parts FRONT
121     }
122   \endcode
123 
124   \sa SoText2, SoAsciiText, SoProfile
125 */
126 
127 // *************************************************************************
128 
129 #include <Inventor/nodes/SoText3.h>
130 
131 #include <cstring>
132 #include <cfloat> // FLT_MAX, FLT_MIN
133 
134 #ifdef HAVE_CONFIG_H
135 #include <config.h>
136 #endif // HAVE_CONFIG_H
137 
138 #include <Inventor/SoPrimitiveVertex.h>
139 #include <Inventor/actions/SoGLRenderAction.h>
140 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
141 #include <Inventor/bundles/SoMaterialBundle.h>
142 #include <Inventor/details/SoTextDetail.h>
143 #include <Inventor/elements/SoFontNameElement.h>
144 #include <Inventor/elements/SoFontSizeElement.h>
145 #include <Inventor/elements/SoGLShapeHintsElement.h>
146 #include <Inventor/elements/SoMaterialBindingElement.h>
147 #include <Inventor/elements/SoLazyElement.h>
148 #include <Inventor/elements/SoGLCacheContextElement.h>
149 #include <Inventor/elements/SoComplexityTypeElement.h>
150 #include <Inventor/elements/SoComplexityElement.h>
151 #include <Inventor/elements/SoCreaseAngleElement.h>
152 #include <Inventor/elements/SoGLMultiTextureEnabledElement.h>
153 #include <Inventor/elements/SoTextOutlineEnabledElement.h>
154 #include <Inventor/misc/SoState.h>
155 #include <Inventor/misc/SoNormalGenerator.h>
156 #include <Inventor/nodes/SoProfile.h>
157 #include <Inventor/nodes/SoNurbsProfile.h>
158 #include <Inventor/SbLine.h>
159 #include <Inventor/SbBox2f.h>
160 #include <Inventor/lists/SbList.h>
161 #include <Inventor/elements/SoCacheElement.h>
162 #include <Inventor/system/gl.h>
163 
164 #ifdef COIN_THREADSAFE
165 #include <Inventor/threads/SbMutex.h>
166 #endif // COIN_THREADSAFE
167 
168 #if COIN_DEBUG
169 #include <Inventor/errors/SoDebugError.h>
170 #endif // COIN_DEBUG
171 
172 #include "coindefs.h" // COIN_OBSOLETED()
173 #include "nodes/SoSubNodeP.h"
174 #include "fonts/glyph3d.h"
175 #include "caches/SoGlyphCache.h"
176 
177 // *************************************************************************
178 
179 /*!
180   \enum SoText3::Part
181   Used to specify which parts should be rendered/generated.
182 */
183 /*!
184   \var SoText3::Part SoText3::FRONT
185   Front of characters.
186 */
187 /*!
188   \var SoText3::Part SoText3::SIDES
189   Sides of characters.
190 */
191 /*!
192   \var SoText3::Part SoText3::BACK
193   Back of characters.
194 */
195 /*!
196   \var SoText3::Part SoText3::ALL
197   All parts.
198 */
199 
200 /*!
201   \enum SoText3::Justification
202   Used to specify horizontal string alignment.
203 */
204 /*!
205   \var SoText3::Justification SoText3::LEFT
206   Left edges of strings are aligned.
207 */
208 /*!
209   \var SoText3::Justification SoText3::RIGHT
210   Right edges of strings are aligned.
211 */
212 /*!
213   \var SoText3::Justification SoText3::CENTER
214   Strings are centered.
215 */
216 
217 
218 /*!
219   \var SoMFString SoText3::string
220 
221   The strings to render.
222 
223   Array defaults to contain a single empty string.
224 */
225 /*!
226   \var SoSFFloat SoText3::spacing
227   Vertical spacing. 1.0 is the default spacing.
228 */
229 /*!
230   \var SoSFEnum SoText3::justification
231   Horizontal justification. Default is alignment at the left border.
232 */
233 /*!
234   \var SoSFBitMask SoText3::parts
235   Character parts. Default is to show only the front-facing part.
236 */
237 
238 // FIXME: missing features, pederb 20000224
239 //   - Texture coordinates are not generated yet.
240 //   - Normals for SIDES should be smoothed.
241 
242 // *************************************************************************
243 
244 class SoText3P {
245 public:
SoText3P(SoText3 * master)246   SoText3P(SoText3 * master) : master(master) { }
247 
248   void render(SoState * state, const cc_font_specification * fontspec, unsigned int part);
249   void generate(SoAction * action, const cc_font_specification * fontspec, unsigned int part);
250 
251   SbList <float> widths;
252   void setUpGlyphs(SoState * state, SoText3 * textnode);
253   SbBox3f maxglyphbbox;
254   SoNormalGenerator * normalgenerator;
255 
256   SoGlyphCache * cache;
257 
lock(void)258   void lock(void) {
259 #ifdef COIN_THREADSAFE
260     this->mutex.lock();
261 #endif // COIN_THREADSAFE
262   }
unlock(void)263   void unlock(void) {
264 #ifdef COIN_THREADSAFE
265     this->mutex.unlock();
266 #endif // COIN_THREADSAFE
267   }
268 private:
269 #ifdef COIN_THREADSAFE
270   // FIXME: a mutex for every instance seems a bit excessive,
271   // especially since MSWindows might have rather strict limits on the
272   // total amount of mutex resources a process (or even a user) can
273   // allocate. so consider making this a class-wide instance instead.
274   // -mortene.
275   SbMutex mutex;
276 #endif // COIN_THREADSAFE
277   SoText3 * master;
278 };
279 
280 #define PRIVATE(p) ((p)->pimpl)
281 #define PUBLIC(p) ((p)->master)
282 
283 // *************************************************************************
284 
285 SO_NODE_SOURCE(SoText3);
286 
287 // *************************************************************************
288 
SoText3(void)289 SoText3::SoText3(void)
290 {
291   SO_NODE_INTERNAL_CONSTRUCTOR(SoText3);
292 
293   SO_NODE_ADD_FIELD(string, (""));
294   SO_NODE_ADD_FIELD(spacing, (1.0f));
295   SO_NODE_ADD_FIELD(justification, (SoText3::LEFT));
296   SO_NODE_ADD_FIELD(parts, (SoText3::FRONT));
297 
298   SO_NODE_DEFINE_ENUM_VALUE(Justification, LEFT);
299   SO_NODE_DEFINE_ENUM_VALUE(Justification, RIGHT);
300   SO_NODE_DEFINE_ENUM_VALUE(Justification, CENTER);
301   SO_NODE_SET_SF_ENUM_TYPE(justification, Justification);
302 
303   SO_NODE_DEFINE_ENUM_VALUE(Part, FRONT);
304   SO_NODE_DEFINE_ENUM_VALUE(Part, SIDES);
305   SO_NODE_DEFINE_ENUM_VALUE(Part, BACK);
306   SO_NODE_DEFINE_ENUM_VALUE(Part, ALL);
307   SO_NODE_SET_SF_ENUM_TYPE(parts, Part);
308 
309   PRIVATE(this) = new SoText3P(this);
310   PRIVATE(this)->normalgenerator = new SoNormalGenerator(FALSE, 0xff);
311   PRIVATE(this)->cache = NULL;
312 }
313 
~SoText3()314 SoText3::~SoText3()
315 {
316   if (PRIVATE(this)->cache) PRIVATE(this)->cache->unref();
317   delete PRIVATE(this)->normalgenerator;
318   delete PRIVATE(this);
319 }
320 
321 // doc in parent
322 void
initClass(void)323 SoText3::initClass(void)
324 {
325   SO_NODE_INTERNAL_INIT_CLASS(SoText3, SO_FROM_INVENTOR_2_1);
326 }
327 
328 // doc in parent
329 void
computeBBox(SoAction * action,SbBox3f & box,SbVec3f & center)330 SoText3::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
331 {
332   SoState * state = action->getState();
333 
334   PRIVATE(this)->lock();
335   PRIVATE(this)->setUpGlyphs(state, this);
336   SoCacheElement::addCacheDependency(state, PRIVATE(this)->cache);
337 
338   const cc_font_specification * fontspec = PRIVATE(this)->cache->getCachedFontspec();
339 
340   int i, n = PRIVATE(this)->widths.getLength();
341   if (n == 0) {
342     PRIVATE(this)->unlock();
343     return; // empty bbox
344   }
345   float maxw = FLT_MIN;
346   for (i = 0; i < n; i++) {
347     maxw = SbMax(maxw, PRIVATE(this)->widths[i]);
348   }
349 
350   if (maxw == FLT_MIN) { // There is no text to bound. Returning.
351     PRIVATE(this)->unlock();
352     return;
353   }
354 
355   SbBox2f maxbox;
356 
357   const float maxy = 0;
358   float miny = -this->spacing.getValue() * fontspec->size * (n-1);
359 
360   float minx, maxx;
361   switch (this->justification.getValue()) {
362   case SoText3::LEFT:
363     minx = 0.0f;
364     maxx = maxw;
365     break;
366   case SoText3::RIGHT:
367     minx = -maxw;
368     maxx = 0.0f;
369     break;
370   case SoText3::CENTER:
371     maxx = maxw * 0.5f;
372     minx = -maxx;
373     break;
374   default:
375     assert(0 && "unknown justification");
376     minx = maxx = 0.0f;
377     break;
378   }
379 
380   // check profiles and extend bounding box if necessary
381   float profsize = 0;
382   float minz = -1.0f, maxz = 0.0f;
383 
384   const SoNodeList profilenodes = SoProfileElement::get(state);
385   int numprofiles = profilenodes.getLength();
386   if ( numprofiles > 0) {
387     assert(profilenodes[0]->getTypeId().isDerivedFrom(SoProfile::getClassTypeId()));
388     for (int i = numprofiles-1; i >= 0; i--) {
389       SoProfile *pn = (SoProfile *)profilenodes[i];
390       if (pn->isOfType(SoNurbsProfile::getClassTypeId())) {
391         // Don't use SoProfile::getVertices() for SoNurbsProfile
392         // nodes as this would cause a call to the GLU library, which
393         // requires a valid GL context. Instead we approximate using
394         // SoNurbsProfile::getTrimCurve(), and use the control points
395         // to calculate the bounding box. This is an approximation,
396         // but the same technique is used in So[Indexed]NurbsSurface
397         // and So[Indexed]NurbsCurve. To avoid this approximation we
398         // would need our own NURBS library.    pederb, 20000926
399         SoNurbsProfile * np = (SoNurbsProfile*) pn;
400         float * knots;
401         int32_t numknots;
402         int dim;
403         int32_t numpts;
404         float * points;
405         np->getTrimCurve(state, numpts, points, dim,
406                          numknots, knots);
407         for (int j = 0; j < numpts; j++) {
408           if (-points[j*dim] > maxz) maxz = -points[j*dim];
409           if (-points[j*dim] < minz) minz = -points[j*dim];
410           if (points[j*dim+1] > profsize) profsize = points[j*dim+1];
411         }
412       }
413       else {
414         int32_t num;
415         SbVec2f *coords;
416         pn->getVertices(state, num, coords);
417         for (int j = 0; j < num; j++) {
418           if (-coords[j][0] > maxz) maxz = -coords[j][0];
419           if (-coords[j][0] < minz) minz = -coords[j][0];
420           if (coords[j][1] > profsize) profsize = coords[j][1];
421         }
422       }
423     }
424   }
425   else {
426     // extrude
427     if (this->parts.getValue() == SoText3::BACK) {
428       maxz = -1.0f;
429     }
430     else if (this->parts.getValue() == SoText3::FRONT) {
431       minz = 0.0f;
432     }
433   }
434 
435   box.setBounds(SbVec3f(minx, miny, minz), SbVec3f(maxx, maxy, maxz));
436 
437   // Expanding bbox so that glyphs like 'j's and 'q's are completely inside.
438   box.extendBy(SbVec3f(0,PRIVATE(this)->maxglyphbbox.getMin()[1] - (n-1) * fontspec->size, 0));
439   box.extendBy(PRIVATE(this)->maxglyphbbox);
440 
441   box.extendBy(SbVec3f(box.getMax()[0] + profsize, box.getMax()[1] + profsize, 0));
442   box.extendBy(SbVec3f(box.getMin()[0] - profsize, box.getMin()[1] - profsize, 0));
443 
444   center = box.getCenter();
445   PRIVATE(this)->unlock();
446 }
447 
448 
449 /*!
450   Not implemented. Should probably have been private in OIV. Let us
451   know if you need this method for anything, and we'll implement it.
452 */
453 SbBox3f
getCharacterBounds(SoState * COIN_UNUSED_ARG (state),int COIN_UNUSED_ARG (stringindex),int COIN_UNUSED_ARG (charindex))454 SoText3::getCharacterBounds(SoState * COIN_UNUSED_ARG(state), int COIN_UNUSED_ARG(stringindex), int COIN_UNUSED_ARG(charindex))
455 {
456   COIN_OBSOLETED();
457   return SbBox3f();
458 }
459 
460 // doc in parent
461 void
GLRender(SoGLRenderAction * action)462 SoText3::GLRender(SoGLRenderAction * action)
463 {
464   if (!this->shouldGLRender(action))
465     return;
466 
467   PRIVATE(this)->lock();
468 
469   SoState * state = action->getState();
470 
471   // FIXME: implement this feature. 20040820 mortene.
472   static SbBool warned = FALSE;
473   if (!warned) {
474     const int stackidx = SoTextOutlineEnabledElement::getClassStackIndex();
475     const SbBool outlinepresence = state->isElementEnabled(stackidx);
476 
477     if (outlinepresence && SoTextOutlineEnabledElement::get(state)) {
478 #if COIN_DEBUG
479       SoDebugError::postWarning("SoText3::GLRender",
480                                 "Support for rendering SoText3 nodes in outline "
481                                 "(i.e. heeding the SoTextOutlineEnabledElement) "
482                                 "not yet implemented.");
483 #endif // COIN_DEBUG
484       warned = TRUE;
485     }
486   }
487 
488 
489   PRIVATE(this)->setUpGlyphs(state, this);
490   SoCacheElement::addCacheDependency(state, PRIVATE(this)->cache);
491 
492   const cc_font_specification * fontspec = PRIVATE(this)->cache->getCachedFontspec();
493 
494   SoMaterialBindingElement::Binding binding = SoMaterialBindingElement::get(state);
495   SoMaterialBundle mb(action);
496   mb.sendFirst();
497 
498   const unsigned int prts = this->parts.getValue();
499   SoLazyElement * lazyelement = SoLazyElement::getInstance(state);
500   const int numdiffuse = lazyelement->getNumDiffuse();
501 
502   SbBool matperpart = (binding != SoMaterialBindingElement::OVERALL);
503 
504   if (prts & SoText3::FRONT) {
505     PRIVATE(this)->render(state, fontspec, SoText3::FRONT);
506   }
507   if (prts & SoText3::SIDES) {
508     if (matperpart && (numdiffuse > 1))
509         mb.send(1, FALSE);
510     PRIVATE(this)->render(state, fontspec, SoText3::SIDES);
511   }
512   if (prts & SoText3::BACK) {
513     if (matperpart && (numdiffuse > 2))
514       mb.send(2, FALSE);
515     PRIVATE(this)->render(state, fontspec, SoText3::BACK);
516   }
517 
518   if (SoComplexityTypeElement::get(state) == SoComplexityTypeElement::OBJECT_SPACE) {
519     SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DO_AUTO_CACHE);
520     SoGLCacheContextElement::incNumShapes(state);
521   }
522   PRIVATE(this)->unlock();
523 }
524 
525 // doc in parent
526 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)527 SoText3::getPrimitiveCount(SoGetPrimitiveCountAction * action)
528 {
529   if (action->is3DTextCountedAsTriangles()) {
530     // will cause a call to generatePrimitives()
531     // slow, but we can't be bothered to implement a new loop to count triangles
532     inherited::getPrimitiveCount(action);
533   }
534   else {
535     action->addNumText(this->string.getNum());
536   }
537 }
538 
539 // doc in parent
540 void
generatePrimitives(SoAction * action)541 SoText3::generatePrimitives(SoAction * action)
542 {
543   SoState * state = action->getState();
544 
545   PRIVATE(this)->lock();
546   PRIVATE(this)->setUpGlyphs(state, this);
547 
548   if (PRIVATE(this)->cache) {
549     const cc_font_specification * fontspec = PRIVATE(this)->cache->getCachedFontspec();
550     unsigned int prts = this->parts.getValue();
551 
552     if (prts & SoText3::FRONT) {
553       PRIVATE(this)->generate(action, fontspec, SoText3::FRONT);
554     }
555     if (prts & SoText3::SIDES) {
556       PRIVATE(this)->generate(action, fontspec, SoText3::SIDES);
557     }
558     if (prts & SoText3::BACK) {
559       PRIVATE(this)->generate(action, fontspec, SoText3::BACK);
560     }
561   }
562   PRIVATE(this)->unlock();
563 }
564 
565 // doc in parent
566 SoDetail *
createTriangleDetail(SoRayPickAction * COIN_UNUSED_ARG (action),const SoPrimitiveVertex * v1,const SoPrimitiveVertex * COIN_UNUSED_ARG (v2),const SoPrimitiveVertex * COIN_UNUSED_ARG (v3),SoPickedPoint * COIN_UNUSED_ARG (pp))567 SoText3::createTriangleDetail(SoRayPickAction * COIN_UNUSED_ARG(action),
568                               const SoPrimitiveVertex * v1,
569                               const SoPrimitiveVertex * COIN_UNUSED_ARG(v2),
570                               const SoPrimitiveVertex * COIN_UNUSED_ARG(v3),
571                               SoPickedPoint * COIN_UNUSED_ARG(pp))
572 {
573   // generatePrimitives() places text details inside each primitive vertex
574   assert(v1->getDetail());
575   return v1->getDetail()->copy();
576 }
577 
578 void
render(SoState * state,const cc_font_specification * fontspec,unsigned int part)579 SoText3P::render(SoState * state, const cc_font_specification * fontspec,
580                  unsigned int part)
581 {
582   int i, n = this->widths.getLength();
583 
584   int firstprofile = -1;
585   int32_t profnum;
586   SbVec2f *profcoords;
587   float nearz =  FLT_MAX;
588   float farz  = -FLT_MAX;
589 
590   float creaseangle = SoCreaseAngleElement::get(state);
591 
592   SbBool do2Dtextures = FALSE;
593   SbBool do3Dtextures = FALSE;
594   if (SoGLMultiTextureEnabledElement::get(state, 0)) {
595     do2Dtextures = TRUE;
596     if (SoGLMultiTextureEnabledElement::getMode(state, 0) ==
597         SoMultiTextureEnabledElement::TEXTURE3D) {
598       do3Dtextures = TRUE;
599     }
600   }
601   // FIXME: implement proper support for 3D-texturing, and get rid of
602   // this. (20031010 handegar)
603   if (do3Dtextures) {
604     static SbBool first = TRUE;
605     if (first) {
606       first = FALSE;
607 #if COIN_DEBUG
608       SoDebugError::postWarning("SoText3::GLRender",
609                                 "3D-textures not supported for this node type yet.");
610 #endif // COIN_DEBUG
611     }
612   }
613 
614 
615   const SoNodeList & profilenodes = SoProfileElement::get(state);
616   int numprofiles = profilenodes.getLength();
617   // Indicates if a valid profile has been specified. If it has not,
618   // text will be rendered extruded.
619   SbBool validprofile = FALSE;
620 
621   if (numprofiles > 0) {
622     assert(profilenodes[0]->getTypeId().isDerivedFrom(SoProfile::getClassTypeId()));
623     // Find near/far z (for modifying position of front/back)
624 
625     for (int l=numprofiles-1; l>=0; l--) {
626       SoProfile * pn = (SoProfile *) profilenodes[l];
627       pn->getVertices(state, profnum, profcoords);
628 
629       if (profnum > 0) {
630         if (profcoords[profnum-1][0] > farz) farz = profcoords[profnum-1][0];
631         if (profcoords[0][0] < nearz) nearz = profcoords[0][0];
632         if (pn->linkage.getValue() == SoProfile::START_FIRST) {
633           if (firstprofile == -1) {
634             firstprofile = l;
635             validprofile = TRUE;
636           }
637           break;
638         }
639       }
640     }
641     nearz = -nearz;
642     farz = -farz;
643   }
644 
645   // If no profiles have been specified, or if no valid coordinates have
646   // been given, set near and far / front and back values to default.
647   if (!validprofile) {
648     nearz = 0.0;
649     farz = -1.0;
650   }
651 
652   float ypos = 0.0f;
653   for (i = 0; i < n; i++) {
654 
655     float xpos = 0.0f;
656     switch (PUBLIC(this)->justification.getValue()) {
657     case SoText3::RIGHT:
658       xpos = -this->widths[i];
659       break;
660     case SoText3::CENTER:
661       xpos = - this->widths[i] * 0.5f;
662       break;
663     }
664 
665     SbString str = PUBLIC(this)->string[i];
666     cc_glyph3d * prevglyph = NULL;
667     const char * p = str.getString();
668     size_t length = cc_string_utf8_validate_length(p);
669     // No assertion as zero length is handled correctly (results in a new line)
670 
671     for (unsigned int strcharidx = 0; strcharidx < length; strcharidx++) {
672       uint32_t glyphidx = 0;
673 
674       glyphidx = cc_string_utf8_get_char(p);
675       p = cc_string_utf8_next_char(p);
676 
677       cc_glyph3d * glyph = cc_glyph3d_ref(glyphidx, fontspec);
678       const SbVec2f * coords = (SbVec2f *) cc_glyph3d_getcoords(glyph);
679 
680       // Get kerning
681       if (strcharidx > 0) {
682         float kerningx, kerningy;
683         cc_glyph3d_getkerning(prevglyph, glyph, &kerningx, &kerningy);
684         xpos += kerningx * fontspec->size;
685       }
686       if (prevglyph) {
687         cc_glyph3d_unref(prevglyph);
688       }
689       prevglyph = glyph;
690 
691       if (part != SoText3::SIDES) {  // FRONT & BACK
692         const int * ptr = cc_glyph3d_getfaceindices(glyph);
693         glBegin(GL_TRIANGLES);
694 
695         while (*ptr >= 0) {
696           SbVec2f v0, v1, v2;
697           float zval;
698           if (part == SoText3::FRONT) {
699             glNormal3f(0.0f, 0.0f, 1.0f);
700             v2 = coords[*ptr++];
701             v1 = coords[*ptr++];
702             v0 = coords[*ptr++];
703             zval = nearz;
704           }
705           else {  // BACK
706             glNormal3f(0.0f, 0.0f, -1.0f);
707             v0 = coords[*ptr++];
708             v1 = coords[*ptr++];
709             v2 = coords[*ptr++];
710             zval = farz;
711           }
712           if(do2Dtextures)
713             glTexCoord2f(v0[0] + xpos/fontspec->size,
714                          v0[1] + ypos/fontspec->size);
715           glVertex3f(v0[0] * fontspec->size + xpos, v0[1] * fontspec->size + ypos, zval);
716           if(do2Dtextures)
717             glTexCoord2f(v1[0] + xpos/fontspec->size,
718                          v1[1] + ypos/fontspec->size);
719           glVertex3f(v1[0] * fontspec->size + xpos, v1[1] * fontspec->size + ypos, zval);
720           if(do2Dtextures)
721             glTexCoord2f(v2[0] + xpos/fontspec->size,
722                          v2[1] + ypos/fontspec->size);
723           glVertex3f(v2[0] * fontspec->size + xpos, v2[1] * fontspec->size + ypos, zval);
724 
725         }
726         glEnd();
727       }
728       else { // SIDES
729 
730         if (!validprofile) {  // no profile - extrude
731           const int * ptr = cc_glyph3d_getedgeindices(glyph);
732           SbVec2f v0, v1;
733           int counter = 0;
734 
735           glBegin(GL_QUADS);
736 
737           while (*ptr >= 0) {
738             v1 = coords[*ptr++];
739             v0 = coords[*ptr++];
740             const int * ccw = (int *) cc_glyph3d_getnextccwedge(glyph, counter);
741             const int * cw  = (int *) cc_glyph3d_getnextcwedge(glyph, counter);
742             SbVec3f vleft(coords[*(ccw+1)][0], coords[*(ccw+1)][1], 0);
743             SbVec3f vright(coords[*cw][0], coords[*cw][1], 0);
744             counter++;
745 
746             // create two 'normal' vectors pointing out from the edges
747             SbVec3f normala(vright[0] - v0[0], vright[1] - v0[1], 0.0f);
748             normala = normala.cross(SbVec3f(0.0f, 0.0f,  1.0f));
749             if (normala.length() > 0)
750               normala.normalize();
751 
752             SbVec3f normalb(v1[0] - vleft[0], v1[1] - vleft[1], 0.0f);
753             normalb = normalb.cross(SbVec3f(0.0f, 0.0f,  1.0f));
754             if (normalb.length() > 0)
755               normalb.normalize();
756 
757             SbBool flatshading = FALSE;
758             float dot = normala.dot(normalb);
759 
760             if(acos(dot) > creaseangle) {
761               normala = SbVec3f(v1[0] - v0[0], v1[1] - v0[1], 0.0f);
762               normala = normala.cross(SbVec3f(0.0f, 0.0f, 1.0f));
763               if (normala.length() > 0)
764                 normala.normalize();
765 
766               flatshading = TRUE;
767             }
768 
769             if (!flatshading) {
770               if(do2Dtextures)
771                  glTexCoord2f(v1[0] + xpos/fontspec->size,
772                               v1[1] + ypos/fontspec->size);
773               glNormal3fv(normala.getValue());
774               glVertex3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, 0.0f);
775 
776               if(do2Dtextures)
777                 glTexCoord2f(v0[0] + xpos/fontspec->size,
778                              v0[1] + ypos/fontspec->size);
779               glNormal3fv(normalb.getValue());
780               glVertex3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, 0.0f);
781 
782               if(do2Dtextures)
783                 glTexCoord2f(v0[0] + xpos/fontspec->size,
784                              v0[1] + ypos/fontspec->size);
785               glNormal3fv(normalb.getValue());
786               glVertex3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, -1.0f);
787 
788               if(do2Dtextures)
789                 glTexCoord2f(v1[0] + xpos/fontspec->size,
790                              v1[1] + ypos/fontspec->size);
791               glNormal3fv(normala.getValue());
792               glVertex3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, -1.0f);
793 
794             }
795             else {
796               glNormal3fv(normala.getValue());
797               if(do2Dtextures)
798                 glTexCoord2f(v1[0] + xpos/fontspec->size,
799                              v1[1] + ypos/fontspec->size);
800               glVertex3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, 0.0f);
801 
802               if(do2Dtextures)
803                 glTexCoord2f(v0[0] + xpos/fontspec->size,
804                              v0[1] + ypos/fontspec->size);
805               glVertex3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, 0.0f);
806 
807               if(do2Dtextures)
808                 glTexCoord2f(v0[0] + xpos/fontspec->size,
809                              v0[1] + ypos/fontspec->size);
810               glVertex3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, -1.0f);
811 
812               if(do2Dtextures)
813                 glTexCoord2f(v1[0] + xpos/fontspec->size,
814                              v1[1] + ypos/fontspec->size);
815               glVertex3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, -1.0f);
816             }
817           }
818           glEnd();
819 
820         }
821         else {  // profile
822           assert(validprofile && firstprofile >= 0);
823 
824           const int * indices = cc_glyph3d_getedgeindices(glyph);
825           int ind = 0;
826           SbVec3f normala, normalb;
827 
828           SbList <SbVec3f> vertexlist;
829           this->normalgenerator->reset(FALSE);
830 
831           while (*indices >= 0) {
832 
833             int i0 = *indices++;
834             int i1 = *indices++;
835             SbVec3f va(coords[i0][0], coords[i0][1], nearz);
836             SbVec3f vb(coords[i1][0], coords[i1][1], nearz);
837             const int * ccw = (int *) cc_glyph3d_getnextccwedge(glyph, ind);
838             const int * cw  = (int *) cc_glyph3d_getnextcwedge(glyph, ind);
839             SbVec3f vleft(coords[*(ccw+1)][0], coords[*(ccw+1)][1], nearz);
840             SbVec3f vright(coords[*cw][0], coords[*cw][1], nearz);
841             ind++;
842 
843             va[0] = va[0] * fontspec->size;
844             va[1] = va[1] * fontspec->size;
845             vb[0] = vb[0] * fontspec->size;
846             vb[1] = vb[1] * fontspec->size;
847             vleft[0] = vleft[0] * fontspec->size;
848             vleft[1] = vleft[1] * fontspec->size;
849             vright[0] = vright[0] * fontspec->size;
850             vright[1] = vright[1] * fontspec->size;
851 
852             // create two 'normal' vectors pointing out from the edges
853             SbVec3f normala(vleft[0] - va[0], vleft[1] - va[1], 0.0f);
854             normala = normala.cross(SbVec3f(0.0f, 0.0f,  -1.0f));
855             if (normala.length() > 0)
856               normala.normalize();
857 
858             SbVec3f normalb(vb[0] - vright[0], vb[1] - vright[1], 0.0f);
859             normalb = normalb.cross(SbVec3f(0.0f, 0.0f,  -1.0f));
860             if (normalb.length() > 0)
861               normalb.normalize();
862 
863             SoProfile * pn = (SoProfile *) profilenodes[firstprofile];
864             pn->getVertices(state, profnum, profcoords);
865 
866             SbVec3f vc,vd;
867             SbVec2f starta(va[0], va[1]);
868             SbVec2f startb(vb[0], vb[1]);
869 
870             for (int j=firstprofile; j<numprofiles; j++) {
871               SoProfile * pn = (SoProfile *) profilenodes[j];
872               pn->getVertices(state, profnum, profcoords);
873 
874               for (int k=1; k<profnum; k++) {
875 
876                 // Calc points for two next faces
877                 vd[0] = starta[0] + (profcoords[k][1] * normalb[0]);
878                 vd[1] = starta[1] + (profcoords[k][1] * normalb[1]);
879                 vd[2] = -profcoords[k][0];
880                 vc[0] = startb[0] + (profcoords[k][1] * normala[0]);
881                 vc[1] = startb[1] + (profcoords[k][1] * normala[1]);
882                 vc[2] = -profcoords[k][0];
883 
884                 // The windows tesselation sometimes return
885                 // illegal/empty tris. A test must be done to
886                 // prevent stdout from being flooded with
887                 // normalize() warnings from inside the normal
888                 // generator.
889 
890                 if ((va != vd) && (va != vb) && (vd != vb)) {
891                   vertexlist.append(va);
892                   vertexlist.append(vd);
893                   vertexlist.append(vb);
894                   normalgenerator->triangle(va,vd,vb);
895                 }
896 
897                 if ((vb != vd) && (vb != vc) && (vd != vc)) {
898                   vertexlist.append(vb);
899                   vertexlist.append(vd);
900                   vertexlist.append(vc);
901                   normalgenerator->triangle(vb,vd,vc);
902                 }
903 
904                 va = vd;
905                 vb = vc;
906 
907               }
908             }
909 
910           }
911 
912           normalgenerator->generate(creaseangle);
913           const SbVec3f * normals = normalgenerator->getNormals();
914           const int size = vertexlist.getLength();
915 
916           // NOTE: We add the xpos and ypos to each vertex at this
917           // point because Linux systems seems to accumulate an error
918           // when calculating the normals (ie. two 'o's in a row
919           // doesn't get the same normals due to the xpos
920           // difference). This doesn't happen on Windows so it is
921           // probably a floating point precision issue linked to the
922           // compilator. (Tested on MSVC 6 and GCC 2.95.4) (20031010
923           // handegar).
924 
925           glBegin(GL_TRIANGLES);
926 
927           for (int z = 0;z < size;z += 3) {
928 
929             // FIXME: Add proper texturing for profile
930             // coords. (20031010 handegar)
931 
932             glNormal3fv(normals[z+2].getValue());
933             glVertex3fv(SbVec3f(vertexlist[z+2][0] + xpos,
934                                 vertexlist[z+2][1] + ypos,
935                                 vertexlist[z+2][2]).getValue());
936 
937             glNormal3fv(normals[z+1].getValue());
938             glVertex3fv(SbVec3f(vertexlist[z+1][0] + xpos,
939                                 vertexlist[z+1][1] + ypos,
940                                 vertexlist[z+1][2]).getValue());
941 
942             glNormal3fv(normals[z].getValue());
943             glVertex3fv(SbVec3f(vertexlist[z][0] + xpos,
944                                 vertexlist[z][1] + ypos,
945                                 vertexlist[z][2]).getValue());
946           }
947           glEnd();
948 
949           vertexlist.truncate(0);
950 
951         }
952 
953       }
954 
955       float advancex, advancey;
956       cc_glyph3d_getadvance(glyph, &advancex, &advancey);
957       xpos += advancex * fontspec->size;
958 
959     }
960     if (prevglyph) {
961       cc_glyph3d_unref(prevglyph);
962       prevglyph = NULL;
963     }
964     ypos -= fontspec->size * PUBLIC(this)->spacing.getValue();
965   }
966 }
967 
968 // render text geometry
969 void
render(SoState * COIN_UNUSED_ARG (state),unsigned int COIN_UNUSED_ARG (part))970 SoText3::render(SoState * COIN_UNUSED_ARG(state), unsigned int COIN_UNUSED_ARG(part))
971 {
972   assert(FALSE && "obsoleted");
973 }
974 
975 void
generate(SoAction * COIN_UNUSED_ARG (action),unsigned int COIN_UNUSED_ARG (part))976 SoText3::generate(SoAction * COIN_UNUSED_ARG(action), unsigned int COIN_UNUSED_ARG(part))
977 {
978   assert(FALSE && "obsoleted");
979 }
980 
981 // generate text geometry
982 void
generate(SoAction * action,const cc_font_specification * fontspec,unsigned int part)983 SoText3P::generate(SoAction * action, const cc_font_specification * fontspec,
984                    unsigned int part)
985 {
986   SoState * state = action->getState();
987 
988   // SoCreaseAngleElement is not enabled for SoGetPrimitiveCountAction.
989   float creaseangle = 0.5f;
990   if (state->isElementEnabled(SoCreaseAngleElement::getClassStackIndex())) {
991     creaseangle = SoCreaseAngleElement::get(state);
992   }
993   SoPrimitiveVertex vertex;
994   SoTextDetail detail;
995   detail.setPart(part);
996   vertex.setDetail(&detail);
997 
998   // we might get here from getPrimitiveCount(). We therefore need to
999   // check if lazy element is enabled
1000   if (SoMaterialBindingElement::get(state) !=
1001       SoMaterialBindingElement::OVERALL &&
1002       state->isElementEnabled(SoLazyElement::getClassStackIndex())) {
1003 
1004     SoLazyElement * lazyelement = SoLazyElement::getInstance(state);
1005     const int numdiffuse = lazyelement->getNumDiffuse();
1006 
1007     if (part == SoText3::SIDES && (numdiffuse > 1))
1008       vertex.setMaterialIndex(1);
1009     else if (part == SoText3::BACK && (numdiffuse > 2))
1010       vertex.setMaterialIndex(2);
1011   }
1012 
1013   SbBool do2Dtextures = FALSE;
1014   SbBool do3Dtextures = FALSE;
1015 
1016   // not all actions have these elements enabled
1017   // (for instance SoGetPrimitiveCountAction)
1018   if (state->isElementEnabled(SoMultiTextureEnabledElement::getClassStackIndex())) {
1019     if (SoMultiTextureEnabledElement::get(state)) do2Dtextures = TRUE;
1020   }
1021   // FIXME: implement proper support for 3D-texturing, and get rid of
1022   // this. (20031010 handegar)
1023   if (do3Dtextures) {
1024     static SbBool first = TRUE;
1025     if (first) {
1026       first = FALSE;
1027 #if COIN_DEBUG
1028       SoDebugError::postWarning("SoText3::GLRender",
1029                                 "3D-textures not supported for this node type yet.");
1030 #endif // COIN_DEBUG
1031     }
1032   }
1033 
1034 
1035   int i, n = this->widths.getLength();
1036 
1037   int firstprofile = -1;
1038   int32_t profnum;
1039   SbVec2f *profcoords;
1040   float nearz =  FLT_MAX;
1041   float farz  = -FLT_MAX;
1042 
1043   const SoNodeList & profilenodes = SoProfileElement::get(state);
1044   int numprofiles = profilenodes.getLength();
1045 
1046   if (numprofiles > 0) {
1047     assert(profilenodes[0]->getTypeId().isDerivedFrom(SoProfile::getClassTypeId()));
1048     // Find near/far z (for modifying position of front/back)
1049 
1050     for (int l = numprofiles-1; l >= 0; l--) {
1051       SoProfile * pn = (SoProfile *)profilenodes[l];
1052       pn->getVertices(state, profnum, profcoords);
1053 
1054       if (profnum > 0) {
1055         if (profcoords[profnum-1][0] > farz) farz = profcoords[profnum-1][0];
1056         if (profcoords[0][0] < nearz) nearz = profcoords[0][0];
1057         if (pn->linkage.getValue() == SoProfile::START_FIRST) {
1058           if (firstprofile == -1) firstprofile = l;
1059           break;
1060         }
1061       }
1062     }
1063 
1064     nearz = -nearz;
1065     farz = -farz;
1066   }
1067   else {
1068     nearz = 0.0;
1069     farz = -1.0;
1070   }
1071 
1072   float ypos = 0.0f;
1073   for (i = 0; i < n; i++) {
1074     detail.setStringIndex(i);
1075     float xpos = 0.0f;
1076     switch (PUBLIC(this)->justification.getValue()) {
1077     case SoText3::RIGHT:
1078       xpos = -this->widths[i];
1079       break;
1080     case SoText3::CENTER:
1081       xpos = - this->widths[i] * 0.5f;
1082       break;
1083     }
1084 
1085     SbString str = PUBLIC(this)->string[i];
1086     cc_glyph3d * prevglyph = NULL;
1087     const char * p = str.getString();
1088     size_t length = cc_string_utf8_validate_length(p);
1089     // No assertion as zero length is handled correctly (results in a new line)
1090 
1091     for (unsigned int strcharidx = 0; strcharidx < length; strcharidx++) {
1092       uint32_t glyphidx = 0;
1093 
1094       glyphidx = cc_string_utf8_get_char(p);
1095       p = cc_string_utf8_next_char(p);
1096 
1097       cc_glyph3d * glyph = cc_glyph3d_ref(glyphidx, fontspec);
1098       const SbVec2f * coords = (SbVec2f *) cc_glyph3d_getcoords(glyph);
1099 
1100       detail.setCharacterIndex(strcharidx);
1101 
1102       // Get kerning
1103       if (strcharidx > 0) {
1104         float kerningx, kerningy;
1105         cc_glyph3d_getkerning(prevglyph, glyph, &kerningx, &kerningy);
1106         xpos += kerningx * fontspec->size;
1107       }
1108       if (prevglyph) {
1109         cc_glyph3d_unref(prevglyph);
1110       }
1111       prevglyph = glyph;
1112 
1113       if (part != SoText3::SIDES) {  // FRONT & BACK
1114         const int * ptr = cc_glyph3d_getfaceindices(glyph);
1115         PUBLIC(this)->beginShape(action, SoShape::TRIANGLES, NULL);
1116 
1117         while (*ptr >= 0) {
1118           SbVec2f v0, v1, v2;
1119           float zval;
1120           if (part == SoText3::FRONT) {
1121             vertex.setNormal(SbVec3f(0.0f, 0.0f, 1.0f));
1122             v2 = coords[*ptr++];
1123             v1 = coords[*ptr++];
1124             v0 = coords[*ptr++];
1125             zval = nearz;
1126           }
1127           else {  // BACK
1128             vertex.setNormal(SbVec3f(0.0f, 0.0f, -1.0f));
1129             v0 = coords[*ptr++];
1130             v1 = coords[*ptr++];
1131             v2 = coords[*ptr++];
1132             zval = farz;
1133           }
1134 
1135           if(do2Dtextures) {
1136             vertex.setTextureCoords(SbVec2f(v0[0] + xpos/fontspec->size, v0[1] + ypos/fontspec->size));
1137           }
1138           vertex.setPoint(SbVec3f(v0[0] * fontspec->size + xpos, v0[1] * fontspec->size + ypos, zval));
1139           PUBLIC(this)->shapeVertex(&vertex);
1140 
1141           if(do2Dtextures) {
1142             vertex.setTextureCoords(SbVec2f(v1[0] + xpos/fontspec->size, v1[1] + ypos/fontspec->size));
1143           }
1144           vertex.setPoint(SbVec3f(v1[0] * fontspec->size + xpos, v1[1] * fontspec->size + ypos, zval));
1145           PUBLIC(this)->shapeVertex(&vertex);
1146 
1147           if(do2Dtextures) {
1148             vertex.setTextureCoords(SbVec2f(v2[0] + xpos/fontspec->size, v2[1] + ypos/fontspec->size));
1149           }
1150           vertex.setPoint(SbVec3f(v2[0] * fontspec->size + xpos, v2[1] * fontspec->size + ypos, zval));
1151           PUBLIC(this)->shapeVertex(&vertex);
1152         }
1153 
1154         PUBLIC(this)->endShape();
1155 
1156       }
1157       else { // SIDES
1158         if (profilenodes.getLength() == 0) {  // no profile - extrude
1159 
1160           const int * ptr = cc_glyph3d_getedgeindices(glyph);
1161           SbVec2f v0, v1;
1162           int counter = 0;
1163           PUBLIC(this)->beginShape(action, SoShape::QUADS, NULL);
1164 
1165           while (*ptr >= 0) {
1166             v1 = coords[*ptr++];
1167             v0 = coords[*ptr++];
1168             const int * ccw = (int *) cc_glyph3d_getnextccwedge(glyph, counter);
1169             const int * cw  = (int *) cc_glyph3d_getnextcwedge(glyph, counter);
1170             SbVec3f vleft(coords[*(ccw+1)][0], coords[*(ccw+1)][1], 0);
1171             SbVec3f vright(coords[*cw][0], coords[*cw][1], 0);
1172             counter++;
1173 
1174             v0[0] = v0[0] * fontspec->size;
1175             v0[1] = v0[1] * fontspec->size;
1176             v1[0] = v1[0] * fontspec->size;
1177             v1[1] = v1[1] * fontspec->size;
1178             vleft[0] = vleft[0] * fontspec->size;
1179             vleft[1] = vleft[1] * fontspec->size;
1180             vright[0] = vright[0] * fontspec->size;
1181             vright[1] = vright[1] * fontspec->size;
1182 
1183             // create two 'normal' vectors pointing out from the edges
1184             SbVec3f normala(vright[0] - v0[0], vright[1] - v0[1], 0.0f);
1185             normala = normala.cross(SbVec3f(0.0f, 0.0f,  1.0f));
1186             if (normala.length() > 0)
1187               normala.normalize();
1188 
1189             SbVec3f normalb(v1[0] - vleft[0], v1[1] - vleft[1], 0.0f);
1190             normalb = normalb.cross(SbVec3f(0.0f, 0.0f,  1.0f));
1191             if (normalb.length() > 0)
1192               normalb.normalize();
1193 
1194             SbBool flatshading = FALSE;
1195             float dot = normala.dot(normalb);
1196             if(acos(dot) > creaseangle) {
1197               normala = SbVec3f(v1[0] - v0[0], v1[1] - v0[1], 0.0f);
1198               normala = normala.cross(SbVec3f(0.0f, 0.0f,  1.0f));
1199               if (normala.length() > 0)
1200                 normala.normalize();
1201               flatshading = TRUE;
1202             }
1203 
1204             if (!flatshading) {
1205               if (do2Dtextures) {
1206                 vertex.setTextureCoords(SbVec2f(v1[0] + xpos/fontspec->size,
1207                                                 v1[1] + ypos/fontspec->size));
1208               }
1209               vertex.setNormal(normala);
1210               vertex.setPoint(SbVec3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, 0.0f));
1211               PUBLIC(this)->shapeVertex(&vertex);
1212 
1213               if (do2Dtextures) {
1214                 vertex.setTextureCoords(SbVec2f(v0[0] + xpos/fontspec->size,
1215                                                 v0[1] + ypos/fontspec->size));
1216               }
1217               vertex.setNormal(normalb);
1218               vertex.setPoint(SbVec3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, 0.0f));
1219               PUBLIC(this)->shapeVertex(&vertex);
1220 
1221               if (do2Dtextures) {
1222                 vertex.setTextureCoords(SbVec2f(v0[0] + xpos/fontspec->size,
1223                                                 v0[1] + ypos/fontspec->size));
1224               }
1225               vertex.setNormal(normalb);
1226               vertex.setPoint(SbVec3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, -1.0f));
1227               PUBLIC(this)->shapeVertex(&vertex);
1228 
1229               if (do2Dtextures) {
1230                 vertex.setTextureCoords(SbVec2f(v1[0] + xpos/fontspec->size,
1231                                                 v1[1] + ypos/fontspec->size));
1232               }
1233               vertex.setNormal(normala);
1234               vertex.setPoint(SbVec3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, -1.0f));
1235               PUBLIC(this)->shapeVertex(&vertex);
1236             }
1237             else {
1238               vertex.setNormal(normala);
1239 
1240               if (do2Dtextures) {
1241                 vertex.setTextureCoords(SbVec2f(v1[0] + xpos/fontspec->size,
1242                                                 v1[1] + ypos/fontspec->size));
1243               }
1244               vertex.setPoint(SbVec3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, 0.0f));
1245               PUBLIC(this)->shapeVertex(&vertex);
1246 
1247               if (do2Dtextures) {
1248                 vertex.setTextureCoords(SbVec2f(v0[0] + xpos/fontspec->size,
1249                                                 v0[1] + ypos/fontspec->size));
1250               }
1251               vertex.setPoint(SbVec3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, 0.0f));
1252               PUBLIC(this)->shapeVertex(&vertex);
1253 
1254               if (do2Dtextures) {
1255                 vertex.setTextureCoords(SbVec2f(v0[0] + xpos/fontspec->size,
1256                                                 v0[1] + ypos/fontspec->size));
1257               }
1258               vertex.setPoint(SbVec3f(v0[0]*fontspec->size + xpos, v0[1]*fontspec->size + ypos, -1.0f));
1259               PUBLIC(this)->shapeVertex(&vertex);
1260 
1261               if (do2Dtextures) {
1262                 vertex.setTextureCoords(SbVec2f(v1[0] + xpos/fontspec->size,
1263                                                 v1[1] + ypos/fontspec->size));
1264               }
1265               vertex.setPoint(SbVec3f(v1[0]*fontspec->size + xpos, v1[1]*fontspec->size + ypos, -1.0f));
1266               PUBLIC(this)->shapeVertex(&vertex);
1267 
1268             }
1269           }
1270 
1271           PUBLIC(this)->endShape();
1272 
1273         }
1274         else {  // profile
1275 
1276           const int *indices = cc_glyph3d_getedgeindices(glyph);
1277           int ind = 0;
1278           SbVec3f normala, normalb;
1279 
1280           SbList <SbVec3f> vertexlist;
1281           this->normalgenerator->reset(FALSE);
1282 
1283           while (*indices >= 0) {
1284 
1285             int i0 = *indices++;
1286             int i1 = *indices++;
1287             SbVec3f va(coords[i0][0], coords[i0][1], nearz);
1288             SbVec3f vb(coords[i1][0], coords[i1][1], nearz);
1289             const int *ccw = (int *) cc_glyph3d_getnextccwedge(glyph, ind);
1290             const int *cw  = (int *) cc_glyph3d_getnextcwedge(glyph, ind);
1291             SbVec3f vleft(coords[*(ccw+1)][0], coords[*(ccw+1)][1], nearz);
1292             SbVec3f vright(coords[*cw][0], coords[*cw][1], nearz);
1293             ind++;
1294 
1295             va[0] = va[0] * fontspec->size;
1296             va[1] = va[1] * fontspec->size;
1297             vb[0] = vb[0] * fontspec->size;
1298             vb[1] = vb[1] * fontspec->size;
1299             vleft[0] = vleft[0] * fontspec->size;
1300             vleft[1] = vleft[1] * fontspec->size;
1301             vright[0] = vright[0] * fontspec->size;
1302             vright[1] = vright[1] * fontspec->size;
1303 
1304             // create two 'normal' vectors pointing out from the edges
1305             SbVec3f normala(vleft[0] - va[0], vleft[1] - va[1], 0.0f);
1306             normala = normala.cross(SbVec3f(0.0f, 0.0f,  -1.0f));
1307             if (normala.length() > 0)
1308               normala.normalize();
1309 
1310             SbVec3f normalb(vb[0] - vright[0], vb[1] - vright[1], 0.0f);
1311             normalb = normalb.cross(SbVec3f(0.0f, 0.0f,  -1.0f));
1312             if (normalb.length() > 0)
1313               normalb.normalize();
1314 
1315             SoProfile *pn = (SoProfile *)profilenodes[firstprofile];
1316             pn->getVertices(state, profnum, profcoords);
1317 
1318             SbVec3f vc,vd;
1319             SbVec2f starta(va[0], va[1]);
1320             SbVec2f startb(vb[0], vb[1]);
1321 
1322             for (int j=firstprofile; j<numprofiles; j++) {
1323               SoProfile *pn = (SoProfile *)profilenodes[j];
1324               pn->getVertices(state, profnum, profcoords);
1325 
1326               for (int k=1; k<profnum; k++) {
1327 
1328                 // Calc points for two next faces
1329                 vd[0] = starta[0] + (profcoords[k][1] * normalb[0]);
1330                 vd[1] = starta[1] + (profcoords[k][1] * normalb[1]);
1331                 vd[2] = -profcoords[k][0];
1332                 vc[0] = startb[0] + (profcoords[k][1] * normala[0]);
1333                 vc[1] = startb[1] + (profcoords[k][1] * normala[1]);
1334                 vc[2] = -profcoords[k][0];
1335 
1336                 // The windows tesselation sometimes return
1337                 // illegal/empty tris. A test must be done to prevent
1338                 // stdout from being flooded with normalize() warnings
1339                 // from inside the normal generator.
1340 
1341                 if ((va != vd) && (va != vb) && (vd != vb)) {
1342                   vertexlist.append(va);
1343                   vertexlist.append(vd);
1344                   vertexlist.append(vb);
1345                   normalgenerator->triangle(va,vd,vb);
1346                 }
1347 
1348                 if ((vb != vd) && (vb != vc) && (vd != vc)) {
1349                   vertexlist.append(vb);
1350                   vertexlist.append(vd);
1351                   vertexlist.append(vc);
1352                   normalgenerator->triangle(vb,vd,vc);
1353                 }
1354 
1355                 va = vd;
1356                 vb = vc;
1357 
1358               }
1359 
1360             }
1361 
1362           }
1363 
1364 
1365           normalgenerator->generate(creaseangle);
1366           const SbVec3f * normals = normalgenerator->getNormals();
1367           const int size = vertexlist.getLength();
1368 
1369           PUBLIC(this)->beginShape(action, SoShape::TRIANGLES, NULL);
1370           for (int z = 0;z < size;z += 3) {
1371             vertex.setNormal(normals[z+2].getValue());
1372             vertex.setPoint(SbVec3f(vertexlist[z+2][0] + xpos,
1373                                     vertexlist[z+2][1] + ypos,
1374                                     vertexlist[z+2][2]).getValue());
1375             PUBLIC(this)->shapeVertex(&vertex);
1376 
1377             vertex.setNormal(normals[z+1].getValue());
1378             vertex.setPoint(SbVec3f(vertexlist[z+1][0] + xpos,
1379                                     vertexlist[z+1][1] + ypos,
1380                                     vertexlist[z+1][2]).getValue());
1381             PUBLIC(this)->shapeVertex(&vertex);
1382 
1383             vertex.setNormal(normals[z].getValue());
1384             vertex.setPoint(SbVec3f(vertexlist[z][0] + xpos,
1385                                     vertexlist[z][1] + ypos,
1386                                     vertexlist[z][2]).getValue());
1387             PUBLIC(this)->shapeVertex(&vertex);
1388 
1389           }
1390           PUBLIC(this)->endShape();
1391           vertexlist.truncate(0);
1392 
1393         }
1394       }
1395 
1396       float advancex, advancey;
1397       cc_glyph3d_getadvance(glyph, &advancex, &advancey);
1398       xpos += advancex * fontspec->size;
1399 
1400     }
1401     if (prevglyph) {
1402       cc_glyph3d_unref(prevglyph);
1403       prevglyph = NULL;
1404     }
1405     ypos -= fontspec->size * PUBLIC(this)->spacing.getValue();
1406   }
1407 
1408 
1409 }
1410 
1411 // Documented in superclass.
1412 void
notify(SoNotList * list)1413 SoText3::notify(SoNotList * list)
1414 {
1415   PRIVATE(this)->lock();
1416   if (PRIVATE(this)->cache) {
1417     SoField * f = list->getLastField();
1418     if (f == &this->string) PRIVATE(this)->cache->invalidate();
1419   }
1420   PRIVATE(this)->unlock();
1421   inherited::notify(list);
1422 }
1423 
1424 // recalculate glyphs
1425 void
setUpGlyphs(SoState * state,SoText3 * textnode)1426 SoText3P::setUpGlyphs(SoState * state, SoText3 * textnode)
1427 {
1428   // not all actions have SoCacheElement enabled
1429   if (!state->isElementEnabled(SoCacheElement::getClassStackIndex())) return;
1430   if (this->cache && this->cache->isValid(state)) return;
1431   SoGlyphCache * oldcache = this->cache;
1432 
1433   state->push();
1434   SbBool storedinvalid = SoCacheElement::setInvalid(FALSE);
1435   this->cache = new SoGlyphCache(state);
1436   this->cache->ref();
1437   SoCacheElement::set(state, this->cache);
1438   this->cache->readFontspec(state);
1439   const cc_font_specification * fontspec = this->cache->getCachedFontspec();
1440 
1441   this->widths.truncate(0);
1442 
1443   for (int i = 0; i < textnode->string.getNum(); i++) {
1444 
1445     float stringwidth = 0.0f;
1446     float kerningx = 0;
1447     float kerningy = 0;
1448     float advancex = 0;
1449     float advancey = 0;
1450     cc_glyph3d * prevglyph = NULL;
1451 
1452     const float * maxbbox;
1453     this->maxglyphbbox.makeEmpty();
1454 
1455     SbString str = textnode->string[i];
1456     const char * p = str.getString();
1457     size_t length = cc_string_utf8_validate_length(p);
1458     // No assertion as zero length is handled correctly (results in a new line)
1459 
1460     for (unsigned int strcharidx = 0; strcharidx < length; strcharidx++) {
1461       uint32_t glyphidx = 0;
1462 
1463       glyphidx = cc_string_utf8_get_char(p);
1464       p = cc_string_utf8_next_char(p);
1465 
1466       cc_glyph3d * glyph = cc_glyph3d_ref(glyphidx, fontspec);
1467       this->cache->addGlyph(glyph);
1468       assert(glyph);
1469 
1470       maxbbox = cc_glyph3d_getboundingbox(glyph); // Get max height
1471 
1472       this->maxglyphbbox.extendBy(SbVec3f(0, maxbbox[1] * fontspec->size, 0));
1473       this->maxglyphbbox.extendBy(SbVec3f(0, maxbbox[3] * fontspec->size, 0));
1474 
1475       if (strcharidx > 0)
1476         cc_glyph3d_getkerning(prevglyph, glyph, &kerningx, &kerningy);
1477       cc_glyph3d_getadvance(glyph, &advancex, &advancey);
1478 
1479       stringwidth += (advancex + kerningx) * fontspec->size;
1480       prevglyph = glyph;
1481     }
1482 
1483     if (prevglyph != NULL) {
1484       // Italic font might cause last letter to be outside bbox. Add width if needed.
1485       if (advancex < cc_glyph3d_getwidth(prevglyph))
1486         stringwidth += (cc_glyph3d_getwidth(prevglyph) - advancex) * fontspec->size;
1487     }
1488 
1489     this->widths.append(stringwidth);
1490   }
1491 
1492   state->pop();
1493   SoCacheElement::setInvalid(storedinvalid);
1494 
1495   // unref old cache after creating the new one to avoid recreating glyphs
1496   if (oldcache) oldcache->unref();
1497 
1498 }
1499 
1500 #undef PRIVATE
1501 #undef PUBLIC
1502