00001
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00033
00034 #ifndef GROOVX_VISX_MORPHYFACE_CC_UTC20050626084017_DEFINED
00035 #define GROOVX_VISX_MORPHYFACE_CC_UTC20050626084017_DEFINED
00036
00037 #include "visx/morphyface.h"
00038
00039 #include "geom/bezier4.h"
00040 #include "geom/rect.h"
00041 #include "geom/vec3.h"
00042
00043 #include "gfx/bbox.h"
00044 #include "gfx/canvas.h"
00045 #include "gfx/gxscaler.h"
00046 #include "gfx/linestrip.h"
00047
00048 #include "io/ioproxy.h"
00049 #include "io/reader.h"
00050 #include "io/writer.h"
00051
00052 #include "rutz/algo.h"
00053
00054 #include "rutz/trace.h"
00055 #include "rutz/debug.h"
00056 GVX_DBG_REGISTER
00057
00058 using geom::vec2d;
00059 using geom::vec3d;
00060
00062
00063
00064
00066
00067 namespace
00068 {
00069 const io::version_id MFACE_SVID = 2;
00070
00071 const unsigned int NUM_HAIR_POINTS = 15;
00072
00073
00074 const double* getHairVertices(double top_width, double hair_width)
00075 {
00076 GVX_TRACE("getHairVertices");
00077
00078 static double hair_vertices[NUM_HAIR_POINTS*4];
00079
00080 static const double hair_widths[NUM_HAIR_POINTS] =
00081 {
00082 0.40, 0.90, 0.95,
00083 1.00, 0.97, 0.94,
00084 0.91, 0.88, 0.85,
00085 0.81, 0.78, 0.75,
00086 0.7, 0.6, 0.0
00087 };
00088
00089 geom::bezier4 xbezier(-1.0, -top_width, top_width, 1.0);
00090 geom::bezier4 ybezier( 0.0, 4.0/3.0 , 4.0/3.0 , 0.0);
00091
00092 for (unsigned int i = 0; i < NUM_HAIR_POINTS; ++i)
00093 {
00094 const double u = 0.5 + double(i)/double(2*NUM_HAIR_POINTS-2);
00095
00096 const double x = xbezier.eval(u);
00097 const double y = ybezier.eval(u);
00098
00099 const double tang_x = xbezier.eval_deriv(u);
00100 const double tang_y = ybezier.eval_deriv(u);
00101
00102
00103
00104 const double norm_x = -tang_y;
00105 const double norm_y = tang_x;
00106
00107
00108 const double norm_factor = 1.0 / sqrt(norm_x*norm_x + norm_y*norm_y);
00109
00110 const double hair_x_normal = norm_x * norm_factor;
00111 const double hair_y_normal = norm_y * norm_factor;
00112
00113 const double width_factor = hair_widths[i]*hair_width;
00114
00115 hair_vertices[4*i] = x - hair_x_normal*width_factor;
00116 hair_vertices[4*i+1] = y - hair_y_normal*width_factor;
00117 hair_vertices[4*i+2] = x + hair_x_normal*width_factor;
00118 hair_vertices[4*i+3] = y + hair_y_normal*width_factor;
00119 }
00120
00121 return &hair_vertices[0];
00122 }
00123
00124 void drawHairStrip(Gfx::Canvas& canvas,
00125 const double* vertices, double x_scale, double y_scale)
00126 {
00127 Gfx::MatrixSaver saver(canvas);
00128
00129 canvas.scale(vec3d(x_scale, y_scale, 1.0));
00130
00131 Gfx::QuadStripBlock block(canvas);
00132
00133 for (unsigned int i = 0; i < NUM_HAIR_POINTS; ++i)
00134 {
00135 canvas.vertex2(vec2d(vertices[4*i ], vertices[4*i+1]));
00136 canvas.vertex2(vec2d(vertices[4*i+2], vertices[4*i+3]));
00137 }
00138 }
00139
00140 }
00141
00143
00144
00145
00147
00149
00151
00152 const FieldMap& MorphyFace::classFields()
00153 {
00154 typedef MorphyFace MF;
00155
00156 static const Field FIELD_ARRAY[] =
00157 {
00158 Field("category", &MF::itsMfaceCategory, 0, 0, 10, 1, Field::NEW_GROUP),
00159
00160 Field("faceWidth", &MF::itsFaceWidth, 2.75, 1.5, 3.5, 0.1),
00161 Field("topWidth", &MF::itsTopWidth, 1.15, 0.05, 2.0, 0.05),
00162 Field("bottomWidth", &MF::itsBottomWidth, 1.0, 0.05, 2.0, 0.05),
00163 Field("topHeight", &MF::itsTopHeight, 3.8, 0.5, 5.0, 0.25),
00164 Field("bottomHeight", &MF::itsBottomHeight, -3.0, -5.0, -0.5, 0.25),
00165 Field("strokeWidth", &MF::itsStrokeWidth, 1.0, 0.1, 10.0, 0.1,
00166 Field::TRANSIENT),
00167 Field("lineJoin", &MF::itsLineJoin, true, false, true, true,
00168 Field::TRANSIENT | Field::BOOLEAN),
00169
00170 Field("hairWidth", &MF::itsHairWidth, 0.20, 0.00, 0.5, 0.02, Field::NEW_GROUP),
00171 Field("hairStyle", &MF::itsHairStyle, 0, 0, 1, 1),
00172
00173 Field("eyeYpos", &MF::itsEyeYpos, 0.375, -2.0, 2.0, 0.1, Field::NEW_GROUP),
00174 Field("eyeDistance", &MF::itsEyeDistance, 2.25, 0.0, 5.0, 0.25),
00175 Field("eyeHeight", &MF::itsEyeHeight, 0.9, 0.0, 2.0, 0.1),
00176 Field("eyeAspectRatio", &MF::itsEyeAspectRatio, 1.555556, 0.1, 5.0, 0.1),
00177
00178 Field("pupilXpos", &MF::itsPupilXpos, 0.0, -0.5, 0.5, 0.05, Field::NEW_GROUP),
00179 Field("pupilYpos", &MF::itsPupilYpos, 0.0, -0.5, 0.5, 0.05),
00180 Field("pupilSize", &MF::itsPupilSize, 0.6, 0.0, 1.0, 0.05),
00181 Field("pupilDilation", &MF::itsPupilDilation,
00182 0.5, 0.05, 0.95, 0.05, Field::CHECKED),
00183
00184 Field("eyebrowXpos", &MF::itsEyebrowXpos, 0.0, -0.5, 0.5, 0.02,
00185 Field::NEW_GROUP),
00186 Field("eyebrowYpos", &MF::itsEyebrowYpos, 0.5, 0.0, 1.5, 0.05),
00187 Field("eyebrowCurvature", &MF::itsEyebrowCurvature, 0.8, -2.0, 2.0, 0.1),
00188 Field("eyebrowAngle", &MF::itsEyebrowAngle, -5.0, -50.0, 50.0, 1.0),
00189 Field("eyebrowThickness", &MF::itsEyebrowThickness, 2.0, 0.1, 4.0, 0.1),
00190
00191 Field("noseXpos", &MF::itsNoseXpos, 0.0, -1.0, 1.0, 0.05, Field::NEW_GROUP),
00192 Field("noseYpos", &MF::itsNoseYpos, -0.825, -2.0, 2.0, 0.1),
00193 Field("noseLength", &MF::itsNoseLength, 0.75, 0.0, 2.0, 0.1),
00194 Field("noseWidth", &MF::itsNoseWidth, 1.5, 0.0, 3.0, 0.1),
00195
00196 Field("mouthXpos", &MF::itsMouthXpos, 0.0, -2.0, 2.0, 0.1, Field::NEW_GROUP),
00197 Field("mouthYpos", &MF::itsMouthYpos, -2.0, -3.0, 1.0, 0.1),
00198 Field("mouthWidth", &MF::itsMouthWidth, 2.5, 0.0, 5.0, 0.25),
00199 Field("mouthCurvature", &MF::itsMouthCurvature, 0.6, -2.0, 2.0, 0.1)
00200 };
00201
00202 static FieldMap MFACE_FIELDS(FIELD_ARRAY, &GxShapeKit::classFields());
00203
00204 return MFACE_FIELDS;
00205 }
00206
00207 MorphyFace* MorphyFace::make()
00208 {
00209 GVX_TRACE("MorphyFace::make");
00210 return new MorphyFace;
00211 }
00212
00213 MorphyFace::MorphyFace() :
00214 GxShapeKit(),
00215
00216 itsMfaceCategory(0),
00217
00218 itsFaceWidth(2.75),
00219 itsTopWidth(1.15),
00220 itsBottomWidth(1.0),
00221 itsTopHeight(3.8),
00222 itsBottomHeight(-3.0),
00223
00224 itsHairWidth(0.15),
00225 itsHairStyle(0),
00226
00227 itsEyeYpos(0.375),
00228 itsEyeDistance(2.25),
00229 itsEyeHeight(0.9),
00230 itsEyeAspectRatio(1.555556),
00231
00232 itsPupilXpos(0.0),
00233 itsPupilYpos(0.0),
00234 itsPupilSize(0.6),
00235 itsPupilDilation(0.5),
00236
00237 itsEyebrowXpos(0.0),
00238 itsEyebrowYpos(0.5),
00239 itsEyebrowCurvature(0.8),
00240 itsEyebrowAngle(-5),
00241 itsEyebrowThickness(2.0),
00242
00243 itsNoseXpos(0.0),
00244 itsNoseYpos(-0.825),
00245 itsNoseLength(0.75),
00246 itsNoseWidth(1.5),
00247
00248 itsMouthXpos(0.0),
00249 itsMouthYpos(-2.0),
00250 itsMouthWidth(2.5),
00251 itsMouthCurvature(0.6),
00252
00253 itsStrokeWidth(3.0),
00254 itsLineJoin(true)
00255 {
00256 GVX_TRACE("MorphyFace::MorphyFace");
00257
00258 setFieldMap(MorphyFace::classFields());
00259
00260 setScalingMode(GxScaler::MAINTAIN_ASPECT_SCALING);
00261 setMaxDimension(1.0);
00262 }
00263
00264 MorphyFace::~MorphyFace() throw()
00265 {
00266 GVX_TRACE("MorphyFace::~MorphyFace");
00267
00268 }
00269
00270 io::version_id MorphyFace::class_version_id() const
00271 {
00272 GVX_TRACE("MorphyFace::class_version_id");
00273 return MFACE_SVID;
00274 }
00275
00276 void MorphyFace::read_from(io::reader& reader)
00277 {
00278 GVX_TRACE("MorphyFace::read_from");
00279
00280 reader.ensure_version_id("MorphyFace", 2,
00281 "Try cvs tag xml_conversion_20040526",
00282 SRC_POS);
00283
00284 readFieldsFrom(reader, classFields());
00285
00286 reader.read_base_class("GxShapeKit", io::make_proxy<GxShapeKit>(this));
00287 }
00288
00289 void MorphyFace::write_to(io::writer& writer) const
00290 {
00291 GVX_TRACE("MorphyFace::write_to");
00292
00293 writer.ensure_output_version_id("MorphyFace", MFACE_SVID, 2,
00294 "Try groovx0.8a4", SRC_POS);
00295
00296 writeFieldsTo(writer, classFields(), MFACE_SVID);
00297
00298 writer.write_base_class("GxShapeKit", io::make_const_proxy<GxShapeKit>(this));
00299 }
00300
00301
00303
00305
00306 void MorphyFace::grRender(Gfx::Canvas& canvas) const
00307 {
00308 GVX_TRACE("MorphyFace::grRender");
00309
00310 Gfx::AttribSaver attribSaver(canvas);
00311
00312
00313
00314 Gfx::LineStrip ls;
00315 ls.lineJoin(itsLineJoin);
00316
00317
00318
00319
00320
00321 const vec3d eye_ctrlpnts[4] =
00322 {
00323 vec3d(-3.0/7.0, 0.0, 0.0),
00324 vec3d(-2.0/7.0, 2.0/3.0, 0.0),
00325 vec3d( 2.0/7.0, 2.0/3.0, 0.0),
00326 vec3d( 4.0/7.0, 0.0, 0.0)
00327 };
00328
00329 const int eye_subdivisions = 20;
00330
00331 for (int left_right = -1; left_right < 2; left_right += 2)
00332 {
00333 Gfx::MatrixSaver msaver(canvas);
00334
00335
00336 canvas.scale(vec3d(left_right*1.0, 1.0, 1.0));
00337
00338
00339 canvas.translate(vec3d(rutz::abs(itsEyeDistance)/2.0,
00340 itsEyeYpos, 0.0));
00341
00342
00343 {
00344 GVX_TRACE("MorphyFace::grRender-draw eye outline");
00345
00346 const vec3d s1(itsEyeHeight*itsEyeAspectRatio,
00347 -itsEyeHeight,
00348 1.0);
00349 const vec3d s2(itsEyeHeight*itsEyeAspectRatio,
00350 itsEyeHeight,
00351 1.0);
00352
00353 ls.closeLoop(true);
00354 ls.begin(canvas, 0.01*itsStrokeWidth);
00355
00356 ls.drawBezier4(s1*eye_ctrlpnts[3], s1*eye_ctrlpnts[2],
00357 s1*eye_ctrlpnts[1], s1*eye_ctrlpnts[0],
00358 eye_subdivisions, 1
00359
00360 );
00361 ls.drawBezier4(s2*eye_ctrlpnts[0], s2*eye_ctrlpnts[1],
00362 s2*eye_ctrlpnts[2], s2*eye_ctrlpnts[3],
00363 eye_subdivisions, 1
00364
00365 );
00366 ls.end();
00367 ls.closeLoop(false);
00368 }
00369
00370
00371 {
00372 GVX_TRACE("MorphyFace::grRender-draw eyebrow");
00373
00374 Gfx::MatrixSaver msaver3(canvas);
00375
00376 canvas.translate(vec3d(itsEyebrowXpos, itsEyebrowYpos, 0.0));
00377 canvas.rotate(vec3d::unit_z(), itsEyebrowAngle);
00378
00379 Gfx::AttribSaver asaver(canvas);
00380
00381 canvas.setLineWidth(itsEyebrowThickness);
00382
00383 const vec3d s(itsEyeHeight*itsEyeAspectRatio,
00384 itsEyeHeight*itsEyebrowCurvature,
00385 1.0);
00386
00387 ls.begin(canvas, 0.01*itsStrokeWidth*itsEyebrowThickness);
00388 ls.drawBezier4(s*eye_ctrlpnts[0], s*eye_ctrlpnts[1],
00389 s*eye_ctrlpnts[2], s*eye_ctrlpnts[3],
00390 eye_subdivisions);
00391 ls.end();
00392 }
00393
00394
00395 {
00396 GVX_TRACE("MorphyFace::grRender-draw pupil");
00397
00398 Gfx::MatrixSaver msaver4(canvas);
00399
00400 canvas.translate(vec3d(left_right*itsPupilXpos,
00401 itsPupilYpos, 0.0));
00402
00403 double radius = 0.5 * itsPupilSize * itsEyeHeight;
00404
00405 static const int num_slices = 20;
00406 static const int num_loops = 1;
00407
00408 canvas.drawCircle(radius*rutz::abs(itsPupilDilation), radius, true,
00409 num_slices, num_loops);
00410 }
00411 }
00412
00413
00414
00415
00416
00417 ls.closeLoop(true);
00418 ls.begin(canvas, 0.015*itsStrokeWidth);
00419 ls.drawBezier4(vec3d(itsFaceWidth, 0.0, 0.0),
00420 vec3d(itsBottomWidth*itsFaceWidth,
00421 itsBottomHeight*4.0/3.0, 0.0),
00422 vec3d(-itsBottomWidth*itsFaceWidth,
00423 itsBottomHeight*4.0/3.0, 0.0),
00424 vec3d(-itsFaceWidth, 0.0, 0.0),
00425 50, 1);
00426 ls.drawBezier4(vec3d(-itsFaceWidth, 0.0, 0.0),
00427 vec3d(-itsTopWidth*itsFaceWidth,
00428 itsTopHeight*4.0/3.0, 0.0),
00429 vec3d(itsTopWidth*itsFaceWidth,
00430 itsTopHeight*4.0/3.0, 0.0),
00431 vec3d(itsFaceWidth, 0.0, 0.0),
00432 50, 1);
00433 ls.end();
00434 ls.closeLoop(false);
00435
00436
00437
00438
00439
00440 {
00441 GVX_TRACE("MorphyFace::grRender-draw nose");
00442
00443 Gfx::MatrixSaver msaver5(canvas);
00444
00445 canvas.translate(vec3d(itsNoseXpos, itsNoseYpos, 0.0));
00446
00447 const vec2d s(rutz::abs(itsNoseWidth)/2.0,
00448 rutz::abs(itsNoseLength));
00449
00450 ls.begin(canvas, 0.01*itsStrokeWidth);
00451 ls.vertex(s*vec2d(-0.75, 0.5));
00452 ls.vertex(s*vec2d(-1.0, 0.0));
00453 ls.vertex(s*vec2d(-0.75, -0.333333));
00454 ls.vertex(s*vec2d(-0.25, -0.333333));
00455 ls.vertex(s*vec2d( 0.0, -0.5));
00456 ls.vertex(s*vec2d( 0.25, -0.333333));
00457 ls.vertex(s*vec2d( 0.75, -0.333333));
00458 ls.vertex(s*vec2d( 1.0, 0.0));
00459 ls.vertex(s*vec2d( 0.75, 0.5));
00460 ls.end();
00461 }
00462
00463
00464
00465
00466
00467 {
00468 GVX_TRACE("MorphyFace::grRender-draw mouth");
00469
00470 Gfx::MatrixSaver msaver6(canvas);
00471
00472 canvas.translate(vec3d(itsMouthXpos, itsMouthYpos, 0.0));
00473
00474 const vec3d s(itsMouthWidth, itsMouthCurvature, 1.0);
00475
00476 ls.begin(canvas, 0.01*itsStrokeWidth);
00477 ls.drawBezier4(s*vec3d(-0.5, 0.5, 0.0),
00478 s*vec3d(-0.2, -0.833333, 0.0),
00479 s*vec3d( 0.2, -0.833333, 0.0),
00480 s*vec3d( 0.5, 0.5, 0.0),
00481 30);
00482 ls.end();
00483 }
00484
00485
00486
00487
00488
00489 {
00490 GVX_TRACE("MorphyFace::grRender-draw hair");
00491
00492 Gfx::AttribSaver saver(canvas);
00493
00494 canvas.setPolygonFill((itsHairStyle == 0));
00495
00496 const double* hair_vertices = getHairVertices(itsTopWidth, itsHairWidth);
00497
00498
00499 drawHairStrip(canvas, hair_vertices, itsFaceWidth, itsTopHeight);
00500
00501
00502 drawHairStrip(canvas, hair_vertices, -itsFaceWidth, itsTopHeight);
00503 }
00504 }
00505
00507
00509
00510 void MorphyFace::grGetBoundingBox(Gfx::Bbox& bbox) const
00511 {
00512 GVX_TRACE("MorphyFace::grGetBoundingBox");
00513
00514 geom::bezier4 xbezier_top(-1.0, -itsTopWidth, itsTopWidth, 1.0);
00515 geom::bezier4 xbezier_bottom(1.0, itsBottomWidth, -itsBottomWidth, -1.0);
00516
00517 double top_width = xbezier_top.eval_max();
00518 double bottom_width = xbezier_bottom.eval_max();
00519
00520 dbg_eval(3, top_width); dbg_eval_nl(3, bottom_width);
00521
00522 double max_width = rutz::max(1.0, rutz::max(top_width, bottom_width));
00523
00524 dbg_eval_nl(3, max_width);
00525
00526 const double l = -max_width * itsFaceWidth * (1 + itsHairWidth);
00527 const double r = max_width * itsFaceWidth * (1 + itsHairWidth);
00528 const double t = itsTopHeight * (1 + itsHairWidth);
00529 const double b = itsBottomHeight;
00530
00531 bbox.drawRect(geom::rect<double>::ltrb(l,t,r,b));
00532 }
00533
00534 static const char __attribute__((used)) vcid_groovx_visx_morphyface_cc_utc20050626084017[] = "$Id: morphyface.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00535 #endif // !GROOVX_VISX_MORPHYFACE_CC_UTC20050626084017_DEFINED