%tutorialhelp%

Introduction

This tutorial will cover the creation of a box selection mechanism. This is the familiar process of dragging out a rectangle on the screen to select everything within it. It is another one of the features that a basic scene editor would have.

To accomplish this, we will be introducing two new objects. We will use a ManualObject to create the box on screen, and we will use a PlaneBoundedVolumeListSceneQuery to determine what should be selected in the scene. This is only an introduction to manual objects. They can be used to generate a wide range of objects for your scene without ever needing to use 3D modeling software.

The full source for this tutorial is here.

Note: There is also source available that uses the BaseApplication framework and Ogre 1.7 here.

selection_visual.png

Prerequisites

This tutorial assumes that you already know how to set up an Ogre project and compile it successfully. Knowledge of the topics from previous tutorials is also assumed.

The base code for this tutorial is here.

ManualObjects

A manual object allows us to construct a mesh without having to design it beforehand in 3D modeling software like Blender. To understand how we can do this, it will be helpful to introduce a few basic concepts related to how objects are represented in a graphics library like Ogre.

A mesh in Ogre can be thought of as consisting of two pieces. They are referred to as vertex buffers and index buffers. If you have any experience with OpenGl, then this might sound somewhat familiar.

A vertex buffer defines a series of points in 3D space. Each element in a vertex buffer is defined by several attributes. The only required attribute is the position of the vertex. The other attributes allow you to change things like the color or texture that will be applied to the vertex when it is rendered.

Depending on the mode, every three indices may define a triangle to be drawn by the GPU. The order of the vertices will determine which way the triangle will face. A triangle which is drawn counter-clockwise is facing the camera. A triangle that is drawn clockwise is facing away from the camera. This is important because often the back face of a triangle is culled. In other words, it is not rendered.

All meshes have a vertex buffer, but not all of them have an index buffer. The mesh we will be creating for this tutorial will not have an index buffer, because we are creating an empty rectangle. A solid rectangle would require an index buffer to define the triangles that would make up its face.

Creating a Mesh

There are two ways to create our own mesh within Ogre. The first way is to build a class that inherits from SimpleRenderable and directly provide the vertex and index buffers to it. This method is powerful, but a bit cryptic. An example is given in Generating A Mesh. We will use the simpler method of creating a ManualObject. Instead of setting all of the attributes and indices by hand, we will simply call methods like position.

We want to generate a rectangular outline on the screen for selection purposes. We could use CEGUI or the builtin overlay system, but instead we are going to generate a simple 2D mesh using the ManualObject class.

The SelectionBox Class

To keep things clean we are going to put our selection box functionality into its own class. Create a new class in your project called SelectionBox that inherits from Ogre::ManualObject.

SelectionBox.h
#ifndef SELECTIONBOX_H
#define SELECTIONBOX_H

#include <OgreManualObject.h>

class SelectionBox : public Ogre::ManualObject
{
public:
  SelectionBox(const Ogre::String& name);
  virtual ~SelectionBox();

  void setCorners(float left, float top, float right, float bottom);
  void setCorners(const Ogre::Vector2& topLeft, const Ogre::Vector2& bottomRight);
};

#endif /* SELECTIONBOX_H */

We want the selection box to render as a 2D object. We also want to make sure that it renders after our scene elements. This is so it doesn't get covered up by the objects we are trying to select. Add the following to the body of the SelectionBox constructor:

setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY);
setUseIdentityProjection(true);
setUseIdentityView(true);
setQueryFlags(0);

The first function makes sure the mesh will be rendered with the overlay. The next two functions set the projection and view matrices for our object. Ogre abstracts all of these details away for us, so we won't get into exactly what these functions do. The reason you're using Ogre is probabaly because you don't want to reinvent the wheel.

The important part to know is that using the indentity matrix for both translates into rendering our rectangle as a 2D object. The other important thing to notice is that we really must treat the object like a 2D object in some cases. For instance, if a function asks for the z value of our object we will use -1, since our object has been projected into the 2D x-y plane of the screen and effectively no longer has a z value. Finally, we set the query flags to zero. This will exclude our selection box from being included in any scene query results.

Now we will start to build the actual rectangle. We have one issue that needs to cleared up first. We will be using the location of the cursor in our functions. The problem is that the normalized mouse positions run from 0, 1 for the x and y axes, but the manual object functions are expecting coordinates that run from -1, 1. To make things a little more complicated, the y coordinate also runs in the other direction. CEGUI defines the top of the screen as y = 0, just like Ogre does, but in the coordinate system we need to use now, the top of the screen is y = 1 and the bottom is y = -1. This can be fixed by transforming the coordinates before we use them. Add the following to the setCorners method with four parameters:

left = 2 * left - 1;
right = 2 * right - 1;
top = 1 - 2 * top;
bottom = 1 - 2 * bottom;

If you stare at that for a little bit, you can probably convince yourself it does exactly what we need it to. Now that we have the correctly transformed coordinates, we can actually build the rectangle.

clear();
begin("Examples/KnotTexture", Ogre::RenderOperation::OT_LINE_STRIP);
position(left, top, -1);
position(right, top, -1);
position(left, bottom, -1);
position(left, top, -1);
end();

The very first thing we do is call clear since we will be calling this a number of times, and we want the rectangles drawn in previous frames to disappear. Next we need to call the begin method to start building our mesh. Its first parameter is the name of the material you want to use for the object. The next parameter is the render operation we want to use to build our mesh. We have chosen the line strip method, which connects all positions with a straight line. Next we start building the mesh. Each call to position adds another point into our vertex buffer. To finalize the mesh we call end.

The last thing we will do is set the bounding box for our object. Many scene managers cull objects which move offscreen. Even though we asked Ogre to project our manual object as if it were essentially a 2D object, it is still really a 3D object in our scene. This means that if we attach the object to a scene node (as we're about to do), it may disappear when the node moves off camera. To fix this we will set the bounding box of the object to be infinite so that the camera will always be inside it and will never cull the object.

setBoundingBox(Ogre::AxisAlignedBox::BOX_INFINITE);

This line should be placed after the clear call. Every time we call clear the bounding box is reset.

The last thing we need to do with this class is finish the overloaded setCorners method with two vector parameters. Just make a call to the setCorners method we've already defined.

setCorners(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);

This overload will allow us to use vectors instead of providing all four corners of the box separately. The SelectionBox class is now complete. Compile and run your application to make sure it works. The functionality should not have changed yet.

Box Selection

Now we will implement the actual selection code. First, we have to set up a few things. Make sure to include SelectionBox.h in our header:

BasicApp.h
#include "SelectionBox.h"

We also need to add a pointer to a SelectionBox object.

BasicApp.h
SelectionBox* mSelectionBox;

And add an initialization to the contructor:

BasicApp.cpp
mSelectionBox(0),

Now we need to create an instance of our new class and attach it to our root scene node. Then we want the scene manager to create a plane-bounded volume query for us. Add the following to the end of createScene:

mSelectionBox = new SelectionBox("SelectionBox");
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(mSelectionBox);

mVolQuery = mSceneMgr->createPlaneBoundedVolumeQuery(Ogre::PlaneBoundedVolumeList());

As usual, we make sure to clean up in the destructor. Add the following to ~BasicApp:

mSceneMgr->destroyQuery(mVolQuery);

if(mSelectionBox)
  delete mSelectionBox;

Notice we let the scene manager clean up the queries for us.

Box Selection is Volume Selection

Really what we're trying to do is select everything that is contained within a volume in our scene. The rectangle we draw on the screen can be thought of like the very edge of the opening of an infinitely long piece of square tubing, like we're looking through a rain gutter. Here's a simple visual:
volume_visual.png
The volume goes on infinitely in one direction because it will only be enclosed by five planes. It would require six to completely enclose. Everything that is contained within this infinite square tube will be selected when we let go of the left mouse button.

The process will start when the left mouse button is pressed down. At that point, we'll need to get a starting vector for drawing our selection box. We will also turn on the flag that says we are currently selecting objects and make the selection box visible. Add the following to the if statement for the left mouse button in mousePressed:

CEGUI::MouseCursor* mouse = &context.getMouseCursor();
mStart.x = mouse->getPosition().d_x / (float)arg.state.width;
mStart.y = mouse->getPosition().d_y / (float)arg.state.height;
mStop = mStart;
 
mSelecting = true;
mSelectionBox->clear();
mSelectionBox->setVisible(true);

One important thing to notice is that we're using the CEGUI mouse position and not the position from OIS. This is because OIS sometimes thinks the mouse is somewhere different than where CEGUI is displaying it. We want our application to sync with what the user sees, so we rely on the CEGUI coordinates.

The next thing we need to do is hide the selection box and perform the selection when the user releases the mouse button. We will fill in performSelection shortly. Add the following to the if statement for the left mouse button in mouseReleased:

performSelection(mStart, mStop);
mSelecting = false;
mSelectionBox->setVisible(false);

Whenever the mouse is moved we need to update the position of the selection box. Add the following to mouseMoved:

if (mSelecting)
{
  CEGUI::MouseCursor* mouse = &context.getMouseCursor();
  mStop.x = mouse->getPosition().d_x / (float)me.state.width;
  mStop.y = mouse->getPosition().d_y / (float)me.state.height;
 
  mSelectionBox->setCorners(mStart, mStop);
}

We calculate the stop vector for our selection box, and then we pass both the start and stop vectors to our overloaded setCorners method.

Compile and run your application. You can now draw a rectangle using the mouse. Cool.

PlaneBoundedVolumeListSceneQuery

Now that we have the selection box working, we are going to set up our volume selection. First, we'll quickly fill in our swap method.

void BasicApp::swap(float& x, float& y)
{
  float temp = x;
  x = y;
  y = temp;
}

Now we'll begin writing up the selection code. Add the following to performSelection:

float left = first.x, right = second.x;
float top = first.y, bottom = second.y;
 
if (left > right)
  swap(left, right);
 
if (top > bottom)
  swap(top, bottom);

We unpack the first and second vectors into four float values representing the four corners of our selection box. Then we make sure the rectangle is oriented correctly. Our selection rectangle could be drawn in reverse order if the user clicks and then moves the mouse up or left. For our purposes, we always want the points organized so that the lowest values are top and left.

After that we want to check our selection rectangle's area. Our current selection method will fail if the rectangle is too small.

if ((right - left) * (bottom - top) < 0.0001)
  return;

This determines the two side lengths of our rectangle and then uses them to check if the area is below 0.0001. In your own projects, it would be better to perform a standard ray scene query instead of simply returning. This would effectively overcome the limitations of our current method.

We will now perform the query itself. A plane-bounded volume list query uses a series of planes to enclose an area, and it returns any objects inside of that volume. We will create five planes to build our selection volume. To do this, we will create four rays that come straight out from the plane of the camera. This can be difficult to visualize, so here is a simple image to help clarify things:
ray_visual.png
You can probably see how these rays can be used to form the volume we're looking for now. The first thing we'll do is create the rays.

Ogre::Ray topLeft = mCamera->getCameraToViewportRay(left, top);
Ogre::Ray topRight = mCamera->getCameraToViewportRay(right, top);
Ogre::Ray bottomLeft = mCamera->getCameraToViewportRay(left, bottom);
Ogre::Ray bottomRight = mCamera->getCameraToViewportRay(right, bottom);

It could be argued that the getCameraToViewportRay has a somewhat confusing name. You might have thought of the camera as being at a single point, but the camera is actually modeled as a frustum (a pyramid with a flat top). Therefore, the method returns rays that are all perpendicular to the screen and not rays that shoot out from a single point like cone. This actually turns out to be what you want the majority of the time. That's what makes it a good model.

The next thing we do is create the five planes we are going to use.

Ogre::Plane frontPlane, topPlane, leftPlane, bottomPlane, rightPlane;

frontPlane = Ogre::Plane(
  topLeft.getOrigin(),
  topRight.getOrigin(),
  bottomRight.getOrigin());

topPlane = Ogre::Plane(
  topLeft.getOrigin(),
  topLeft.getPoint(10),
  topRight.getPoint(10));

leftPlane = Ogre::Plane(
  topLeft.getOrigin(),
  bottomLeft.getPoint(10),
  topLeft.getPoint(10));

bottomPlane = Ogre::Plane(
  bottomLeft.getOrigin(),
  bottomRight.getPoint(10),
  bottomLeft.getPoint(10));

rightPlane = Ogre::Plane(
  topRight.getOrigin(),
  topRight.getPoint(10),
  bottomRight.getPoint(10));

As I'm sure you remember from your school days, three points in space uniquely define an infinite plane. To form the front plane, we take the origins of the topLeft, topRight, and bottomRight rays as our points. Notice the order determines which way the plane faces just like it does with the vertices in an index buffer. The order we've used makes sure the front plane faces away from the camera. We want all of the planes facing towards the inside of our volume.

Using the points 10 units down the rays to define the other planes is arbitrary. The only thing that matters is that they are all the same distance down the ray. We could have used a point 1 unit down the rays or a point 1000000 units down the ray. They all would have defined the same infinite plane.

Next we need to put our planes into a PlaneBoundedVolume, then place that into a PlaneBoundedVolumeList so that they can be used by our query.

Ogre::PlaneBoundedVolume vol;

vol.planes.push_back(frontPlane);
vol.planes.push_back(topPlane);
vol.planes.push_back(leftPlane);
vol.planes.push_back(bottomPlane);
vol.planes.push_back(rightPlane);

Ogre::PlaneBoundedVolumeList volList;
volList.push_back(vol);

This may seem like overkill, but that is because this system is designed to handle much more complicated volume queries than the simple one we are constructing. We are now ready to execute the actual query.

mVolQuery->setVolume(volList);
Ogre::SceneQueryResult result = mVolQuery->execute();

We pass our PlaneBoundedVolumeList to our query, and then we call execute. Now we will iterate through the results and select any valid movables we've found. But first we need to call deselectObjects, which we will write in just a moment.

deselectObjects();

Ogre::SceneQueryResultMovableList::iterator it;
for (it = result.movables.begin(); it != result.movables.end(); ++it)
  selectObject(*iter);

That's the entire performSelection method. You can also use query flags with volume queries. Now let's fill in our two missing methods. Add the following to your implementation:

void BasicApp::deselectObjects()
{
  std::list<Ogre::MovableObject*>::iterator it;

  for (it = mSelected.begin(); it != mSelected.end(); ++it)
    (*it)->getParentSceneNode()->showBoundingBox(false);
}

This iterates through our list of selected objects and turns off their bounding boxes. Notice the important parenthesis around the dereference of it, without them we would be be dereferencing the parent scene node pointer instead of the movable object pointer. Lastly, we will fill in our selectObject method.

void BasicApp::selectObject(Ogre::MovableObject* obj)
{
  obj->getParentSceneNode()->showBoundingBox(true);
  mSelected.push_back(obj);
}

Compile and run the application. You can now box select objects in your scene.

A Final Note About Selection

You have probably noticed that selection relies on the bounding box of the objects in our scene and not on the mesh itself. This means that scene querys will always be too accepting in what they consider a hit. Don't worry, there are ways of performing pixel perfect raycasts, but they involve tradeoffs in performance. You can read Raycasting to the polygon level for more information on implementing this feature within Ogre. If you are integrating a physics library like Newton, then it should also provide methods for performing these more accurate raycasts.

Learning the techniques in these tutorials was not a waste, though. Pixel perfect raycasting is very performance intensive, so it should not be used sparingly. One of the more common techniques actually involves performing a bounding box query like we've learned, and then performing a second, more accurate raycast to determine exactly where the hit was. You will find many examples of these multi-tiered approaches to programming simulations. It is an important design pattern to begin to think about. Very similar methods are used to get better performance when dealing with complicated path-finding problems.

Conclusion

The first part of this tutorial introduced the concept of a manual object. This is one of the simpler ways to manually create a mesh in Ogre. We created a new class that inherited from ManualObject and used it to create our selection rectangle. We also mentioned how to set the projection and view matrices of our object so that it was displayed as a 2D object.

The second part of the tutorial focused on setting up and running a plane-bounded volume query. This involved defining a series of planes that would create an enclosed volume in our scene based on the placement of our selection rectangle - a process sometimes referred to as box selection.

Exercises

Easy

  1. Add ninjas to your robot army. Then make it so your volume query will select either ninjas or robots depending on the current mode.

Intermediate

  1. Create an interface with CEGUI that displays an icon for each selected entity.
  2. Allow the user to command the selected units to walk in place. Have everyone else Idle.

Difficult

  1. Try to change the shape of the SelectionBox and implement the corresponding PlaneBoundedVolumeQuery (i.e. change the SelectionBox into a SelectionTriangle).

Advanced

  1. Further extend your interface from the Intermediate exercise to allow the user to select subgroups of entities by clicking on the GUI instead of box selecting units in the scene. Make it so they can select multiple entities by holding down shift while clicking on either the interface or the unit in the scene.

Full Source

The full source for this tutorial is here.

Next

Intermediate Tutorial 5


Alias: Intermediate_Tutorial_4

<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.