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