Animation, Walking Between Points, and Basic Quaternions

Original Version By Clay Culver.
C# changes by DigitalCyborg

Note: This was written for Ogre 1.2, and is known to compile against it. Any problems you encounter while working with this tutorial should be posted to the OgreDotNet Forum.

Introduction

In this tutorial we will be covering how to take an Entity, animate it, and have it walk between predefined points. This will also cover the basics of Quaternion rotation by showing how to keep the Entity facing the direction it is moving.

Prerequisites

This tutorial will assume that you already know how to set up an OgreDotNet project and make it compile successfully. This tutorial also makes use of .NETs LinkedList<T> data structure. While no prior knowledge of how to use it, you should at least know what templates are.

If you are not familiar with STL, Clay recommends picking up STL Pocket Reference ISBN 0-596-00556-3. This will save you a lot of time in the future. I think most of the stuff that's in the STL has equivalent data structures which live in System.Collections and System.Collections.Generic. If you know of a good .NET Framework book, please update this wiki & share your knowledge so that the whole ODN community can benefit. Otherwise, you can probably find the information in the .NET Development pages

Getting Started

First, you need to create a new project for the demo. Add a file called “MoveDemo.cs” to the project, and add this to it:

using System;
 using System.Drawing;
 using Math3D;
 using OgreDotNet;
 using System.Collections.Generic;
 
 namespace OgreDotNetTutorial
 {
    public class MoveDemoApplication : ExampleApplication
    {
 
        protected Entity mEntity = null;    // The entity of the object we are animating
        protected SceneNode mNode = null;   // The SceneNode of the object we are moving
        protected AnimationState mAnimationState = null; //The AnimationState the moving object
 
        protected float mDistance = 0.0f;              //The distance the object has left to travel
        protected Vector3 mDirection = Vector3.Zero;   // The direction the object is moving
        protected Vector3 mDestination = Vector3.Zero; // The destination the object is moving towards
        protected LinkedList<Vector3> mWalkList = null; // A doubly linked containing the waypoints
 
        protected float mWalkSpeed = 35.0f;  // The speed at which the object is moving
        protected bool mWalking = false;     // Whether or not the object is moving
        
        protected override void CreateScene()
        {
              //temporary entity and SceneNode
              Entity ent;
              SceneNode node;
        }
            
        protected bool nextLocation()
        {
             return true;
        }
 
        protected override bool FrameStarted(FrameEvent e)
        {
            if (mRenderWindow.Closed || mDone) return false;
 
            mDeltaTime = e.TimeSinceLastFrame;
 
            UpdateDebugOverlay();
       
            return true;
        }
 
        [MTAThread]
        static void Main(string[] args)
        {
            using (MoveDemoApplication app = new MoveDemoApplication())
            {
                app.Start();
            }
        }
    }
 }

Be sure you can compile this code before continuing.

Setting up the Scene

Before we begin, notice that we have already defined some variables in MoveDemoApplication. The mEntity will hold the entity we create, mNode will hold the node we create, mAnimationState will hold the animation state of the object, mDistance will contain the distance from our location to the next waypoint, mDirection will hold the direction the object is moving, mDestination will hold the next waypoint location, mWalkList will contain all the points we wish the object to walk to, mWalkSpeed will hold our speed and mWalking will indicate whether or not we are moving.

Go to the CreateScene() method and add the following code to it. First we are going to set the ambient light to full so that we can see objects we put on the screen.

// Set the default lighting and background color
            mSceneManager.SetAmbientLight(Color.White);
            mViewport.SetBackgroundColour(Color.Black);

Next we will create a Robot on the screen so that we can play with him. To do this we will create the entity for the Robot, then create a SceneNode for him to dangle from.

// Create the Robot entity
            mEntity = mSceneManager.CreateEntity("Robot", "robot.mesh");
 
            // Create the Robot's SceneNode
            mNode = mSceneManager.GetRootSceneNode().CreateChildSceneNode("RobotNode",
                new Vector3(0.0f, 0.0f, 25.0f));
            mNode.AttachObject(mEntity);

This all should be very basic, so I will not go into detail about any of it. In the next chunk of code, we are going to tell the robot where he needs to be moved to. For those of you who don't know anything about .NETs LinkedList, it is an efficient? implementation of a doublely linked list that we can use as a two ended queue. We will only be using a few of its methods. The AddFirst and AddLast methods put items at the front and back of the list respectively. The First and Last methods return the items at the front and back of the list respectively. The RemoveFirst and RemoveLast methods remove the items from the front and back of the list respectively. Finally, the Count method returns the number of entries in the list. This code creates the list, then adds three Vectors to it. We will use these later when we make the robot move

// Create the walking list
            mWalkList = new LinkedList<Vector3>();
            mWalkList.AddLast(new Vector3(550.0f, 0.0f, 50.0f));
            mWalkList.AddLast(new Vector3(-100.0f, 0.0f, -200.0f));
            mWalkList.AddLast(new Vector3(0.0f, 0.0f, 25.0f));

Next, we want to place some objects on the scene to show where the robot is supposed to be moving. This will allow us to see the robot moving with respect to other objects on the screen. Notice the negative Y component to their position. This puts the objects under where the robot is moving to, and he will stand on top of them when he gets to the correct place.

// Create knot objects so we can see movement
            ent = mSceneManager.CreateEntity("Knot1", "knot.mesh");
            node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("Knot1Node",
                new Vector3(0.0f, -10.0f, 25.0f));
            node.AttachObject(ent);
            node.SetScale(0.1f, 0.1f, 0.1f);
            //
            ent = mSceneManager.CreateEntity("Knot2", "knot.mesh");
            node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("Knot2Node",
                new Vector3(550.0f, -10.0f, 50.0f));
            node.AttachObject(ent);
            node.SetScale(0.1f, 0.1f, 0.1f);
            //
            ent = mSceneManager.CreateEntity("Knot3", "knot.mesh");
            node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("Knot3Node",
                new Vector3(-100.0f, -10.0f, -200.0f));
            node.AttachObject(ent);
            node.SetScale(0.1f, 0.1f, 0.1f);

Finally, we want to set the camera to a good viewing point to see this from. We will move the camera to get a better position.

// Set the camera to look at our handywork
            mCamera.SetPosition(90.0f, 280.0f, 535.0f);
            mCamera.Pitch(new Radian(new Degree(210.0f)));
            mCamera.Yaw(new Radian(new Degree(-15.0f)));
            mCamera.Roll(new Radian(new Degree(180.0f)));

If you are paying close attention and comparing this code with Clay's tutorial, you'll notice that the pitch & yaw are different and we have a roll element too. I couldn't really figure out why I needed this (other than to see the Scene)... Perhaps someone more knowledgeable about ODN (Rastaman?) can explain why this was necessary. I've included the OGRE cpp code snippet that I'm talking about below (I commented it out to help indicate that its not part of the C# port of the tutorial:

// Set the camera to look at our handywork
            //mCamera->setPosition(90.0f, 280.0f, 535.0f);
            //mCamera->pitch(Degree(-30.0f));
            //mCamera->yaw(Degree(-15.0f));

Now compile and run the code. You should see something like this: http://www.idleengineer.net/images/tutorial_01.png

Animation

We are now going to setup some basic animation. Animation in OGRE is very simple. To do this, you need to get the AnimationState from the Entity object, set its options, and enable it. This will make the animation active, but you will also need to add time to it after each frame in order for the animation to run. We'll take this one step at a time. First, add this to the bottom of CreateScene.

// Set idle animation
            mAnimationState = mEntity.GetAnimationState("Idle");
            mAnimationState.SetLoop(true);
            mAnimationState.SetEnabled(true);

The first line of code gets the AnimationState out of the entity. In the second line we call SetLoop( true ), which makes the animation loop over and over. For some animations (like the death animation), we would want to set this to false instead. The third line actually enables the Animation. But wait...where did we get “Idle” from? How did this magic string slip in there? Every mesh has their own set of Animations defined for them. In order to see all of the Animations for the particular mesh you are working on, you need to download the OgreMeshViewer and view the mesh from there.

Now, if we compile and run the demo we see...nothing has changed. This is because we need to update the animation state with a time every frame. Find the FrameStarted method, and add this line of code:

//Update the Animation State.
            mAnimationState.AddTime(mDeltaTime);

Now build and run the application. You should see a robot performing his idle animation standing in place. pretty spiffy huh?

Moving the Robot

Now we are going to perform the slightly tricky task of making the robot walk from waypoint to waypoint. Before we begin I would like to re-describe some the variables that we are storing in the class. We are going to use 4 variables to accomplish the task of moving the robot. The direction the robot is moving is stored in mDirection. The robot's current destination is stored in mDestination. The distance the robot has left to travel is stored in mDistance. Last but not least, the robot's speed is stored in mWalkSpeed.

Since the variables are already initialized we can use them to set the robot in motion. To make the robot move, we simply tell it to change animations. However, we only want to start the robot moving if there is another location to move to. the nextLocation is used for this.

Add this code to FrameStarted method just above mAnimationState.AddTime(mDeltaTime) which should be just above the return line.

if (!mWalking)
            //either we've not started walking or reached a way point
            {
                //check if there are places to go
                if (nextLocation())
                {
                    LinkedListNode<Vector3> tmp;
 
                    //Start the walk animation
                    mAnimationState = mEntity.GetAnimationState("Walk");
                    mAnimationState.SetLoop(true);
                    mAnimationState.SetEnabled(true);
                    mWalking = true;
                    
                    //Update the destination using the walklist.
 
                }//if(nextLocation())
            }

If you compile and run the code right now, the robot will walk in place. This is because the robot starts out with a mWalking = false and our nextLocation function always returns true. In later steps we will be adding a bit more intelligence to the nextLocation function. Note that we also left a placeholder for the code to update the Robot's destination. we'll get to that later too :-)

Now we are going to actually move the robot in the scene. To do this we need to have him move a small bit every frame. We will be adding the following code just after our previous if statement and just above the return line of the method. This code will handle the case when the robot is actually moving; (mWalking == true)

else //we're in motion
            {
                //determine how far to move this frame
                float move = mWalkSpeed * mDeltaTime;
                mDistance -= move;

Now, we need to check and see if we've arrived at the waypoint. That is, if mDistance is now less than or equal to zero, we need to set the point and setup the move to the next point. Note that we are setting mDirection to the ZERO vector and that we are setting mWalking to false.(the robot stops at each waypoint, then checks to see if it has another waypoint).

//Check to see if we've arrived at a waypoint
                if (mDistance <= 0.0f)
                {
                    //set our node to the destination we've just reached & reset direction to 0
                    mNode.SetPosition( mDestination );
                    mDirection = Vector3.Zero;
                    mWalking = false;
                }//if(mDistance <= 0.0f)
                else
                { 
                    //Rotation code goes here
 
                    //movement code goes here
                }

Now that we have moved to the point, we need to setup the motion to the next point. Once we know if we need to move to another point or not, we can set the appropriate animation; walking if there is another point to go to and idle if there are no more destination points. This is a simple matter of setting the Idle animation if there are no more locations. update Framestarted just below the close of the if statement

} //if(nextLocation())

                else //nowhere to go. set the idle animation. (or Die)
                {
                    mAnimationState = mEntity.GetAnimationState("Idle");
                    //mAnimationState = mEntity.GetAnimationState("Die");
                    //mAnimationState.SetLoop(false);
                }

Note that we have no need to set the walking animation again if there are more points in the queue to walk to. Since the robot is already walking there is no reason to tell him to do so again. However, if the robot needs to go to another point, then we need to rotate him to face that point. For now we leave a placeholder comment in the else clause; remember this spot as we will come back to it later.

This takes care of when we are very close to the target position. Now we need to handle the normal case, when we are just on the way to the position but we're not there yet. To do that we will translate the robot in the direction we are traveling, and move it by the amount specified by the move variable. This is accomplished by adding the following code:

else
                {
                    //movement code goes here
                    mNode.Translate(mDirection * move);
                    
                    //Rotation code goes here
                }

We are almost done. Our code now does everything except set up the variables required for movement. If we can properly set the movement variables our Robot will move like he is supposed to. Find the NextLocation function. This function returns false when we run out of points to go to. This will be the first of code line in our function. (Note you should leave the return true statement at the bottom of the function.)

if (mWalkList.Count == 0) 
                return false;

Now we need to set the movement variables. We'll do this where we left this placeholder earlier

//Update the destination using the walklist.

First we will extract the destination vector from the front of the LinkedList and then add the LinkedListNode that held it to the end of the list. This will make the robot keep walking around from point to point.
We will set the direction vector by subtracting the SceneNode's current position from the destination. We have a problem though. Remember how we multiplied mDirection by the move amount in FrameStarted? If we do this, we need the direction vector to be a unit vector (that is, it's length equals one). The normalise function does this for us, and returns the old length of the vector. Handy that, since we need to also set the distance to the destination.

//Update the destination using the walklist.
                    LinkedListNode<Vector3> tmp;  //temporary listNode
                    mDestination = mWalkList.First.Value; //get the next destination.
                    tmp = mWalkList.First; //save the node that held it
                    mWalkList.RemoveFirst(); //remove that node from the front of the list
                    mWalkList.AddLast(tmp);  //add it to the back of the list.
 
                    //update the direction and the distance
                    mDirection = mDestination - mNode.GetPosition();
                    mDistance = mDirection.Normalize();

Now compile and run the code. It works! Mostly... The robot now walks to all the points, but he is always facing the Vector3.UnitX direction (his default). We will need to change the direction he is facing when he is moving towards points.

What we need to do is get the direction the Robot is facing, and use the rotate function to rotate the object in the right position. Insert the following code where we left the following placeholder comment earlier.

//Rotation code goes here

The first line gets the direction the Robot is facing. The second line builds a Quaternion representing the rotation from the current direction to the destination direction. The third line actually rotates the Robot.

Vector3 src = mNode.GetOrientation() * Vector3.UnitX;
                    Quaternion quat = src.GetRotationTo(mDirection);
                    mNode.Rotate( quat );

A lot is going on in that small chunk code! A few questions pop into mind. The most pertenant is: "What is a Quaternion?" Basically, Quaternions are representations of rotations in 3 dimensional space. They are used to keep track of how the object is positioned in space. In the first line we call the getOrientation method, which returns a vector representing the way the Robot is currently facing. By multiplying it by the UnitX vector, we obtain the direction the robot is currently facing, which we store in src. In the second line, the getRotationTo method gives us a Quaternion that represents the rotation from the direction the Robot is facing to the direction we want him to be facing. In the third line we use the quaternion to rotate the node so that it faces the new orientation.

There is only one problem with the code we have created. There is a special case where SceneNode::rotate will fail. If we are trying to turn the Robot 180 degrees, the rotate code will bomb with a divide by zero error. In order to fix that, we will test to see if we are performing a 180 degree rotation. If so, we will simply yaw the Robot by 180 degrees instead of using rotate. To do this, delete the three lines we just put in and replace them with this:

Vector3 src = mNode.GetOrientation() * Vector3.UnitX;
                    if((1.0f + src.Dot(mDirection)) < 0.0001f)
                    {
                        mNode.Yaw(180.0f);
                    }
                    else
                    {
                        Quaternion quat = src.GetRotationTo(mDirection);
                        mNode.Rotate( quat );
                    }

All of this should now be self explanatory except for what is wrapped in that if statement. If two unit vectors oppose each other (that is, the angle between them is 180 degrees), then their dot product will be -1. So, if we dotProduct the two vectors together and the result equals 1.0f, then we need to yaw by 180 degrees, otherwise we use rotate instead. Why do I add 1.0f and check to see if it is less than 0.0001f? Don't ever forget about floating point rounding error. You should never directly compare two floating point numbers. Finally, note that in this case the dot product of these two vectors will fall in the range -1, 1. In case it is not abundantly clear, you need to know at minimum basic linear algebra to do graphics programming! At the very least you should review the Quaternion and Rotation Primer and consult a book on basic vector and matrix operations.

Now our code is complete! Compile and run the demo to see the Robot walk the points he was given.

Here's the full code

using System;
 using System.Drawing;
 using Math3D;
 using OgreDotNet;
 using System.Collections.Generic;
 
 namespace OgreDotNetTutorial
 {
    public class MoveDemoApplication : ExampleApplication
    {
        protected Entity mEntity = null;    // The entity of the object we are animating
        protected SceneNode mNode = null;   // The SceneNode of the object we are moving
        protected AnimationState mAnimationState = null; //The AnimationState the moving object
 
        protected float mDistance = 0.0f;              //The distance the object has left to travel
        protected Vector3 mDirection = Vector3.Zero;   // The direction the object is moving
        protected Vector3 mDestination = Vector3.Zero; // The destination the object is moving towards
        protected LinkedList<Vector3> mWalkList = null; // A doubly linked containing the waypoints
 
        protected float mWalkSpeed = 35.0f;  // The speed at which the object is moving
        protected bool mWalking = false;     // Whether or not the object is moving
 
        protected override void CreateScene()
        {
            //temporary entity and SceneNode
            Entity ent;
            SceneNode node;
 
            // Set the default lighting and background color
            mSceneManager.SetAmbientLight(Color.White);
            mViewport.SetBackgroundColour(Color.Black);
 
            // Create the Robot entity
            mEntity = mSceneManager.CreateEntity("Robot", "robot.mesh");
 
            // Create the Robot's SceneNode
            mNode = mSceneManager.GetRootSceneNode().CreateChildSceneNode("RobotNode",
                new Vector3(0.0f, 0.0f, 25.0f));
            mNode.AttachObject(mEntity);
 
            // Create the walking list
            mWalkList = new LinkedList<Vector3>();
            mWalkList.AddLast(new Vector3(550.0f, 0.0f, 50.0f));
            mWalkList.AddLast(new Vector3(-100.0f, 0.0f, -200.0f));
            mWalkList.AddLast(new Vector3(0.0f, 0.0f, 25.0f));
 
            // Create knot objects so we can see movement
            ent = mSceneManager.CreateEntity("Knot1", "knot.mesh");
            node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("Knot1Node",
                new Vector3(0.0f, -10.0f, 25.0f));
            node.AttachObject(ent);
            node.SetScale(0.1f, 0.1f, 0.1f);
            //
            ent = mSceneManager.CreateEntity("Knot2", "knot.mesh");
            node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("Knot2Node",
                new Vector3(550.0f, -10.0f, 50.0f));
            node.AttachObject(ent);
            node.SetScale(0.1f, 0.1f, 0.1f);
            //
            ent = mSceneManager.CreateEntity("Knot3", "knot.mesh");
            node = mSceneManager.GetRootSceneNode().CreateChildSceneNode("Knot3Node",
                new Vector3(-100.0f, -10.0f, -200.0f));
            node.AttachObject(ent);
            node.SetScale(0.1f, 0.1f, 0.1f);
 
            // Set the camera to look at our handywork
            mCamera.SetPosition(90.0f, 280.0f, 535.0f);
            mCamera.Pitch(new Radian(new Degree(210.0f)));
            mCamera.Yaw(new Radian(new Degree(-15.0f)));
            mCamera.Roll(new Radian(new Degree(180.0f)));
 
            // Set idle animation
            mAnimationState = mEntity.GetAnimationState("Idle");
            mAnimationState.SetLoop(true);
            mAnimationState.SetEnabled(true);
        }
 
        protected bool nextLocation()
        {
            if (mWalkList.Count == 0)
                return false;
            return true;
        }
 
        protected override bool FrameStarted(FrameEvent e)
        {
            if (mRenderWindow.Closed || mDone) return false;
 
            mDeltaTime = e.TimeSinceLastFrame;
 
            UpdateDebugOverlay();
 
            if (!mWalking)
            //either we've not started walking or reached a way point
            {
                //check if there are places to go
                if (nextLocation())
                {
                    //Start the walk animation
                    mAnimationState = mEntity.GetAnimationState("Walk");
                    mAnimationState.SetLoop(true);
                    mAnimationState.SetEnabled(true);
                    mWalking = true;
 
                    //Update the destination using the walklist.
                    LinkedListNode<Vector3> tmp;  //temporary listNode
                    mDestination = mWalkList.First.Value; //get the next destination.
                    tmp = mWalkList.First; //save the node that held it
                    mWalkList.RemoveFirst(); //remove that node from the front of the list
                    mWalkList.AddLast(tmp);  //add it to the back of the list.
 
                    //update the direction and the distance
                    mDirection = mDestination - mNode.GetPosition();
                    mDistance = mDirection.Normalize();
                }//if(nextLocation())
                else //nowhere to go. set the idle animation. (or Die)
                {
                    mAnimationState = mEntity.GetAnimationState("Idle");
                    //mAnimationState = mEntity.GetAnimationState("Die");
                    //mAnimationState.SetLoop(false);
                }
            }
            else //we're in motion
            {
                //determine how far to move this frame
                float move = mWalkSpeed * mDeltaTime;
                mDistance -= move;
 
                if (mDistance <= 0.0f)
                {
                    mNode.SetPosition(mDestination);
                    mDirection = Vector3.Zero;
 
                    //Check to see if we've arrived at a waypoint
                    if (mDistance <= 0.0f)
                    {
                        //set our node to the destination we've just reached & reset direction to 0
                        mNode.SetPosition(mDestination);
                        mDirection = Vector3.Zero;
                        mWalking = false;
                    }//if(mDistance <= 0.0f)
                }
                else
                {
                    //movement code goes here
                    mNode.Translate(mDirection * move);
 
                    //Rotation code goes here
                    Vector3 src = mNode.GetOrientation() * Vector3.UnitX;
                    if ((1.0f + src.Dot(mDirection)) < 0.0001f)
                    {
                        mNode.Yaw(180.0f);
                    }
                    else
                    {
                        Quaternion quat = src.GetRotationTo(mDirection);
                        mNode.Rotate(quat);
                    }
                }
            }
 
            //Update the Animation State.
            mAnimationState.AddTime(mDeltaTime);
 
            return true;
        }
 
        [MTAThread]
        static void Main(string[] args)
        {
            using (MoveDemoApplication app = new MoveDemoApplication())
            {
                app.Start();
            }
        }
    }
 }

Exercises for Further Study


Easy Questions

  1. Add more points to the robot's path. Be sure to also add more knots that sit under his position so you can track where he is supposed to go.
  2. Robots who have outlived their usefulness should not continue existing! When the robot has finished walking, have him perform the death animation instead of idle. The animation for death is “Die”.

Difficult Questions

  1. One of the limitations of this class is that you cannot add points to the robot's walking path after you have created the object. Fix this problem by implementing a new method which takes in a Vector3 and adds it to the mWalkList deque. (Hint, if the robot has not finished walking you will only need to add the point to the end of the deque. If the robot has finished, you will need to make him start walking again, and call nextLocation to start him walking again.)

Expert Questions

  1. Another major limitation of this class is that it only tracks one object. Reimplement this class so that it can move and animate any number of objects independently of each other. (Hint, you should create another class that contains everything that needs to be known to animate one object completely. Store this in a STL map object so that you can retrieve data later based on a key.) You get bonus points if you can do this without registering any additional frame listeners.
  2. After making the previous change, you might have noticed that Robots can now collide with each other. Fix this by either creating a smart path finding function, or detecting when robots collide and stopping them from passing through each other.

Credits

Thanks Clay Culver for providing the original tutorial

<HR>
Creative Commons Copyright -- Some rights reserved.


THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

  • "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
  • "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
  • "Licensor" means the individual or entity that offers the Work under the terms of this License.
  • "Original Author" means the individual or entity who created the Work.
  • "Work" means the copyrightable work of authorship offered under the terms of this License.
  • "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
  • "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

  • to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
  • to create and reproduce Derivative Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
  • For the avoidance of doubt, where the work is a musical composition:
    • Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
    • Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
    • Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).


The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.

4. Restrictions

The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

  • You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
  • You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
  • If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability.

EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

  • This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
  • Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

  • Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
  • Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
  • If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
  • No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
  • This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.