using System;
using Chernobyl;
using Chernobyl.Collections.Generic;
using Chernobyl.Collections.Generic.Event;
using Chernobyl.Dependency;
using Chernobyl.Graphics;
using Chernobyl.Graphics.Drawing;
using Chernobyl.Graphics.Material.Shader.Parameters;
using Chernobyl.Graphics.Material.Shader.Parameters.Semantics;
using Chernobyl.Graphics.Polygon;
using Chernobyl.Graphics.Polygon.Buffers;
using Chernobyl.Graphics.Texture;
using Chernobyl.Mathematics;
using Chernobyl.Mathematics.Vectors;
using Chernobyl.Measures;
using Chernobyl.Plugin;
using Chernobyl.StateMachine;
namespace Sample
{
///
/// A plug-in to Chernobyl! Here are the steps to creating a plug-in:
///
/// 1) Create a type that implements the interface IPlugin. You don't have
/// to implement any methods or properties from this interface as it is
/// an empty interface (for now). See the documentation on the IPlugin
/// interface for more details.
/// 2) You must put your library somewhere where the .NET runtime can locate
/// it using Assembly.Load(string). This is not a Chernobyl thing but a
/// .NET thing. If your unsure of where to put the library, then throw it
/// in the appropriate bin directory with the rest of the Chernobyl libraries.
/// You can have Visual Studio due this for you automatically by importing
/// the file "common.targets" into your Visual Studio file. Just open
/// your .csproj sample project and place the import line after the
/// property groups that deal with debug/release configurations (open
/// this plug-in's .csproj file in a text editor to see an example of this).
/// Go here for more information on this topic:
/// http://msdn.microsoft.com/en-us/library/92x05xfs.aspx
/// For more information on how the .NET runtime locates assemblies, go
/// here: http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx
/// 3) Add the full name of your assembly to the .config file of the .NET
/// executable that runs your project. Chernobyl has project to get it
/// (and you) started called "Chernobyl.Run". In this project you should
/// see a file called "app.config". Open that file up and locate the
/// "appSettings" section. Below that, you should see an "add" element
/// with the name of "Chernobyl.Plugins". The value should look like a
/// bunch of crazy shit. That crazy shit are the FULL names of each of
/// the Chernobyl plug-ins separated by semi-colons (;). For example,
/// the Input plug-in forChernobyl looks like this:
///
/// Chernobyl.Input, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
///
/// You'll need to add your plug-in to this list. This plug-in is called:
///
/// Sample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
///
/// This plug-in should be in that list already. The method for loading
/// plug-ins will probably change slightly in the future but, for now,
/// use this method. Also, .config files and full assembly names are a
/// .NET thing so check out the following for information on them, respectively:
/// http://msdn.microsoft.com/en-us/library/1xtk877y%28v=VS.80%29.aspx
/// http://msdn.microsoft.com/en-us/library/yfsftwz6.aspx (see the section "Specifying Assembly Names" specifically)
///
public class MyChernobylPlugin : IPlugin
{
///
/// Initializes a new instance of the class.
/// Chernobyl plug-ins must have either a constructor that takes no
/// parameters or a constructor that takes an
/// instance.
///
/// The instance that gives out services for use
/// by this type and takes services from this type for use by other systems.
public MyChernobylPlugin(IEventCollection services)
{
Services = services;
// The inject call ensures that properties marked with an "Inject"
// attribute are properly resolved. Basically, the inject call will
// go through all of the properties in the class that are marked with
// "Inject" or "Provide". Those that are marked with "Inject" will
// be given the service that matches their type. It is about the same
// as doing this for the ConfigurationState property:
//
// ConfigurationState = services.OfType().First();
//
// I say about because the inject call also ensures that dependencies
// are handled correctly. It also ensures the service actually exists
// before giving the property anything. If a property is marked with
// a "Provide" attribute then that property is being "provided" to
// the IEventCollection instance as a service that can be
// used by other systems. It is the same as doing this:
//
// services.Add(this.SomeService);
services.Inject(this);
}
///
/// The that represents the configuration or
/// initialization state of the application.
///
[Inject]
public IConfigurationState ConfigurationState
{
set { value.Left += ConfigurationEnded; }
}
///
/// An event handler that is invoked when the state of the application
/// has left the configuration state.
///
/// The sender of the event.
/// The instance
/// containing the event data.
void ConfigurationEnded(object sender, EventArgs e)
{
// now that everything has been configured, we can create/load content
//////////////////////////////////////////////////////////////////////////
// 2D RENDERING
//////////////////////////////////////////////////////////////////////////
// create the sprite. "radiation_warning_symbol" is the name of the
// XNA resource. This XNA resource has loaded into the Chernobyl.Xna
// project.
MySprite = new Sprite(Services, "radiation_warning_symbol");
// move the sprite up and to the right
MySprite.Translate(200.0f, 400.0f);
// attach the sprite to the draw graph. The Draw2D is just a "marker"
// to a location in a main draw graph where we can render
MySprite.DrawableParent = TheScene.Draw2D;
//////////////////////////////////////////////////////////////////////////
// 3D RENDERING
// from: http://www.switchonthecode.com/tutorials/creating-a-textured-box-in-xna
//////////////////////////////////////////////////////////////////////////
const float shapeSize = 3;
Vector3 shapePosition = Vector3.Zero;
// create the points of the cube
Vector3 topLeftFront = shapePosition + new Vector3(-1.0f, 1.0f, -1.0f) * shapeSize;
Vector3 bottomLeftFront = shapePosition + new Vector3(-1.0f, -1.0f, -1.0f) * shapeSize;
Vector3 topRightFront = shapePosition + new Vector3(1.0f, 1.0f, -1.0f) * shapeSize;
Vector3 bottomRightFront = shapePosition + new Vector3(1.0f, -1.0f, -1.0f) * shapeSize;
Vector3 topLeftBack = shapePosition + new Vector3(-1.0f, 1.0f, 1.0f) * shapeSize;
Vector3 topRightBack = shapePosition + new Vector3(1.0f, 1.0f, 1.0f) * shapeSize;
Vector3 bottomLeftBack = shapePosition + new Vector3(-1.0f, -1.0f, 1.0f) * shapeSize;
Vector3 bottomRightBack = shapePosition + new Vector3(1.0f, -1.0f, 1.0f) * shapeSize;
// create the normals
Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f) * shapeSize;
Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f) * shapeSize;
Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f) * shapeSize;
Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f) * shapeSize;
Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f) * shapeSize;
Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f) * shapeSize;
// create the texture coordinates
Vector2 textureTopLeft = new Vector2(0.5f * shapeSize, 0.0f * shapeSize);
Vector2 textureTopRight = new Vector2(0.0f * shapeSize, 0.0f * shapeSize);
Vector2 textureBottomLeft = new Vector2(0.5f * shapeSize, 0.5f * shapeSize);
Vector2 textureBottomRight = new Vector2(0.0f * shapeSize, 0.5f * shapeSize);
// create the cube vertices
VertexTextureNormal[] shapeVertices = new VertexTextureNormal[36];
// Front face.
shapeVertices[0] = new VertexTextureNormal(topLeftFront, textureTopLeft, frontNormal);
shapeVertices[1] = new VertexTextureNormal(bottomLeftFront, textureBottomLeft, frontNormal);
shapeVertices[2] = new VertexTextureNormal(topRightFront, textureTopRight, frontNormal);
shapeVertices[3] = new VertexTextureNormal(bottomLeftFront, textureBottomLeft, frontNormal);
shapeVertices[4] = new VertexTextureNormal(bottomRightFront, textureBottomRight, frontNormal);
shapeVertices[5] = new VertexTextureNormal(topRightFront, textureTopRight, frontNormal);
// Back face.
shapeVertices[6] = new VertexTextureNormal(topLeftBack, textureTopRight, backNormal);
shapeVertices[7] = new VertexTextureNormal(topRightBack, textureTopLeft, backNormal);
shapeVertices[8] = new VertexTextureNormal(bottomLeftBack, textureBottomRight, backNormal);
shapeVertices[9] = new VertexTextureNormal(bottomLeftBack, textureBottomRight, backNormal);
shapeVertices[10] = new VertexTextureNormal(topRightBack, textureTopLeft, backNormal);
shapeVertices[11] = new VertexTextureNormal(bottomRightBack, textureBottomLeft, backNormal);
// Top face.
shapeVertices[12] = new VertexTextureNormal(topLeftFront, textureBottomLeft, topNormal);
shapeVertices[13] = new VertexTextureNormal(topRightBack, textureTopRight, topNormal);
shapeVertices[14] = new VertexTextureNormal(topLeftBack, textureTopLeft, topNormal);
shapeVertices[15] = new VertexTextureNormal(topLeftFront, textureBottomLeft, topNormal);
shapeVertices[16] = new VertexTextureNormal(topRightFront, textureBottomRight, topNormal);
shapeVertices[17] = new VertexTextureNormal(topRightBack, textureTopRight, topNormal);
// Bottom face.
shapeVertices[18] = new VertexTextureNormal(bottomLeftFront, textureTopLeft, bottomNormal);
shapeVertices[19] = new VertexTextureNormal(bottomLeftBack, textureBottomLeft, bottomNormal);
shapeVertices[20] = new VertexTextureNormal(bottomRightBack, textureBottomRight, bottomNormal);
shapeVertices[21] = new VertexTextureNormal(bottomLeftFront, textureTopLeft, bottomNormal);
shapeVertices[22] = new VertexTextureNormal(bottomRightBack, textureBottomRight, bottomNormal);
shapeVertices[23] = new VertexTextureNormal(bottomRightFront, textureTopRight, bottomNormal);
// Left face.
shapeVertices[24] = new VertexTextureNormal(topLeftFront, textureTopRight, leftNormal);
shapeVertices[25] = new VertexTextureNormal(bottomLeftBack, textureBottomLeft, leftNormal);
shapeVertices[26] = new VertexTextureNormal(bottomLeftFront, textureBottomRight, leftNormal);
shapeVertices[27] = new VertexTextureNormal(topLeftBack, textureTopLeft, leftNormal);
shapeVertices[28] = new VertexTextureNormal(bottomLeftBack, textureBottomLeft, leftNormal);
shapeVertices[29] = new VertexTextureNormal(topLeftFront, textureTopRight, leftNormal);
// Right face.
shapeVertices[30] = new VertexTextureNormal(topRightFront, textureTopLeft, rightNormal);
shapeVertices[31] = new VertexTextureNormal(bottomRightFront, textureBottomLeft, rightNormal);
shapeVertices[32] = new VertexTextureNormal(bottomRightBack, textureBottomRight, rightNormal);
shapeVertices[33] = new VertexTextureNormal(topRightBack, textureTopRight, rightNormal);
shapeVertices[34] = new VertexTextureNormal(topRightFront, textureTopLeft, rightNormal);
shapeVertices[35] = new VertexTextureNormal(bottomRightBack, textureBottomRight, rightNormal);
// now we put the vertices into a MeshElementBuffer; by doing this
// we are giving the vertices to the graphics API (XNA, DirectX, OpenGL, etc)
// Note that we could use the MeshElementBuffer constructor here, but
// the MeshElementBuffer class has Create methods which simplify the
// creation of the buffer. It is important that you try to share
// buffers whenever possible as it will help performance (for both
// drawing and memory consumption).
IBuffer meshElementBuffer = new Mesh(Services, shapeVertices);
// now that we have our data, we need to setup up the mesh's draw graph
// The draw graph describes how the mesh will be rendered and what data
// to use when rendering it.
// Here we create a shader parameter, specifically, we create a color
// texture shader parameter. A shader parameter is responsible for
// setting shader parameters within a draw graph. There are different
// types of shader parameters within Chernobyl, all of them set different
// parameters. The ColorTextureParameter is responsible for setting the
// texture that is used for color (some textures may be used for
// other purposes such as a bump/normal map or for shadows).
IShaderParameter colorTextureParam = new ColorTextureParameter(Services, new Texture2D(Services, "radiation_warning_symbol"));
// Here we create a DrawBuffer. A DrawBuffer is an IDrawable that is
// responsible for specifying which buffer to use when a mesh is to
// be drawn. Note that we use a factory to create the IDrawBuffer.
// It cannot be guaranteed that the factory will return immediately
// so this code is not safe but does show the basic idea.
// TODO: make this code safer.
IDrawBuffer drawBuffer = null;
DrawBufferFactory.Create(
(factorySender, creationResult) => drawBuffer = creationResult.CreatedInstance,
new Pair, IBuffer>(Services, meshElementBuffer));
// Now we create the render which is responsible for telling the
// graphics API to render the object with the specified data (textures,
// mesh elements, etc). It also tells the graphics API where to render
// the object, at what orientation, and the type of primitive to render.
IRender render = new Render(Services, PrimitiveType.TriangleList);
// now we hook up all of the IDrawables into the draw graph so that they
// will be properly rendered
Drawable.MakeParentChild(TheScene.Draw3D, colorTextureParam, drawBuffer, render);
// rotate the cube to get a better view (note that we rotate the render
// which holds transformation information; i.e. it is an ITransform)
render.Pitch((Degree)30.0f);
render.Yaw((Degree)10.0f);
// note that, if you prefer radians you can do something like this:
// render.Pitch((Radian)0.523598776);
// render.Yaw((Radian)0.174532925);
// Chernobyl requires you to cast to the appropriate value in order
// to prevent issues arising from the user thinking they are specifying
// degrees but are actually specifying radians. This helps prevent
// user errors.
// now move the cube up 3, to the right 5, and forward 50
render.Translate(3.0f, 5.0f, -50.0f);
// One thing to note: the Chernobyl Sprite uses a texture parameter,
// draw buffer, and render just like a 3D mesh does. In fact, a sprite is
// a mesh in that sense. The differences being that a sprite is added
// to the 2D section of drawing, the sprite makes creation of 2D
// objects easier, it creates a 2D collision volume for you, and does
// a few tricks to make them more performance optimal (such as sharing
// the MeshElementBuffer between all Sprites).
}
///
/// The main used be Chernobyl for rendering.
///
[Inject]
public Scene TheScene { get; set; }
///
/// The instance that we will use to create an .
///
[Inject]
public IDrawBufferFactory DrawBufferFactory { get; set; }
Sprite MySprite { get; set; }
IEventCollection Services { get; set; }
}
}