using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.IO;namespace EditControls
{
public struct SceneCamera
{
public int Color;
public bool Grid;
public bool Orthographic;
public Vector3 Pos;
public Quaternion Ori;
public float Fov;
};
public struct ScenePresentation
{
public int Width;
public int Height;
public IntPtr Handle;
public SceneCamera Camera;
public Matrix Projection
{
get
{
if (Camera.Orthographic)
{
return Matrix.OrthoLeftHanded(Width, Height, 0.1f, 10000.0f);
}
else
{
System.Diagnostics.Debug.Assert(Camera.Fov > 0, @"Camera FOV must be greater than 0.");
return Matrix.PerspectiveFieldOfViewLeftHanded(
(float)(Camera.Fov*Math.PI/90.0), (float)Width/(float)Height, 0.1f, 10000.0f);
}
}
}
public Matrix View
{
get
{
return Matrix.Translation(-Camera.Pos) *
Matrix.RotationQuaternion(Quaternion.Conjugate(Camera.Ori));
}
}
public Viewport Viewport
{
get
{
Viewport vp = new Viewport();
vp.X = 0;
vp.Y = 0;
vp.Width = Width;
vp.Height = Height;
vp.MinZ = 0;
vp.MaxZ = 1;
return vp;
}
}
}
public partial class Device3d : Component
{
public Device3d()
{
InitializeComponent();
scene = new Scene();
}
public Device3d(IContainer container)
{
container.Add(this);
InitializeComponent();
scene = new Scene();
}
public new void Dispose()
{
TeardownDevice();
base.Dispose();
}
public void SetupDevice(System.Windows.Forms.Control ctrl)
{
TeardownDevice();
PresentParameters[] pp = new PresentParameters[1];
pp[0] = new PresentParameters();
pp[0].AutoDepthStencilFormat = DepthFormat.D24X8;
pp[0].BackBufferCount = 1;
pp[0].BackBufferFormat = Format.X8R8G8B8;
pp[0].BackBufferHeight = 1024; // TODO: make this desktop size
pp[0].BackBufferWidth = 768; // TODO: make this desktop size
pp[0].EnableAutoDepthStencil = true;
pp[0].IsWindowed = true;
pp[0].PresentationInterval = PresentInterval.Immediate;
pp[0].PresentFlag = PresentFlag.DiscardDepthStencil;
pp[0].SwapEffect = SwapEffect.Discard;
try
{
id3d9 = new Device(0, DeviceType.Hardware, ctrl.Handle,
CreateFlags.SoftwareVertexProcessing, pp);
control = ctrl;
fileCache = new FileCache();
grid = new Grid(this);
}
catch(System.Exception e)
{
TeardownDevice();
System.Windows.Forms.MessageBox.Show(e.Message, "D3D Exception");
}
}
public void TeardownDevice()
{
if (id3d9 != null)
{
id3d9.Dispose();
id3d9 = null;
control = null;
}
if (grid != null)
{
grid.Dispose();
grid = null;
}
if (fileCache != null)
{
fileCache.Dispose();
fileCache = null;
}
}
internal Scene scene;
public Scene Scene {
get { return scene; }
}
// Return true if you want to descend to children of this scene.
public delegate bool SceneTraverser(Node node);
ScenePresentation curPresentation;
public void PresentScene(ScenePresentation sp)
{
bool decorations = true;
curPresentation = sp;
SceneTraverser sceneTraverser = new SceneTraverser(this.RenderSceneNode);
if (id3d9 == null)
{
return;
}
try
{
// setup viewport
id3d9.Viewport = sp.Viewport;
// setup transforms
id3d9.Transform.World = Matrix.Identity;
id3d9.Transform.Projection = sp.Projection;
id3d9.Transform.View = sp.View;
// clear the backbuffer and begin the scene
id3d9.Clear(ClearFlags.ZBuffer | ClearFlags.Target, sp.Camera.Color, 1.0f, 0);
id3d9.BeginScene();
// evaluate the scene items, and draw them
scene.Traverse(sceneTraverser);
if (decorations)
{
// the grid comes last
grid.Draw(sp);
}
// finish the scene and present
id3d9.EndScene();
System.Drawing.Rectangle src = new System.Drawing.Rectangle(
0, 0, sp.Width, sp.Height);
id3d9.Present(src, src, sp.Handle);
}
catch (System.Exception e)
{
System.Windows.Forms.MessageBox.Show(e.Message, "D3D9 Exception");
TeardownDevice();
}
}
internal bool RenderSceneNode(Node node)
{
foreach (Geometry g in node.Geometry)
{
g.RenderToDevice(this, curPresentation);
}
return true;
}
public Effect GetEffect(string name)
{
return fileCache.Load<Effect>(name, null, new ObjectLoader<Effect>(this.LoadEffect));
}
public Texture GetTexture(string name)
{
return fileCache.Load<Texture>(name, null, new ObjectLoader<Texture>(this.LoadTexture));
}
internal Dictionary<Mesh3d, Mesh3d> meshes;
public Mesh3d MakeMesh(
VertexElement[] elements, int numFaces)
{
Mesh3d m = new Mesh3d(this, elements, numFaces);
meshes.Add(m, m);
return m;
}
Effect LoadEffect(string name)
{
try
{
Effect fx = Effect.FromFile(id3d9, name, null, null, null, ShaderFlags.NotCloneable, null);
return fx;
}
catch(Microsoft.DirectX.DirectXException dxe)
{
// re-type the exception
throw new System.Exception(dxe.AdditionalInformation.Replace(": ", ":\n"));
}
}
Texture LoadTexture(string texture)
{
try
{
return new Texture(id3d9, texture);
}
catch (System.Exception)
{
return null;
}
}
public Device Device { get { return id3d9; } }
Microsoft.DirectX.Direct3D.Device id3d9;
System.Windows.Forms.Control control;
Grid grid;
FileCache fileCache;
}
public class Mesh3d : IDisposable
{
Device3d device;
VertexElement[] declaration;
GraphicsBuffer gb;
VertexBuffer vb;
VertexDeclaration vd;
int numFaces;
internal Mesh3d(Device3d dev, VertexElement[] decl, int nf)
{
device = dev;
int size;
declaration = decl;
numFaces = nf;
vd = device.MakeDeclaration(decl, out size);
gb = new GraphicsBuffer(size*numFaces*3);
}
public void Dispose()
{
device.meshes.Remove(this);
gb.Dispose();
Unload();
}
public void Unload()
{
if (vb != null)
{
vb.Dispose();
vb = null;
}
if (vd != null)
{
vd.Dispose();
vd = null;
}
}
public void Issue()
{
if (vd == null)
{
int size;
vd = device.MakeDeclaration(declaration, out size);
}
if (vb == null)
{
vb = new VertexBuffer(device.Device, gb.SizeInBytes, Usage.None,
VertexFormats.None, Pool.Default, null);
}
device.Device.SetStreamSource(0, vb, 0);
device.Device.VertexDeclaration = vd;
device.Device.DrawPrimitives(PrimitiveType.TriangleList, 0, numFaces);
}
}
public delegate Type ObjectLoader<Type>(string fileName);
public class FileCache : IDisposable
{
public FileCache()
: this(@"media", @"fx", @"texture", @"model" )
{
}
public FileCache(string fname, params string[] subfolders)
{
System.IO.FileInfo fi = new System.IO.FileInfo(fname);
unchecked
{
if (fi.Attributes == (FileAttributes)0xffffffff) fi = new System.IO.FileInfo(@"..\\" + fname);
if (fi.Attributes == (FileAttributes)0xffffffff) fi = new System.IO.FileInfo(@"..\\..\\" + fname);
if (fi.Attributes == (FileAttributes)0xffffffff) fi = new System.IO.FileInfo(@"..\\..\\..\\" + fname);
if (fi.Attributes == (FileAttributes)0xffffffff) throw new System.IO.FileNotFoundException(
String.Format("Can't find the '{1}' folder.\n{0}", fi.FullName, fname));
}
if (subfolders.Length > 0)
{
foreach (string sf in subfolders)
{
searchPaths.Add(System.IO.Path.Combine(fi.FullName, sf));
}
}
else
{
searchPaths.Add(fi.FullName);
}
}
public void Dispose()
{
foreach (NameAndValue nav in foundPaths.Values)
{
IDisposable id = nav.Value as IDisposable;
if (id != null)
{
id.Dispose();
nav.Value = null;
}
}
foundPaths = null;
searchPaths = null;
}
public Type Load<Type>(string name, string relative, ObjectLoader<Type> loader)
where Type : class
{
NameAndValue nav = null;
NameAndRelative nar = new NameAndRelative(name, relative);
if (!foundPaths.TryGetValue(nar, out nav))
{
string toFind = relative == null ? name : Path.Combine(Path.GetDirectoryName(relative), name);
string found = FindPath(toFind);
if (found == null && relative == null) {
found = FindPath(name);
}
if (found == null) {
string msg = String.Format("Could not find file {2}{0}{1}.",
relative == null ? "" : " relative to ", relative == null ? "" : relative, name);
throw new FileNotFoundException(msg, name);
}
nav = new NameAndValue();
nav.Name = found;
nav.Value = loader(nav.Name);
foundPaths.Add(nar, nav);
}
Type r = nav.Value as Type;
if (r == null)
{
throw new System.Exception(String.Format("Failed to load {0} as {1} (bad file type?)",
name, typeof(Type).Name));
}
return r;
}
string FindPath(string name)
{
foreach (string s in searchPaths)
{
FileInfo fi = new FileInfo(Path.Combine(s, name));
if (fi.Exists)
{
return fi.FullName;
}
}
return null;
}
List<string> searchPaths = new List<string>();
System.Collections.Generic.Dictionary<NameAndRelative, NameAndValue> foundPaths =
new System.Collections.Generic.Dictionary<NameAndRelative, NameAndValue>();
class NameAndValue
{
public string Name;
public object Value;
}
struct NameAndRelative : System.IEquatable<NameAndRelative>
{
public static System.Globalization.TextInfo textInfo = System.Globalization.CultureInfo.InvariantCulture.TextInfo;
public NameAndRelative(string n, string r)
{
Name = String.Intern(textInfo.ToLower(n));
Relative = (r == null) ? null : String.Intern(textInfo.ToLower(r));
hashCode = Name.GetHashCode() ^ (r == null ? 0 : (Relative.GetHashCode() << 1));
}
public override int GetHashCode()
{
return hashCode;
}
public string Name;
public string Relative;
public int hashCode;
#region IEquatable<NameAndRelative> Members
public bool Equals(NameAndRelative other)
{
return Name == other.Name && Relative == other.Relative;
}
#endregion
}
};
}