Beginner Tutorial 3: Terrain, Sky, Fog, and the Root object
Original version by Clay Culver
Note: This was written for PyOgre 1.0.5, and is known to work with it. If this is not the current version of PyOgre and something is not working or you do not understand anything that is contained here, please post your questions to the PyOgre Forum.
Table of contents
- Getting Started
- The Root Object and SceneManager Creation
- 1. Definitions
- 2. Fair Use Rights
- 3. License Grant
- 4. Restrictions
- 5. Representations, Warranties and Disclaimer
- 6. Limitation on Liability.
- 7. Termination
- 8. Miscellaneous
This tutorial assumes you have knowledge of Python programming and you have already installed PyOgre. This tutorial builds on the material covered in PyOgre Beginner Tutorial 2.
In this tutorial we will be exploring how to manipulate terrain, sky, and fog in your Ogre applications. After this tutorial you should understand the differences between Skyboxes, Skyplanes, and Skydomes and be able to use them. You will also know the difference between the different types of fog, and how to use it.
As you go through the tutorial you should be slowly adding code to your own project and watching the results as we build it. If you are having problems you can download the source here.
As with the previous tutorials, we will be using a pre-constructed code base as our starting point. Create a file named beginner_3.py:
# this code is in the public domain from pyogre import ogre import SampleFramework class TutorialApplication(SampleFramework.Application): def _createScene(self): pass def _chooseSceneManager(self): pass if __name__ == '__main__': ta = TutorialApplication() ta.go()
Program controls: Use the WASD keys to move, and the mouse to look around. The Escape key exits the program. Note that you will need the "terrain.cfg" file in the current directory. This can be obtained from PyOgre's demo folder.
In this demo we will be rendering Terrain in Ogre. To do this we need to set the SceneManager to the TerrainSceneManager instead of the default one that the ExampleApplication sets up for us. Find the _chooseSceneManager function, and add the following code:
self.sceneManager = self.root.getSceneManager(ogre.ST_EXTERIOR_CLOSE)
The Root object (self.root is an instance of Root) is the "core" Ogre object. You can see a UML diagram of the relationships between Ogre objects here. You have now seen almost all of these objects in practice, with the exception of the RenderSystem. In this chunk of code, we are telling the Root node that we want a SceneManager of the ST_EXTERIOR_CLOSE type. The Root object then queries the SceneManagerEnumerator to find the SceneManager of the type you requested and returns it.
After your application is set up, you rarely ever have to deal with Ogre's Root object, and you don't ever interact with the SceneManagerEnumerator directly.
I would like to go ahead and talk to you about SceneManager creation and storage to save you the confusion in the future. SceneManagers are not Singletons. You can create as many of them as you want, and unlike SceneNodes/Lights/etc you can create them directly with a "sm = ogre.SceneManager()" statement (you do not have to use Root's getSceneManager method). You can have multiple SceneManagers populated with multiple separate geometries and entities at the same time. You can swap between any of these at any time by recreating the Viewport (this is covered in PyOgre Intermediate Tutorial 4) or display multiple SceneManagers at the same time using multiple Viewports (this will be covered in an advanced tutorial).
Why do we use the getSceneManager function instead of creating our SceneManager object manually? Well, the plugin system in Ogre gives us a great deal of flexibility when working with single SceneManagers. There are only a few scene types defined in the SceneType enum. Until now, the SampleFramework.Application has been choosing ST_GENERIC as our SceneManager. You might think that this is the base SceneManager class, but as long as you did not fiddle with the plugins.cfg file, then that's not what you have been using! The OctreeSceneManager registers itself as ST_GENERIC and overrides the base SceneManager class if you are using the OctreeSceneManager plugin. The OctreeSceneManager uses a system of culling items that are not actually on the Scene (and thus it's generally faster than the regular SceneManager). If you removed the OctreeSceneManager plugin from your plugins.cfg you would *probably* be using the basic SceneManager when you request ST_GENERIC.
This is pretty neat, but you can run into some problems. Especially considering the fact that you aren't guaranteed to get the SceneManager you ask for (some plugins conflict and try to overwrite the same ST_* type), but this rarely happens in practice. Normally if you need to do advanced things with SceneManagers then you should create them yourself. When you do standard things with the SceneManagers you can simply use the getSceneManager function. The last thing to note is that calling getSceneManager with a specific SceneType returns the same SceneManager over and over. That is, the SceneManagerEnumerator is not a factory class, and you should never delete the SceneManager that's returned.
Now that we have that cleared up, time to actually create Terrain. The base SceneManager defines the setWorldGeometry method, which subclasses use for most scene creation purposes. With the TerrainSceneManager class, it expects a filename from which to load a terrain configuration. Find the createScene member function and add this line of code:
Run your program. It's that easy. You might want to set the Camera to start in a place that's over the terrain if it bothers you that its initial position is under the terrain.
There are many options in the terrain.cfg file, and I am only going to cover the most basic for changing the images used to generate the terrain. A more detailed explanation of the terrain config file can be found here. One major thing to note about the TerrainSceneManager is that it has been designed with paging functionality in mind, but it has not been implemented yet. Paging terrain is a system where the terrain is broken into chunks, and only displayed when they user would be able to see it. This allows you define a huge world and be able to use it without dropping the framerate by a significant amount. There is an Ogre plugin that does this: Paging Scene Manager.
The TerrainSceneManager uses Heightmaps to generate terrain. You can specify the heightmap you want to use by setting the "Heightmap.image" property. You can set the texture that is used for the terrain by setting the WorldTexture property. The terrain scene manager also allows you to specify a "DetailTexture" property, which is interlaced with the WorldTexture to make the terrain look more realistic. You should find each of the images currently specified by terrain.cfg and take a look at them (they should be in the Media/materials/textures folder).
Details on how to create heightmaps have been discussed ad nauseum on the forums. Search for heightmap and you are sure to find something that you are looking for.
We just spent the entire previous tutorial going over lights and shadows, but the bad news is it's not easy to get this to work in the TerrainSceneManager. At a later date I may devote a full tutorial to getting this working with the TerrainSceneManager. For now, just know that it's much easier to take the detail texture and add lighting effects to it than it is to get standard lighting working. We will also go over a way to do "Fake Darkness" in the Fog section. If you are looking to use lighting with the terrain, you should look into using the Paging Scene Manager, since it has better support for these features.
Ogre provides three different types of sky: SkyBoxes, SkyDomes, and SkyPlanes. We will take a look at each of these in detail.
A SkyBox is basically a giant cube that surrounds all of the objects in the scene. The best way to describe it is to just show it to you. Add this line of code to createScene:
Compile and run the program. Neat huh? (Note the SkyBox is grainy because the actual texture is low resolution; a higher resolution SkyBox would look much better.) There are several useful parameters for SkyBoxes that we can set when calling setSkyBox. The first option is whether or not to enable the SkyBox. If you want to later disable the SkyBox simply call 'mSceneMgr->setSkyBox( false, "" );'. The second parameter is the material script to use for the SkyBox.
The third paramater and fourth paramaters to setSkyBox are fairly important to understand. The third parameter sets the distance that the SkyBox is away from the Camera, and the fourth parameter sets whether or not the SkyBox is drawn before the rest of the scene or afterwards. So, lets see what happens when you change the distance parameter for the SkyBox from the default 5000 units to something very close:
self.sceneManager.setSkyBox(True, "Examples/SpaceSkyBox", 10)
Nothing changed! This is because the fourth parameter that controls whether to draw the SkyBox first or not is set to true by default. If the SkyBox is drawn first, then anything rendered afterwards (like our Terrain) will be drawn on top of it, thus making the SkyBox always appear in the background. (Note that you shouldn't set the distance above to be closer than the near clip distance on the Camera or it will not be shown!) It is not actually desirable to draw the SkyBox first, because the full thing is rendered. When you draw it last, only the visible portions are drawn, which will provide a modest speed improvement. So, lets try setting our SkyBox to be drawn last:
self.sceneManager.setSkyBox(True, "Examples/SpaceSkyBox", 5000, False)
Again, this looks just like it did before, but now the parts of the SkyBox that are not visible won't be rendered. There is one thing you have to be careful about when using this technique though. If you set the SkyBox to be too close, you could be cutting part of the scene geometry off. For example, try this:
self.sceneManager.setSkyBox(True, "Examples/SpaceSkyBox", 100, False)
As you can see now, the terrain "pokes through" the SkyBox. Deffinatly not what we want. If you use SkyBoxes in your application you will have to decide how you want to use them. The speedup you get from rendering the SkyBox after the terrain is very modest, and you have to be careful not to obscure your geometry (unless that is what you are going for). Generally speaking, leaving everything past the second parameter as default is a very safe choice.
SkyDomes are very similar to SkyBoxes, and you use them by calling setSkyDome A giant cube is created around the Camera and rendered onto, but the bigest difference is the texture is "projected" onto the SkyBox in a spherical manner. You are still looking at a cube, but it looks as if the texture is wrapped around the surface of a sphere. The primary drawback to this method is that the bottom of the cube will be untextured, so you always need to have some type of terrain that hides the base.
The example texture that Ogre provides for SkyDomes will let you see this clearly. Clear out the setSkyBox call from createScene and add this code instead:
self.sceneManager.setSkyDome(True, "Examples/CloudySky", 5, 8)
When you run this, move the Camera to the dead center of the terrain and move the Camera so that's positioned fairly close to the surface of the terrain (this looks the best). After looking at this, hit the R button to switch to the mesh view. As you can see, we are still looking at a cube (without the base), but it looks as if the clouds are wrapped around a sphere at the top. (Also note that the movement of the clouds is a property of the "Examples/CloudySky" material, not of SkyDomes in general.)
The first two paramaters of setSkyDome are the same as setSkyBox, and you can turn the SkyDome off by calling 'mSceneMgr->setSkyDome( false, "" );'. The third parameter is the curvature used for the SkyDome. The API reference suggests using values between 2 and 65; lower for better distance effect, but higher values for less distortion and a smoother effect. Try setting the third paramater to 2 and 65 and look at the difference. The distance effect that the API reference was referring to can be clearly seen in these screenshots. This is setting the curvature to 2:
This is setting the curvature to 64:
The fourth parameter is the number of times the texture is tiled, which you will need to tweak depending on the size of your texture. Be sure to note that this parameter is a Real value (floating point) and not an integer. You can tile it 1.234 times, if that's what looks good for your application. The fifth and sixth parameters are distance and drawFirst, respectively, which we have already covered in the SkyBox section.
SkyPlanes are very different from SkyBoxes and SkyDomes. Instead of a cube to render the sky texture on, we use just a single plane. (Note for all of the following SkyPlane configurations you need to be somewhere towards the middle of the terrain and close to the ground.) Clear out all SkyDome code from createScene. The first thing we are going to do is create a plane, and face it downwards. The setSkyPlane method that we will be calling does not have a distance parameter like SkyBox and SkyPlane. Instead that parameter is set in the d variable of Plane:
plane = ogre.Plane((0, -1, 0), -1000)
Now that we have the plane defined, we can create the SkyPlane. Note that the fourth parameter is the size of the SkyPlane (in this case 1500x1500 units) and the fifth parameter is how many times to tile the texture:
self.sceneManager.setSkyPlane(True, plane, "Examples/SpaceSkyPlane", 1500, 75)
Run the program. There are two problems with the SkyPlane this creates here. First of all, the texture that is used is too low resolution, and it doesn't tile well. That could be fixed by simply creating a good, high resolution sky texture that tiles well. However, the primary problem with this technique is that if you look towards the horizon, you can see where the SkyPlane ends. Even if you had a good texture, it would not look good at all if you can see to the horizon. This basic use of a SkyPlane is really only useful when you have high walls (or hills) all around the viewpoint. Using a SkyPlane in that situation would be considerably less graphics intensive than creating a full SkyBox/SkyDome.
Fortunently, that is not all we can do with a skyplane. The sixth parameter to the skyplane is the familiar "renderFirst" parameter which we have already covered in the SkyBox and SkyDome sections. The seventh parameter allows you to specify the curvature of the SkyPlane, so that we are no longer using a plane, but a curved surface instead. We also have to now set the number of x and y segments used to create the SkyPlane (initially the SkyPlane was one big square, but if we want curvature we need to have the plane made up of smaller squares). The eighth and ninth parameters to the function are the number of x and y segments, respectively:
self.sceneManager.setSkyPlane(True, plane, "Examples/SpaceSkyPlane", 1500, 50, True, 1.5, 150, 150)
Run the application. Now our SkyPlane looks much better, though again the tiling could use some work. You could also use this with the cloud material instead:
self.sceneManager.setSkyPlane(True, plane, "Examples/CloudySky", 1500, 40, True, 1.5, 150, 150)
Run the application. The motion of the clouds and the way it is tiled seems to make it look slightly worse than a SkyDome, especially when you get near the edge of the Terrain and look out onto the horizon.
One other note, you can clear the SkyPlane by calling 'sceneManager.setSkyPlane(False, ogre.Plane(), "")'
Which sky to use depends entirely on your application. If you have to see all around you, even in the negative y direction, then really your only real choice is to use a SkyBox. If you have terrain, or some kind of floor which blocks the view of the negative y direction, then using a SkyDome seems to give more realistic results. For areas where you cannot see to the horizon (such as a valley surrounded by mountains on all sides, or the inner courtyard of a castle), a SkyPlane will give you very good looking results for very little GPU costs. The primary reason to use a SkyPlane, as we will see in the next section, is because it plays nicely with fog effects.
These are only suggestions. For your application you should experiment and use whatever looks the best.
Fog in Ogre is very easy to use. There is one caveat that you need to know about before you try to use fog in your program. When you use the TerrainSceneManager, you must be careful to call the setFog function before the setWorldGeometry function. (In other SceneManagers it generally doesn't matter). Depending on which is called first, a different vertex program will be chosen to create the fog and terrain.
Before we get started, clear out all contents of the createScene function except for the call to setWorldGeometry.
The most important thing to know about setting fog is that it doesn't actually create a fog entity in empty space as you might imagine you would. Instead, fog is merely a filter applied to whatever objects you are currently looking at. This has some interesting implications. The most relevant of which is that when you stare off into nothingness (IE when you are not looking at an object), you do not see fog. In fact, you only see whatever the viewport background color is. So, in order to have fog look correct, we have to set the background to whatever the fog color currently is.
There are two basic types of fog: linear and exponential. Linear fog gets thicker in a linear fashion, while exponential fog gets thicker exponentially (every distance unit the fog thickness increases by more than it did the previous distance unit). It's easier to see the difference than to explain it, so on to the examples.
The first type of fog we will look at is linear, and it's the easiest fog to understand. The first thing we are going to do after we call setWorldGeometry is set the viewport's background color. We could do this by overriding the createViewport function (like we did in the last tutorial), but sometimes we need to set it without recreating the viewport every time. This is how we do that:
fadeColour = (0.9, 0.9, 0.9) self.renderWindow.getViewport(0).backgroundColour = fadeColour
You could use the "numViewports" attributes to get the number of viewports and iterate through them if you have more than one viewport, but since this is rarely the case (and since we know we only have one viewport), we can just get the viewport directly. Once we set the background color, we can now create the fog:
self.sceneManager.setWorldGeometry("terrain.cfg") self.sceneManager.setFog(ogre.FOG_LINEAR, fadeColour, 0, 50, 500)
The first parameter to the setFog function is the type of fog (in this case, linear). The second parameter to setFog is the color of the fog we are using (in this case a very very light grey). The third parameter is not used in linear fog. The fourth and fifth parameters specify the range where the fog gets thicker. In this case we have set the fog starting point to be 50 and the stopping point to be 500. This means that from 0 to 50 units in front of the camera, there is no fog. From 50 to 500 units away from the Camera, the fog gets thicker in a linear fashion. At 500 units away from the Camera, you can no longer see anything other than fog. Compile and run the application.
Another type of fog that we can use is exponential fog. Instead of setting starting and stopping bounds for fog, we instead set a density for the fog (the fourth and fifth parameters are unused). Replace the previous call to setFog with this:
self.sceneManager.setFog(ogre.FOG_EXP, fadeColour, 0.005)
Compile and run the application. This creates a different look to the fog that is generated. There is also another exponential fog function which is more severe than the first one (IE fog gets much thicker each unit you move away from the Camera compared to the first fog function). Note that there is more fog-per-density when using FOG_EXP2. Replace the previous call to setFog with this:
self.sceneManager.setFog(ogre.FOG_EXP, fadeColour, 0.003)
Compile and run the application again. Fog is mostly interchangeable between the three functions that Ogre provides. You should experiment with all three fog functions and see which looks best in your application.
You can run into some interesting problems when trying to use fog with a SkyBox and SkyDome. Since SkyDomes and SkyBoxes are just cubes, using them with fog is problematic since fog works in a spherical manner. Clear out the contents of the createScene method. If we cleverly choose our SkyDome and fog parameters, we can see the problem directly:
self.sceneManager.setWorldGeometry("terrain.cfg") fadeColour = (0.9, 0.9, 0.9) self.sceneManager.setFog(ogre.FOG_LINEAR, fadeColour, 0, 50, 515) self.renderWindow.getViewport(0).backgroundColour = fadeColour self.sceneManager.setSkyDome(True, "Examples/CloudySky", 5, 500)
Compile and run the application. If you move the camera around, you will see different portions of the SkyDome poke through the fog depending on what part of the SkyDome you are looking at (notice the blue coming through on the sides, but not in the middle): http://www.idleengineer.net/images/beginner03_fogbox.png
This is certainly not what we want. Another option is to use a SkyPlane instead. Replace the code in createScene with this:
self.sceneManager.setWorldGeometry("terrain.cfg") fadeColour = (0.9, 0.9, 0.9) self.sceneManager.setFog(ogre.FOG_LINEAR, fadeColour, 0, 50, 515) self.renderWindow.getViewport(0).backgroundColour = fadeColour self.sceneManager.setSkyDome(True, "Examples/CloudySky", 5, 500)
This looks correct. If we look upwards we can see sky (which is the case in real life if the fog is just right), but it's not poking through in funny ways. No matter if you use curavature or not, this solves our problem of the user being able to see the horizon where the SkyPlane does not look right.
You may not want to use sky at all when you set fog, because if the fog is thick enough you cannot see the sky anyway. The trick with fog that we described above allows us to perform a nifty graphic hack that can be useful in some cases. Instead of setting the fog to a bright color, lets set it to be very dark and see what happens (note we have set the SkyPlane to be only 10 units away from the camera, which is before the fog sets in):
self.sceneManager.setWorldGeometry("terrain.cfg") fadeColour = (0.1, 0.1, 0.1) self.renderWindow.getViewport(0).backgroundColour = fadeColour self.sceneManager.setFog(ogre.FOG_LINEAR, fadeColour, 0.0, 10, 150) plane = ogre.Plane((0, -1, 0), -10) self.sceneManager.setSkyPlane(True, plane, "Examples/SpaceSkyPlane", 100, 45, True, 0.5, 150, 150)
Compile and run the application. This is what we get: http://www.idleengineer.net/images/beginner03_darkness.png
Not too terrible. Of course, once you are able to, you should use proper lighting instead of this hack, but it does show the flexibility of fog, and some of the interesting things you can do with the engine. Using black fog might also be an interesting way to do a "blindness" or "darkness" spell effect if you are writing a game that uses first-person view.<HR>
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.
- "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.
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.
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.
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.
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.
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.
- 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.
- 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.