Class MNNormalSpec
See Also: Class MNMESH , Class MNNormalFace
class MNNormalSpec : public IPipelineClient, public FlagUser
Description:
This class is available in release 5.0 and later only.
This class is an interface used to store user-specified normals
(as created in the Edit Normals modifier). These normals have very
limited pipeline support. They are used for viewport display, but not
for rendering.
The MNNormalSpec contains three types of normals:
- Unspecified - these are the usual normals that are computed from
smoothing groups. All normals are unspecified by default.
- Specified - these are normals that are intended for use by particular
corners of particular faces, without regard to smoothing groups. For
instance, you can create a box, apply Edit Normals, select a group of
normals at a particular vertex, and click "Unify". Now those three
faces are told to specifically use that one unified normal, and they
ignore their smoothing groups at that vertex (which would normally
tell them they should each have their own normal).
- Explicit - these are normals that are set to particular values.
For instance, if the user wants to use the Edit Normals Move or Rotate
commands to set a normal to something other than its default value,
it has to be made explicit, so it won't be recomputed based on the
face normals. All explicit normals are also considered to be specified..
Flags:
MNNORMAL_NORMALS_BUILT
Indicates that non-specified normals have been constructed using
smoothing groups. If not set, non-specified normals may be invalid.
MNNORMAL_NORMALS_COMPUTED
Indicates that non-explicit normals have been computed using geometrically
computed face normals. (If not set, only explicit normals may be assumed
to be pointing the right direction.)
Data Members
All data members are private.
int mNumNormalAlloc, mNumFaceAlloc;
The current allocation length of the mpNormal and mpFace arrays.
int mNumNormals, mNumFaces;
The number of normals and faces in the mpNormal and mpFace arrays.
(May be less than the actual allocation above.)
MNNormalFace *mpFace;
The array of normal faces.
Point3 *mpNormal;
The array of normals, all of which should be either length 1 or (occasionally) 0.
BitArray mNormalExplicit;
Indicates whether mpNormal[i] is explicit or computed from face normals.
BitArray mNormalSel;
Current normal selection.
float mDisplayLength;
The length to use when displaying, hit testing, or moving normals.
MNMesh *mpParent;
A pointer to the "parent" MNMesh that owns this MNNormalSpec. This parent
information is required for some operations, such as display. (Such
operations should indicate below where parent information is required.)
:
Methods:
Prototype:
MNNormalSpec ();
Remarks:
Constructor. Initializes all data members.
Prototype:
~ MNNormalSpec ();
Remarks:
Destructor. Calls ClearAndFree().
Prototype:
void Initialize();
Remarks:
Initializes all data members. Do not call if memory has already been
allocated, or that memory will be leaked.
Prototype:
bool NAlloc (int num, bool keep=TRUE);
Remarks:
Sets the size of the normal array
Return Value:
True if successful; false indicates a failed memory allocation.
Prototype:
void NShrink ();
Remarks:
Reduces the allocation size down to the actual number of normals.
Prototype:
bool FAlloc (int num, bool keep=TRUE);
Remarks:
Sets the size of the face array.
Return Value:
True if successful; false indicates a failed memory allocation.
Prototype:
void FShrink ();
Remarks:
Reduces the allocation size down to the actual number of faces.
Prototype:
void Clear ();
Remarks:
Clears out all data, but doesn't necessarily free array memory.
Prototype:
void ClearAndFree ();
Remarks:
Clears out all data and frees all memory.
Prototype:
int GetNumFaces () const
Remarks:
Returns the current number of faces in the MNNormalSpec
Prototype:
bool SetNumFaces (int numFaces);
Remarks:
Sets the current number of faces in the MNNormalSpec,
increasing the allocation size as needed.
Return Value:
True if successful; false indicates a failed memory allocation.
Prototype:
int GetNumNormals () const
Remarks:
Returns the current number of normals in the MNNormalSpec
Prototype:
bool SetNumNormals (int numNormals);
Remarks:
Sets the current number of normals in the MNNormalSpec,
increasing the allocation size as needed.
Return Value:
True if successful; false indicates a failed memory allocation.
Prototype:
Point3 & Normal (int normID) const
Remarks:
Returns the normal indicated. Since it returns a reference,
you can use it as a set method as well:
Normal(i) = Normalize (Point3(1,1,0));
(Note that all normals should be normalized to a length of 1.)
Prototype:
Point3 * GetNormalArray () const
Remarks:
Returns a pointer to the whole normal array.
Prototype:
bool GetNormalExplicit (int normID) const
Remarks:
Indicates whether a given normal is explicit or not.
Prototype:
void SetNormalExplicit (int normID, bool value);
Remarks:
Sets a particular normal to be explicit or not. Note that if
you make a normal non-explicit, it may need to be recomputed,
so you may want to call ComputeNormals or at least clear the
MNNORMAL_NORMALS_COMPUTED flag.
Parameters:
int normID
The index of the normal
bool value
True to make the normal explicit; false to make it non-explicit.
Prototype:
MNNormalFace & Face(int faceID) const
Remarks:
Returns the indicated face.
Prototype:
MNNormalFace * GetFaceArray () const
Remarks:
Returns a pointer to the whole face array.
Prototype:
void SetParent (MNMesh *pMesh);
Remarks:
Tells the MNNormalSpec what MNMesh "owns" it.
This "Parent" MNMesh is used in methods such as
Display, Hit-Testing, and certain operations like
Unify to get information about the vertices that
normals are based on. (There's no vertex info
in the MNNormalSpec itself.)
If you have an isolated MNNormalSpec which doesn't
really have an associated "parent", you can
temporarily set this to a mesh with the right sort
of faces and vertices, but you should clear it
afterwards by calling SetParent (NULL). See the
Edit Normals modifier source in
maxsdk\samples\mesh\EditablePoly\EditNormals.cpp
for an example of this sort of usage.
Prototype:
Point3 & GetNormal (int face, int corner);
Remarks:
Returns the normal used by the indicated face, in the indicated corner.
Prototype:
void SetNormal (int face, int corner, Point3 & normal);
Remarks:
Creates a new (explicit) normal and uses it in the indicated
corner of the indicated face. If "normal" is not already
normalized, this method will take care of it.
Prototype:
int GetNormalIndex (int face, int corner);
Remarks:
Returns the index of the normal used in the indicated corner
of the indicated face.
Prototype:
void SetNormalIndex (int face, int corner, int normalIndex);
Remarks:
Sets the index of the normal used in the indicated corner of
the indicated face, and marks it as specified.
Prototype:
int NewNormal (Point3 & normal, bool explic=true);
Remarks:
Creates a new normal at the end of the normal array.
Parameters:
Point3 & normal
The desired normal direction. Will be normalized to a length
of 1 by the method if needed.
bool explic=true
Indicates whether the new normal should be considered explicit
or not.
Prototype:
void SetSelection (BitArray & newSelection);
Remarks:
Sets the current normal selection.
Prototype:
BitArray & GetSelection();
Remarks:
Returns the current normal selection.
Prototype:
void SetDisplayLength (float displayLength);
Remarks:
Sets the current length used for normal display, hit-testing, and Translations.
Prototype:
float GetDisplayLength ();
Remarks:
Returns the current length used for normal display, hit-testing, and Translations.
Prototype:
void Display (GraphicsWindow *gw, bool showSel);
Remarks:
Requires an accurate "parent" pointer (see SetParent).
Displays the current normals in the graphics window indicated.
If "showSel" is true, selected normals are displayed in the
usual subobject selection color.
Prototype:
bool HitTest (GraphicsWindow *gw, HitRegion *hr, DWORD flags, SubObjHitList& hitList);
Remarks:
Requires an accurate "parent" pointer (see SetParent). Hit-tests on the current normals.
Parameters:
GraphicsWindow *gw
The window to hit-test in.
HitRegion *hr
A hit region, typically generated by a call like MakeHitRegion(hr,type, crossing,4,p);
DWORD flags
Hit testing flags. Please see BaseObject::HitTest for a description of
these flags and of the "type" and "crossing" variables used to generate
the HitRegion.
SubObjHitList & hitList
Where the hits get stored.
Return Value:
True if a hit was found; false if not.
Prototype:
Box3 GetBoundingBox (Matrix3 *tm=NULL, bool selectedOnly=false);
Remarks:
Requires an accurate "parent" pointer (see SetParent). Computes the bounding box of the normals.
Parameters:
Matrix3 tm=NULL
An optional transform for computing the bounding box in a different
space (such as world space).
bool selectedOnly=false
Indicates whether all normals should be included in the bounding box,
or only selected ones.
Prototype:
void ClearNormals ();
Remarks:
This method dumps all unspecified normals. Best to use only from within BuildNormals,
since it leaves all unspecified normals in faces initialized to -1.
Prototype:
void CollapseDeadFaces ();
Remarks:
Requires an accurate "parent" pointer (see SetParent).
This method is used in conjunction with the parent MNMesh's
CollapseDeadFaces method to keep the normal faces in synch with
the parent MNMesh faces. It removes any normal face whose
equivalent face in the parent mesh is considered "Dead".
Called by MNMesh::CollapseDeadFaces, so you generally don't
need or want to call it separately.
Prototype:
void BuildNormals ();
Remarks:
Requires an accurate "parent" pointer (see SetParent).
Fills in the mpSpecNormal data by building all the unspecified normals,
and computing non-explicit ones. Does nothing if face array is not
allocated yet!
Prototype:
void ComputeNormals ();
Remarks:
Requires an accurate "parent" pointer (see SetParent).
This method just recomputes the directions of non-explicit normals,
without rebuilding the normal list.
Prototype:
void CheckNormals ();
Remarks:
Requires an accurate "parent" pointer (see SetParent).
This checks our flags and calls BuildNormals or ComputeNormals as needed.
Prototype:
MNNormalSpec & operator= (const MNNormalSpec & from);
Remarks:
Typical = operator. Allocates arrays in this, and makes copies of all
the data in "from". Does NOT copy "Parent" pointer.
Prototype:
void CopySpecified (const MNNormalSpec & from);
Remarks:
This is similar to operator=, but copies only the specified and explicit
information from "from". Result will need to have BuildNormals and ComputeNormals
called.
Prototype:
MNNormalSpec & operator+= (const MNNormalSpec & from);
Remarks:
Adds the faces and normals from "from" to our normal spec, renumbering the
normals so they don't conflict with existing ones. Called by the
"AppendAllChannels" method below (which itself is called by MNMesh::operator+=).
Prototype:
void MNDebugPrint (bool printAll=false);
Remarks:
Uses "DebugPrint" to output information about this MNNormalSpece to the
Debug buffer in DevStudio.
Parameters:
bool printAll=false
If false, only explicit normals and faces using specified normals will be
printed out. If true, all normals and faces will be completely printed out.
Here is what the output looks like on a box with mostly default (non-specified)
normals, but with one corner "Unified" into a single specified normal:
If printAll = true, you'll see:
MNNormalSpec Debug Output: 22 normals, 6 faces
Normal (Non ) 0: 0.577350, -0.577350, 0.577350
Normal (Non ) 1: 0.000000, 0.000000, -1.000000
Normal (Non ) 2: 0.000000, 0.000000, -1.000000
Normal (Non ) 3: 0.000000, 0.000000, -1.000000
Normal (Non ) 4: 0.000000, 0.000000, -1.000000
Normal (Non ) 5: 0.000000, 0.000000, 1.000000
Normal (Non ) 6: 0.000000, 0.000000, 1.000000
Normal (Non ) 7: 0.000000, 0.000000, 1.000000
Normal (Non ) 8: 0.000000, -1.000000, 0.000000
Normal (Non ) 9: 0.000000, -1.000000, 0.000000
Normal (Non ) 10: 0.000000, -1.000000, 0.000000
Normal (Non ) 11: 1.000000, 0.000000, 0.000000
Normal (Non ) 12: 1.000000, 0.000000, 0.000000
Normal (Non ) 13: 1.000000, 0.000000, 0.000000
Normal (Non ) 14: 0.000000, 1.000000, 0.000000
Normal (Non ) 15: 0.000000, 1.000000, 0.000000
Normal (Non ) 16: 0.000000, 1.000000, 0.000000
Normal (Non ) 17: 0.000000, 1.000000, 0.000000
Normal (Non ) 18: -1.000000, 0.000000, 0.000000
Normal (Non ) 19: -1.000000, 0.000000, 0.000000
Normal (Non ) 20: -1.000000, 0.000000, 0.000000
Normal (Non ) 21: -1.000000, 0.000000, 0.000000
Normal Selection:
0 6 13 17
Normal faces: _ before normal index means non-specified.
Face 0: _1 _2 _3 _4
Face 1: _5 0 _6 _7
Face 2: _8 _9 0 _10
Face 3: _11 _12 _13 0
Face 4: _14 _15 _16 _17
Face 5: _18 _19 _20 _21
If printAll is false, you'll just see:
MNNormalSpec Debug Output: 22 normals, 6 faces
Normal Selection:
0 6 13 17
Face 1: _ 0 _ _
Face 2: _ _ 0 _
Face 3: _ _ _ 0
Prototype:
bool CheckAllData (int numParentFaces);
Remarks:
Performs a series of internal checks to verify that the normal
data is consistent. If there are any problems, messages are
sent out via DebugPrint.
Parameters:
int numParentFaces
The number of faces in the parent MNMesh. (This method does not
require an accurate "parent" pointer, but it does require this
number to be accurate.
Return Value:
True if everything checks out ok, false if not.
Prototype:
IOResult Save (ISave *isave);
Remarks:
Called by the system. Saves the MNNormalSpec to the buffer.
Prototype:
IOResult Load (ILoad *iload);
Remarks:
Called by the system. Loads the MNNormalSpec from the buffer.
Prototype:
bool Transform (Matrix3 & xfm, BOOL useSel=false, BitArray *normalSelection=NULL);
Remarks:
Transforms the normals. Note that since normals are always considered to be unit
length vectors, scales and translations are not effective. Translations drop out
because we use the VectorTransform method to transform the normals, and scales drop
out because we renormalize the normals to a length of 1 afterwards.
Parameters:
Matrix3 & xfm
The desired transform.
BOOL useSel=false
Indicates whether all normals should be translated, or only selected ones.
BitArray *normalSelection=NULL
The desired transform.
BOOL useSel=false
If non-NULL, this represents a selection of normals that should be used instead of
the usual selection, when deciding which normals to transform.
Return Value:
True if something was modified. False would indicate that no normals were changed,
perhaps because there are no normals in the spec or because none were selected.
Prototype:
bool Translate (Point3 & translate, BOOL useSel=true, BitArray *normalSelection=NULL);
Remarks:
This is used to give a translation-like effect to normals. It's used in the Edit
Normals "Move" mode. Essentially it drags the "top" of the normals by the amount
given, and then renormalizes the vectors to unit length. It uses the current
display length as well, so the formula is basically
mpNormal[i] = Normalize (mpNormal[i]*mDisplayLength + translate);
This gives a fairly natural result in Edit Normals Move.
Parameters:
Point3 & translate
The desired translation.
BOOL useSel=false
Indicates whether all normals should be translated, or only selected ones.
BitArray *normalSelection=NULL
If non-NULL, this represents a selection of normals that should be used instead of
the usual selection, when deciding which normals to translate.
Return Value:
True if something was modified. False would indicate that no normals were changed,
perhaps because there are no normals in the spec or because none were selected.
Prototype:
bool BreakNormals (BOOL useSel=true, BitArray *normalSelection=NULL);
Remarks:
"Breaks" normals so that none are shared between faces. Broken normals are set to Specified (but not explicit.)
Parameters:
BOOL useSel=false
Indicates whether all normals should be affected, or only selected ones.
BitArray *normalSelection=NULL
If non-NULL, this represents a selection of normals that should be used instead of
the usual selection, when deciding which normals to affect. (Irrelevant if
useSel=false.)
Return Value:
True if something was modified. False would indicate that no normals were changed,
perhaps because there are no normals present or because none were selected,
or because selected normals were already fully broken and specified.
If the return value is true, the MNNORMAL_NORMALS_BUILT and
MNNORMAL_NORMALS_COMPUTED flags are cleared, because the newly broken
normals need to be rebuilt and computed.
Prototype:
bool UnifyNormals (BOOL useSel=true, BitArray *normalSelection=NULL);
Remarks:
Requires an accurate "parent" pointer (see SetParent).
This method unifies selected normals so that there's a maximum of one
per vertex. For instance, a default box has 3 normals at every vertex.
You can select 2 or 3 of them and click "Unify" in Edit Normals, and
the normals will be shared across the faces that use them. See Edit
Normals documentation for more information.
This method does not unify normals that are based at different vertices.
If you want separate vertices to use the same normal, you must use more
direct, low-level methods like SetNormalIndex.
Unified normals are set to specified (but not explicit).
Parameters:
BOOL useSel=false
Indicates whether all normals should be affected, or only selected ones.
BitArray *normalSelection=NULL
If non-NULL, this represents a selection of normals that should be used instead of
the usual selection, when deciding which normals to affect. (Irrelevant if
useSel=false.)
Return Value:
True if something was modified. False would indicate that no normals were changed,
perhaps because there are no normals present or because none were selected,
or because selected normals were already fully unified and specified.
If the return value is true, the MNNORMAL_NORMALS_BUILT and
MNNORMAL_NORMALS_COMPUTED flags are cleared, because the newly unified
normals need to be rebuilt and computed.
Prototype:
bool SpecifyNormals (BOOL useSel=true, BitArray *normalSelection=NULL);
Remarks:
Specifies the indicated normals. DOESN'T remove the explicitness of the normals.
(That should be done separately with MakeNormalsExplicit, value=false.)
Parameters:
BOOL useSel=false
Indicates whether all normals should be affected, or only selected ones.
BitArray *normalSelection=NULL
If non-NULL, this represents a selection of normals that should be used instead of
the usual selection, when deciding which normals to affect. (Irrelevant if
useSel=false.)
Return Value:
True if something was modified. False would indicate that no normals were changed,
perhaps because there are no normals present or because none were selected, or because
they were all already specified.
Prototype:
bool MakeNormalsExplicit (BOOL useSel=true, BitArray *normalSelection=NULL, bool value=true);
Remarks:
Makes the indicated normals both specified and explicit.
Parameters:
BOOL useSel=false
Indicates whether all normals should be affected, or only selected ones.
BitArray *normalSelection=NULL
If non-NULL, this represents a selection of normals that should be used instead of
the usual selection, when deciding which normals to affect. (Irrelevant if
useSel=false.)
bool value=true
I Indicates whether the normals should be set to explicit, or non-explicit.
Return Value:
True if something was modified. False would indicate that no normals were changed,
perhaps because there are no normals present or because none were selected.
If value=false and the return value is true, the MNNORMAL_NORMALS_COMPUTED
flag is cleared, because the newly nonexplicit normals need to be computed
.
Prototype:
bool ResetNormals (BOOL useSel=true, BitArray *normalSelection=NULL);
Remarks:
Resets the indicated normals to be completely non-explicit and non-specified.
Parameters:
BOOL useSel=false
Indicates whether all normals should be affected, or only selected ones.
BitArray *normalSelection=NULL
If non-NULL, this represents a selection of normals that should be used instead of
the usual selection, when deciding which normals to affect. (Irrelevant if
useSel=false.)
Return Value:
True if something was modified. False would indicate that no normals were changed,
perhaps because there are no normals present or because none were selected.
If the return value is true, the MNNORMAL_NORMALS_BUILT and
MNNORMAL_NORMALS_COMPUTED flags are cleared, because the newly nonspecified
normals need to be rebuilt and computed.