static void Main(string[] args) { //We choosed OpenGL because it is cross-platform and we only have the openGL shader //So please do not change this unless you change the shader ! IrrlichtDevice device = new IrrlichtDevice(DriverType.Direct3D9, new Dimension2D(640, 480), 32, false, true, true, false); //We set a new working directory device.FileSystem.WorkingDirectory = "../../medias"; device.OnEvent += new OnEventDelegate(device_OnEvent); //We set a basic caption string caption = "Irrlicht .NET CP Shaders and Particles Example"; //We set our handlers Driver = device.VideoDriver; Scene = device.SceneManager; //We have already seen that... The only special thing is the pointless emitter (what a funny name isn't it ?) //Which is detailed just down. Texture fire = Driver.GetTexture("fire.bmp"); ParticleSystemSceneNode particles = Scene.AddParticleSystemSceneNode(false, null, -1); particles.SetEmitter(new PointlessEmitter()); //particles.AddAffector(new PointlessAffector()); particles.SetMaterialTexture(0, fire); particles.SetMaterialType(MaterialType.TransparentAddColor); particles.SetMaterialFlag(MaterialFlag.Lighting, false); particles.ParticleSize = new Dimension2Df(50, 50); particles.ParticlesAreGlobal = false; particles = Scene.AddParticleSystemSceneNode(false, null, -1); particles.SetEmitter(new PointlessEmitter()); //particles.AddAffector(new PointlessAffector()); particles.SetMaterialTexture(0, fire); particles.SetMaterialType(MaterialType.TransparentAddColor); particles.SetMaterialFlag(MaterialFlag.Lighting, false); particles.ParticleSize = new Dimension2Df(50, 50); particles.Position = new Vector3D(0, 400, 0); particles.ParticlesAreGlobal = false; particles = Scene.AddParticleSystemSceneNode(false, null, -1); particles.SetEmitter(new PointlessEmitter()); //particles.AddAffector(new PointlessAffector()); particles.SetMaterialTexture(0, fire); particles.SetMaterialType(MaterialType.TransparentAddColor); particles.SetMaterialFlag(MaterialFlag.Lighting, false); particles.ParticleSize = new Dimension2Df(50, 50); particles.Position = new Vector3D(0, -400, 0); particles.ParticlesAreGlobal = false; //Here we only create 3 cubes and add a texture... Nothing exciting SceneNode cube1, cube2, cube3; cube1 = Scene.AddCubeSceneNode(40f, null, -1); cube2 = Scene.AddCubeSceneNode(40f, null, -1); cube3 = Scene.AddCubeSceneNode(40f, null, -1); cube1.SetMaterialTexture(0, Driver.GetTexture("rockwall.bmp")); cube2.SetMaterialTexture(0, Driver.GetTexture("rockwall.bmp")); cube3.SetMaterialTexture(0, Driver.GetTexture("rockwall.bmp")); //Here comes the fun... We create two low level shaders (taken from Irrlich's shader Example) and we set the base material as //Solid for the first one and transparent for the second int mat = Driver.GPUProgrammingServices.AddShaderMaterialFromFiles("opengl.vsh", "opengl.psh", OnShaderSet, MaterialType.Solid, 0); int mat2 = Driver.GPUProgrammingServices.AddShaderMaterialFromFiles("opengl.vsh", "opengl.psh", OnShaderSet, MaterialType.TransparentAddColor, 0); //And now we add both materials... Notice that no cast is needed because SetMaterialType //Has an overload especially made for shaders ! cube2.SetMaterialType(mat); cube3.SetMaterialType(mat2); cube2.Position = new Vector3D(0, -40, 0); cube3.Position = new Vector3D(0, 40, 0); //We create a fixed cam for our render target scene CameraSceneNode fixedcam = Scene.AddCameraSceneNode(null); fixedcam.Position = new Vector3D(50, 0, -1000); fixedcam.FarValue = 10000f; fixedcam.AddAnimator(Scene.CreateFlyCircleAnimator(fixedcam.Target, 5000f, 0.001f)); //And a fps cam for our main scene CameraSceneNode fpscam = Scene.AddCameraSceneNodeFPS(null, 100f, 400f, false); Scene.ActiveCamera.Position = new Vector3D(50, 0, -1000); Scene.ActiveCamera.FarValue = 10000f; Texture mask = Driver.AddTexture(new Dimension2D(128, 128), "", ColorFormat.A8R8G8B8); //Here is another cool feature from .NET CP //Direct access to textures via Texture.Lock/Unlock and Texture.SetPixel/GetPixel //Uncomment these lines to see the effect :) /*int w = mask.OriginalSize.Width; int h = mask.OriginalSize.Height; double maxdistance = Math.Sqrt(Math.Pow(w, 2) + Math.Pow(h, 2)); mask.Lock(); for (int x = 0; x < w; x++) for (int y = 0; y < h; y++) { double distance = Math.Sqrt(Math.Pow((w - x), 2) + Math.Pow((h - y), 2)); double coeff = distance / maxdistance; int color = (int)(255 - (255 * coeff)); Color pixel = new Color((int)(color / 1.5), (int)(color / 1.5), (int)(color / 2), (int)(color / 2)); mask.SetPixel(x, y, pixel); } mask.Unlock(); //Again a useful feature... Direct texture saving to common formats such as png,jpg,bmp or gif... //Without coding anything, your texture is directly saved in Irrlicht's working directory ! mask.Save("image.png");*/ //You may have noticed that GetPixel/SetPixel is very slow even if you lock the texture. //Another insecure but quite faster way to proceed is to convert our lock to a pointer. //It needs unsafe code but works very fast... //However it is very unsecure and you must know perfectly what you are doing... //That's why Modify/Retrieve exists (look down) /*int w = mask.OriginalSize.Width; int h = mask.OriginalSize.Height; double maxdistance = Math.Sqrt(Math.Pow(w, 2) + Math.Pow(h, 2)); IntPtr lockresult = mask.Lock(); unsafe { int* directacces = (int*)(void*)lockresult; int pitch = mask.Pitch / 4; for (int x = 0; x < w; ++x) for (int y = 0; y < h; ++y) { double distance = Math.Sqrt(Math.Pow((w - x), 2) + Math.Pow((h - y), 2)); double coeff = distance / maxdistance; int color = (int)(255 - (255 * coeff)); Color pixel = new Color((int)(color / 1.5), (int)(color / 1.5), (int)(color / 2), (int)(color / 2)); directacces[x + y * pitch] = pixel.NativeColor; } } mask.Unlock();*/ //Here is the SAFEST AND FASTEST way to modify our texture. //A simple delegate which takes the coords of the pixel and returns the color //It is called on each pixel and you can even return false if you don't want to change the pixel. //We can now create our mask without speed or compatibility issue ! int w = mask.OriginalSize.Width / 2; int h = mask.OriginalSize.Height / 2; double maxdistance = Math.Sqrt(Math.Pow(w, 2) + Math.Pow(h, 2)); //We create a delegate to modify each pixel ModifyPixel del = delegate(int x, int y, out Color col) { //Here is our formula... You can modify it as you wish, this is just an example ! double distance = Math.Sqrt(Math.Pow((w - x), 2) + Math.Pow((h - y), 2)); double coeff = distance / maxdistance; int color = (int)(255 - (255 * coeff)); //We now set the out color col = new Color((int)(color / 1.5), (int)(color / 1.5), (int)(color / 2), (int)(color / 2)); //And we return true since we created a color. //If you return false, the current pixel won't be modified ! return true; }; //And we modify our texture with our delegate mask.Modify(del); //Uncomment this line to save our mask on a portable network graphic file ! //You do not need to know jpg, bmp, gif, png... formats since they are converted automatically //mask.Save("mask.png"); Console.WriteLine(); Console.WriteLine("============================================"); Console.WriteLine("Features List :"); Console.WriteLine(); for (VideoDriverFeature feat = 0; feat < VideoDriverFeature.Count; feat++) Console.WriteLine(feat + " = " + Driver.QueryFeature(feat)); Console.WriteLine("============================================"); //Here is our logo Texture logo = Driver.GetTexture("NETCPlogo.png"); //Here is another feature, RenderTarget Texture renderTarget = Driver.CreateRenderTargetTexture(new Dimension2D(320, 240)); int lastfps = -1, fps = 0; while (device.Run() && !Exit) { Driver.BeginScene(true, true, Color.Gray); //Driver.SetRenderTarget(renderTarget, true, true, Color.TransparentBlue); //Scene.ActiveCamera = fixedcam; //Scene.DrawAll(); //Driver.SetRenderTarget(null, true, true, Color.Gray); Scene.ActiveCamera = fpscam; Scene.DrawAll(); //Ok, seems like Irrlicht 1.3.1 doesn't like drawing render target on OpenGL //I guess we will disable this for now... //Driver.Draw2DImage(renderTarget, new Position2D(0, 0), false); Driver.Draw2DImage(mask, new Rect(new Position2D(), Driver.ScreenSize), new Rect(new Position2D(), mask.OriginalSize), Color.White, true); //And finally our logo is painted Driver.Draw2DImage(logo, new Position2D(0, Driver.ScreenSize.Height - logo.OriginalSize.Height), new Rect(new Position2D(0, 0), logo.OriginalSize), Color.White, true); Driver.EndScene(); fps = Driver.FPS; if(fps != lastfps) { device.WindowCaption = caption + " - FPS : " + fps; lastfps = fps; } } device.Close(); }
public static void Main(string[] args) { //Shall we fullscreen ? (new verb I just invented) bool fullscreen = false; //We check the optimal video mode (resolution and screen depth) //As a default we choose 800x600x32 VideoMode optimalmode; optimalmode.Resolution = new Dimension2D(800, 600); optimalmode.Depth = 32; //OpenGL is platform independent whereas DirectX isn't //As a default we choose OpenGL DriverType drivertype = DriverType.OpenGL; //We set a cool caption for our example string caption = "Irrlicht.NET CP Example on "; //Is the application started on a Windows platform ? //You may wonder why I did not wrote "Environment.OSVersion.Platform == PlatformID.Unix" //The reason is simple : We do not need to know if we are on an unix platform, //We need to know if we ARE NOT ON A WINDOWS platform, that's different indeed. if (Environment.OSVersion.Platform != PlatformID.Win32Windows && Environment.OSVersion.Platform != PlatformID.Win32NT && Environment.OSVersion.Platform != PlatformID.Win32S) { caption += "Linux/Unix System"; //If the script is compiled on debug mode, we set 1024x768x32 with fullscreen. //I guess most of users will have at least a 15' screen... #if !DEBUG fullscreen = true; optimalmode.Resolution = new Dimension2D(1024, 768); optimalmode.Depth = 32; #endif } else //We ARE on a Windows Platform { caption += "Windows"; //Uncomment this line and the engine will use DirectX 9 on a Windows system //I don't like Direct3D 9 but well the choice is yours ;) //drivertype = DriverType.Direct3D9; //Here we are, if the engine is compiled with release settings, //We can play with a funny toy : fakedevice. //It does not seem to work on Linux (perhaps one of those bugs that only appear on some cursed computers like mine) //With this device, we will determine the ideal video mode by forcing //The application to launch on the desktop video mode //If a bug occurs (meaning video mode is null for instance) //We set the same video mode as on Linux : 1024x768x32 which is the most common video mode. #if !DEBUG IrrlichtDevice fakedevice = new IrrlichtDevice(DriverType.Null, new Dimension2D(), 16, false, false, false, false); optimalmode = fakedevice.DesktopVideoMode; if(optimalmode.Resolution.Width == 0 || optimalmode.Resolution.Height == 0) { optimalmode.Resolution = new Dimension2D(1024, 768); optimalmode.Depth = 32; } fakedevice.Dispose(); fullscreen = true; #endif } //We add a cool caption which says which renderer (OpenGL or Direct3D9 for instance) we use caption += " With " + drivertype + " - "; //Here we are, we create the device with settings we determined before IrrlichtDevice device = new IrrlichtDevice(drivertype, optimalmode.Resolution, optimalmode.Depth, fullscreen, true, //Stencil Buffer (for shadow) false, //Vertical Synchronisation (use it if you want your application not to go over 70 FPS) false); //Anti Aliasing scene = device.SceneManager; //We get some object such as the scene manager driver = device.VideoDriver; guienv = device.GUIEnvironment; device.FileSystem.WorkingDirectory = "../../medias"; //We set Irrlicht's current directory to %application directory%/media device.CursorControl.Visible = false; //Let's hide the cursor device.OnEvent += new OnEventDelegate(device_OnEvent); //We had a simple delegate that will handle every event device.WindowCaption = caption; //And we set a basic caption //We create a floor mesh which is actually an hill plane mesh without height AnimatedMesh floormesh = scene.AddHillPlaneMesh("_MyHill_", new Dimension2Df(250, 250), new Dimension2D(5, 5), 0f, new Dimension2Df(0, 0), new Dimension2Df(10, 10)); //We make the planar texture mapping to set the texture resolution scene.MeshManipulator.MakePlanarTextureMapping(floormesh.GetMesh(0), 0.006f); //We create a new mesh with tangents. It is needed for Parallax mapping Mesh tFloor = scene.MeshManipulator.CreateMeshWithTangents(floormesh.GetMesh(0)); Texture heightmap = driver.GetTexture("rockwall_height.bmp"); //Our normal map driver.MakeNormalMapTexture(heightmap, 10f); //We make a quite exagerated normal map (just to show off) //And here we are, we finally create the scene node SceneNode floor = scene.AddMeshSceneNode(tFloor, null, -1); //We get the material Material mat = floor.GetMaterial(0); mat.Texture1 = driver.GetTexture("rockwall.bmp"); //Diffuse texture mat.Texture2 = heightmap; //Normal map mat.MaterialType = MaterialType.ParallaxMapSolid; //The beautiful Parallax Mapping mat.MaterialTypeParam = 0.035f; //Parameter for the height of parallax mapping //We create a FPS camera which is a basic camera controlled by //Arrow keys and mouse such as the camera in Quake or Doom CameraSceneNode cam = scene.AddCameraSceneNodeFPS(null, 50f, 100f, true); cam.Position = new Vector3D(0, 100, -100); cam.FarValue = 10000f; //We want to see all the scene //We add the sword (don't tell me that it is not a katana, I already know it !) SceneNode katana = scene.AddMeshSceneNode(scene.GetMesh("katana.x").GetMesh(0), cam, -1); Vector3D KInitialRotation = new Vector3D(0, 20, -90); Vector3D KInitialPosition = new Vector3D(23, -3, 40); //We set our materials (the n 1 is the sword and the 0 the stick) katana.Scale = new Vector3D(100f, 100f, 100f); mat = katana.GetMaterial(1); mat.DiffuseColor = new Color(255, 90, 90, 90); mat.AmbientColor = new Color(255, 90, 90, 90); mat.EmissiveColor = new Color(255, 90, 90, 90); mat.SpecularColor = new Color(255, 90, 90, 90); Material mat2 = katana.GetMaterial(0); mat2.DiffuseColor = new Color(255, 10, 10, 10); mat2.EmissiveColor = new Color(255, 120, 80, 0); //We add a little shining effect with particles on the sword ParticleSystemSceneNode particles = scene.AddParticleSystemSceneNode(false, katana, -1); particles.SetEmitter(particles.CreateBoxEmitter(scene.GetMesh("katana.x").GetMesh(0).GetMeshBuffer(1).BoundingBox, new Vector3D(0, 0.002f, 0), 1000, 1000, new Color(0, 255, 255, 255), new Color(0, 255, 255, 255), 200, 200, 0)); particles.ParticleSize = new Dimension2Df(1f, 3f); //A funny size for our funny effect particles.AddAffector(particles.CreateFadeOutParticleAffector(new Color(0, 0, 0, 0), 100)); particles.SetMaterialTexture(0, driver.GetTexture("fire.bmp")); particles.SetMaterialType(MaterialType.TransparentAddColor); particles.SetMaterialFlag(MaterialFlag.Lighting, false); //We create three dwarves with shadows AnimatedMeshSceneNode dwarf = scene.AddAnimatedMeshSceneNode(scene.GetMesh("dwarf.x")); dwarf.AnimationSpeed = 15; dwarf.Position = new Vector3D(0, 0, 100); dwarf.AddShadowVolumeSceneNode(-1, true, 10000f); //Wow... It was really hard to create it ! dwarf.Scale = new Vector3D(1.5f, 1.5f, 1.5f); //Here we have our light that will simply rotate around the dwarf LightSceneNode dwarflight = scene.AddLightSceneNode(dwarf, new Vector3D(0, 0, 0), Colorf.White, 10000f, -1); dwarflight.AddAnimator(scene.CreateFlyCircleAnimator(new Vector3D(0, 100, 0), 100f, 0.001f)); dwarf = scene.AddAnimatedMeshSceneNode(scene.GetMesh("dwarf.x")); dwarf.AnimationSpeed = 10; dwarf.Position = new Vector3D(-100, 0, 100); dwarf.AddShadowVolumeSceneNode(-1, true, 10000f); dwarf.Scale = new Vector3D(1.5f, 1.5f, 1.5f); dwarf = scene.AddAnimatedMeshSceneNode(scene.GetMesh("dwarf.x")); dwarf.AnimationSpeed = 20; dwarf.Position = new Vector3D(100, 0, 100); dwarf.AddShadowVolumeSceneNode(-1, true, 10000f); dwarf.Scale = new Vector3D(1.5f, 1.5f, 1.5f); //We set the shadow color. //I reduced a lot the opacity since our light is not supposed to be the sun //And the scene is at night... Thus a shadow is not something very visible. scene.ShadowColor = new Color(100, 0, 0, 0); //We had a simple billboard to represent physically the light BillboardSceneNode lightbill = scene.AddBillboardSceneNode(dwarflight, new Dimension2Df(20, 20), -1); Texture fire = driver.GetTexture("fire.bmp"); lightbill.SetMaterialTexture(0, fire); lightbill.SetMaterialType(MaterialType.TransparentAddColor); lightbill.SetMaterialFlag(MaterialFlag.Lighting, false); //We add a simple skybox scene.AddSkyBoxSceneNode(null, new Texture[] { driver.GetTexture("irrlicht2_up.jpg"), driver.GetTexture("irrlicht2_dn.jpg"), driver.GetTexture("irrlicht2_lf.jpg"), driver.GetTexture("irrlicht2_rt.jpg"), driver.GetTexture("irrlicht2_ft.jpg"), driver.GetTexture("irrlicht2_bk.jpg") }, -1); //We had a simple texture that will be rendered each time Texture matthias = driver.GetTexture("matthias.png"); //Here is our logo Texture logo = driver.GetTexture("NETCPlogo.png"); //Just a simple demonstration of a feature which is not on Irrlicht .NET : //Listing children of the node as a simple Array. foreach (SceneNode node in cam.Children) Console.WriteLine("Child : " + node.ToString() + " Parent : " + node.Parent.ToString()); //Another cool new feature : //FileSystem class with features like listing every file that Irrlicht can load //(including files on zip archives for instance) //foreach (FileListItem item in device.FileSystem.FileList) // Console.WriteLine(item.ToString()); double random = 0; Random chance = new Random(); int lastfps = -1; while (device.Run() && !Exit) { //We get the FPS to synchronize all movements //Notice that I advice NOT TO USE THE FPS to synchronize //Because it is updated every second whereas it can change at every time //Use the time between each loop instead //(We use it here because it is not very important but be very careful) int FPS = driver.FPS; if(FPS != lastfps) { //And here we are, we set the caption of the main window device.WindowCaption = caption + "FPS : " + FPS; lastfps = FPS; } if (FPS < 10) FPS = 70; random += ((2000f / FPS) + chance.Next(-10, 10)) / 1000f; //If someone has played the demo about thirty years... if (random >= double.MaxValue) random = 0; //Sinuses and Cosines are very useful because we all know that //they are between -1 and 1. Thus we don't need to add some "if" //And we can control the domain easilly //We had a realstic move of the sword because try to hold one during 10 minutes //You won't be static... Tested ! //A nice thing would be to generate it using the camera movement speed or things like that... katana.Position = KInitialPosition + new Vector3D((float)(Math.Sin(random) / 6.0f), (float)(Math.Sin(random) / 10f), (float)(Math.Cos(random) / 6.0f)); katana.Rotation = KInitialRotation + new Vector3D((float)(Math.Cos(random) / 2f), (float)(Math.Cos(random)), (float)(Math.Sin(random) / 2f)); //We clear the back buffer and begin the scene driver.BeginScene(true, true, Color.From(255, 50, 50, 50)); //First we draw all 3D Objects such as our sword or the dwarves //Notice that the order is very important... Try to move this line just before the //driver.EndScene and you will see the difference scene.DrawAll(); //3 rectangles that could for instance represent Health, Mana and Endurance points... driver.Draw2DRectangle(new Rect(new Position2D(10, 5), new Dimension2D(300, 15)), Color.Red); driver.Draw2DRectangle(new Rect(new Position2D(10, 25), new Dimension2D(300, 15)), Color.Blue); driver.Draw2DRectangle(new Rect(new Position2D(10, 45), new Dimension2D(300, 15)), Color.Green); //A little image loaded before that could represent for instance our character driver.Draw2DImage(matthias, new Position2D(driver.ScreenSize.Width - matthias.OriginalSize.Width, 0), new Rect(new Position2D(0, 0), matthias.OriginalSize), Color.White, true); //And finally our logo is painted driver.Draw2DImage(logo, new Position2D(0, driver.ScreenSize.Height - logo.OriginalSize.Height), new Rect(new Position2D(0, 0), logo.OriginalSize), Color.White, true); //Finally we draw everything in the GUI Environment... CF the GUI example guienv.DrawAll(); //End of the scene, the back buffer is displayed driver.EndScene(); } //ALWAYS DISPOSE THE DEVICE AT THE END //It is REQUIRED on Linux because if you don't, XWindow will stay at the old video mode //And you can't imagine how ugly it is not to get back to the original video mode device.Close(); }