Tuesday, July 31, 2012

Cut an Action of Biped Animation

If you animate a biped for a video game, you have to arrange a series of short discrete actions of the biped one after another in a row on one single timeline. When you export the biped animation, you give each of the actions a start time, an end time and a unique name and then the video game identifies each of the actions by the name to play it. The data is represented so that an action comes after the previous action has finished playing. One action is definitely not along the continuity from the previous one and on to next one even though they are so arranged.
Here is a serious problem with 3ds Max 2011. 3ds Max 2011 doesn't provide you with the method that splits the animation sequence of a biped into discrete actions. When you have finished making a action and freshly start making a new action, you will find that your new keys somehow affect the movement of the previous action. How strange it is! That is because 3ds Max tries to help smooth out your animation between your keys. This is known as interpolation, which has been done for every key of any biped movement in the previous action. So it also smoothes out between the last key of the previous action and the new key of the new action. If the last key of a part of the biped is up front in the previous action, away from the new key of the next new action, things look worse. That's not what you want. You want to stop affecting the previous action.
Unfortunately I couldn't find such an option as no interpolation for biped keys. If it were not for a biped, you could use [Set Tangents to Step] in [Track View-Curve Editor]. Alas, it doesn't work for bipeds. How can you sever the previous action and isolate each action. 

I finally came to use two redundant keys which confine the unintentional interpolation between them. Here is how I do it.
  1. Let's say you have finished the first action of a biped animation.
    Let's assume you have finished it at frame 50. Move the time slider onto the 50th frame. Make sure you are in [Auto Key] mode.

  2. Let's select all the tracks of your biped's Center Of Mass(COM).
    Click any part of the biped in one of the viewports. Note those pink rectangles in the right figure. At the top of the right column, click Motion tab to activate Motion panel and find [Track Selection] roll-out on it. Click the fourth button, [Lock COM Keying] with the lock icon on it in the roll-out and you will see it turned into yellow like it is pressed. Click all the three buttons to the left of [Lock COM Keying] button and all the three buttons also turn into yellow.

  3. Let's select the whole biped.
    Note those red rectangles in the figure below. Open [Schematic View] by clicking [Schematic View(Open)] button, the 5th button from the right of the main toolbar. Find the root node of the biped's tree diagram and double-click it to select all the parts of the whole biped.

  4. Let's manually set keys.
    Make sure you don't lose the selection. Note those yellow rectangles in the right picture above. Go to to Motion panel and open [Key info] roll-out. Click [Set Key] button, i.e. the first button so as to set the keys over the whole biped.

  5. Let's copy all the keys at the finishing frame to the next frame.
    Make sure again that the whole biped is still selected. At the bottom of the main window with the viewports, click the collective key at frame 50 on [Track Bar] under [Time Slider] and it will get brighter to indicate it's selected. Press and hold Shift key on your keyboard and meanwhile click and drag the selected key, i.e. the 50th and release your mouse at the very next frame, i.e. 51st. Now the finishing key is copied as shown in the next picture. Note the two black squares at 50, 51 in the next picture.

  6. Let's release [Track Selection] buttons.
    Back to Motion panel, click the yellow [Lock COM Keying] button to turn it into the previous color and then toggle off the three buttons to the left of [Lock COM Keying] button. All the four buttons should not be yellow.

  7. Start working a new action from the second next frame.
    Move the time slider onto the second next frame, i.e. the 52nd. Start your new action. The previous action will not be affected at all by the next new action.

Now we can delimit each action of a biped in order to separate them on one single timeline.

Saturday, July 21, 2012

iPhone(iOS) multi-touch with Ogre without OIS

I found that OGRE's OIS is not capable of dealing with multi-touch on iPhone and iPad and that it does not recognize the orientation of the device. So I started writing my own code for it. Finally I uploaded my code as Xcode 4.3 static library project onto github.com. The address of my git repository's home page is http://github.com/DuncanSungWKim/OgreAssistKit.
In this blog I briefly explain how to use it. This guide assumes that you are using the modified BaseApplication from my git repository which I tweaked to give it an option that disables OIS. You may have set up your project according to the prerequisite guide. However, you can always use your own framework.
I don't point out every detail because I believe that you, as a seasoned programmer, can handle any easy chores entailed in this process.

  1. Let's add the base class constructor BaseApplication(false) to your own BaseApplication-derived class' initialization list.
    The 'false' argument disables OIS. This is not in the original framework code. I added that option by myself.

  2. Let's add a native window handle as instance(member) variable to your own BaseApplication-derived class.
    We need to prevent OGRE from creating its own window and give it our own native window which we create by ourself. This variable is used to pass the handle over. Initialize this variable through a parameter of your own BaseApplication-derived class's constructor.
    This variable should be of int type.

  3. Let's override BaseApplication::configure() as following.
    bool YourOwnBaseApplicationDerivedClass::configure()
    {
        mRoot->initialise( false ) ;
        mWindow = OgreAssistKit::CreateRenderWindowWithNative(
                                    mRoot, m_iNativeWindowHandle ) ;
        return true ;
    }

  4. Let's inherit the interface class OgreAssistKit::TouchListener.
    Implement each of the methods in your own BaseApplication-derived class.

  5. Let's add the native window handle as instance(member) variable to the app delegate objective-C class.
    This variable should be of pointer type to class OgreAssistKit_UIWindow. I also assumes that you already have the pointer to your own BaseApplication-derived class instance in this class' @interface portion. For example,
    @interface YourAppDelegate : UIResponder <UIApplicationDelegate>
    {
        OgreAssistKit_UIWindow* m_oNativeWindow ;
        YourOwnBaseApplicationDerivedClass*
                     m_pYourOwnBaseApplicationDerivedClassInstance ;
    }
    

  6. Let's create our own native window and pass it over.
    Add the following code to application:didFinishLaunchingWithOptions: method before creating your own BaseApplication-derived class instance.
    m_oNativeWindow = [ [OgreAssistKit_UIWindow alloc]
                      initWithFrame:[UIScreen mainScreen].bounds ] ;
    
    Then pass it as an argument to your own BaseApplication-derived class' constructor through  reinterpret_cast while creating the instance.
    After being created, it needs to be known to the native window through the interface OgreAssistKit::TouchListener as following.
    m_oNativeWindow->m_pTouchInput->SetListener(
                   m_pYourOwnBaseApplicationDerivedClassInstance ) ;
    

  7. Let's sense orientation changes.
    Add the next code at the end of application:didFinishLaunchingWithOptions: method.
    [ [NSNotificationCenter defaultCenter]
        addObserver:self
        selector:@selector(orientationDidChange:)
        name:UIDeviceOrientationDidChangeNotification
        object:nil ] ;
    [ [UIDevice currentDevice]
        beginGeneratingDeviceOrientationNotifications ] ;
    Then add orientationDidChange: method into the app delegate class' @implementation portion as following.
    -(void) orientationDidChange:(NSNotification*)a_oNotif
    {
        UIDeviceOrientation ort =
           [UIDevice currentDevice].orientation ;
        m_oNativeWindow->m_pTouchInput->set_Orientation( 
                                      OgreAssistKit::ToOgre(ort) ) ;
    }

  8. Let's clean up on termination.
    In applicationWillTerminate: method, call UIDevice's [endGeneratingDeviceOrientationNotifications], NSNotificationCenter's [removeObserve:] and OgreAssistKit_UIWindow_CleanUp().
    Do not forget to release the native window, either.

Tuesday, July 17, 2012

Why Ogre::MeshManager::createPlane upVector is UNIT_Z

It really confused me that Ogre::MeshManager::createPlane() method takes both a plane with a normal vector and an up vector as 'upVector'. When you already know the upward normal vector due to the plane the method takes, what's the use of 'upVector' parameter? I found the answer from the next linked web forum page.


'upVector' parameter is actually the V axis of the texture. The U axis of the texture is obtained by the cross product of the normal vector and the V axis vector. If 'upVector' parameter is UNIT_Z, the V axis of the texture is along the Z axis.

If 'upVector' should be UNIT_Y as you might intuitively think because of the name, the U axis can not exist so the texture can not be shown. Further, the app can crash. I think it should have been named 'vAxis' to prevent this confusion.