Welcome to MSDN Blogs Sign in | Join | Help

Temple scene with Maya Fluids, Particle Dynamics and Realflow Meshes

Here is a brief demonstration of Maya fluids, particle dynamics and Nurbs combined together to create an early morning Temple scene effect. The light source is being animated with soft body particle dynamics, while the smoke emitting from sticks is typical fluid. The background haze & ray effect is created with a spot light shadowing a mask onto a 3d fluid container.

The flowing water effect is created with Realflow meshes.

For more, visit: http://gpalem.web.officelive.com/vfx/

Animating a Tree Growth

Here is a brief demonstration of tree growth animation in Vue. Animating the Diameter, Gnarl, Falloff, length and other related parameters gives the required result. Also note the growing grass at the base of the tree and the animated clouds.

For more, visit: http://gpalem.web.officelive.com/vfx/

Fixing JDKMidi to auto sort MIDI Events

After reviewing many existing C/C++ MIDI libraries, it appears JDKMidi seems to be the one having the features required by many midddlware libraries.

However, JDKMIDI library (as of Revision 560) suffers a huge drawback of not being able to auto sort the MIDI events. You have to supply the events in correct time order. Fortunately, this is very easy to correct and here is how it can be done. (Get the complete code with modifications from: http://musicnote.svn.sourceforge.net/viewvc/musicnote/MusicNote/libjdkmidi/)

MIDI Event insertions in JDKMidi library are taken care by the MIDITrack class. It has MIDITrack::PutEvent method that inserts MIDITimedBigMessage objects into its internal buffered chunks. With below modification to that method, now JDKMidi will automatically sort the events based on their time while inserting.

  bool MIDITrack::PutEvent ( const MIDITimedBigMessage &msg )
{
if ( num_events >= buf_size )
{
if ( !Expand() )
return false;
}

//GetEventAddress ( num_events++ )->Copy ( msg );

//Fix by Gopalakrishna Palem: Automatically sorts the events while inserting

MIDITimedBigMessage prevChunkLastMsg(msg);
int nStartEvId = 0; // the EventId where the shifting should start from

for( ; nStartEvId < num_events; ++nStartEvId)
{
if(MIDITimedBigMessage::CompareEvents(*GetEventAddress(nStartEvId), msg ) ==1 ) // if found an event larger than the new entry
{
if(nStartEvId == MIDITrackChunkSize - 1) // if last entry in the chunk buf
{
MIDITimedBigMessage* pLastEntry = GetEventAddress(nStartEvId);
prevChunkLastMsg.Copy(*pLastEntry); // Save the last entry
pLastEntry->Copy(msg); // copy the new message to the last entry
nStartEvId ++;
}
break;
}
}

int nChunkId = nStartEvId / MIDITrackChunkSize;
int nChunkMax = num_events / MIDITrackChunkSize;

while(nChunkId <= nChunkMax)
{
int nLastEvId = (nChunkId+1)*MIDITrackChunkSize -1;
MIDITimedBigMessage tempMsg(*GetEventAddress(nLastEvId)); // Save the last entry of the current Chunk

MIDITimedBigMessage* pSrcMsg = GetEventAddress(nStartEvId);
MIDITimedBigMessage* pDstMsg = pSrcMsg + 1;
memmove(pDstMsg, pSrcMsg, sizeof(MIDITimedBigMessage) * (nLastEvId - nStartEvId)); // Shift the first n-1 entries right

pSrcMsg->Copy(prevChunkLastMsg); // copy the saved last entry of the previous chunk into the first position of current chunk

prevChunkLastMsg.Copy(tempMsg); // pass on the saved last entry to next chunk beginning

nStartEvId = ++nChunkId * MIDITrackChunkSize;
}

num_events++;

return true;
}

However, there is still a small problem with this approach. The MIDITimedBigMessage::CompareEvents method does not return correct values when comparing note On/Off events occurring at the same time. To fix it we need to apply the below modifications as well:

  int  MIDITimedBigMessage::CompareEvents (const MIDITimedBigMessage &m1, const MIDITimedBigMessage &m2  )
{
bool n1 = m1.IsNoOp();
bool n2 = m2.IsNoOp();

// NOP's always are larger.

if ( n1 && n2 ) return 0; // same, do not care.

if ( n2 ) return 2; // m2 is larger

if ( n1 ) return 1; // m1 is larger

if ( m1.GetTime() > m2.GetTime() ) return 1; // m1 is larger

if ( m2.GetTime() > m1.GetTime() ) return 2; // m2 is larger

// Fix by Gopalakrishna Palem: if times are the same, a note off should come first. Note on is larger

bool m1IsOff = (m1.GetStatus() == NOTE_OFF || (m1.GetStatus() == NOTE_ON && m1.byte2 == 0));
bool m2IsOff = (m2.GetStatus() == NOTE_OFF || (m2.GetStatus() == NOTE_ON && m2.byte2 == 0));

if (!m1IsOff && m2IsOff) return 1; // m1 is larger

if (!m2IsOff && m1IsOff) return 2; // m2 is larger

return 0; // both are equal.

}

With these modifications, your JDKMidi is ready to take MIDI events in any order. Download the modified version and have fun.

Maya 2010 to include Compositing and Camera Tracking Tools

Autodesk, as usual, announced its next version of Maya at Siggraph. The interesting part, however, is the range of tools planned to be shipped as part of it:

  • Maya Composite high dynamic range compositing system
  • Matchmoving camera tracking system
  • Additional MentalRay for batch rendering nodes etc...

Not to mention the entire Maya unlimited 2009 tool-set as part of its unified Nucleus dynamic simulation framework.

Certainly this is a great step towards unifying multiple tools of the pipeline into a single product. Lets wait and see how it fares in the market.

Get more details from Autodesk directly at: http://usa.autodesk.com/adsk/servlet/pc/index?siteID=123112&id=13577897

How to make QT SDK work with VisualStudio 2008 (Express/Complete)

You can use the following steps to make QT SDK work with your Visual Studio 2008 (either express version or commercial version).
  • Download the QT SDK from http://www.qtsoftware.com/downloads/sdk-windows-cpp
  • If you do not have VisualStudio 2008 get it from http://www.microsoft.com/express/download/
  • Once QT and VisualStudio 2008 both are installed and ready, open the VisualStudio 2008 Command Prompt (from Microsoft Visual Studio 2008 | Visual Studio Tools menu of Program Files)
  • Change directory to the QT SDK installation folder in the Command Prompt. Usually it would be of the form "C:\Qt\2009.01\qt"
  • Execute the configure.exe by typing configure in the command prompt. This should start configuring the QT for VisualStudio 2008 and Windows. Let it complete.
  • Once the configuration is complete, build QT for use with Visualstudio 2008 by issuing the command nmake
  • Configuration and the following nmake will take some time. Once they are complete, your QT SDK will be ready for use with VisualStudio.
  • To let VisualStudio recognize the include and library files of QT, add their paths to the VisulStudio Tools|Options|Projects and Solutions|VC++ Directories section. Typically you would want to add C:\Qt\2009.01\qt\include to the Include, C:\Qt\2009.01\qt\lib to the libraries and C:\Qt\2009.01\qt\bin to the Executables sections.
  • Also you can add C:\Qt\2009.01\qt\bin to the Path System variable so that the QT dlls get probed correctly when loaded from your application. (You can use MyComputer|Properties|Advanced|Environment Variables)

Now your QT SDK is ready to be used with Visual Studio 2008. Go to Examples directory of your QT SDK installation (typically, C:\Qt\2009.01\qt\examples) and open the examples.sln file from your Visual Studio 2008. (If double-clicking the .sln file does not open automatically with Visual Studio, open it manually from within Visual Studio.

Build and run the tutorial1 project. If your Include, Library and Path System variable settings are correct, you should have the project output build and run successfully.

You can associate the .ui files to the QTDesigner by right clicking any .ui file in the solution explorer and selecting "Open With" option. In the Open With options dialog, click 'Add' button and select the designer.exe from the QTSDK bin directory (typically, it would be C:\Qt\2009.01\qt\bin\designer.exe). Click 'Set as Default' to let the QTDesigner handle the .ui files.

Posted by P.Gopalakrishna | 2 Comments
Filed under: ,

How to make my water surface animate in Vue?

When a Water object is added to the Vue scene, it will be in a staic form without any animation applied to its surface. If you would like to have the water surface animated, then we have to enable its Displaced Water Surface property. This property allows the waves at the surface to be converted to true geometry that can be animated.

To enable this property, Right-click the water object and choose Edit Object option. Under the Geometry options, check the Displaced Water Surface option. You can optionally edit the altitude function of the waves by clicking the Edit Function button. The Wind direction setting controls the direction of the wind, as seen from above (the azimuth). Setting it to zero will make the wind blow from left to right in the Top view and hence the waves of the water surface will also be moving from left to right. Setting it to 90 will make the wind blow from top to bottom in the Top view, making the waves move from top to bottom accordingly. Set the value to your liking and close the dialog box. Now your water object is animation ready.

But, you should be having more than one frame in your scene timeline to actually see the animation and have it rendered out. An easy way of doing this is by clicking on the Timeline icon on the toolbar and selecting the Set Animation End from the Timeline popup menu. (You can close the Animation Wizard if it appears.) The Set Animation End option allows you to drag the animation endtime to set a duration for the animation. Once you have a reasonable length set, click on the Generate Preview icon to the right of the Timeline to generate a preview. The Render Animation icon next to it allows the actual animation to be rendered out with the applied settings.

Tweak these settings and have fun with your water surface.

Gopalakrishna Palem
http://www.geocities.com/krishnapg/

Posted by P.Gopalakrishna | 2 Comments
Filed under: ,

Listing the codecs intalled on the machine

Often an application requires to list all the Audio/Video compressors installed on the machine. We can achieve this easily with the ICreateDevEnum interface available from DirectShow. ICreateDevEnum exposes a method named CreateClassEnumerator that allows us to create enumerators for different categories of devices, such as audio/video codecs, Directx Media Objects (DMO) etc.

The typical usage is as shown below:

     CComPtr<ICreateDevEnum> pSysDevEnum;
    if(FAILED(hr = pSysDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum)))
    {
        return hr;
    }
    
    IEnumMoniker *pEnum = NULL;
    hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnum, 0);
    if (hr == S_OK) 
    {
        // Use the pEnum to enumerate the devices
        ...    
        // Do not forget to release it when done.
        pEnum->Release();
    }    

The below code enumerates the Audio compressors available on the machine and adds their friendly names to an application defined combo box.

HRESULT CAudioConvertDlg::AddCompressorsToComboList(CString *pStrErrMsg)
{
    HRESULT hr = E_FAIL;

    this->m_ctrlCompressorsCombo.ResetContent();

    CComPtr<ICreateDevEnum> pCreateDevEnum;
    if(FAILED(hr = pCreateDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum)))
    {
        *pStrErrMsg += "Unable to Create System Device Enumerator";	return hr;
    }
    
    CComPtr<IEnumMoniker> pEnumMoniker;
    if(FAILED(hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioCompressorCategory,&pEnumMoniker,0)))
    {
        *pStrErrMsg += "Unable to Create AudioCompressor Enumerator";	return hr;
    }
    
    CComPtr<IMoniker> pMoniker;
    while(pEnumMoniker->Next(1,&pMoniker,NULL) == S_OK)
    {
        CComPtr<IPropertyBag> pBag;	// Use the Moniker's PropertyBag to get the Object's name
        if(SUCCEEDED(pMoniker->BindToStorage(0,0,IID_IPropertyBag,(void**)&pBag)))
        {
            VARIANT var;
            var.vt = VT_BSTR;
            if(SUCCEEDED(pBag->Read(L"FriendlyName",&var,NULL)))
            {
                TCHAR szName[512];
                GetTcharFromWchar(var.bstrVal,szName,sizeof(szName));
                SysFreeString(var.bstrVal);
                this->m_ctrlCompressorsCombo.AddString(szName);
            }
        }
        pMoniker.Release();
    }

    this->m_ctrlCompressorsCombo.SetCurSel(0);

    return hr = S_OK;   // Everything went fine !!
}    

Displaying the currently selected components with MSelectionList

The MGlobal::getActiveSelectionList() offers a convenient way of accessing the currently selected objects in the view. However, if you wish to find the selected components then you need to use the MSelectionList's getDagPath() method. It returns the dagPath of the seleted object and also the selected components of it. Once we have the dagPath of the selected objects and its selected components, we can use the corresponding MIt classes to iterate over the individual elements of the component. For example, the below code lists the CVs of the currently selected NURBS surface for the selected components on it.

    MSelectionList selList;

    MGlobal::getActiveSelectionList(selList);

    cout << "Current Selection List has " << selList.length() << " objects" << endl;      

    MDagPath dagPath;
    MObject  selComponent;
    MFnDagNode nodeFn;

    for(unsigned int i=0; i < selList.length(); ++i)
    {
        // Get the selected Component
        selList.getDagPath(i, dagPath, selComponent);
        
        nodeFn.setObject(dagPath);
        
        // Display the name of the Object selected
        cout << nodeFn.name().asChar();
        
        // Component could be NULL if no individual components are selected
        if(selComponent.isNull()) cout << "Component is NULL !!" << endl;

        // Use MItCurveCV or MItMeshVertex or MItMeshPolygon etc... based on the type of Object.
        // You can use nodeFn::hasFn() to check if the selected object is of some type
        MStatus  stat;
        MItSurfaceCV itCV(dagPath, selComponent, true, &stat);

        if(stat == MStatus::kSuccess)
        {
            while(!itCV.isDone())
            {
                MPoint pt = itCV.position();
                cout << "{" << pt.x << "," << pt.y <<"," << pt.z << "}" << endl;
                itCV.next();
            }
        }            
    }

How to find the Shading Groups for a Shape node in Maya API?

Given the DagPath for any Mesh object, listing the Shading Groups connected to it is easy. The MFnMesh::getConnectedSetsAndMembers() will do the trick as shown below:

MStatus ListShadingGroups(const MDagPath& inputShapeDagPath)
{
if(inputShapeDagPath.hasFn(MFn::kMesh))
{
// Find the Shading Engines Connected to the SourceNode
MFnMesh fnMesh(inputShapeDagPath.node());

// A ShadingGroup will have a MFnSet
MObjectArray sets, comps;
fnMesh.getConnectedSetsAndMembers(inputShapeDagPath.instanceNumber(), sets, comps, true);

// Each set is a Shading Group. Loop through them
for(unsigned int i = 0; i < sets.length(); ++i)
{
MFnDependencyNode fnDepSGNode(sets[i]);

cout << fnDepSGNode.name() << endl;
}
}

return MStatus::kSuccess;
}

Subdivision surfaces can also be worked out similarily, using the MFnSubd::getConnectedSetsAndMembers().

However, for a NURBS surface, we have to do little bit more work with the use of Plugs. A Shading Group will be connected to the instObjGroups attribute of the shape. This instObjGroups is an array that is indexed by the instance number of the shape. Thus, the code to list the connected shading groups will look like below:

MStatus ListShadingGroups(const MDagPath& inputShapeDagPath)
{
if(inputShapeDagPath.hasFn(MFn::kNurbsSurface))
{
MFnNurbsSurface fnNurbs(inputShapeDagPath.node());

MObject instObjGroupsAttr = fnNurbs.attribute("instObjGroups");

MPlug instPlug(inputShapeDagPath.node(), instObjGroupsAttr);

// Get the instance that our node is referring to;
// In other words get the Plug for instObjGroups[intanceNumber];

MPlug instPlugElem = instPlug.elementByLogicalIndex(inputShapeDagPath.instanceNumber());

// Find the ShadingGroup plugs that we are connected to as Source
MPlugArray SGPlugArray;
instPlugElem.connectedTo(SGPlugArray, false, true);

// Loop through each ShadingGroup Plug
for(unsigned int i=0; i < SGPlugArray.length(); ++i)
{
MFnDependencyNode fnDepSGNode(SGPlugArray[i].node());

cout << fnDepSGNode.name() << endl;
}
}

return MStatus::kSuccess;
}

Tracking C++ variable state changes

Tracking class variables' state changes can be tricky, especially if we are using too many asynchronous constructs. This is especially true for game and graphic application scenarios where hundreds and perhaps even thousands of object fly around changing their values in who knows which thread. (Who is forcing that character position into that map corner - physics thread or AI thread?) Couple of years back I wrote few tiny classes to address this problem as part of my Object Intropsection Library (OIL). Here are few details of it. Go through them and feel free to use them as you see fit for your application.

While a class can be a complex datastructure, its internal members in the end have to be simple data types such as int, char, float, double etc... (For our present purpose we ignore any classes that fall outside this category). The state of a class object thus encompasses the state of each of its individual simple members. Being able to track the invidiual member changes provides one way of tracking the state change for the entire object. On the otherhand, when a class is more mathemtical oriented (such as Vector3 or Quaternion etc...) with the functionality built more into its operators than into its methods, then tracking its "mutation" operators shall give us indication about the state change of the object.

We can combine these two and track a complex object by tracking its internal "suspect" variables, which by their nature (of being simple datatypes) can only change their value through operators. This might sound a little but clumsy, but if we take a careful look at the complete list of C++ operators, we have only a handful of them that are "value changing" kind. Below are they:

     ///<Summary>
/// Enumeration Constants for the Function Context of Value-Changes;
/// </Summary>
/// <Remarks>
/// Note that we override only the functions (and operators) that trigger a value
/// change for our CProperty object. Thus, operators such as &, &&, || etc. are not
/// overloaded as they do not change our object's content.
/// </Remarks>
enum enumFunContext { FUN_UNKNOWN = -1, FUN_CONSTRUCTOR, ///< Constructor FUN_COPYCONSTRUCTOR, ///< Copy Constructor FUN_DESTRUCTOR, ///< Destructor OP_ASSIGN, ///< Operator = OP_PLUSASSIGN, ///< Operator += OP_MINUSASSIGN, ///< Operator -= OP_STARASSIGN, ///< Operator *= OP_SLASHASSIGN, ///< Operator /= OP_MODASSIGN, ///< Operator %= OP_ANDASSIGN, ///< Operator &= OP_ORASSIGN, ///< Operator |= OP_XORASSIGN, ///< Operator ^= OP_LSHIFTASSIGN, ///< Operator <<= OP_RSHIFTASSIGN, ///< Operator >>= OP_PREINCREMENT, ///< Operator ++ OP_PREDECREMENT, ///< Operator -- OP_POSTINCREMENT, ///< Operator ++(int) OP_POSTDECREMENT, ///< Operator --(int) };

Thus we can define a simple templated class as shown below that can override these operators for us.

     template<typename DATATYPE>
class CProperty : public IsClassType<DATATYPE>::ResultantType, public CEventSource
{
public:
typedef typename IsClassType<DATATYPE>::ResultantType ClassType;
typedef typename IsPredefinedType<DATATYPE>::ResultantType PredefinedType;

enum { IsClass = Loki::TypeTraits<DATATYPE>::isClass };
typedef Loki::Int2Type<IsClass> TypeSelector;

typedef CValueChangingEventArgs<DATATYPE> ChangingEvArgs;
typedef CValueChangedEventArgs<DATATYPE> ChangedEvArgs;

protected:
PredefinedType m_Data; // Create the Object for Predefined Types public: CEvent ValueChanging; ///< Event Raised before the value changes CEvent ValueChanged; ///< Event Raised after the value changed ... };

The above shown CProperty templated class lets us define member variables as Properties in C++ classes, in that it allow us to have "value changing" and "value changed" events notified for those Property variables. The ValueChanging event is raised when the value is about to change, while the ValueChanged is raised when the change is over. These keep track of the data value changes by handling the afore discussed operator functions.

The problem, however, is that we should be able to track the changes for both pre-defined types (such as int, float etc...) and for the user-defined class types. For native types the C++ compiler automatically allows all the above defined operators and hence we don't have a problem with them. But it is the user-defined class types that we have a problem with - because not all user-defined class types will have all these operators defined on them !! For example, I have never seen a string class having the operator*= defined for it !! (and pray god we don't see one in future either)

To handle these undefined cases, CProperty class defines the operators as templated functions, which on many a good C++ compilers will be ignored and will not be processed further, unless the objects actually use them (a good indication that the given class has indeed defined them).

For an user-defined class type, we keep track of the changes for the class objects by making the CProperty a derived class for the given class. Thus we get a chance to handle the operators before the base class takes a look at it. However, if the given DataType is not a class then we could not use this strategy of deriving from it. (What does it mean to derive from a viod* ??)

Thus, in case of given DataType being a class type, we derive from it. In all other cases, we actually create an object of that type and use CProperty as a wrapper over it. Making CProperty a derived class is necessary because objects of the given DataType should be able to call any extra functions that might be present in that type in their class (but not defined here in CProperty class). Since non-class types (such as pre-defined types and pointer types) do not have that necessity, it is enough to wrap over those objects.

We use the IsClassType<DATATYPE> mechanism to determine if the give DATATYPE is a class type or not. It is a template meta-programming construct defined as below:

     /// <Summary>
/// We should be able to invoke Copy Constructor on the Empty type.
/// So we define it here. Loki::EmptyType does not have Copy Constructor;
/// </Summary>
class EmptyType { public: template<typename T> EmptyType(const T& ) { } EmptyType() { } }; /// <Summary>
/// IsClassType -- Tests if the given DATATYPE is a Class or not.
/// If the DATATYPE is a Class then ResultantType is set to DATATYPE;
/// Else ResultantType is set to EmptyType;
/// </Summary>
template<typename DATATYPE> struct IsClassType { typedef typename Loki::Select < Loki::TypeTraits<DATATYPE>::isClass == true, DATATYPE, EmptyType >::Result ResultantType; };

The next point to consider is that among the operators we have considered, there are unary operators (such as ++, -- etc...) and then there are binary operators (such as +=, = etc...). We consider each individually. In fact, we can have a single generic implementation for all binary operators with a simple macro as below:

#define OVERLOAD_BINARY_OPERATOR(operator_func, DATA_OP, OP_FUNC_CTX)	\
protected:\
template<typename T> \
inline void Func_##OP_FUNC_CTX(const T& other, Loki::Int2Type<true>&) \
{\
ChangingEvArgs evArgs((DATATYPE)*this, OP_FUNC_CTX); \
RaiseEvent(&ValueChanging, &evArgs);\
if(evArgs.GetCancel() == false)\
{\
ClassType::operator_func(other);\
ChangedEvArgs evArgs((DATATYPE)*this, OP_FUNC_CTX); \
RaiseEvent(&ValueChanged, &evArgs);\
}\
}\
template<typename T>\
inline void Func_##OP_FUNC_CTX(const T& other, Loki::Int2Type<false>∧&)\
{\
ChangingEvArgs evArgs(m_Data, OP_FUNC_CTX);\
RaiseEvent(&ValueChanging, &evArgs);\
if(evArgs.GetCancel() == false)\
{\
DATATYPE OldValue = m_Data;\
m_Data DATA_OP (DATATYPE)(*const_cast<T*>(&other));\
ChangedEvArgs evArgs(OldValue, m_Data, OP_FUNC_CTX);\
RaiseEvent(&ValueChanged, &evArgs);\
}\
}\
template<typename T, typename ObjType>\
inline void Func_##OP_FUNC_CTX(const T& other, Loki::Type2Type<ObjType> &)\
{\
Func_##OP_FUNC_CTX(other, TypeSelector());\
}\
template<ypename T, typename ObjType>\
inline void Func_##OP_FUNC_CTX(const T& other, Loki::Type2Type<CProperty<ObjType> >&)\
{\
Func_##OP_FUNC_CTX((ObjType)other, TypeSelector());\
}\
public:\
template<typename T>\
inline CProperty& operator_func(const T& other)\
{\
Func_##OP_FUNC_CTX(other, Loki::Type2Type<T>());\
return *this;\
}

With this we can now define all the binary operators simply as:

     /////////////////////////////////////////
/// Assignment Operator
/////////////////////////////////////////
OVERLOAD_BINARY_OPERATOR(operator=, =, OP_ASSIGN); /////////////////////////////////////////
/// Plus-Assign += Operator
/////////////////////////////////////////
OVERLOAD_BINARY_OPERATOR(operator+=, +=, OP_PLUSASSIGN); /////////////////////////////////////////
/// Minus-Assign -= Operator
/////////////////////////////////////////
OVERLOAD_BINARY_OPERATOR(operator-=, -=, OP_MINUSASSIGN); ...

As for the unary operators, a typical implementation would look like below:

     protected:
/////////////////////////////////////////
/// PostIncrement Operator
/////////////////////////////////////////
template<bool bIsClass> inline void OpPostIncrement() { ChangingEvArgs evArgs((DATATYPE)*this, OP_POSTINCREMENT); RaiseEvent(&ValueChanging, &evArgs); if(evArgs.GetCancel() == false) { ClassType::operator++(0); ChangedEvArgs evArgs((DATATYPE)*this, OP_POSTINCREMENT); RaiseEvent(&ValueChanged, &evArgs); } } template<> inline void OpPostIncrement<false>() { ChangingEvArgs evArgs(m_Data, OP_POSTINCREMENT); RaiseEvent(&ValueChanging, &evArgs); if(evArgs.GetCancel() == false) { DATATYPE OldValue = m_Data; m_Data++; ChangedEvArgs evArgs(OldValue, m_Data, OP_POSTINCREMENT); RaiseEvent(&ValueChanged, &evArgs); } } public: inline CProperty& operator ++() { OpPreIncrement<IsClass>(); return *this; }

Note that we define each operator in two flavors, one for the pre-defined types and the other for the user-defined type. The selection is done automatically by the compiler based on the result of IsClass enum value defined in the CProperty class. For example, in the above code, when operator++ is invoked, it will be routed to the OpPostIncrement<true> or OpPostIncrement<false> based on OpPostIncrement<IsClass>. The difference is, for the class types we call the base class's ClassType::operator++, where as for native types we just do m_Data++ on the wrapped object.

Equipped with this mechanism, we can now track the changes to a variable by declaring it to be of CProperty type and subscribing to it's valuechanged (and/or valuechanging) events as shown below:

 	// Define the variable
	CProperty<int> Obj1 = 20;
	
	// Add a listener for it
	Obj1.ValueChanged.Subscribe(&HandleValueChanged_Func);
	
	// Define the Listener
	void HandleValueChanged_Func(const CEventSource* EvSrc,CEventHandlerArgs* EvArgs)
	{		
	  CValueChangedEventArgs<int>* pEvArgs = (CValueChangedEventArgs<int>*) EvArgs;
	  TCHAR sz[256];
	  _stprintf(sz, _T("Value Changed from %d to %d"), pEvArgs->OldValue(), pEvArgs->NewValue());
	  MessageBox(NULL, sz, _T("Change Event"), MB_OK);
	}		

Once the event is raised, there is no restriction on what you can do inside the event handler. A log entry can generated, or debug break can be called or notification can be displayed or ... what not.

Tracking the member variables of class is similar. Just declare the member variable to be of CProperty type:

 //////////////////////////////
/// A class that uses CProperty to track its member variables
class CTestClass { public: CProperty<int> Value; // A member variable that is of type CProperty ... }; int _tmain(int argc, _TCHAR* argv[]) { // Declare the object CTestClass var; // Subscribe to the notifications for interested variables var.Value.ValueChanging.Subscribe(&HandleValueChanged_Func); var.Value = 10; // Raises the notification }

Tracking the changes for the instances of class is also similar. Just declare the class object to be of CProperty type:

 //////////////////////////////
/// A class that we use CProperty against to track its instance variables
class CTestInt { int m_nVal; public: CTestInt() { } CTestInt(int nVal) : m_nVal(nVal) {} template<typename T> CTestInt& operator=(const T& nVal) { m_nVal = (int)nVal; return *this; } CTestInt& operator++() { ++m_nVal; return *this; } CTestInt& operator--() { --m_nVal; return *this; } operator int() { return m_nVal; } operator float(){ return (float)m_nVal; } operator char() { return (char)m_nVal; } ... }; int _tmain(int argc, _TCHAR* argv[]) { // Declare an instace of the class CProperty<CTestInt> Obj1 = 20; // Add listener for Obj1 Obj1.ValueChanged.Subscribe(&HandleValueChanged_Func); Obj1 = 10; // Raises the notification }

More elaborate usage examples can be found in the attached project.

If you are using your class in too many places then converting the instances to CProperty in all places can be difficult, especially when you have method signatures using that class type. In such cases you can do a little trickery. Just wrap your class in a temporary namespace and declare a custom typedef in the usual scope. For example, to track the instace variables of the CTestInt class shown above, we can wrap the CTestInt in a namespace and define a custom typedef as shown below:

/// Wrap the class into a custom namespace
namespace _MyTracking
{
    class CTestInt
    {
        int m_nVal;
    public:
        CTestInt() { }
        ...
    };	
}
/// Declare the typedef to make the CTestInt instances to be a type of CProperty
typedef CProperty<_MyTracking::CTestInt> CTestInt;

Now any code that previously was CTestInt now automatically starts using the CProperty type !! This is quite useful when you are passing your CTestInt objects across many methods and you don't want to change their function signatures. Ofcourse, when you are done with the tracking, you can remove the namespace and the typedef and your code is back to normal!!

As usual, this is yet a simple beginning and you can do wonders by extending the design. For example, when a "value changing" notification is raised, we can always have the option of "cancelling" the change !!

Go ahead, explore the options and extend the design. Have fun.

Having trouble with PoserFusion Shelf in Maya?

If you are encountering errors with Poser Fusion shelf buttons in Maya such as "Error: No Selection, or the Currently Selected object is not linked to a Poser file", then try the below steps and see if its helps.

First of all ensure that your POSER_LOCATION environment variable is set correctly. It should read something like "C:\Program Files\Smith Micro\Poser Pro".

Next, ensure that the PoserFusion plug-in is loaded in Maya. You can do this by going to Window > Settings|Preferences > Plugin Manager and then checking the "MpzFile.mll" to be loaded state.

Once these two steps are done, you should be able import any Poser file into Maya. You should select the "Poser(*.*)" option in the file dialog while importing the Poser file.

Once the file import succeeds, now all your PoserFusion shelf buttons start working correctly. Details on how to use these shelf buttons can be found in the PoserFusion for Maya section starting on page 38 of the Poser Pro Reference Manual.pdf available in the Poser Pro application folder.

Posted by P.Gopalakrishna | 15 Comments
Filed under:

How to create and manipulate Terabyte size Arrays with Win32API

If you are looking for a way of creating and accessing very large arrays, arrays that can handle content in the order of Tera Bytes, then probably you might find the File Mapping techniques useful for that purpose.

File mapping is the association of a file's contents with a portion of the virtual address space of a process. It allows the process to work efficiently with a large data files without having to map the whole file into memory. Processes read from and write to the file view using pointers, just as they would with dynamically allocated memory. This improves efficiency because the file resides on disk, but the file view resides in memory, the page-in and page-out happening seamlessly behind the scenes.

In a typical Win32 environment, applications have 4 gigabyte (GB) of virtual address space available. The virtual address space is divided so that 2 GB is available to the application and the other 2 GB is available only to the system. The size of a file mapping object that is backed by a named file is limited by disk space. The size of a file view is limited to the largest available contiguous block of unreserved virtual memory. This is at most 2 GB minus the virtual memory already reserved by the process.

To create a huge array, create multiple temporary files, each of _MaxFileSize, _MaxFileSize being any convenient maximum file size limit such as 2GB, and access their content by mapping and unmapping portions of them to the main memory as required. Creating the temporary files and mapping/unmapping them can be taken care by the CreateFile, CreateFileMapping, MapViewOfFile, and UnmapViewOfFile API. A simple implementation of this will look like below:

#define _TWOGB	(0x80000000)
#define _64KB (0x00010000)
#define _64MB (0x04000000)

class HugeArray
{
public:
enum : unsigned long
{ _ElemSize = sizeof(BYTE),
_ViewSize = _64KB,
_MaxFileSize = _TWOGB,
_MaxNoOfCachedViews = 16,
_MaxNoOfElemsPerFile= (_MaxFileSize/_ElemSize),
_MaxNoOfViewsPerFile= (_MaxFileSize/_ViewSize) //=0x00008000
};
protected:
HANDLE* hFileMappings; // The File mappings ATL::CAtlFile* Files; // The Files that were mapped unsigned int nFileCount; // Number of Files BIGNUMBER _RequestedLen; // No.of Elements requested BIGNUMBER _AllocatedLen; // No.of Elements actually allocated ATL::CAtlMap<unsigned int, LPVOID> pCachedViews; //[FileIndex, FileView] map for few cached views std::vector <CBitVector&> bvFileViewCleared; // Keeps track of views that need be cleared upon mapping public: HugeArray() // Directory where to create the memory mapped files : _RequestedLen(0), _AllocatedLen(0), hFileMappings(0), Files(0), nFileCount(0) { } // _nCount: Number of Elements to allocate space for
// lpszDirPath: Directory where to create the memory mapped files
// nInitVal: Value that should be used to initialize the allocated space
HugeArray(const BIGNUMBER& _nCount, LPCTSTR lpszDirPath = _T("."), int nInitVal=0) : _RequestedLen(0), _AllocatedLen(0), hFileMappings(0), Files(0), nFileCount(0) { Allocate(_nCount, lpszDirPath, nInitVal); } ~HugeArray() { DeAllocate(); } // Allocates space for _nCount number of elements (each of _ElemSize)
//returns the error code in case of failures
DWORD Allocate(const BIGNUMBER& _nCount, LPCTSTR lpszDirPath = _T("."), int nInitVal=0) { // Make sure things are clean DeAllocate(); // Prepare the file name prefixes ATL::CString strDir(lpszDirPath), strPrefix; TCHAR TempFileName[MAX_PATH+32]; // Make sure the directory path is terminate with "\" if(strDir.Right(1) != _T("\\")) strDir += _T("\\"); // Compute the required number of files unsigned long lNumFiles = (_nCount/(unsigned long)_MaxNoOfElemsPerFile).GetNumberIntoLong() + 1; // Allocate space for the handles if(NULL == (Files = new ATL::CAtlFile[lNumFiles]) || NULL == (hFileMappings = new HANDLE[lNumFiles])) return E_OUTOFMEMORY; for(unsigned long l=0; l < lNumFiles; ++l) { // Get a unique name for the file strPrefix.Format(_T("%dHA"), l); if(!GetTempFileName(strDir, strPrefix, 0, TempFileName)) return ErrorMessage(); // Create the file if(FAILED(Files[l].Create(TempFileName, GENERIC_READ|GENERIC_WRITE|STANDARD_RIGHTS_ALL, FILE_SHARE_READ, OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY| FILE_ATTRIBUTE_NOT_CONTENT_INDEXED| FILE_FLAG_DELETE_ON_CLOSE|FILE_FLAG_SEQUENTIAL_SCAN))) return ErrorMessage(); // Set the file size to the maximum
if(FAILED(Files[l].SetSize((0x00000000ffffffff & _MaxFileSize))))
return ErrorMessage();
// Create the File Mapping if(NULL == (hFileMappings[l] = ::CreateFileMapping(Files[l], 0, PAGE_READWRITE, 0, 0, 0))) return ErrorMessage(); // Create the Clearance vector bvFileViewCleared.push_back(CBitVector()); } _RequestedLen = _AllocatedLen = _nCount; nFileCount = lNumFiles; return S_OK; } void DeAllocate() { // Unmap the cached views POSITION pos = pCachedViews.GetStartPosition(); while(pos != NULL) { LPVOID pViewBase = pCachedViews.GetNext(pos)->m_value; if(pViewBase != NULL) ::UnmapViewOfFile(pViewBase); } pCachedViews.RemoveAll(); // Close the File Mapping Handles for(unsigned int i = 0; i < nFileCount; ++i) { if(hFileMappings[i] != NULL) ::CloseHandle(hFileMappings[i]); } // Free the File Mapping Handles memory if(hFileMappings != NULL) delete[] hFileMappings; // Free the Files Memory (which automatically closes the Files too) if(Files != NULL) delete[] Files; // Clear the bitvector bvFileViewCleared.clear(); // Reset Values to NULL hFileMappings = NULL; Files = NULL; nFileCount = 0; _AllocatedLen = 0; _RequestedLen = 0; } inline BYTE* _GetAt(unsigned long lFileIndex, unsigned long nViewIndex, unsigned long nViewOffset) { _ASSERTE(lFileIndex < nFileCount); unsigned long nFileViewIndex = ((lFileIndex & 0x0000ffff) << 16) | (nViewIndex & 0x0000ffff); LPVOID pViewBase = NULL; if(pCachedViews.Lookup(nFileViewIndex, pViewBase) == false) // Cache Miss { if(pCachedViews.GetCount() >= _MaxNoOfCachedViews) // if Cache is full, make room to load the new map { POSITION pos = pCachedViews.GetStartPosition(); pViewBase = pCachedViews.GetAt(pos)->m_value; if(pViewBase != NULL) ::UnmapViewOfFile(pViewBase); pCachedViews.RemoveAtPos(pos); } // Map the View if(NULL == (pViewBase = ::MapViewOfFile(hFileMappings[lFileIndex], FILE_MAP_WRITE, 0, nViewIndex*_ViewSize, _ViewSize))) { ATL::CString strFilePath; strFilePath.Format(_T("File Index: %d"), lFileIndex); ::MessageBox(NULL, _T("Unable to map view for: ") + strFilePath, _T("Error"), MB_OK|MB_ICONERROR); return (BYTE*)pViewBase; } // Clear the contents of the view if this is the first time it is mapped if(bvFileViewCleared[lFileIndex].IsBitSet(nViewIndex)==false) { ::memset(pViewBase, 0, _ViewSize); bvFileViewCleared[lFileIndex] += nViewIndex; // set the cleared flag } // Cache the mapped view pCachedViews[nFileViewIndex] = pViewBase; } return ((BYTE*)pViewBase + nViewOffset); } inline BYTE& operator[](const BIGNUMBER& nIndex) { unsigned long lFileIndex = (nIndex / (unsigned long)_MaxNoOfElemsPerFile).GetNumberIntoLong(); unsigned long lFileOffset = (nIndex % (unsigned long)_MaxNoOfElemsPerFile).GetNumberIntoLong(); // Compute the offset in file unsigned long nViewIndex = (lFileOffset / (unsigned long)_ViewSize); // Compute the view that should be containing the offset unsigned long nViewOffset = (lFileOffset % (unsigned long)_ViewSize); // Compute the offset in the view return *_GetAt(lFileIndex, nViewIndex, nViewOffset); } inline BYTE& operator[](const unsigned long ulIndex) { unsigned long lFileIndex = (ulIndex / (unsigned long)_MaxNoOfElemsPerFile); unsigned long lFileOffset = (ulIndex % (unsigned long)_MaxNoOfElemsPerFile); // Compute the offset in file unsigned long nViewIndex = (lFileOffset / (unsigned long)_ViewSize); // Compute the view that should be containing the offset unsigned long nViewOffset = (lFileOffset % (unsigned long)_ViewSize); // Compute the offset in the view return *_GetAt(lFileIndex, nViewIndex, nViewOffset); } // Attemps to allocate space for _nCount number of elements.
// If the currently allocate space is more than what is required, this would do nothing.
// Else Deallocates the existing space and allocates new space.
// In any case clears the contents.
bool ReDim(const BIGNUMBER& _nCount, LPCTSTR lpszDirPath = _T("."), int nInitVal=0) { if(_AllocatedLen < _nCount) { return (S_OK == Allocate(_nCount, lpszDirPath, nInitVal)); } else { _RequestedLen = _nCount; ClearContents(); } return false; } void ClearContents() { // Unmap the cached views POSITION pos = pCachedViews.GetStartPosition(); while(pos != NULL) { LPVOID pViewBase = pCachedViews.GetNext(pos)->m_value; if(pViewBase != NULL) ::UnmapViewOfFile(pViewBase); } pCachedViews.RemoveAll(); // Clear the Clearance vector and Create it again bvFileViewCleared.clear(); for(unsigned int i = 0; i < nFileCount; ++i) bvFileViewCleared.push_back(CBitVector()); } const BIGNUMBER& Length() const { return _RequestedLen; } };

The HugeArray class shown above facilitates large Byte arrays to be allocated and accessed by using the file mapping. It uses multiple files of _MaxFileSize to allocate the space and maps views on the files. Views are mapped and unmapped as required, with a limited number of views cached at a time. _MaxNoOfCachedViews governs this limit.

CachedViews are picked in FIFO fashion to unmap so as to make space for new views. (FIFO could be replaced with LRU or any other efficent techqniue, but that requires keeping track of the view usage and hence might incur extra processing - but then the best method has to be decided based on the target scenario.)

The _ViewSize governs the size of the view that is mapped. (_MaxFileSize/_ViewSize) gives the max no. of views per file. As per our other metrics above, since the MaxNoOfViewsPerFile require only 16bits, we can use the upper 16 bits (of a 32-bit integer) to keep track of which file this view belongs to. Thus, we are restricting the maximum number of files to be 2^16 (=65536). With 2GB per file, this restricts us to have at most 65536 * 2GB address space. Thus our HugeArray class cannot work for arrays that require more than 131072GB space.

If we require more space to be mapped, then we need to maintain the FileIndex and View offset seperately (instead of merging them both into one 32-bit integer), thus increasing the number of bits for FileIndex making it possible to have more files mapped.

A typical usage of this call will look like:

#include "HugeArray.h"

int _tmain(int argc, _TCHAR* argv[])
{
BIGNUMBER bSize("4294967296"); // 4GB HugeArray ha(bSize, _T(".")); // Lets allocate an array of 4GB ha[0] = 0; ha[1] = 1; ha[2] = 2; ha[bSize-1] = (BYTE)((bSize-1).GetNumberIntoLong() % 256); ha[bSize-2] = (BYTE)((bSize-2).GetNumberIntoLong() % 256); for(BIGNUMBER i=bSize-3; i >= 0; --i) { ha[i] = (BYTE)(i.GetNumberIntoLong())%256; if(ha[i+2] != ((i+2).GetNumberIntoLong()%256)) printf("\nError at %s\n", i.GetNumberIntoString(sz)); } }

A sample project demonstrating this can be downloaded from the attachments.

Ofcourse, this class can be templatized to allow the elements to complex records than just Bytes. Only that care should be taken to ensure the records fall within the file boundary and do not spawn across.

A point worth noting is, this mechanism relies on the assumption that we never require to have all the content in the memory at once and that the element access follow the temporal and special proximity when possible.

Multiplication Circuit for Prime Factorization

Deriving a multiplication circuit for the Prime Factorization problem is trivial if you know how to convert the arithmetic and relational expressions into propositional logic. Below I will show few expressions and their corresponding CNF formulation.

Converting Arithmetic and Relational expressions to Propositional Logic

Here symbols such as x, y, z, a, b, c etc... each denote a single boolean value that can take either true or false value.

We use ≠ to represent inequality, and ! to denote logical Not. The sign = is used to denote equality (and not assignment). Thus it is same as == of C/C++; For example, the expression if x then y=1 means whenever x is true, y also should be true, which is same as if x then assert(y==1) in C++.

For CNF expressions we use to represent logical AND, to represent logical OR, and - to represent complement. Thus x and -(-(x)) should essentially mean the same.

Expression CNF
if x then y=1 (-xy)
if x then y=0 (-x-y)
xy (x-y)
xy (-xy)
x = y (-xy) ∧ (x-y)
xy (xy) ∧ (-x-y)
x > y (xy) ∧ (x-y) ∧ (-x -y)
x < y (xy) ∧ (-xy) ∧ (-x -y)
(x & y) = z (-x-yz) ∧ (x-z) ∧ (y-z)
if(x) then (a = b) (-xa-b) ∧ (-x-ab)
if(x) then (ab) (-xab) ∧ (-x-a-b)
if(!x) then (a = b) (xa-b) ∧ (x-ab)
if(!x) then (ab) (xab) ∧ (x-a-b)
if(x & y) then (a = b) (-x-ya-b) ∧ (-x-y-ab)
if(!x & !y) then (a = b) (xya-b) ∧ (xy-ab)
if(x = y) then (a = b) (-x-y-ab) ∧ (-x-ya-b) ∧ (xya-b) ∧ (xy-ab)

Once we have the understanding of above conversions, we can start building complex circuits such as Adder, Bit-shifter etc... easily.

CNF Expression for Full-Adder

          |i (Carry-in)
          |
        |‾‾‾‾‾|
  x-----|     |----S (Sum)
        | F A |
  y-----|_____|----C (Carry-out)

In the above full-adder, inputs are x, y and i, while the outputs are s and c. Equations are as below:

S ≡ (x + y + i) % 2 ≡ x ⊕ y ⊕ i;

C ≡ (x + y + i) / 2 ≡ x.y + x.i + y.i;

CNF for the Carry-out is:

(x ∨ y ∨ -c) ∧ (x ∨ i ∨ -c) ∧ (y ∨ i ∨ -c) ∧ (-x ∨ -y ∨ c) ∧ (-x ∨ -i ∨ c) ∧ (-y ∨ -i ∨ c)

CNF for the Sum is:

(x ∨ y ∨ i ∨ -s) ∧ (x ∨ -y ∨ -i ∨ -s) ∧ (-x ∨ y ∨ -i ∨ -s) ∧ (-x ∨ -y ∨ i ∨ -s) ∧ (-x ∨ -y ∨ -i ∨ s) ∧ (-x ∨ y ∨ i ∨ s) ∧ (x ∨ -y ∨ i ∨ s) ∧ (x ∨ y ∨ -i ∨ s)

Multiplier Circuit in DIMACS CNF for Prime Factorizaion

Once we have the CNF expressions for the sum and carry of Full adder, we can use them to create the CNF expression for the Multiplier circuit for Prime Factorization problem. Given a product in n bits, we need to express it as the product of two numbers that take no more than (n+1)/2 bits. We can create a simple class to take care of the carry, sum and the factors A, B variable numberings as shown below:

// Class to compute the variable indices for the A, B, Carry and Sum
class CVariables
{
  int nBits;	// Number of bits in the product
  int nBitsBy2;	// nBitsBy2 == (nBits+1)/2
  TwoDimArray _c;	// Holds the Var indices for Carry
  TwoDimArray _s;	// Holds the Var indices for Sum
  Array _p;	// Holds the product bits
public:
  inline CVariables(const char* pBits, int n) : nBits(n), nBitsBy2((n+1)/2) 
  { 
    _c.ReDim(nBitsBy2+1, n+1);
    _s.ReDim(nBitsBy2+1, n+1);
    _p.ReDim(n+1);

    // for each column upto last column
    for(int j=2, nCount=0; j < n; ++j)
    {
      // for each row
      for(int i=2; i <= nBitsBy2; ++i)
      {
        _c(i, j) = C_Start() + nCount++;
        if(i==j) break;
      }
    }

    // for each column upto last column
    for(int j=2, nCount=0; j <= n; ++j)
    {
      // for each row upto the middle
      for(int i=2; i < nBitsBy2; ++i)
      {
        if(i==j) break;
        _s(i, j) = S_Start() + nCount++;				
      }
    }

    // Extract the product bits
    for(int nIndex=n; *pBits != '\0'; ++pBits, nIndex--)
    {
      switch(*pBits)
      {
      case '0': _p(nIndex) = 0;  break;
      case '1': _p(nIndex) = 1; break;
      default: fprintf(stderr, "\nInvalid Product bit: %c", *pBits); return;
      }
    }
  }

  inline int A_Start() const  { return 1;	}
  inline int A_Length() const { return nBits;	}
  inline int A_End() const	{ return A_Length(); }

  inline int B_Start() const	{ return A_End() + 1;	}
  inline int B_Length() const	{ return nBitsBy2;	}
  inline int B_End() const	{ return B_Start() + B_Length() -1;	}

  inline int C_Start() const	{ return B_End() + 1;	}
  inline int C_Length() const { return (nBits*(nBitsBy2-1)) - (nBitsBy2*(nBitsBy2-1)/2) - (nBitsBy2-1);	}
  inline int C_End() const	{ return C_Start() + C_Length() -1; }

  inline int S_Start() const	{ return C_End() + 1;	}
  inline int S_Length() const	{ return (nBits*(nBitsBy2-2)) - ((nBitsBy2-2)*(nBitsBy2-1)/2) - (nBitsBy2-2);	}
  inline int S_End() const	{ return S_Start() + S_Length() - 1; }

  inline int A(int nIndex) const	
  {
    //_ASSERTE(nIndex >= 1 && nIndex <= nBits);
    return A_Start() + (nIndex-1);
  }

  inline int B(int nIndex) const
  {
    //_ASSERTE(nIndex >= 1 && nIndex <= nBitsBy2);
    return B_Start() + (nIndex-1);
  }

  inline int C(int i, int j) const
  {
    //_ASSERTE(i >= 2 && i <= nBitsBy2 && j>=1 && j <=nBits);
    return _c(i, j);
  }

  inline int S(int i, int j) const
  {
    //_ASSERTE(i >= 2 && i <= nBitsBy2 && j>=1 && j <=nBits);
    return _s(i, j);
  }

  inline int P(int nIndex) const { return _p(nIndex); }
};  
  

Once we have this, we can go ahead and write the multiplier circuit as below:

// Returns the number of clauses written
int PrintMultiplicationTable(FILE* fp, const CVariables& var)
{
  int i, j;
  int jMax = var.A_Length(); // n
  int iMax = var.B_Length(); // (n+1)/2
  int Svar;	// Variable that holds the column wise sum upto now
  int nClauseCount = 0;

  // for each column upto last column
  for(j=2; j < jMax; ++j)
  {
    Svar = var.A(j);

    for(i=2; i < iMax; ++i)	// for each row upto the middle
    {
      if(i == j) break;

      nClauseCount += PrintSum(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1), var.S(i, j));

      nClauseCount += PrintCarry(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1), var.C(i, j));

      Svar = var.S(i, j);
    }

    if(i == j)	// Did we reach down a1 ??
    {
      if(var.P(j) == 1)	
        nClauseCount += PrintSum1(fp, Svar, var.B(i));
      else if(var.P(j)==0)	
        nClauseCount += PrintSum0(fp, Svar, var.B(i));

      nClauseCount += PrintCarry(fp, Svar, var.B(i), var.C(i, j)); // A(j)=1 Cin=0
    }
    else	// We have not reached upto a1
    {
      if(var.P(j)==1)	
        nClauseCount += PrintSum1(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1));
      else if(var.P(j)==0) 
        nClauseCount += PrintSum0(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1));

      nClauseCount += PrintCarry(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1), var.C(i, j));
    }
  }

  // here j==n;  Take care of the last column now. For this column we dont have any carry-outs.
  Svar = var.A(j);	// Value of A[n] usually is FALSE

  for(i=2; i < iMax; ++i)
  {
    if(i == j) break;

    nClauseCount += PrintSum(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1), var.S(i, j));

    nClauseCount += PrintCarry0(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1));

    Svar = var.S(i, j);
  }
  if(i == j)	
    _ASSERTE(false);	// We should never hit this !!
  else
  {
    if(var.P(j)==1)	
      nClauseCount += PrintSum1(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1));
    else
      _ASSERTE(false);	// P[n] should always be equal to 1

    nClauseCount += PrintCarry0(fp, Svar, var.B(i), var.A(j-i+1), var.C(i, j-1));
  }
  
  return nClauseCount;
}  

The PrintCarry, PrintSum etc... are helper functions to output the CNF expressions for Sum, Carry values and their code can be found in the attached project. You need GMP Multiprecision library to build and test it.

How to check if my code is executing inside a Managed App or Native App?

If you check the CRT source code (inside VC\crt\src\crt0.c) you can find an interesting function check_managed_app() that returns 1 if managed app, 0 if not based on the COM Runtime Descriptor in the Image Data Directory of the PE or PE+ header.

You can generalize it to test if you are running as part of a managed process or native process.

extern "C" int WRAPPER_API check_managed_app (LPVOID pImageBase)
{
PIMAGE_DOS_HEADER pDOSHeader;
PIMAGE_NT_HEADERS pPEHeader;

pDOSHeader = (PIMAGE_DOS_HEADER)pImageBase;
if ( pDOSHeader == NULL || pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE )
return 0;

pPEHeader = (PIMAGE_NT_HEADERS)((char *)pDOSHeader + pDOSHeader->e_lfanew);

if ( pPEHeader->Signature != IMAGE_NT_SIGNATURE )
return 0;

if (pPEHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
return 0;

if (pPEHeader->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
return 0;

return pPEHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress != 0;
}

You can invoke it from managed code as shown below:

    [DllImport("MyWrapper.dll")]
private static extern bool check_managed_app(System.IntPtr pBaseAddress);
bool Interface1.AmIInsideManagedApp()
{
return check_managed_app(Process.GetCurrentProcess().MainModule.BaseAddress);
}

A similar call from C++ should look like below:

extern  "C" IMAGE_DOS_HEADER __ImageBase;

check_managed_app(&__ImageBase);

Disclaimer: The usual "use it at your own risk" conditions apply.

Well-designed libraries

It is very rare that we come across a well-designed library every day, and even rare that we get a chance to work on them on daily basis. While it is hard to define what makes any given library "well-designed", it is rather easy to identify what is not. However, there are few exceptionally good libraries that from the moment you lay your hands on give you the feeling "…this has quality…"

Of course, there are unbeatable whole complex operating systems that were well-designed in the past and give unmatched performance even for today, which are becoming rather rare artifacts these days.

However, if you happen to work on Maya API, then more often than not you get that "…this is a good design, after all…" feeling. To see what this means, consider the following:

MStatus MDagPath::set (const MDagPath &src)

This is just a typical function in the MDagPath class that sets the DAG Path of the current object equal to the specified DAG Path. It returns an MStatus value indicating success or failure (or any other meaningful value within that context). There is nothing much complex or strange here, as this is a typical pattern in Maya API that almost every method accepts few parameters of interest and return status value indicating the success or failure of the operation. However, contrast it with the below method from the same class.

unsigned int MDagPath::length (MStatus* ReturnStatus = NULL) const

This method determines the number of DAG Nodes in the Path not including the root. However, what is of more interest for us here is not what this method does, but how it is defined. Note the MStatus now made as an optional parameter instead of return value, while the return value is position is used for a more meaningful value (the actual length, which is of more interest to the programmer in that context).

This subtle but important difference demonstrates how the library was designed to "adapt" itself to meet the priorities of the programmers/programming. MStatus values are an essential luxury provided by the API – some might need it, some may not. Thus they are made secondary citizens, always taking a less prominent place, perhaps as an optional parameter or a return value that can be always ignore when not required, while the primary concentration is given to the actual values that make more sense in the given context.

If you are wondering why this is so important, contrast this design with that of few libraries that demand status or result values to be return values always. While using them, you often find yourself saying "...huh…I have to declare a variable now to get that return value…"

A good library is one that minimizes those sighing moments.

Convenience matters apart, what is the afore-mentioned design really useful for? Can we get something more practical out of it?

Consider the following macro definition.

#define POPULATE_PROPERTY(hWndListView, FnObj, Prop)	\
{\
WTL::CString PropStr(OIL::ToString(FnObj.Prop())); \
\
LVITEM lvi = { 0 }; \
lvi.mask = LVIF_TEXT; \
lvi.iItem = MAXINT; \
lvi.pszText = _T(#Prop); \
lvi.iItem = ::SendMessage(hWndListView, LVM_INSERTITEM, 0, (LPARAM)&lvi); \
\
lvi.iSubItem = 1; \
lvi.pszText = (LPTSTR)(LPCTSTR)PropStr; \
::SendMessage(hWndListView, LVM_SETITEM, 0, (LPARAM)&lvi); \
}

Invoking the above macro with a call like below should result in the name of the method and its return value get populated in a list-view (similar to a property grid).

void PopulateKDagNode(HWND hWndListView, MObject& obj, bool bPopulateBaseClass /*= true*/)
{
if(bPopulateBaseClass)
PopulateKDependencyNode(hWndListView, obj, bPopulateBaseClass);

MFnDagNode DagNode(obj);

POPULATE_PROPERTY(hWndListView, DagNode, parentCount);
POPULATE_PROPERTY(hWndListView, DagNode, childCount);
POPULATE_PROPERTY(hWndListView, DagNode, inUnderWorld);
POPULATE_PROPERTY(hWndListView, DagNode, inModel);
POPULATE_PROPERTY(hWndListView, DagNode, isInstanceable);
POPULATE_PROPERTY(hWndListView, DagNode, isInstanced);
POPULATE_PROPERTY(hWndListView, DagNode, fullPathName);
POPULATE_PROPERTY(hWndListView, DagNode, partialPathName);
POPULATE_PROPERTY(hWndListView, DagNode, transformationMatrix);
POPULATE_PROPERTY(hWndListView, DagNode, isIntermediateObject);
POPULATE_PROPERTY(hWndListView, DagNode, objectColor);
POPULATE_PROPERTY(hWndListView, DagNode, usingObjectColor);
}

That's the complete listing of all "properties" of the given DagNode. This is introspection, which is made easy and elegant only because of the afore-discussed design. (The OIL::ToString() is a templated method from the Object Introspection Library http://sourceforge.net/projects/oil).

This is how a good designed library can make it possible to have it used for more than what it is designed for.

The introspection reminds me of one more library that has these "well designed" components in it: OpenSceneGraph (http://www.openscenegraph.org/). The Array class in it has an enum Type that identifies the type of the object being held inside it.

class OSG_EXPORT Array : public Object
{
public:
enum Type
{
ArrayType = 0,
ByteArrayType = 1,
ShortArrayType = 2,
IntArrayType = 3,
UByteArrayType = 4,
...
}
...
}

While we are thinking we could have used templates, below that class we find a templated version:

template<typename T, Array::Type ARRAYTYPE, int DataSize, int DataType>
class TemplateArray : public Array, public std::vector<T>
{
...
}

Further below, we find few typedefs.

typedef TemplateArray<GLfloat,Array::FloatArrayType,1,GL_FLOAT> FloatArray;

typedef TemplateArray<Vec2,Array::Vec2ArrayType,2,GL_FLOAT> Vec2Array;

In the end the array uses templates after all, but in a way that is easy for introspection. More complete details of this can be found inside the include\osg\Array header file.

One more elegant property of Maya API that we can observe is its ability to withstand mistakes (let's call it fault-tolerance). That is, we can invoke a method on a non-compatible object without bringing down the application or system. In a 3D rich world where objects that have similar properties are abounding (such as plane, surface, sphere, circle etc…) it is easy to take an object and make a method that is not suitable for that object (for e.g. trying to find the "volume" of a circle from its radius). Maya API allows this, without resulting in any adverse effects. It is very rare to see this kind of "fault tolerance" in libraries. (If you are interested in knowing how one could implement this kind of fault tolerance in one's own library, please refer to the implementation of Movie Creation Library available at http://www.geocities.com/krishnapg/createmovie.html. It uses C++ function pointers to inhibit any of its functional errors from affecting the hosting application. This allows the host application to run smooth as usual despite any errors in the movie creation.)

Of course, this doesn't mean the above discussed libraries are "the best" and cannot be made better. Nope. On the contrary, this only point to the strengths of those libraries, (while carefully ignoring weakness, if any,) that any new library designer and developers should carefully consider laying their foundations on for their own designs. Every system will have its own weaknesses – but it is those systems that have them the least win the day in the end.

More Posts Next page »
 
Page view tracker