Class MNNormalSpec

3DS Max Plug-In SDK

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.