]> git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/meshtex/MeshEntity.cpp
Merge commit 'b017c473e86330d5908858b8add1b16674b0a1a8' into garux-merge
[xonotic/netradiant.git] / contrib / meshtex / MeshEntity.cpp
1 /**
2  * @file MeshEntity.cpp
3  * Implements the MeshEntity class.
4  * @ingroup meshtex-core
5  */
6
7 /*
8  * Copyright 2012 Joel Baxter
9  *
10  * This file is part of MeshTex.
11  *
12  * MeshTex is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * MeshTex is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with MeshTex.  If not, see <http://www.gnu.org/licenses/>.
24  */
25
26 #include <cmath>
27
28 #include "MeshEntity.h"
29 #include "MeshEntityMessages.h"
30
31 #include "ishaders.h"
32 #include "texturelib.h"
33
34
35 /**
36  * Size of buffer for composing messages to send to the info callback.
37  */
38 #define INFO_BUFFER_SIZE 1024
39
40 /**
41  * Stop successive refinement of path length estimates when the change in values
42  * is equal to or less than this tolerance.
43  */
44 #define UNITS_ERROR_BOUND 0.5
45
46 /**
47  * Macro to get ROW_SLICE_TYPE from COL_SLICE_TYPE and vice versa, used in
48  * code that can operate on either kind of slice. This does mean that the
49  * numerical values assigned to ROW_SLICE_TYPE and COL_SLICE_TYPE are
50  * meaningful.
51  *
52  * @param sliceType Kind of slice to find the other of, so to speak.
53  */
54 #define OtherSliceType(sliceType) (1 - (sliceType))
55
56 /**
57  * Macro to find the number of control points in a slice, which is equal to
58  * the number of slices of the other kind.
59  *
60  * @param sliceType Kind of slice to measure.
61  */
62 #define SliceSize(sliceType) (_numSlices[OtherSliceType(sliceType)])
63
64 /**
65  * Macro to get rid of negative-zero values.
66  *
67  * @param floatnum Number to be sanitized.
68  */
69 #define SanitizeFloat(floatnum) ((floatnum) == -0.0f ? 0.0f : (floatnum))
70
71
72 /**
73  * For a given slice kind, which texture axis (S or T) normally changes
74  * along it.
75  */
76 MeshEntity::TextureAxis MeshEntity::_naturalAxis[NUM_SLICE_TYPES] =
77    { S_TEX_AXIS, T_TEX_AXIS };
78
79 /**
80  * For a given slice kind, whether Radiant's "natural" scale along a texture
81  * axis is backwards compared to the progression of the indices of the
82  * orthogonal slices.
83  */
84 bool MeshEntity::_radiantScaleInverted[NUM_SLICE_TYPES] = { false, true };
85
86 /**
87  * For a given slice kind, whether Radiant's interpretation of tiling along a
88  * texture axis is backwards compared to the progression of the indices of
89  * the orthogonal slices.
90  */
91 bool MeshEntity::_radiantTilesInverted[NUM_SLICE_TYPES] = { false, false };
92
93 /**
94   * Message format strings for describing texture mapping on a slice.
95   */
96 const char *MeshEntity::_infoSliceFormatString[NUM_SLICE_TYPES] =
97    { INFO_ROW_FORMAT, INFO_COL_FORMAT };
98
99 /**
100  * Message format strings for describing texture mapping on a slice in the
101  * unusual case where the scale value is infinite.
102  */
103 const char *MeshEntity::_infoSliceInfscaleFormatString[NUM_SLICE_TYPES] =
104    { INFO_ROW_INFSCALE_FORMAT, INFO_COL_INFSCALE_FORMAT };
105
106 /**
107  * Message format strings for warning that a scale value is infinite and
108  * cannot be transferred to the Set S/T Scale dialog.
109  */
110 const char *MeshEntity::_warningSliceInfscaleFormatString[NUM_SLICE_TYPES] =
111    { WARNING_ROW_INFSCALE, WARNING_COL_INFSCALE };
112
113 /**
114  * Message format strings for an illegal slice number error.
115  */
116 const char *MeshEntity::_errorBadSliceString[NUM_SLICE_TYPES] =
117    { ERROR_BAD_ROW, ERROR_BAD_COL };
118
119 /**
120  * Message format strings for a scale = 0 error.
121  */
122 const char *MeshEntity::_errorSliceZeroscaleString[NUM_SLICE_TYPES] =
123    { ERROR_ROW_ZEROSCALE, ERROR_COL_ZEROSCALE };
124
125 /**
126  * Message format strings for a tiles = 0 error.
127  */
128 const char *MeshEntity::_errorSliceZerotilesString[NUM_SLICE_TYPES] =
129    { ERROR_ROW_ZEROTILES, ERROR_COL_ZEROTILES };
130
131
132 /**
133  * Constructor. If the constructor is unable to process the input mesh, then
134  * the internal valid flag (queryable through IsValid) is set false, and the
135  * errorReportCallback is invoked.
136  *
137  * @param mesh                  The patch mesh to construct a wrapper for.
138  * @param infoReportCallback    Callback for future informational messages.
139  * @param warningReportCallback Callback for future warning messages.
140  * @param errorReportCallback   Callback for future error messages.
141  */
142 MeshEntity::MeshEntity(scene::Node& mesh,
143                        const MessageCallback& infoReportCallback,
144                        const MessageCallback& warningReportCallback,
145                        const MessageCallback& errorReportCallback) :
146    _mesh(mesh),
147    _infoReportCallback(infoReportCallback),
148    _warningReportCallback(warningReportCallback),
149    _errorReportCallback(errorReportCallback)
150 {
151    // Get a handle on the control points, for future manipulation.
152    _meshData = GlobalPatchCreator().Patch_getControlPoints(_mesh);
153    // Record some useful characteristics of the mesh.
154    _numSlices[ROW_SLICE_TYPE] = static_cast<int>(_meshData.x());
155    _numSlices[COL_SLICE_TYPE] = static_cast<int>(_meshData.y());
156    const char *shaderName = GlobalPatchCreator().Patch_getShader(_mesh);
157    IShader *shader = GlobalShaderSystem().getShaderForName(shaderName);
158    qtexture_t *texture = shader->getTexture();
159    if (texture != NULL)
160    {
161       _naturalTexUnits[S_TEX_AXIS] = texture->width / 2.0f;
162       _naturalTexUnits[T_TEX_AXIS] = texture->height / 2.0f;
163    }
164    // We don't need the shader for anything else now.
165    shader->DecRef();
166    // Check for valid mesh; bail if not.
167    if (_numSlices[ROW_SLICE_TYPE] < 3 ||
168        _numSlices[COL_SLICE_TYPE] < 3 ||
169        texture == NULL)
170    {
171       _valid = false;
172       _errorReportCallback(ERROR_BAD_MESH);
173       return;
174    }
175    _valid = true;
176    // Find the worldspace extents of the mesh now... they won't change during
177    // the lifetime of this object.
178    UpdatePosMinMax(X_POS_AXIS);
179    UpdatePosMinMax(Y_POS_AXIS);
180    UpdatePosMinMax(Z_POS_AXIS);
181    // We'll calculate the S/T extents lazily.
182    _texMinMaxDirty[S_TEX_AXIS] = true;
183    _texMinMaxDirty[T_TEX_AXIS] = true;
184 }
185
186 /**
187  * Destructor. Note that this only destroys the wrapper object, not the patch
188  * mesh itself.
189  */
190 MeshEntity::~MeshEntity()
191 {
192 }
193
194 /**
195  * Query if the patch mesh is valid, in the characteristics that this wrapper
196  * class cares about. If not valid then the results of operations on this
197  * wrapper object are undefined.
198  *
199  * @return true if valid, false if not.
200  */
201 bool
202 MeshEntity::IsValid() const
203 {
204    return _valid;
205 }
206
207 /**
208  * Get information about the patch mesh.
209  * 
210  * A message string describing general mesh information (number of rows/cols,
211  * min/max texture coords, extent in worldspace) will be composed and sent to
212  * the infoReportCallback that was specified when this wrapper object was
213  * constructed.
214  * 
215  * Optionally this method can do additional reporting on a specific
216  * "reference row" and "reference column". If a reference row and/or column
217  * is specified, then information about the reference slice(s) will be added
218  * to the information message. If a reference row/col is specified AND a
219  * corresponding row/col TexInfoCallback is specified, then the scale and
220  * tiling values for the reference slice will also be passed to the relevant
221  * callback.
222  *
223  * @param refRow             Pointer to reference row number; NULL if none.
224  * @param refCol             Pointer to reference column number; NULL if none.
225  * @param rowTexInfoCallback Pointer to callback for reference row info; NULL
226  *                           if none.
227  * @param colTexInfoCallback Pointer to callback for reference column info; NULL
228  *                           if none.
229  */
230 void
231 MeshEntity::GetInfo(const int *refRow,
232                     const int *refCol,
233                     const TexInfoCallback *rowTexInfoCallback,
234                     const TexInfoCallback *colTexInfoCallback)
235 {
236    // Prep a message buffer to compose the response.
237    char messageBuffer[INFO_BUFFER_SIZE + 1];
238    messageBuffer[INFO_BUFFER_SIZE] = 0;
239    size_t bufferOffset = 0;
240    // Get reference row info if requested; this will be written into the message
241    // buffer as well as sent to the row callback (if any).
242    if (refRow != NULL)
243    {
244       ReportSliceTexInfo(ROW_SLICE_TYPE, *refRow, _naturalAxis[ROW_SLICE_TYPE],
245                          messageBuffer + bufferOffset,
246                          INFO_BUFFER_SIZE - bufferOffset,
247                          rowTexInfoCallback);
248       // Move the message buffer pointer along.
249       bufferOffset = strlen(messageBuffer);
250    }
251    // Get reference column info if requested; this will be written into the
252    // message buffer as well as sent to the column callback (if any).
253    if (refCol != NULL)
254    {
255       ReportSliceTexInfo(COL_SLICE_TYPE, *refCol, _naturalAxis[COL_SLICE_TYPE],
256                          messageBuffer + bufferOffset,
257                          INFO_BUFFER_SIZE - bufferOffset,
258                          colTexInfoCallback);
259       // Move the message buffer pointer along.
260       bufferOffset = strlen(messageBuffer);
261    }
262    // Make sure we have up-to-date S/T extents.
263    UpdateTexMinMax(S_TEX_AXIS);
264    UpdateTexMinMax(T_TEX_AXIS);
265    // Add general mesh info to the message.
266    snprintf(messageBuffer + bufferOffset, INFO_BUFFER_SIZE - bufferOffset,
267             INFO_MESH_FORMAT,
268             _numSlices[ROW_SLICE_TYPE],
269             SanitizeFloat(_texMin[S_TEX_AXIS]), SanitizeFloat(_texMax[S_TEX_AXIS]),
270             _numSlices[COL_SLICE_TYPE],
271             SanitizeFloat(_texMin[T_TEX_AXIS]), SanitizeFloat(_texMax[T_TEX_AXIS]),
272             SanitizeFloat(_posMin[X_POS_AXIS]), SanitizeFloat(_posMax[X_POS_AXIS]),
273             SanitizeFloat(_posMin[Y_POS_AXIS]), SanitizeFloat(_posMax[Y_POS_AXIS]),
274             SanitizeFloat(_posMin[Z_POS_AXIS]), SanitizeFloat(_posMax[Z_POS_AXIS]));
275    // Send the response.
276    _infoReportCallback(messageBuffer);
277 }
278
279 /**
280  * For each of the specified texture axes, shift the lowest-valued texture
281  * coordinates off of the mesh until an integral texture coordinate (texture
282  * boundary) is on the mesh edge.
283  *
284  * @param axes The texture axes to align.
285  */
286 void
287 MeshEntity::MinAlign(TextureAxisSelection axes)
288 {
289    // Implement this by applying MinAlignInt to each specified axis.
290    ProcessForAxes(&MeshEntity::MinAlignInt, axes);
291 }
292
293 /**
294  * For each of the specified texture axes, shift the highest-valued texture
295  * coordinates off of the mesh until an integral texture coordinate (texture
296  * boundary) is on the mesh edge.
297  *
298  * @param axes The texture axes to align.
299  */
300 void
301 MeshEntity::MaxAlign(TextureAxisSelection axes)
302 {
303    // Implement this by applying MaxAlignInt to each specified axis.
304    ProcessForAxes(&MeshEntity::MaxAlignInt, axes);
305 }
306
307 /**
308  * For each of the specified texture axes, perform either MinMaxAlignStretch
309  * or MinMaxAlignShrink; the chosen operation will be the one with the least
310  * absolute change in the value of the texture scale.
311  *
312  * @param axes The texture axes to align.
313  */
314 void
315 MeshEntity::MinMaxAlignAutoScale(TextureAxisSelection axes)
316 {
317    // Implement this by applying MinMaxAlignAutoScaleInt to each specified axis.
318    ProcessForAxes(&MeshEntity::MinMaxAlignAutoScaleInt, axes);
319 }
320
321 /**
322  * For each of the specified texture axes, align a texture boundary to one
323  * edge of the mesh, then increase the texture scale to align a texture
324  * boundary to the other edge of the mesh as well.
325  *
326  * @param axes The texture axes to align.
327  */
328 void
329 MeshEntity::MinMaxAlignStretch(TextureAxisSelection axes)
330 {
331    // Implement this by applying MinMaxAlignStretchInt to each specified axis.
332    ProcessForAxes(&MeshEntity::MinMaxAlignStretchInt, axes);
333 }
334
335 /**
336  * For each of the specified texture axes, align a texture boundary to one
337  * edge of the mesh, then decrease the texture scale to align a texture
338  * boundary to the other edge of the mesh as well.
339  *
340  * @param axes The texture axes to align.
341  */
342 void
343 MeshEntity::MinMaxAlignShrink(TextureAxisSelection axes)
344 {
345    // Implement this by applying MinMaxAlignShrinkInt to each specified axis.
346    ProcessForAxes(&MeshEntity::MinMaxAlignShrinkInt, axes);
347 }
348
349 /**
350  * Set the texture scaling along the rows or columns of the mesh. This
351  * affects only the texture axis that is naturally associated with rows (S)
352  * or columns (T) according to the chosen sliceType.
353  * 
354  * The scaling may be input either as a multiple of the natural scale that
355  * Radiant would choose for this texture, or as the number of tiles of the
356  * texture that should fit on the mesh's row/column.
357  * 
358  * Among the slices perpendicular to the direction of scaling, an alignment
359  * slice is used to fix the position of the texture boundary.
360  * 
361  * A reference slice may optionally be chosen among the slices parallel to
362  * the scaling direction. If a reference slice is not specified, then the
363  * texture coordinates are independently determined for each slice. If a
364  * reference slice is specified, its texture coordinates are calculated first
365  * and used to affect the other slices. The reference slice's amount of
366  * texture tiling will be re-used for all other slices; optionally, the
367  * texture coordinate at each control point within the reference slice can be
368  * copied to the corresponding control point in every other slice.
369  *
370  * @param sliceType           Choose to scale along rows or columns.
371  * @param alignSlice          Pointer to alignment slice description; if NULL,
372  *                            slice 0 is assumed.
373  * @param refSlice            Pointer to reference slice description,
374  *                            including how to use the reference; NULL if no
375  *                            reference.
376  * @param naturalScale        true if naturalScaleOrTiles is a factor of the
377  *                            Radiant natural scale; false if
378  *                            naturalScaleOrTiles is a number of tiles.
379  * @param naturalScaleOrTiles Scaling determinant, interpreted according to
380  *                            the naturalScale parameter.
381  */
382 void
383 MeshEntity::SetScale(SliceType sliceType,
384                      const SliceDesignation *alignSlice,
385                      const RefSliceDescriptor *refSlice,
386                      bool naturalScale,
387                      float naturalScaleOrTiles)
388 {
389    // We're about to make changes!
390    CreateUndoPoint();
391
392    // Check for bad inputs. Also convert from natural scale to raw scale.
393    if (alignSlice != NULL && !alignSlice->maxSlice)
394    {
395       if (alignSlice->index < 0 ||
396           alignSlice->index >= (int)SliceSize(sliceType))
397       {
398          _errorReportCallback(_errorBadSliceString[OtherSliceType(sliceType)]);
399          return;
400       }
401    }
402    if (refSlice != NULL && !refSlice->designation.maxSlice)
403    {
404       if (refSlice->designation.index < 0 ||
405           refSlice->designation.index >= (int)_numSlices[sliceType])
406       {
407          _errorReportCallback(_errorBadSliceString[sliceType]);
408          return;
409       }
410    }
411    TextureAxis axis = _naturalAxis[sliceType];
412    float rawScaleOrTiles = naturalScaleOrTiles;
413    if (naturalScale)
414    {
415       // In this case, naturalScaleOrTiles (copied to rawScaleOrTiles) was a
416       // natural-scale factor.
417       if (rawScaleOrTiles == 0)
418       {
419          _errorReportCallback(_errorSliceZeroscaleString[sliceType]);
420          return;
421       }
422       // If Radiant's internal orientation is backwards, account for that.
423       if (_radiantScaleInverted[sliceType])
424       {
425          rawScaleOrTiles = -rawScaleOrTiles;
426       }
427       // Raw scale is the divisor necessary to get texture coordinate from
428       // worldspace distance, so we can derive that from the "natural" scale.
429       rawScaleOrTiles *= _naturalTexUnits[axis];
430    }
431    else
432    {
433       // In this case, naturalScaleOrTiles (copied to rawScaleOrTiles) was a
434       // tiling amount.
435       if (rawScaleOrTiles == 0)
436       {
437          // XXX We could try to make zero-tiles work ("infinite scale": all
438          // values for this axis are the same along the slice). Need to sort
439          // out divide-by-zero dangers down the road.
440          _errorReportCallback(_errorSliceZerotilesString[sliceType]);
441          return;
442       }
443       // If Radiant's internal orientation is backwards, account for that.
444       if (_radiantTilesInverted[sliceType])
445       {
446          rawScaleOrTiles = -rawScaleOrTiles;
447       }
448    }
449
450    // Make sure we have a definite number for the alignment slice, and for the
451    // reference slice if any.
452    int alignSliceInt =
453       InternalSliceDesignation(alignSlice, (SliceType)OtherSliceType(sliceType));
454    RefSliceDescriptorInt descriptor;
455    RefSliceDescriptorInt *refSliceInt = // will be NULL if no reference
456       InternalRefSliceDescriptor(refSlice, sliceType, descriptor);
457
458    // Generate the surface texture coordinates using the mesh control points
459    // and the designated scaling/tiling.
460    AllocatedMatrix<float> surfaceValues(_meshData.x(), _meshData.y());
461    GenScaledDistanceValues(sliceType, alignSliceInt, refSliceInt,
462                            naturalScale, rawScaleOrTiles,
463                            surfaceValues);
464
465    // Derive the control point values necessary to achieve those surface values.
466    GenControlTexFromSurface(axis, surfaceValues);
467
468    // Done!
469    CommitChanges();
470 }
471
472 /**
473  * Set the mesh's texture coordinates according to a linear combination of
474  * factors. This equation can be used to set the texture coordinates at the
475  * control points themselves, or to directly set the texture coordinates at
476  * the locations on the mesh surface that correspond to each half-patch
477  * interval.
478  * 
479  * An alignment row is used as the zero-point for any calculations of row
480  * number or of distance along a column surface when processing the equation.
481  * An alignment column is similarly used. (Note that the number identifying
482  * the alignment row/column is the real number used to index into the mesh;
483  * it's not the modified number as affected by the alignment column/row when
484  * processing the equation. We don't want to be stuck in a chicken-and-egg
485  * situation.)
486  * 
487  * Calculations of distance along row/col surface may optionally be affected
488  * by a designated reference row/col. The reference row/col can be used as a
489  * source of end-to-end distance only, in which case the proportional spacing
490  * of the control points within the affected row/col will determine the
491  * distance value to be used for each control point. Or, the distance value
492  * for every control point in the reference row/col can be copied to each
493  * corresponding control point in the other rows/cols.
494  *
495  * @param sFactors      Factors to determine the S texture coords; NULL if S
496  *                      axis unaffected.
497  * @param tFactors      Factors to determine the T texture coords; NULL if T
498  *                      axis unaffected.
499  * @param alignRow      Pointer to zero-point row; if NULL, row 0 is assumed.
500  * @param alignCol      Pointer to zero-point column; if NULL, column 0 is
501  *                      assumed.
502  * @param refRow        Pointer to reference row description, including how
503  *                      to use the reference; NULL if no reference.
504  * @param refCol        Pointer to reference column description, including
505  *                      how to use the reference; NULL if no reference.
506  * @param surfaceValues true if calculations are for S/T values on the mesh
507  *                      surface; false if calculations are for S/T values at
508  *                      the control points.
509  */
510 void
511 MeshEntity::GeneralFunction(const GeneralFunctionFactors *sFactors,
512                             const GeneralFunctionFactors *tFactors,
513                             const SliceDesignation *alignRow,
514                             const SliceDesignation *alignCol,
515                             const RefSliceDescriptor *refRow,
516                             const RefSliceDescriptor *refCol,
517                             bool surfaceValues)
518 {
519    // We're about to make changes!
520    CreateUndoPoint();
521
522    // Make sure we have a definite number for the alignment slices, and for the
523    // reference slices if any.
524    int alignRowInt = InternalSliceDesignation(alignRow, ROW_SLICE_TYPE);
525    int alignColInt = InternalSliceDesignation(alignRow, COL_SLICE_TYPE);
526    RefSliceDescriptorInt rowDescriptor;
527    RefSliceDescriptorInt *refRowInt = // will be NULL if no row reference
528       InternalRefSliceDescriptor(refRow, ROW_SLICE_TYPE, rowDescriptor);
529    RefSliceDescriptorInt colDescriptor;
530    RefSliceDescriptorInt *refColInt = // will be NULL if no column reference
531       InternalRefSliceDescriptor(refCol, COL_SLICE_TYPE, colDescriptor);
532
533    // Get the surface row/col distance values at each half-patch interval, if
534    // the input factors care about distances.
535    AllocatedMatrix<float> rowDistances(_meshData.x(), _meshData.y());
536    AllocatedMatrix<float> colDistances(_meshData.x(), _meshData.y());
537    if ((sFactors != NULL && sFactors->rowDistance != 0.0f) ||
538        (tFactors != NULL && tFactors->rowDistance != 0.0f))
539    {
540       GenScaledDistanceValues(ROW_SLICE_TYPE,
541                               alignColInt,
542                               refRowInt,
543                               true,
544                               1.0f,
545                               rowDistances);
546    }
547    if ((sFactors != NULL && sFactors->colDistance != 0.0f) ||
548        (tFactors != NULL && tFactors->colDistance != 0.0f))
549    {
550       GenScaledDistanceValues(COL_SLICE_TYPE,
551                               alignRowInt,
552                               refColInt,
553                               true,
554                               1.0f,
555                               colDistances);
556    }
557
558    // Modify the S axis if requested.
559    if (sFactors != NULL)
560    {
561       GeneralFunctionInt(*sFactors, S_TEX_AXIS, alignRowInt, alignColInt, surfaceValues,
562                          rowDistances, colDistances);
563    }
564
565    // Modify the T axis if requested.
566    if (tFactors != NULL)
567    {
568       GeneralFunctionInt(*tFactors, T_TEX_AXIS, alignRowInt, alignColInt, surfaceValues,
569                          rowDistances, colDistances);
570    }
571
572    // Done!
573    CommitChanges();
574 }
575
576 /**
577  * Update the internally stored information for the min and max extent of the
578  * mesh on the specified worldspace axis.
579  *
580  * @param axis The worldspace axis.
581  */
582 void
583 MeshEntity::UpdatePosMinMax(PositionAxis axis)
584 {
585    // Iterate over all control points to find the min and max values.
586    _posMin[axis] = _meshData(0, 0).m_vertex[axis];
587    _posMax[axis] = _posMin[axis];
588    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
589    {
590       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
591       {
592          float current = _meshData(rowIndex, colIndex).m_vertex[axis];
593          if (current < _posMin[axis])
594          {
595             _posMin[axis] = current;
596          }
597          if (current > _posMax[axis])
598          {
599             _posMax[axis] = current;
600          }
601       }
602    }
603 }
604
605 /**
606  * Update the internally stored information for the min and max extent of the
607  * mesh on the specified texture axis.
608  *
609  * @param axis The texture axis.
610  */
611 void
612 MeshEntity::UpdateTexMinMax(TextureAxis axis)
613 {
614    // Bail out if no operations have possibly changed these values.
615    if (!_texMinMaxDirty[axis])
616    {
617       return;
618    }
619
620    // Iterate over all control points to find the min and max values.
621    _texMin[axis] = _meshData(0, 0).m_texcoord[axis];
622    _texMax[axis] = _texMin[axis];
623    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
624    {
625       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
626       {
627          float current = _meshData(rowIndex, colIndex).m_texcoord[axis];
628          if (current < _texMin[axis])
629          {
630             _texMin[axis] = current;
631          }
632          if (current > _texMax[axis])
633          {
634             _texMax[axis] = current;
635          }
636       }
637    }
638
639    // See if the min and max are on texture boundaries.
640    _texMinAligned[axis] = (floorf(_texMin[axis]) == _texMin[axis]);
641    _texMaxAligned[axis] = (floorf(_texMax[axis]) == _texMax[axis]);
642
643    // Values are good until next relevant operation.
644    _texMinMaxDirty[axis] = false;
645 }
646
647 /**
648  * Interface to the Radiant undo buffer; save the current state of the mesh
649  * to allow rollback to this point by an undo operation.
650  */
651 void
652 MeshEntity::CreateUndoPoint()
653 {
654    GlobalPatchCreator().Patch_undoSave(_mesh);
655 }
656
657 /**
658  * Commit the changes to the mesh so that they will be reflected in Radiant.
659  */
660 void
661 MeshEntity::CommitChanges()
662 {
663    GlobalPatchCreator().Patch_controlPointsChanged(_mesh);
664    // Radiant undo-buffer behavior requires this:
665    CreateUndoPoint();
666 }
667
668 /**
669  * Convert from SliceDesignation to a slice number. Interpret max slice if
670  * necessary, and fall back to slice 0 if unspecified.
671  *
672  * @param sliceDesignation Pointer to slice description; may be NULL.
673  * @param sliceType        Slice kind (row or column).
674  *
675  * @return The slice number.
676  */
677 int
678 MeshEntity::InternalSliceDesignation(const SliceDesignation *sliceDesignation,
679                                      SliceType sliceType)
680 {
681    if (sliceDesignation != NULL)
682    {
683       // Interpret "max slice" if necessary.
684       if (sliceDesignation->maxSlice)
685       {
686          return _numSlices[sliceType] - 1;
687       }
688       else
689       {
690          return sliceDesignation->index;
691       }
692    }
693    else
694    {
695       // 0 if unspecified.
696       return 0;
697    }
698 }
699
700 /**
701  * Convert from RefSliceDescriptor to RefSliceDescriptorInt. Interpret max
702  * slice if necessary. Populate specified RefSliceDescriptorInt if input is
703  * non-NULL and return pointer to it; otherwise return NULL.
704  *
705  * @param refSlice          Pointer to reference slice description; may be
706  *                          NULL.
707  * @param sliceType         Slice kind (row or column).
708  * @param [out] refSliceInt RefSliceDescriptorInt to populate.
709  *
710  * @return NULL if input RefSliceDescriptor is NULL; else, pointer to populated
711  *         RefSliceDescriptorInt.
712  */
713 MeshEntity::RefSliceDescriptorInt *
714 MeshEntity::InternalRefSliceDescriptor(const RefSliceDescriptor *refSlice,
715                                        SliceType sliceType,
716                                        RefSliceDescriptorInt& refSliceInt)
717 {
718    if (refSlice != NULL)
719    {
720       // Preserve totalLengthOnly.
721       refSliceInt.totalLengthOnly = refSlice->totalLengthOnly;
722       // Convert slice designator to a slice number.
723       refSliceInt.index = InternalSliceDesignation(&(refSlice->designation), sliceType);
724       return &refSliceInt;
725    }
726    else
727    {
728       // NULL if unspecified.
729       return NULL;
730    }
731 }
732
733 /**
734  * Given a slice and a number of times that its texture should tile along it,
735  * find the appropriate texture scale. This result is determined by the
736  * surface length along the slice.
737  *
738  * @param sliceType Slice kind (row or column).
739  * @param slice     Slice number, among slices of that kind in mesh.
740  * @param axis      The texture axis of interest.
741  * @param tiles     Number of times the texture tiles.
742  *
743  * @return The texture scale corresponding to the tiling amount.
744  */
745 float
746 MeshEntity::GetSliceTexScale(SliceType sliceType,
747                              int slice,
748                              TextureAxis axis,
749                              float tiles)
750 {
751    // XXX Similar to GenScaledDistanceValues; refactor for shared code?
752
753    // We're going to be walking patches along the mesh, choosing the patches
754    // that surround/affect the slice we are interested in. We'll calculate
755    // the length of the slice's surface across each patch & add those up.
756    
757    // A SlicePatchContext will contain all the necessary information to
758    // evaluate our slice's surface length within each patch. Some aspects of
759    // the SlicePatchContext will vary as we move from patch to patch, but we
760    // can go ahead and calculate now any stuff that is invariant along the
761    // slice's direction.
762    SlicePatchContext context;
763    context.sliceType = sliceType;
764    if (slice != 0)
765    {
766       // This is the position of the slice within each patch, in the direction
767       // orthogonal to the slice. Even-numbered slices are at the edge of the
768       // patch (position 1.0), while odd-numbered slices are in the middle
769       // (position 0.5).
770       context.position = 1.0f - ((float)(slice & 0x1) / 2.0f);
771    }
772    else
773    {
774       // For the first slice, we can't give it the usual treatment for even-
775       // numbered slices (since there is no patch "before" it), so it gets
776       // position 0.0 instead.
777       context.position = 0.0f;
778    }
779    // This is the slice of the same kind that defines the 0.0 edge of the
780    // patch. It will be the next lowest even-numbered slice. (Note the
781    // integer division here.)
782    context.edgeSlice[sliceType] = 2 * ((slice - 1) / 2);
783
784    // Now it's time to walk the patches.
785
786    // We start off with no cumulative distance yet.
787    float cumulativeDistance = 0.0f;
788
789    // By iterating over the number of control points in this slice by
790    // increments of 2, we'll be walking the slice in patch-sized steps. Since
791    // we are only interested in the total length, we don't need to check in at
792    // finer granularities.
793    for (unsigned halfPatch = 2; halfPatch < SliceSize(sliceType); halfPatch += 2)
794    {
795       // Find the slice-of-other-kind that defines the patch edge orthogonal
796       // to our slice.
797       context.edgeSlice[OtherSliceType(sliceType)] = 2 * ((halfPatch - 1) / 2);
798       // Estimate the slice length along the surface of the patch.
799       float segmentLengthEstimate = EstimateSegmentLength(0.0f, 1.0f, context);
800       // Recursively refine that estimate until it is good enough, then add it
801       // to our cumulative distance.
802       cumulativeDistance += RefineSegmentLength(0.0f, 1.0f, context,
803                                                 segmentLengthEstimate,
804                                                 UNITS_ERROR_BOUND);
805    }
806
807    // The scale along this slice is defined as the surface length divided by
808    // the "natural" number of texture units along that length.
809    return cumulativeDistance / (tiles * _naturalTexUnits[axis]);
810 }
811
812 /**
813  * Populate the SliceTexInfo for the indicated slice and texture axis.
814  *
815  * @param sliceType  Slice kind (row or column).
816  * @param slice      Slice number, among slices of that kind in mesh.
817  * @param axis       The texture axis of interest.
818  * @param [out] info Information on scale, tiles, and min/max for the
819  *                   specified texture axis.
820  *
821  * @return true on success, false if slice cannot be processed.
822  */
823 bool
824 MeshEntity::GetSliceTexInfo(SliceType sliceType,
825                             int slice,
826                             TextureAxis axis,
827                             SliceTexInfo& info)
828 {
829    // Bail out now if slice # is bad.
830    if (slice < 0 ||
831        slice >= (int)_numSlices[sliceType])
832    {
833       _errorReportCallback(_errorBadSliceString[sliceType]);
834       return false;
835    }
836
837    // Calculate the # of times the texture is tiled along the specified axis
838    // on this slice, and find the min and max values for that axis.
839    float texBegin =
840       MatrixElement(_meshData, sliceType, slice, 0).m_texcoord[axis];
841    float texEnd =
842       MatrixElement(_meshData, sliceType, slice, SliceSize(sliceType) - 1).m_texcoord[axis];
843    info.tiles = texEnd - texBegin;
844    if (texBegin < texEnd)
845    {
846       info.min = texBegin;
847       info.max = texEnd;
848    }
849    else
850    {
851       info.min = texEnd;
852       info.max = texBegin;
853    }
854
855    // Calculate the texture scale along this slice, using the tiling info
856    // along with the texture size and the length of the slice's surface.
857    info.scale = GetSliceTexScale(sliceType, slice, axis, info.tiles);
858    return true;
859 }
860
861 /**
862  * Take the information from GetSliceTexInfo and sanitize it for reporting.
863  * Optionally print to a provided message buffer and/or supply data to a
864  * provided TexInfoCallback.
865  *
866  * @param sliceType         Slice kind (row or column).
867  * @param slice             Slice number, among slices of that kind in mesh.
868  * @param axis              The texture axis of interest.
869  * @param messageBuffer     Buffer for message data; NULL if none.
870  * @param messageBufferSize Size of the message buffer.
871  * @param texInfoCallback   Callback for passing texture scale/tiles info;
872  *                          NULL if none.
873  */
874 void
875 MeshEntity::ReportSliceTexInfo(SliceType sliceType,
876                                int slice,
877                                TextureAxis axis,
878                                char *messageBuffer,
879                                unsigned messageBufferSize,
880                                const TexInfoCallback *texInfoCallback)
881 {
882    // Fetch the raw info.
883    SliceTexInfo info;
884    if (!GetSliceTexInfo(sliceType, slice, axis, info))
885    {
886       return;
887    }
888
889    // Account for Radiant-inverted.
890    if (_radiantScaleInverted[sliceType])
891    {
892       info.scale = -info.scale;
893    }
894    if (_radiantTilesInverted[sliceType])
895    {
896       info.tiles = -info.tiles;
897    }
898
899    // Send texture info to callback if one is provided.
900    bool infscale = (info.scale > FLT_MAX || info.scale < -FLT_MAX);
901    if (texInfoCallback != NULL)
902    {
903       if (!infscale)
904       {
905          (*texInfoCallback)(SanitizeFloat(info.scale), SanitizeFloat(info.tiles));
906       }
907       else
908       {
909          // "Infinite scale" prevents us from invoking the callback, so
910          // raise a warning about that.
911          _warningReportCallback(_warningSliceInfscaleFormatString[sliceType]);
912       }
913    }
914
915    // Write texture info to buffer if one is provided.
916    if (messageBuffer != NULL)
917    {
918       if (!infscale)
919       {
920          snprintf(messageBuffer, messageBufferSize,
921             _infoSliceFormatString[sliceType], slice,
922             SanitizeFloat(info.scale), SanitizeFloat(info.tiles),
923             SanitizeFloat(info.min), SanitizeFloat(info.max));
924       }
925       else
926       {
927          // Special handling for "infinite scale".
928          snprintf(messageBuffer, messageBufferSize,
929             _infoSliceInfscaleFormatString[sliceType], slice,
930             SanitizeFloat(info.tiles),
931             SanitizeFloat(info.min), SanitizeFloat(info.max));
932       }
933    }
934 }
935
936 /**
937  * Apply some function with the InternalImpl signature to each of the
938  * designated texture axes. The undo point and state commit operations
939  * are handled here for such functions.
940  *
941  * @param internalImpl The function to apply.
942  * @param axes         The texture axes to affect.
943  */
944 void
945 MeshEntity::ProcessForAxes(InternalImpl internalImpl,
946                            TextureAxisSelection axes)
947 {
948    // We're about to make changes!
949    CreateUndoPoint();
950
951    // Apply the function to the requested axes.
952    bool sChanged = false;
953    bool tChanged = false;
954    if (axes != T_TEX_AXIS_ONLY)
955    {
956       sChanged = (*this.*internalImpl)(S_TEX_AXIS);
957    }
958    if (axes != S_TEX_AXIS_ONLY)
959    {
960       tChanged = (*this.*internalImpl)(T_TEX_AXIS);
961    }
962
963    // Done! Commit changes if necessary.
964    if (sChanged || tChanged)
965    {
966       CommitChanges();
967    }
968 }
969
970 /**
971  * Add an offset to all control point values for the given texture axis.
972  *
973  * @param axis  The texture axis to affect.
974  * @param shift The offset to add.
975  */
976 void
977 MeshEntity::Shift(TextureAxis axis,
978                   float shift)
979 {
980    // Iterate over all control points and add the offset.
981    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
982    {
983       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
984       {
985          _meshData(rowIndex, colIndex).m_texcoord[axis] += shift;
986       }
987    }
988
989    // This operation might have changed texture min/max.
990    _texMinMaxDirty[axis] = true;
991 }
992
993 /**
994  * On the given texture axis, find the distance of all control point values
995  * from the current minimum value and multiply that distance by the given
996  * scale factor.
997  *
998  * @param axis  The texture axis to affect.
999  * @param scale The scale factor.
1000  */
1001 void
1002 MeshEntity::Scale(TextureAxis axis,
1003                   float scale)
1004 {
1005    // Make sure the min value is updated; we'll need it below.
1006    UpdateTexMinMax(axis);
1007
1008    // Iterate over all control points and apply the scale factor.
1009    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
1010    {
1011       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
1012       {
1013          // Leave the current minimum edge in place and scale out from that.
1014          _meshData(rowIndex, colIndex).m_texcoord[axis] =
1015             ((_meshData(rowIndex, colIndex).m_texcoord[axis] - _texMin[axis]) * scale) +
1016             _texMin[axis];
1017       }
1018    }
1019
1020    // This operation might have changed texture min/max.
1021    _texMinMaxDirty[axis] = true;
1022 }
1023
1024 /**
1025  * Implementation of MinAlign for a single texture axis.
1026  *
1027  * @param axis The texture axis to affect.
1028  *
1029  * @return true if the mesh was changed, false if not.
1030  */
1031 bool
1032 MeshEntity::MinAlignInt(TextureAxis axis)
1033 {
1034    // Make sure the min-aligned value is updated.
1035    UpdateTexMinMax(axis);
1036
1037    // If already aligned, we're done.
1038    if (_texMinAligned[axis])
1039    {
1040       // Didn't make changes.
1041       return false;
1042    }
1043
1044    // Otherwise shift by the necessary amount to align.
1045    Shift(axis, ceilf(_texMin[axis]) - _texMin[axis]);
1046
1047    // Made changes.
1048    return true;
1049 }
1050
1051 /**
1052  * Implementation of MaxAlign for a single texture axis.
1053  *
1054  * @param axis The texture axis to affect.
1055  *
1056  * @return true if the mesh was changed, false if not.
1057  */
1058 bool
1059 MeshEntity::MaxAlignInt(TextureAxis axis)
1060 {
1061    // Make sure the max-aligned value is updated.
1062    UpdateTexMinMax(axis);
1063
1064    // If already aligned, we're done.
1065    if (_texMaxAligned[axis])
1066    {
1067       // Didn't make changes.
1068       return false;
1069    }
1070
1071    // Otherwise shift by the necessary amount to align.
1072    Shift(axis, ceilf(_texMax[axis]) - _texMax[axis]);
1073
1074    // Made changes.
1075    return true;
1076 }
1077
1078 /**
1079  * Implementation of MinMaxAlignAutoScale for a single texture axis.
1080  *
1081  * @param axis The texture axis to affect.
1082  *
1083  * @return true if the mesh was changed, false if not.
1084  */
1085 bool
1086 MeshEntity::MinMaxAlignAutoScaleInt(TextureAxis axis)
1087 {
1088    // Make sure the max value is updated.
1089    UpdateTexMinMax(axis);
1090
1091    // Choose to stretch or shrink, based on which will cause less change.
1092    if ((_texMax[axis] - floorf(_texMax[axis])) < 0.5)
1093    {
1094       return MinMaxAlignStretchInt(axis);
1095    }
1096    else
1097    {
1098       return MinMaxAlignShrinkInt(axis);
1099    }
1100 }
1101
1102 /**
1103  * The meat of MinMaxAlignStretchInt and MinMaxAlignShrinkInt.
1104  *
1105  * @param axis The texture axis to affect.
1106  * @param op   Whether to stretch or shrink.
1107  *
1108  * @return true if the mesh was changed, false if not.
1109  */
1110 bool
1111 MeshEntity::MinMaxAlignScale(TextureAxis axis,
1112                              ScaleOperation op)
1113 {
1114    // First make sure we are min-aligned.
1115    bool changed = MinAlignInt(axis);
1116
1117    // Make sure the min/max values are updated.
1118    UpdateTexMinMax(axis);
1119
1120    // More work to do if not max-aligned.
1121    if (!_texMaxAligned[axis])
1122    {
1123       // Find the current tiling.
1124       float oldRepeats = _texMax[axis] - _texMin[axis];
1125       // Find the desired tiling, depending on whether we are stretching or
1126       // shrinking.
1127       float newRepeats;
1128       if (op == STRETCH_SCALE_OP)
1129       {
1130          newRepeats = floorf(_texMax[axis]) - _texMin[axis];
1131       }
1132       else
1133       {
1134          newRepeats = ceilf(_texMax[axis]) - _texMin[axis];
1135       }
1136       // Apply the necessary scaling to get the desired tiling.
1137       Scale(axis, newRepeats / oldRepeats);
1138       // Made changes.
1139       changed = true;
1140    }
1141
1142    return changed;
1143 }
1144
1145 /**
1146  * Implementation of MinMaxAlignStretch for a single texture axis.
1147  *
1148  * @param axis The texture axis to affect.
1149  *
1150  * @return true if the mesh was changed, false if not.
1151  */
1152 bool
1153 MeshEntity::MinMaxAlignStretchInt(TextureAxis axis)
1154 {
1155    // Hand off to MinMaxAlignScale.
1156    return MinMaxAlignScale(axis, STRETCH_SCALE_OP);
1157 }
1158
1159 /**
1160  * Implementation of MinMaxAlignShrink for a single texture axis.
1161  *
1162  * @param axis The texture axis to affect.
1163  *
1164  * @return true if the mesh was changed, false if not.
1165  */
1166 bool
1167 MeshEntity::MinMaxAlignShrinkInt(TextureAxis axis)
1168 {
1169    // Hand off to MinMaxAlignScale.
1170    return MinMaxAlignScale(axis, SHRINK_SCALE_OP);
1171 }
1172
1173 /**
1174  * Calculate the d(x, y, or z)/dt of a patch slice, evaluated at a given t
1175  * (parameter for the Bezier function, between 0 and 1).
1176  *
1177  * @param axis    The worldspace axis of interest.
1178  * @param t       Bezier parameter.
1179  * @param context The slice and patch.
1180  *
1181  * @return d(x, y, or z)/dt at the given t.
1182  */
1183 float
1184 MeshEntity::SliceParametricSpeedComponent(PositionAxis axis,
1185                                           float t,
1186                                           const SlicePatchContext& context)
1187 {
1188    float a = 1.0f - context.position;
1189    float b = 2.0f * context.position * a;
1190    a *= a;
1191    float c = context.position * context.position;
1192    float d = 2.0f * t - 2.0f;
1193    float e = 2.0f - 4.0f * t;
1194    float f = 2.0f * t;
1195    int patchStartCol = context.edgeSlice[COL_SLICE_TYPE];
1196    int patchStartRow = context.edgeSlice[ROW_SLICE_TYPE];
1197
1198    if (context.sliceType == ROW_SLICE_TYPE)
1199    {
1200       return
1201          _meshData(patchStartRow+0, patchStartCol+0).m_vertex[axis] * a * d +
1202          _meshData(patchStartRow+0, patchStartCol+1).m_vertex[axis] * a * e +
1203          _meshData(patchStartRow+0, patchStartCol+2).m_vertex[axis] * a * f +
1204          _meshData(patchStartRow+1, patchStartCol+0).m_vertex[axis] * b * d +
1205          _meshData(patchStartRow+1, patchStartCol+1).m_vertex[axis] * b * e +
1206          _meshData(patchStartRow+1, patchStartCol+2).m_vertex[axis] * b * f +
1207          _meshData(patchStartRow+2, patchStartCol+0).m_vertex[axis] * c * d +
1208          _meshData(patchStartRow+2, patchStartCol+1).m_vertex[axis] * c * e +
1209          _meshData(patchStartRow+2, patchStartCol+2).m_vertex[axis] * c * f;
1210    }
1211    else
1212    {
1213       return
1214          _meshData(patchStartRow+0, patchStartCol+0).m_vertex[axis] * a * d +
1215          _meshData(patchStartRow+1, patchStartCol+0).m_vertex[axis] * a * e +
1216          _meshData(patchStartRow+2, patchStartCol+0).m_vertex[axis] * a * f +
1217          _meshData(patchStartRow+0, patchStartCol+1).m_vertex[axis] * b * d +
1218          _meshData(patchStartRow+1, patchStartCol+1).m_vertex[axis] * b * e +
1219          _meshData(patchStartRow+2, patchStartCol+1).m_vertex[axis] * b * f +
1220          _meshData(patchStartRow+0, patchStartCol+2).m_vertex[axis] * c * d +
1221          _meshData(patchStartRow+1, patchStartCol+2).m_vertex[axis] * c * e +
1222          _meshData(patchStartRow+2, patchStartCol+2).m_vertex[axis] * c * f;
1223    }
1224 }
1225
1226 /**
1227  * Calculates the rate of change in worldspace units of a patch slice,
1228  * evaluated at a given t (parameter for the Bezier function, between 0 and
1229  * 1).
1230  *
1231  * @param t       Bezier parameter.
1232  * @param context The slice and patch.
1233  *
1234  * @return Path length.
1235  */
1236 float
1237 MeshEntity::SliceParametricSpeed(float t,
1238                                  const SlicePatchContext& context)
1239 {
1240    float xDotEval = SliceParametricSpeedComponent(X_POS_AXIS, t, context);
1241    float yDotEval = SliceParametricSpeedComponent(Y_POS_AXIS, t, context);
1242    float zDotEval = SliceParametricSpeedComponent(Z_POS_AXIS, t, context);
1243    return sqrtf(xDotEval*xDotEval + yDotEval*yDotEval + zDotEval*zDotEval);
1244 }
1245
1246 /**
1247  * Estimate the surface length of a slice segment, using ten point
1248  * Gauss-Legendre integration of the parametric speed function. The value
1249  * returned will always be positive (absolute value).
1250  *
1251  * @param startPosition Bezier parameter value for the start point of the
1252  *                      slice segment.
1253  * @param endPosition   Bezier parameter value for the end point of the slice
1254  *                      segment.
1255  * @param context       The slice and patch.
1256  *
1257  * @return Estimate of segment length.
1258  */
1259 float
1260 MeshEntity::EstimateSegmentLength(float startPosition,
1261                                   float endPosition,
1262                                   const SlicePatchContext& context)
1263 {
1264    // Gauss-Legendre implementation taken from "Numerical Recipes in C".
1265
1266    static float x[] = {0.0f, 0.1488743389f, 0.4333953941f, 0.6794095682f, 0.8650633666f, 0.9739065285f};
1267    static float w[] = {0.0f, 0.2955242247f, 0.2692667193f, 0.2190863625f, 0.1494513491f, 0.0666713443f};
1268
1269    float xm = 0.5f * (endPosition + startPosition);
1270    float xr = 0.5f * (endPosition - startPosition);
1271    float s = 0.0f;
1272
1273    for (unsigned j = 1; j <= 5; j++) {
1274       float dx = xr * x[j];
1275       s += w[j] * (SliceParametricSpeed(xm + dx, context) +
1276                    SliceParametricSpeed(xm - dx, context));
1277    }
1278
1279    return fabsf(s * xr);
1280 }
1281
1282 /**
1283  * Recursively improve the estimate of the surface length of a slice segment,
1284  * by estimating the length of its halves, until the change between estimates
1285  * is equal to or less than an acceptable error threshold.
1286  *
1287  * @param startPosition         Bezier parameter value for the start point of
1288  *                              the slice segment.
1289  * @param endPosition           Bezier parameter value for the end point of the
1290  *                              slice segment.
1291  * @param context               The slice and patch.
1292  * @param segmentLengthEstimate Starting estimate for segment legnth.
1293  * @param maxError              Max acceptable variance between estimates.
1294  *
1295  * @return Improved estimate of segment length.
1296  */
1297 float
1298 MeshEntity::RefineSegmentLength(float startPosition,
1299                                 float endPosition,
1300                                 const SlicePatchContext& context,
1301                                 float segmentLengthEstimate,
1302                                 float maxError)
1303 {
1304    // Estimate the lengths of the two halves of this segment.
1305    float midPosition = (startPosition + endPosition) / 2.0f;
1306    float leftLength = EstimateSegmentLength(startPosition, midPosition, context);
1307    float rightLength = EstimateSegmentLength(midPosition, endPosition, context);
1308
1309    // If the sum of the half-segment estimates is too far off from the
1310    // whole-segment estimate, then we're in a regime with too much error in
1311    // the estimates. Recurse to refine the half-segment estimates.
1312    if (fabsf(segmentLengthEstimate - (leftLength + rightLength)) > maxError)
1313    {
1314       leftLength = RefineSegmentLength(startPosition, midPosition,
1315                                        context,
1316                                        leftLength,
1317                                        maxError / 2.0f);
1318       rightLength = RefineSegmentLength(midPosition, endPosition,
1319                                         context,
1320                                         rightLength,
1321                                         maxError / 2.0f);
1322    }
1323
1324    // Return the sum of the (refined) half-segment estimates.
1325    return (leftLength + rightLength);
1326 }
1327
1328 /**
1329  * Derive control point texture coordinates (on a given texture axis) from a
1330  * set of mesh surface texture coordinates.
1331  *
1332  * @param axis          The texture axis of interest.
1333  * @param surfaceValues The surface texture coordinates.
1334  */
1335 void
1336 MeshEntity::GenControlTexFromSurface(TextureAxis axis,
1337                                      const Matrix<float>& surfaceValues)
1338 {
1339    // The control points on even rows & even columns (i.e. patch corners)
1340    // have texture coordinates that match the surface values.
1341    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1342    {
1343       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1344       {
1345          _meshData(rowIndex, colIndex).m_texcoord[axis] =
1346             surfaceValues(rowIndex, colIndex);
1347       }
1348    }
1349
1350    // Set the control points on odd rows & even columns (i.e. the centers of
1351    // columns that are patch edges).
1352    for (unsigned rowIndex = 1; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1353    {
1354       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1355       {
1356          _meshData(rowIndex, colIndex).m_texcoord[axis] =
1357             2.0f * surfaceValues(rowIndex, colIndex) -
1358             (surfaceValues(rowIndex - 1, colIndex) +
1359              surfaceValues(rowIndex + 1, colIndex)) / 2.0f;
1360       }
1361    }
1362
1363    // Set the control points on even rows & odd columns (i.e. the centers of
1364    // rows that are patch edges).
1365    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1366    {
1367       for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1368       {
1369          _meshData(rowIndex, colIndex).m_texcoord[axis] =
1370             2.0f * surfaceValues(rowIndex, colIndex) -
1371             (surfaceValues(rowIndex, colIndex - 1) +
1372              surfaceValues(rowIndex, colIndex + 1)) / 2.0f;
1373       }
1374    }
1375
1376    // And finally on odd rows & odd columns (i.e. patch centers).
1377    for (unsigned rowIndex = 1; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1378    {
1379       for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1380       {
1381          _meshData(rowIndex, colIndex).m_texcoord[axis] =
1382             4.0f * surfaceValues(rowIndex, colIndex) -
1383             (surfaceValues(rowIndex, colIndex - 1) +
1384              surfaceValues(rowIndex, colIndex + 1) +
1385              surfaceValues(rowIndex - 1, colIndex) +
1386              surfaceValues(rowIndex + 1, colIndex)) / 2.0f -
1387             (surfaceValues(rowIndex - 1, colIndex - 1) +
1388              surfaceValues(rowIndex + 1, colIndex + 1) +
1389              surfaceValues(rowIndex - 1, colIndex + 1) +
1390              surfaceValues(rowIndex + 1, colIndex - 1)) / 4.0f;
1391       }
1392    }
1393
1394    // This operation might have changed texture min/max.
1395    _texMinMaxDirty[axis] = true;
1396 }
1397
1398 /**
1399  * Overwrite control point texture coordinates (on a given texture axis) with
1400  * the input texture coordinates.
1401  *
1402  * @param axis   The texture axis of interest.
1403  * @param values The input texture coordinates.
1404  */
1405 void
1406 MeshEntity::CopyControlTexFromValues(TextureAxis axis,
1407                                      const Matrix<float>& values)
1408 {
1409    // Iterate over all control points and just do a straight copy.
1410    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
1411    {
1412       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
1413       {
1414          _meshData(rowIndex, colIndex).m_texcoord[axis] =
1415             values(rowIndex, colIndex);
1416       }
1417    }
1418
1419    // This operation might have changed texture min/max.
1420    _texMinMaxDirty[axis] = true;
1421 }
1422
1423 /**
1424  * Derive a set of surface texture coordinates (on a given texture axis) from
1425  * the control point texture coordinates.
1426  *
1427  * @param axis                The texture axis of interest.
1428  * @param [out] surfaceValues The surface texture coordinates.
1429  */
1430 void
1431 MeshEntity::GenSurfaceFromControlTex(TextureAxis axis,
1432                                      Matrix<float>& surfaceValues)
1433 {
1434    // The surface values on even rows & even columns (i.e. patch corners)
1435    // have texture coordinates that match the control points.
1436    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1437    {
1438       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1439       {
1440          surfaceValues(rowIndex, colIndex) =
1441             _meshData(rowIndex, colIndex).m_texcoord[axis];
1442       }
1443    }
1444
1445    // Set the surface values on odd rows & odd columns (i.e. patch centers).
1446    for (unsigned rowIndex = 1; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1447    {
1448       for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1449       {
1450          surfaceValues(rowIndex, colIndex) =
1451             _meshData(rowIndex, colIndex).m_texcoord[axis] / 4.0f +
1452             (_meshData(rowIndex, colIndex - 1).m_texcoord[axis] +
1453              _meshData(rowIndex, colIndex + 1).m_texcoord[axis] +
1454              _meshData(rowIndex - 1, colIndex).m_texcoord[axis] +
1455              _meshData(rowIndex + 1, colIndex).m_texcoord[axis]) / 8.0f +
1456             (_meshData(rowIndex - 1, colIndex - 1).m_texcoord[axis] +
1457              _meshData(rowIndex + 1, colIndex + 1).m_texcoord[axis] +
1458              _meshData(rowIndex - 1, colIndex + 1).m_texcoord[axis] +
1459              _meshData(rowIndex + 1, colIndex - 1).m_texcoord[axis]) / 16.0f;
1460       }
1461    }
1462
1463    // Set the surface values on even rows & odd columns (i.e. the centers of
1464    // rows that are patch edges).
1465    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1466    {
1467       for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1468       {
1469          surfaceValues(rowIndex, colIndex) =
1470             _meshData(rowIndex, colIndex).m_texcoord[axis] / 2.0f +
1471             (_meshData(rowIndex, colIndex - 1).m_texcoord[axis] +
1472              _meshData(rowIndex, colIndex + 1).m_texcoord[axis]) / 4.0f;
1473       }
1474    }
1475
1476    // And finally on odd rows & even columns (i.e. the centers of columns that
1477    // are patch edges).
1478    for (unsigned rowIndex = 1; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1479    {
1480       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1481       {
1482          surfaceValues(rowIndex, colIndex) =
1483             _meshData(rowIndex, colIndex).m_texcoord[axis] / 2.0f +
1484             (_meshData(rowIndex - 1, colIndex).m_texcoord[axis] +
1485              _meshData(rowIndex + 1, colIndex).m_texcoord[axis]) / 4.0f;
1486       }
1487    }
1488 }
1489
1490 /**
1491  * Copy the control point texture coordinates (on a given texture axis) to
1492  * the output texture coordinates parameter.
1493  *
1494  * @param axis         The texture axis of interest.
1495  * @param [out] values The output texture coordinates.
1496  */
1497 void
1498 MeshEntity::CopyValuesFromControlTex(TextureAxis axis,
1499                                      Matrix<float>& values)
1500 {
1501    // Iterate over all control points and just do a straight copy.
1502    for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
1503    {
1504       for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
1505       {
1506          values(rowIndex, colIndex) =
1507             _meshData(rowIndex, colIndex).m_texcoord[axis];
1508       }
1509    }
1510 }
1511
1512 /**
1513  * Generate a set of values based on surface slice lengths and some amount of
1514  * desired scaling or tiling.
1515  * 
1516  * This method does a great deal of the work for the SetScale public method;
1517  * refer to that method's comment header for more details about the alignment
1518  * slice and reference slice inputs. The main difference from the SetScale
1519  * input parameters is that the scale/tiles factor has been processed some.
1520  * It has been flipped if necessary to account for Radiant's internal
1521  * scale/tiles orientation differing from the sensible external orientation.
1522  * And natural scaling has been converted to raw scaling, which is the actual
1523  * desired divisor to get texture coordinates from worldspace lengths.
1524  *
1525  * @param sliceType       Process rows or colums.
1526  * @param alignSlice      Pointer to alignment slice description; if NULL,
1527  *                        slice 0 is assumed.
1528  * @param refSlice        Pointer to reference slice description, including how
1529  *                        to use the reference; NULL if no reference.
1530  * @param rawScale        true if rawScaleOrTiles is a scale factor; false if
1531  *                        rawScaleOrTiles is a number of tiles.
1532  * @param rawScaleOrTiles Scaling determinant, interpreted according to the
1533  *                        rawScale parameter.
1534  * @param [out] values    The generated values.
1535  */
1536 void
1537 MeshEntity::GenScaledDistanceValues(SliceType sliceType,
1538                                     int alignSlice,
1539                                     const RefSliceDescriptorInt *refSlice,
1540                                     bool rawScale,
1541                                     float rawScaleOrTiles,
1542                                     Matrix<float>& values)
1543 {
1544    // For every half-patch interval along the surface, we want to generate a
1545    // value based on the surface distance along the row (or column) to that
1546    // spot. So, first we need to determine those distances.
1547
1548    // XXX Similar to GetSliceTexScale; refactor for shared code?
1549
1550    // We're going to be walking patches along the mesh, choosing the patches
1551    // that surround/affect the slice we are interested in.
1552    
1553    // A SlicePatchContext will contain all the necessary information to
1554    // evaluate our slice's surface length within each patch.
1555    SlicePatchContext context;
1556    context.sliceType = sliceType;
1557
1558    // Pick the slices that we will measure.
1559    int firstSlice, lastSlice;
1560    if (refSlice != NULL && !refSlice->totalLengthOnly)
1561    {
1562       // If a reference slice is provided, and totalLengthOnly is false, then we
1563       // will only need to measure the reference slice. The values generated for
1564       // it will later be copied to other slices.
1565       firstSlice = lastSlice = refSlice->index;
1566    }
1567    else
1568    {
1569       // Otherwise we'll measure all of the slices.
1570       firstSlice = 0;
1571       lastSlice = _numSlices[sliceType] - 1;
1572    }
1573
1574    // Iterate over the slices that need to be measured.
1575    for (int slice = firstSlice; slice <= lastSlice; slice++)
1576    {
1577       // Some aspects of the SlicePatchContext will vary as we move from patch
1578       // to patch, but we can go ahead and calculate now any stuff that is
1579       // invariant along the slice's direction.
1580       if (slice != 0)
1581       {
1582          // This is the position of the slice within each patch, in the
1583          // direction orthogonal to the slice. Even-numbered slices are at the
1584          // edge of the patch (position 1.0), while odd-numbered slices are in
1585          // the middle (position 0.5).
1586          context.position = 1.0f - ((float)(slice & 0x1) / 2.0f);
1587       }
1588       else
1589       {
1590          // For the first slice, we can't give it the usual treatment for even-
1591          // numbered slices (since there is no patch "before" it), so it gets
1592          // position 0.0 instead.
1593          context.position = 0.0f;
1594       }
1595       // This is the slice of the same kind that defines the 0.0 edge of the
1596       // patch. It will be the next lowest even-numbered slice. (Note the
1597       // integer division here.)
1598       context.edgeSlice[sliceType] = 2 * ((slice - 1) / 2);
1599
1600       // The alignment slice marks the zero-point from which we will be
1601       // calculating the distances. So the cumulative distance there is zero.
1602       MatrixElement(values, sliceType, slice, alignSlice) = 0.0f;
1603
1604       // Now we're going to calculate distances for control points "greater
1605       // than" the one marked by the alignment slice.
1606
1607       // Start with zero cumulative distance.
1608       float cumulativeDistance = 0.0f;
1609       // Each pair of control points delineates a "half patch" (the middle
1610       // control point corresponds to surface coords generated from t=0.5).
1611       // Since distance measurements are done within each patch, and we want to
1612       // measure the distance at half-patch increments, we need to alternate
1613       // doing the segment measurements from 0 to 0.5 and from 0.5 to 1.0 (for
1614       // values of t). We start with a t target of 0.5 or 1.0 depending on the
1615       // even/odd nature of the alignment slice.
1616       float slicewiseFraction = (float)((alignSlice & 0x1) + 1) / 2.0f;
1617       // Iterate over the control points greater than the alignment point.
1618       for (int halfPatch = alignSlice + 1; halfPatch < (int)SliceSize(sliceType); halfPatch++)
1619       {
1620          // Find the slice-of-other-kind that defines the patch edge orthogonal
1621          // to our slice.
1622          context.edgeSlice[OtherSliceType(sliceType)] = 2 * ((halfPatch - 1) / 2);
1623          // Estimate the slice length along the surface of the half patch.
1624          float segmentLengthEstimate =
1625             EstimateSegmentLength(slicewiseFraction - 0.5f, slicewiseFraction, context);
1626          // Recursively refine that estimate until it is good enough, then add it
1627          // to our cumulative distance.
1628          cumulativeDistance +=
1629             RefineSegmentLength(slicewiseFraction - 0.5f, slicewiseFraction, context,
1630                                 segmentLengthEstimate, UNITS_ERROR_BOUND);
1631          // Store that cumulative distance in the output array.
1632          MatrixElement(values, sliceType, slice, halfPatch) = cumulativeDistance;
1633          // Flip to measure the other half patch.
1634          slicewiseFraction = 1.5f - slicewiseFraction;
1635       }
1636
1637       // Now we're going to calculate distances for control points "less
1638       // than" the one marked by the alignment slice.
1639
1640       // Start with zero cumulative distance.
1641       cumulativeDistance = 0.0f;
1642       // We need to alternate doing the segment measurements from 1.0 to 0.5 and
1643       // from 0.5 to 0 (for values of t). We start with a t target of 0.5 or 0
1644       // depending on the even/odd nature of the alignment slice.
1645       slicewiseFraction = (float)((alignSlice - 1) & 0x1) / 2.0f;
1646       // Iterate over the control points less than the alignment point.
1647       for (int halfPatch = alignSlice - 1; halfPatch >= 0; halfPatch--)
1648       {
1649          // Find the slice-of-other-kind that defines the patch edge orthogonal
1650          // to our slice.
1651          context.edgeSlice[OtherSliceType(sliceType)] = 2 * ((halfPatch - 1) / 2);
1652          // Estimate the slice length along the surface of the half patch.
1653          float segmentLengthEstimate =
1654             EstimateSegmentLength(slicewiseFraction + 0.5f, slicewiseFraction, context);
1655          // Recursively refine that estimate until it is good enough, then add it
1656          // to our cumulative distance. (Which is negative on this side!)
1657          cumulativeDistance -=
1658             RefineSegmentLength(slicewiseFraction + 0.5f, slicewiseFraction, context,
1659                                 segmentLengthEstimate, UNITS_ERROR_BOUND);
1660          // Store that cumulative distance in the output array.
1661          MatrixElement(values, sliceType, slice, halfPatch) = cumulativeDistance;
1662          // Flip to measure the other half patch.
1663          slicewiseFraction = 0.5f - slicewiseFraction;
1664       }
1665    }
1666
1667    // Now we may adjust the distance values based on scaling/tiling input.
1668  
1669    // If there's a reference slice, we're going to need to know the total slice
1670    // length, so save that away.
1671    float refTotalLength;
1672    if (refSlice != NULL)
1673    {
1674       refTotalLength =
1675          MatrixElement(values, sliceType, refSlice->index, SliceSize(sliceType) - 1) -
1676          MatrixElement(values, sliceType, refSlice->index, 0);
1677    }
1678    else
1679    {
1680       refTotalLength = 1.0f; // unused, but avoid uninitialized-var warning
1681    }
1682
1683 #if defined(_DEBUG)
1684     ASSERT_MESSAGE(refTotalLength != 0.0f, "calculated length of reference slice is zero");
1685     ASSERT_MESSAGE(rawScaleOrTiles != 0.0f, "internal scale or tiles value is zero");
1686 #endif
1687
1688    // Iterate over the slices we're processing and adjust the distance values
1689    // (remember that this may just be the reference slice).
1690    for (int slice = firstSlice; slice <= lastSlice; slice++)
1691    {
1692       // Figure out what we're going to divide the distances by.
1693       float scaleFactor;
1694       if (rawScale)
1695       {
1696          // In this case we've just been passed in the value to divide by.
1697          scaleFactor = rawScaleOrTiles;
1698          if (refSlice != NULL)
1699          {
1700             // However if there's a reference slice, adjust the divisor by the
1701             // ratio of this slice's length to the reference slice's length.
1702             // (Which is a NOP if this slice actually is the reference slice.)
1703             // Example: if the ref slice is length 2, this slice is length 3,
1704             // and the raw scale factor is 4... then all distances on the ref
1705             // slice would be divided by 4 * 2 / 2 = 4, and distances on this
1706             // slice would be divided by 4 * 3 / 2 = 6.
1707             scaleFactor *=
1708                ((MatrixElement(values, sliceType, slice, SliceSize(sliceType) - 1) -
1709                  MatrixElement(values, sliceType, slice, 0)) /
1710                 refTotalLength);
1711          }
1712       }
1713       else
1714       {
1715          // In this case we've been passed in a desired tiling value. We're
1716          // going to want to eventually divide the distances by length / tiles.
1717          // Example: if this slice is length 6 and the desired tiling is 3, we
1718          // will divide the distances on this slice by 6 / 3 = 2.
1719          scaleFactor =
1720             (MatrixElement(values, sliceType, slice, SliceSize(sliceType) - 1) -
1721              MatrixElement(values, sliceType, slice, 0)) /
1722             rawScaleOrTiles;
1723       }
1724       // Adjust the distances for this slice by the divisor we calculated.
1725       for (unsigned halfPatch = 0; halfPatch < SliceSize(sliceType); halfPatch++)
1726       {
1727          MatrixElement(values, sliceType, slice, halfPatch) /= scaleFactor;
1728       }
1729    }
1730
1731    // One final step if we have a reference slice and totalLengthOnly is false.
1732    // In that case, up until this point we have only been processing the
1733    // reference slice. Now we have to copy the reference slice's values to all
1734    // other slices.
1735    // (These loops also copy the reference slice to itself, which is fine.)
1736    if (refSlice != NULL && !refSlice->totalLengthOnly)
1737    {
1738       for (unsigned slice = 0; slice < _numSlices[sliceType]; slice++)
1739       {
1740          for (unsigned halfPatch = 0; halfPatch < SliceSize(sliceType); halfPatch++)
1741          {
1742             MatrixElement(values, sliceType, slice, halfPatch) =
1743                MatrixElement(values, sliceType, refSlice->index, halfPatch);
1744          }
1745       }
1746    }
1747 }
1748
1749 /**
1750  * Generate coordinates for a specified texture axis based on a linear
1751  * combination of factors. This method does the final work for the
1752  * GeneralFunction public method.
1753  *
1754  * @param factors       Factors to determine the texture coords.
1755  * @param axis          The texture axis to process.
1756  * @param alignRow      Zero-point row.
1757  * @param alignCol      Zero-point column.
1758  * @param surfaceValues true if calculations are for S/T values on the mesh
1759  *                      surface; false if calculations are for S/T values at
1760  *                      the control points.
1761  * @param rowDistances  Surface distance-along-row values (measured from
1762  *                      alignment column) for spots corresponding to each
1763  *                      control point.
1764  * @param colDistances  Surface distance-along-column values (measured from
1765  *                      alignment row) for spots corresponding to each
1766  *                      control point.
1767  */
1768 void
1769 MeshEntity::GeneralFunctionInt(const GeneralFunctionFactors& factors,
1770                                TextureAxis axis,
1771                                int alignRow,
1772                                int alignCol,
1773                                bool surfaceValues,
1774                                const Matrix<float>& rowDistances,
1775                                const Matrix<float>& colDistances)
1776 {
1777    // Grab the "original value" info if the equation uses it.
1778    AllocatedMatrix<float> oldValues(_meshData.x(), _meshData.y());
1779    AllocatedMatrix<float> newValues(_meshData.x(), _meshData.y());
1780    if (factors.oldValue != 0.0f)
1781    {
1782       if (surfaceValues)
1783       {
1784          // Will be manipulating surface values.
1785          GenSurfaceFromControlTex(axis, oldValues);
1786       }
1787       else
1788       {
1789          // Will be manipulating control point values.
1790          CopyValuesFromControlTex(axis, oldValues);
1791       }
1792    }
1793
1794    // Iterate over all values and apply the equation.
1795    for (int rowIndex = 0; rowIndex < (int)_numSlices[ROW_SLICE_TYPE]; rowIndex++)
1796    {
1797       for (int colIndex = 0; colIndex < (int)_numSlices[COL_SLICE_TYPE]; colIndex++)
1798       {
1799          newValues(rowIndex, colIndex) =
1800             factors.oldValue * oldValues(rowIndex, colIndex) +
1801             factors.rowDistance * rowDistances(rowIndex, colIndex) +
1802             factors.colDistance * colDistances(rowIndex, colIndex) +
1803             factors.rowNumber * (rowIndex - alignRow) +
1804             factors.colNumber * (colIndex - alignCol) +
1805             factors.constant;
1806       }
1807    }
1808
1809    // Store the generated values.
1810    if (surfaceValues)
1811    {
1812       // If we're manipulating surface values, figure the necessary control
1813       // point values to make those.
1814       GenControlTexFromSurface(axis, newValues);
1815    }
1816    else
1817    {
1818       // If we're manipulating control point values, store the new values.
1819       CopyControlTexFromValues(axis, newValues);
1820    }
1821 }