3 * Implements the MeshEntity class.
4 * @ingroup meshtex-core
8 * Copyright 2012 Joel Baxter
10 * This file is part of MeshTex.
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.
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.
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/>.
28 #include "MeshEntity.h"
29 #include "MeshEntityMessages.h"
32 #include "texturelib.h"
36 * Size of buffer for composing messages to send to the info callback.
38 #define INFO_BUFFER_SIZE 1024
41 * Stop successive refinement of path length estimates when the change in values
42 * is equal to or less than this tolerance.
44 #define UNITS_ERROR_BOUND 0.5
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
52 * @param sliceType Kind of slice to find the other of, so to speak.
54 #define OtherSliceType(sliceType) (1 - (sliceType))
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.
60 * @param sliceType Kind of slice to measure.
62 #define SliceSize(sliceType) (_numSlices[OtherSliceType(sliceType)])
65 * Macro to get rid of negative-zero values.
67 * @param floatnum Number to be sanitized.
69 #define SanitizeFloat(floatnum) ((floatnum) == -0.0f ? 0.0f : (floatnum))
73 * For a given slice kind, which texture axis (S or T) normally changes
76 MeshEntity::TextureAxis MeshEntity::_naturalAxis[NUM_SLICE_TYPES] =
77 { S_TEX_AXIS, T_TEX_AXIS };
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
84 bool MeshEntity::_radiantScaleInverted[NUM_SLICE_TYPES] = { false, true };
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.
91 bool MeshEntity::_radiantTilesInverted[NUM_SLICE_TYPES] = { false, false };
94 * Message format strings for describing texture mapping on a slice.
96 const char *MeshEntity::_infoSliceFormatString[NUM_SLICE_TYPES] =
97 { INFO_ROW_FORMAT, INFO_COL_FORMAT };
100 * Message format strings for describing texture mapping on a slice in the
101 * unusual case where the scale value is infinite.
103 const char *MeshEntity::_infoSliceInfscaleFormatString[NUM_SLICE_TYPES] =
104 { INFO_ROW_INFSCALE_FORMAT, INFO_COL_INFSCALE_FORMAT };
107 * Message format strings for warning that a scale value is infinite and
108 * cannot be transferred to the Set S/T Scale dialog.
110 const char *MeshEntity::_warningSliceInfscaleFormatString[NUM_SLICE_TYPES] =
111 { WARNING_ROW_INFSCALE, WARNING_COL_INFSCALE };
114 * Message format strings for an illegal slice number error.
116 const char *MeshEntity::_errorBadSliceString[NUM_SLICE_TYPES] =
117 { ERROR_BAD_ROW, ERROR_BAD_COL };
120 * Message format strings for a scale = 0 error.
122 const char *MeshEntity::_errorSliceZeroscaleString[NUM_SLICE_TYPES] =
123 { ERROR_ROW_ZEROSCALE, ERROR_COL_ZEROSCALE };
126 * Message format strings for a tiles = 0 error.
128 const char *MeshEntity::_errorSliceZerotilesString[NUM_SLICE_TYPES] =
129 { ERROR_ROW_ZEROTILES, ERROR_COL_ZEROTILES };
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.
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.
142 MeshEntity::MeshEntity(scene::Node& mesh,
143 const MessageCallback& infoReportCallback,
144 const MessageCallback& warningReportCallback,
145 const MessageCallback& errorReportCallback) :
147 _infoReportCallback(infoReportCallback),
148 _warningReportCallback(warningReportCallback),
149 _errorReportCallback(errorReportCallback)
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();
161 _naturalTexUnits[S_TEX_AXIS] = texture->width / 2.0f;
162 _naturalTexUnits[T_TEX_AXIS] = texture->height / 2.0f;
164 // We don't need the shader for anything else now.
166 // Check for valid mesh; bail if not.
167 if (_numSlices[ROW_SLICE_TYPE] < 3 ||
168 _numSlices[COL_SLICE_TYPE] < 3 ||
172 _errorReportCallback(ERROR_BAD_MESH);
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;
187 * Destructor. Note that this only destroys the wrapper object, not the patch
190 MeshEntity::~MeshEntity()
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.
199 * @return true if valid, false if not.
202 MeshEntity::IsValid() const
208 * Get information about the patch mesh.
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
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
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
227 * @param colTexInfoCallback Pointer to callback for reference column info; NULL
231 MeshEntity::GetInfo(const int *refRow,
233 const TexInfoCallback *rowTexInfoCallback,
234 const TexInfoCallback *colTexInfoCallback)
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).
244 ReportSliceTexInfo(ROW_SLICE_TYPE, *refRow, _naturalAxis[ROW_SLICE_TYPE],
245 messageBuffer + bufferOffset,
246 INFO_BUFFER_SIZE - bufferOffset,
248 // Move the message buffer pointer along.
249 bufferOffset = strlen(messageBuffer);
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).
255 ReportSliceTexInfo(COL_SLICE_TYPE, *refCol, _naturalAxis[COL_SLICE_TYPE],
256 messageBuffer + bufferOffset,
257 INFO_BUFFER_SIZE - bufferOffset,
259 // Move the message buffer pointer along.
260 bufferOffset = strlen(messageBuffer);
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,
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);
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.
284 * @param axes The texture axes to align.
287 MeshEntity::MinAlign(TextureAxisSelection axes)
289 // Implement this by applying MinAlignInt to each specified axis.
290 ProcessForAxes(&MeshEntity::MinAlignInt, axes);
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.
298 * @param axes The texture axes to align.
301 MeshEntity::MaxAlign(TextureAxisSelection axes)
303 // Implement this by applying MaxAlignInt to each specified axis.
304 ProcessForAxes(&MeshEntity::MaxAlignInt, axes);
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.
312 * @param axes The texture axes to align.
315 MeshEntity::MinMaxAlignAutoScale(TextureAxisSelection axes)
317 // Implement this by applying MinMaxAlignAutoScaleInt to each specified axis.
318 ProcessForAxes(&MeshEntity::MinMaxAlignAutoScaleInt, axes);
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.
326 * @param axes The texture axes to align.
329 MeshEntity::MinMaxAlignStretch(TextureAxisSelection axes)
331 // Implement this by applying MinMaxAlignStretchInt to each specified axis.
332 ProcessForAxes(&MeshEntity::MinMaxAlignStretchInt, axes);
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.
340 * @param axes The texture axes to align.
343 MeshEntity::MinMaxAlignShrink(TextureAxisSelection axes)
345 // Implement this by applying MinMaxAlignShrinkInt to each specified axis.
346 ProcessForAxes(&MeshEntity::MinMaxAlignShrinkInt, axes);
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.
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.
358 * Among the slices perpendicular to the direction of scaling, an alignment
359 * slice is used to fix the position of the texture boundary.
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.
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
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.
383 MeshEntity::SetScale(SliceType sliceType,
384 const SliceDesignation *alignSlice,
385 const RefSliceDescriptor *refSlice,
387 float naturalScaleOrTiles)
389 // We're about to make changes!
392 // Check for bad inputs. Also convert from natural scale to raw scale.
393 if (alignSlice != NULL && !alignSlice->maxSlice)
395 if (alignSlice->index < 0 ||
396 alignSlice->index >= (int)SliceSize(sliceType))
398 _errorReportCallback(_errorBadSliceString[OtherSliceType(sliceType)]);
402 if (refSlice != NULL && !refSlice->designation.maxSlice)
404 if (refSlice->designation.index < 0 ||
405 refSlice->designation.index >= (int)_numSlices[sliceType])
407 _errorReportCallback(_errorBadSliceString[sliceType]);
411 TextureAxis axis = _naturalAxis[sliceType];
412 float rawScaleOrTiles = naturalScaleOrTiles;
415 // In this case, naturalScaleOrTiles (copied to rawScaleOrTiles) was a
416 // natural-scale factor.
417 if (rawScaleOrTiles == 0)
419 _errorReportCallback(_errorSliceZeroscaleString[sliceType]);
422 // If Radiant's internal orientation is backwards, account for that.
423 if (_radiantScaleInverted[sliceType])
425 rawScaleOrTiles = -rawScaleOrTiles;
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];
433 // In this case, naturalScaleOrTiles (copied to rawScaleOrTiles) was a
435 if (rawScaleOrTiles == 0)
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]);
443 // If Radiant's internal orientation is backwards, account for that.
444 if (_radiantTilesInverted[sliceType])
446 rawScaleOrTiles = -rawScaleOrTiles;
450 // Make sure we have a definite number for the alignment slice, and for the
451 // reference slice if any.
453 InternalSliceDesignation(alignSlice, (SliceType)OtherSliceType(sliceType));
454 RefSliceDescriptorInt descriptor;
455 RefSliceDescriptorInt *refSliceInt = // will be NULL if no reference
456 InternalRefSliceDescriptor(refSlice, sliceType, descriptor);
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,
465 // Derive the control point values necessary to achieve those surface values.
466 GenControlTexFromSurface(axis, surfaceValues);
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
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
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.
495 * @param sFactors Factors to determine the S texture coords; NULL if S
497 * @param tFactors Factors to determine the T texture coords; NULL if T
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
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.
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,
519 // We're about to make changes!
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);
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))
540 GenScaledDistanceValues(ROW_SLICE_TYPE,
547 if ((sFactors != NULL && sFactors->colDistance != 0.0f) ||
548 (tFactors != NULL && tFactors->colDistance != 0.0f))
550 GenScaledDistanceValues(COL_SLICE_TYPE,
558 // Modify the S axis if requested.
559 if (sFactors != NULL)
561 GeneralFunctionInt(*sFactors, S_TEX_AXIS, alignRowInt, alignColInt, surfaceValues,
562 rowDistances, colDistances);
565 // Modify the T axis if requested.
566 if (tFactors != NULL)
568 GeneralFunctionInt(*tFactors, T_TEX_AXIS, alignRowInt, alignColInt, surfaceValues,
569 rowDistances, colDistances);
577 * Update the internally stored information for the min and max extent of the
578 * mesh on the specified worldspace axis.
580 * @param axis The worldspace axis.
583 MeshEntity::UpdatePosMinMax(PositionAxis axis)
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++)
590 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
592 float current = _meshData(rowIndex, colIndex).m_vertex[axis];
593 if (current < _posMin[axis])
595 _posMin[axis] = current;
597 if (current > _posMax[axis])
599 _posMax[axis] = current;
606 * Update the internally stored information for the min and max extent of the
607 * mesh on the specified texture axis.
609 * @param axis The texture axis.
612 MeshEntity::UpdateTexMinMax(TextureAxis axis)
614 // Bail out if no operations have possibly changed these values.
615 if (!_texMinMaxDirty[axis])
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++)
625 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
627 float current = _meshData(rowIndex, colIndex).m_texcoord[axis];
628 if (current < _texMin[axis])
630 _texMin[axis] = current;
632 if (current > _texMax[axis])
634 _texMax[axis] = current;
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]);
643 // Values are good until next relevant operation.
644 _texMinMaxDirty[axis] = false;
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.
652 MeshEntity::CreateUndoPoint()
654 GlobalPatchCreator().Patch_undoSave(_mesh);
658 * Commit the changes to the mesh so that they will be reflected in Radiant.
661 MeshEntity::CommitChanges()
663 GlobalPatchCreator().Patch_controlPointsChanged(_mesh);
664 // Radiant undo-buffer behavior requires this:
669 * Convert from SliceDesignation to a slice number. Interpret max slice if
670 * necessary, and fall back to slice 0 if unspecified.
672 * @param sliceDesignation Pointer to slice description; may be NULL.
673 * @param sliceType Slice kind (row or column).
675 * @return The slice number.
678 MeshEntity::InternalSliceDesignation(const SliceDesignation *sliceDesignation,
681 if (sliceDesignation != NULL)
683 // Interpret "max slice" if necessary.
684 if (sliceDesignation->maxSlice)
686 return _numSlices[sliceType] - 1;
690 return sliceDesignation->index;
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.
705 * @param refSlice Pointer to reference slice description; may be
707 * @param sliceType Slice kind (row or column).
708 * @param [out] refSliceInt RefSliceDescriptorInt to populate.
710 * @return NULL if input RefSliceDescriptor is NULL; else, pointer to populated
711 * RefSliceDescriptorInt.
713 MeshEntity::RefSliceDescriptorInt *
714 MeshEntity::InternalRefSliceDescriptor(const RefSliceDescriptor *refSlice,
716 RefSliceDescriptorInt& refSliceInt)
718 if (refSlice != NULL)
720 // Preserve totalLengthOnly.
721 refSliceInt.totalLengthOnly = refSlice->totalLengthOnly;
722 // Convert slice designator to a slice number.
723 refSliceInt.index = InternalSliceDesignation(&(refSlice->designation), sliceType);
728 // NULL if unspecified.
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.
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.
743 * @return The texture scale corresponding to the tiling amount.
746 MeshEntity::GetSliceTexScale(SliceType sliceType,
751 // XXX Similar to GenScaledDistanceValues; refactor for shared code?
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.
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;
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
770 context.position = 1.0f - ((float)(slice & 0x1) / 2.0f);
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;
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);
784 // Now it's time to walk the patches.
786 // We start off with no cumulative distance yet.
787 float cumulativeDistance = 0.0f;
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)
795 // Find the slice-of-other-kind that defines the patch edge orthogonal
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,
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]);
813 * Populate the SliceTexInfo for the indicated slice and texture axis.
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.
821 * @return true on success, false if slice cannot be processed.
824 MeshEntity::GetSliceTexInfo(SliceType sliceType,
829 // Bail out now if slice # is bad.
831 slice >= (int)_numSlices[sliceType])
833 _errorReportCallback(_errorBadSliceString[sliceType]);
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.
840 MatrixElement(_meshData, sliceType, slice, 0).m_texcoord[axis];
842 MatrixElement(_meshData, sliceType, slice, SliceSize(sliceType) - 1).m_texcoord[axis];
843 info.tiles = texEnd - texBegin;
844 if (texBegin < texEnd)
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);
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.
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;
875 MeshEntity::ReportSliceTexInfo(SliceType sliceType,
879 unsigned messageBufferSize,
880 const TexInfoCallback *texInfoCallback)
882 // Fetch the raw info.
884 if (!GetSliceTexInfo(sliceType, slice, axis, info))
889 // Account for Radiant-inverted.
890 if (_radiantScaleInverted[sliceType])
892 info.scale = -info.scale;
894 if (_radiantTilesInverted[sliceType])
896 info.tiles = -info.tiles;
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)
905 (*texInfoCallback)(SanitizeFloat(info.scale), SanitizeFloat(info.tiles));
909 // "Infinite scale" prevents us from invoking the callback, so
910 // raise a warning about that.
911 _warningReportCallback(_warningSliceInfscaleFormatString[sliceType]);
915 // Write texture info to buffer if one is provided.
916 if (messageBuffer != NULL)
920 snprintf(messageBuffer, messageBufferSize,
921 _infoSliceFormatString[sliceType], slice,
922 SanitizeFloat(info.scale), SanitizeFloat(info.tiles),
923 SanitizeFloat(info.min), SanitizeFloat(info.max));
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));
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.
941 * @param internalImpl The function to apply.
942 * @param axes The texture axes to affect.
945 MeshEntity::ProcessForAxes(InternalImpl internalImpl,
946 TextureAxisSelection axes)
948 // We're about to make changes!
951 // Apply the function to the requested axes.
952 bool sChanged = false;
953 bool tChanged = false;
954 if (axes != T_TEX_AXIS_ONLY)
956 sChanged = (*this.*internalImpl)(S_TEX_AXIS);
958 if (axes != S_TEX_AXIS_ONLY)
960 tChanged = (*this.*internalImpl)(T_TEX_AXIS);
963 // Done! Commit changes if necessary.
964 if (sChanged || tChanged)
971 * Add an offset to all control point values for the given texture axis.
973 * @param axis The texture axis to affect.
974 * @param shift The offset to add.
977 MeshEntity::Shift(TextureAxis axis,
980 // Iterate over all control points and add the offset.
981 for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
983 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
985 _meshData(rowIndex, colIndex).m_texcoord[axis] += shift;
989 // This operation might have changed texture min/max.
990 _texMinMaxDirty[axis] = true;
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
998 * @param axis The texture axis to affect.
999 * @param scale The scale factor.
1002 MeshEntity::Scale(TextureAxis axis,
1005 // Make sure the min value is updated; we'll need it below.
1006 UpdateTexMinMax(axis);
1008 // Iterate over all control points and apply the scale factor.
1009 for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
1011 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
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) +
1020 // This operation might have changed texture min/max.
1021 _texMinMaxDirty[axis] = true;
1025 * Implementation of MinAlign for a single texture axis.
1027 * @param axis The texture axis to affect.
1029 * @return true if the mesh was changed, false if not.
1032 MeshEntity::MinAlignInt(TextureAxis axis)
1034 // Make sure the min-aligned value is updated.
1035 UpdateTexMinMax(axis);
1037 // If already aligned, we're done.
1038 if (_texMinAligned[axis])
1040 // Didn't make changes.
1044 // Otherwise shift by the necessary amount to align.
1045 Shift(axis, ceilf(_texMin[axis]) - _texMin[axis]);
1052 * Implementation of MaxAlign for a single texture axis.
1054 * @param axis The texture axis to affect.
1056 * @return true if the mesh was changed, false if not.
1059 MeshEntity::MaxAlignInt(TextureAxis axis)
1061 // Make sure the max-aligned value is updated.
1062 UpdateTexMinMax(axis);
1064 // If already aligned, we're done.
1065 if (_texMaxAligned[axis])
1067 // Didn't make changes.
1071 // Otherwise shift by the necessary amount to align.
1072 Shift(axis, ceilf(_texMax[axis]) - _texMax[axis]);
1079 * Implementation of MinMaxAlignAutoScale for a single texture axis.
1081 * @param axis The texture axis to affect.
1083 * @return true if the mesh was changed, false if not.
1086 MeshEntity::MinMaxAlignAutoScaleInt(TextureAxis axis)
1088 // Make sure the max value is updated.
1089 UpdateTexMinMax(axis);
1091 // Choose to stretch or shrink, based on which will cause less change.
1092 if ((_texMax[axis] - floorf(_texMax[axis])) < 0.5)
1094 return MinMaxAlignStretchInt(axis);
1098 return MinMaxAlignShrinkInt(axis);
1103 * The meat of MinMaxAlignStretchInt and MinMaxAlignShrinkInt.
1105 * @param axis The texture axis to affect.
1106 * @param op Whether to stretch or shrink.
1108 * @return true if the mesh was changed, false if not.
1111 MeshEntity::MinMaxAlignScale(TextureAxis axis,
1114 // First make sure we are min-aligned.
1115 bool changed = MinAlignInt(axis);
1117 // Make sure the min/max values are updated.
1118 UpdateTexMinMax(axis);
1120 // More work to do if not max-aligned.
1121 if (!_texMaxAligned[axis])
1123 // Find the current tiling.
1124 float oldRepeats = _texMax[axis] - _texMin[axis];
1125 // Find the desired tiling, depending on whether we are stretching or
1128 if (op == STRETCH_SCALE_OP)
1130 newRepeats = floorf(_texMax[axis]) - _texMin[axis];
1134 newRepeats = ceilf(_texMax[axis]) - _texMin[axis];
1136 // Apply the necessary scaling to get the desired tiling.
1137 Scale(axis, newRepeats / oldRepeats);
1146 * Implementation of MinMaxAlignStretch for a single texture axis.
1148 * @param axis The texture axis to affect.
1150 * @return true if the mesh was changed, false if not.
1153 MeshEntity::MinMaxAlignStretchInt(TextureAxis axis)
1155 // Hand off to MinMaxAlignScale.
1156 return MinMaxAlignScale(axis, STRETCH_SCALE_OP);
1160 * Implementation of MinMaxAlignShrink for a single texture axis.
1162 * @param axis The texture axis to affect.
1164 * @return true if the mesh was changed, false if not.
1167 MeshEntity::MinMaxAlignShrinkInt(TextureAxis axis)
1169 // Hand off to MinMaxAlignScale.
1170 return MinMaxAlignScale(axis, SHRINK_SCALE_OP);
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).
1177 * @param axis The worldspace axis of interest.
1178 * @param t Bezier parameter.
1179 * @param context The slice and patch.
1181 * @return d(x, y, or z)/dt at the given t.
1184 MeshEntity::SliceParametricSpeedComponent(PositionAxis axis,
1186 const SlicePatchContext& context)
1188 float a = 1.0f - context.position;
1189 float b = 2.0f * context.position * a;
1191 float c = context.position * context.position;
1192 float d = 2.0f * t - 2.0f;
1193 float e = 2.0f - 4.0f * t;
1195 int patchStartCol = context.edgeSlice[COL_SLICE_TYPE];
1196 int patchStartRow = context.edgeSlice[ROW_SLICE_TYPE];
1198 if (context.sliceType == ROW_SLICE_TYPE)
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;
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;
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
1231 * @param t Bezier parameter.
1232 * @param context The slice and patch.
1234 * @return Path length.
1237 MeshEntity::SliceParametricSpeed(float t,
1238 const SlicePatchContext& context)
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);
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).
1251 * @param startPosition Bezier parameter value for the start point of the
1253 * @param endPosition Bezier parameter value for the end point of the slice
1255 * @param context The slice and patch.
1257 * @return Estimate of segment length.
1260 MeshEntity::EstimateSegmentLength(float startPosition,
1262 const SlicePatchContext& context)
1264 // Gauss-Legendre implementation taken from "Numerical Recipes in C".
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};
1269 float xm = 0.5f * (endPosition + startPosition);
1270 float xr = 0.5f * (endPosition - startPosition);
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));
1279 return fabsf(s * xr);
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.
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
1291 * @param context The slice and patch.
1292 * @param segmentLengthEstimate Starting estimate for segment legnth.
1293 * @param maxError Max acceptable variance between estimates.
1295 * @return Improved estimate of segment length.
1298 MeshEntity::RefineSegmentLength(float startPosition,
1300 const SlicePatchContext& context,
1301 float segmentLengthEstimate,
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);
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)
1314 leftLength = RefineSegmentLength(startPosition, midPosition,
1318 rightLength = RefineSegmentLength(midPosition, endPosition,
1324 // Return the sum of the (refined) half-segment estimates.
1325 return (leftLength + rightLength);
1329 * Derive control point texture coordinates (on a given texture axis) from a
1330 * set of mesh surface texture coordinates.
1332 * @param axis The texture axis of interest.
1333 * @param surfaceValues The surface texture coordinates.
1336 MeshEntity::GenControlTexFromSurface(TextureAxis axis,
1337 const Matrix<float>& surfaceValues)
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)
1343 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1345 _meshData(rowIndex, colIndex).m_texcoord[axis] =
1346 surfaceValues(rowIndex, colIndex);
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)
1354 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
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;
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)
1367 for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
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;
1376 // And finally on odd rows & odd columns (i.e. patch centers).
1377 for (unsigned rowIndex = 1; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex += 2)
1379 for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
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;
1394 // This operation might have changed texture min/max.
1395 _texMinMaxDirty[axis] = true;
1399 * Overwrite control point texture coordinates (on a given texture axis) with
1400 * the input texture coordinates.
1402 * @param axis The texture axis of interest.
1403 * @param values The input texture coordinates.
1406 MeshEntity::CopyControlTexFromValues(TextureAxis axis,
1407 const Matrix<float>& values)
1409 // Iterate over all control points and just do a straight copy.
1410 for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
1412 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
1414 _meshData(rowIndex, colIndex).m_texcoord[axis] =
1415 values(rowIndex, colIndex);
1419 // This operation might have changed texture min/max.
1420 _texMinMaxDirty[axis] = true;
1424 * Derive a set of surface texture coordinates (on a given texture axis) from
1425 * the control point texture coordinates.
1427 * @param axis The texture axis of interest.
1428 * @param [out] surfaceValues The surface texture coordinates.
1431 MeshEntity::GenSurfaceFromControlTex(TextureAxis axis,
1432 Matrix<float>& surfaceValues)
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)
1438 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
1440 surfaceValues(rowIndex, colIndex) =
1441 _meshData(rowIndex, colIndex).m_texcoord[axis];
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)
1448 for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
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;
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)
1467 for (unsigned colIndex = 1; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
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;
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)
1480 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex += 2)
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;
1491 * Copy the control point texture coordinates (on a given texture axis) to
1492 * the output texture coordinates parameter.
1494 * @param axis The texture axis of interest.
1495 * @param [out] values The output texture coordinates.
1498 MeshEntity::CopyValuesFromControlTex(TextureAxis axis,
1499 Matrix<float>& values)
1501 // Iterate over all control points and just do a straight copy.
1502 for (unsigned rowIndex = 0; rowIndex < _numSlices[ROW_SLICE_TYPE]; rowIndex++)
1504 for (unsigned colIndex = 0; colIndex < _numSlices[COL_SLICE_TYPE]; colIndex++)
1506 values(rowIndex, colIndex) =
1507 _meshData(rowIndex, colIndex).m_texcoord[axis];
1513 * Generate a set of values based on surface slice lengths and some amount of
1514 * desired scaling or tiling.
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.
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.
1537 MeshEntity::GenScaledDistanceValues(SliceType sliceType,
1539 const RefSliceDescriptorInt *refSlice,
1541 float rawScaleOrTiles,
1542 Matrix<float>& values)
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.
1548 // XXX Similar to GetSliceTexScale; refactor for shared code?
1550 // We're going to be walking patches along the mesh, choosing the patches
1551 // that surround/affect the slice we are interested in.
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;
1558 // Pick the slices that we will measure.
1559 int firstSlice, lastSlice;
1560 if (refSlice != NULL && !refSlice->totalLengthOnly)
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;
1569 // Otherwise we'll measure all of the slices.
1571 lastSlice = _numSlices[sliceType] - 1;
1574 // Iterate over the slices that need to be measured.
1575 for (int slice = firstSlice; slice <= lastSlice; slice++)
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.
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);
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;
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);
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;
1604 // Now we're going to calculate distances for control points "greater
1605 // than" the one marked by the alignment slice.
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++)
1620 // Find the slice-of-other-kind that defines the patch edge orthogonal
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;
1637 // Now we're going to calculate distances for control points "less
1638 // than" the one marked by the alignment slice.
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--)
1649 // Find the slice-of-other-kind that defines the patch edge orthogonal
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;
1667 // Now we may adjust the distance values based on scaling/tiling input.
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)
1675 MatrixElement(values, sliceType, refSlice->index, SliceSize(sliceType) - 1) -
1676 MatrixElement(values, sliceType, refSlice->index, 0);
1680 refTotalLength = 1.0f; // unused, but avoid uninitialized-var warning
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");
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++)
1692 // Figure out what we're going to divide the distances by.
1696 // In this case we've just been passed in the value to divide by.
1697 scaleFactor = rawScaleOrTiles;
1698 if (refSlice != NULL)
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.
1708 ((MatrixElement(values, sliceType, slice, SliceSize(sliceType) - 1) -
1709 MatrixElement(values, sliceType, slice, 0)) /
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.
1720 (MatrixElement(values, sliceType, slice, SliceSize(sliceType) - 1) -
1721 MatrixElement(values, sliceType, slice, 0)) /
1724 // Adjust the distances for this slice by the divisor we calculated.
1725 for (unsigned halfPatch = 0; halfPatch < SliceSize(sliceType); halfPatch++)
1727 MatrixElement(values, sliceType, slice, halfPatch) /= scaleFactor;
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
1735 // (These loops also copy the reference slice to itself, which is fine.)
1736 if (refSlice != NULL && !refSlice->totalLengthOnly)
1738 for (unsigned slice = 0; slice < _numSlices[sliceType]; slice++)
1740 for (unsigned halfPatch = 0; halfPatch < SliceSize(sliceType); halfPatch++)
1742 MatrixElement(values, sliceType, slice, halfPatch) =
1743 MatrixElement(values, sliceType, refSlice->index, halfPatch);
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.
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
1764 * @param colDistances Surface distance-along-column values (measured from
1765 * alignment row) for spots corresponding to each
1769 MeshEntity::GeneralFunctionInt(const GeneralFunctionFactors& factors,
1774 const Matrix<float>& rowDistances,
1775 const Matrix<float>& colDistances)
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)
1784 // Will be manipulating surface values.
1785 GenSurfaceFromControlTex(axis, oldValues);
1789 // Will be manipulating control point values.
1790 CopyValuesFromControlTex(axis, oldValues);
1794 // Iterate over all values and apply the equation.
1795 for (int rowIndex = 0; rowIndex < (int)_numSlices[ROW_SLICE_TYPE]; rowIndex++)
1797 for (int colIndex = 0; colIndex < (int)_numSlices[COL_SLICE_TYPE]; colIndex++)
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) +
1809 // Store the generated values.
1812 // If we're manipulating surface values, figure the necessary control
1813 // point values to make those.
1814 GenControlTexFromSurface(axis, newValues);
1818 // If we're manipulating control point values, store the new values.
1819 CopyControlTexFromValues(axis, newValues);