You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

497 lines
20 KiB

  1. /*
  2. ---------------------------------------------------------------------------
  3. Open Asset Import Library (ASSIMP)
  4. ---------------------------------------------------------------------------
  5. Copyright (c) 2006-2010, ASSIMP Development Team
  6. All rights reserved.
  7. Redistribution and use of this software in source and binary forms,
  8. with or without modification, are permitted provided that the following
  9. conditions are met:
  10. * Redistributions of source code must retain the above
  11. copyright notice, this list of conditions and the
  12. following disclaimer.
  13. * Redistributions in binary form must reproduce the above
  14. copyright notice, this list of conditions and the
  15. following disclaimer in the documentation and/or other
  16. materials provided with the distribution.
  17. * Neither the name of the ASSIMP team, nor the names of its
  18. contributors may be used to endorse or promote products
  19. derived from this software without specific prior
  20. written permission of the ASSIMP Development Team.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. ---------------------------------------------------------------------------
  33. */
  34. using System;
  35. using System.Collections.Generic;
  36. using System.ComponentModel;
  37. using System.Data;
  38. using System.Diagnostics;
  39. using System.Drawing;
  40. using System.IO;
  41. using System.Text;
  42. using System.Windows.Forms;
  43. using Microsoft.DirectX;
  44. using Microsoft.DirectX.Direct3D;
  45. namespace Assimp.Viewer
  46. {
  47. public partial class AssimpView : Form
  48. {
  49. public AssimpView()
  50. {
  51. InitializeComponent();
  52. // Window title
  53. this.Text = "Assimp .NET Viewer";
  54. // Ignore WM_ERASEBKGND messages to prevent flicker
  55. this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
  56. // Listen to mouse
  57. this.MouseDown += new MouseEventHandler(AssimpView_MouseDown);
  58. this.MouseMove += new MouseEventHandler(AssimpView_MouseMove);
  59. this.MouseUp += new MouseEventHandler(AssimpView_MouseUp);
  60. this.MouseWheel += new MouseEventHandler(AssimpView_MouseWheel);
  61. }
  62. void AssimpView_MouseWheel(object sender, MouseEventArgs e) {
  63. g_sCamera.vPos.Z *= 1.0f - (e.Delta / 1000.0f);
  64. Refresh();
  65. }
  66. private void AssimpView_MouseDown(object sender, MouseEventArgs e) {
  67. UpdateMouseState(e);
  68. }
  69. private void AssimpView_MouseMove(object sender, MouseEventArgs e) {
  70. UpdateMouseState(e);
  71. }
  72. private void AssimpView_MouseUp(object sender, MouseEventArgs e) {
  73. UpdateMouseState(e);
  74. }
  75. private void UpdateMouseState(MouseEventArgs e) {
  76. g_bMousePressed = (e.Button == MouseButtons.Left);
  77. g_bMousePressedR = (e.Button == MouseButtons.Right);
  78. g_bMousePressedM = (e.Button == MouseButtons.Middle);
  79. g_bMousePressedBoth = (e.Button == (MouseButtons.Left | MouseButtons.Right));
  80. Refresh();
  81. }
  82. protected override void OnPaintBackground(PaintEventArgs pevent) {
  83. /* Do nothing to prevent flicker during resize */
  84. }
  85. private void Form1_Load(object sender, EventArgs e)
  86. {
  87. InitializeDevice();
  88. InitializeAssimp();
  89. }
  90. private void Form1_SizeChanged(object sender, EventArgs e) {
  91. presentParams.BackBufferWidth = Width;
  92. presentParams.BackBufferHeight = Height;
  93. device.Reset(presentParams);
  94. Invalidate();
  95. }
  96. public void InitializeDevice()
  97. {
  98. // Improve Performance
  99. // http://blogs.msdn.com/b/tmiller/archive/2003/11/14/57531.aspx
  100. Device.IsUsingEventHandlers = false;
  101. // For Windowed mode leave Width and Height at 0
  102. var adapter = Manager.Adapters.Default;
  103. presentParams = new PresentParameters();
  104. presentParams.AutoDepthStencilFormat = DepthFormat.D16;
  105. presentParams.EnableAutoDepthStencil = true;
  106. presentParams.Windowed = true;
  107. presentParams.SwapEffect = SwapEffect.Discard;
  108. // Keep precision in floating point calculations
  109. // http://blogs.msdn.com/b/tmiller/archive/2004/06/01/145596.aspx
  110. var createFlags = CreateFlags.FpuPreserve;
  111. // Pure device for performance - all device render states are now write only
  112. var deviceType = DeviceType.Hardware;
  113. var caps = Manager.GetDeviceCaps(adapter.Adapter, deviceType);
  114. if (caps.DeviceCaps.SupportsPureDevice) {
  115. createFlags |= CreateFlags.PureDevice;
  116. }
  117. // Warning: some drivers lie about supporting HT&L. Use Software if you see problems.
  118. if (caps.DeviceCaps.SupportsHardwareTransformAndLight) {
  119. createFlags |= CreateFlags.HardwareVertexProcessing;
  120. }
  121. else {
  122. createFlags |= CreateFlags.SoftwareVertexProcessing;
  123. }
  124. // Create Device
  125. device = new Device(adapter.Adapter, deviceType, this, createFlags, presentParams);
  126. // Add event handlers
  127. device.DeviceResizing += new CancelEventHandler(device_DeviceResizing);
  128. }
  129. private void device_DeviceResizing(object sender, CancelEventArgs e) {
  130. device.Reset();
  131. }
  132. // default pp steps
  133. private static aiPostProcessSteps ppsteps =
  134. aiPostProcessSteps.aiProcess_CalcTangentSpace | // calculate tangents and bitangents if possible
  135. aiPostProcessSteps.aiProcess_JoinIdenticalVertices | // join identical vertices/ optimize indexing
  136. aiPostProcessSteps.aiProcess_ValidateDataStructure | // perform a full validation of the loader's output
  137. aiPostProcessSteps.aiProcess_ImproveCacheLocality | // improve the cache locality of the output vertices
  138. aiPostProcessSteps.aiProcess_RemoveRedundantMaterials | // remove redundant materials
  139. aiPostProcessSteps.aiProcess_FindDegenerates | // remove degenerated polygons from the import
  140. aiPostProcessSteps.aiProcess_FindInvalidData | // detect invalid model data, such as invalid normal vectors
  141. aiPostProcessSteps.aiProcess_GenUVCoords | // convert spherical, cylindrical, box and planar mapping to proper UVs
  142. aiPostProcessSteps.aiProcess_TransformUVCoords | // preprocess UV transformations (scaling, translation ...)
  143. aiPostProcessSteps.aiProcess_FindInstances | // search for instanced meshes and remove them by references to one master
  144. aiPostProcessSteps.aiProcess_LimitBoneWeights | // limit bone weights to 4 per vertex
  145. aiPostProcessSteps.aiProcess_OptimizeMeshes | // join small meshes, if possible;
  146. (aiPostProcessSteps)0;
  147. public void InitializeAssimp() {
  148. var flags = ( ppsteps |
  149. aiPostProcessSteps.aiProcess_GenSmoothNormals | // generate smooth normal vectors if not existing
  150. aiPostProcessSteps.aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes
  151. aiPostProcessSteps.aiProcess_Triangulate | // triangulate polygons with more than 3 edges
  152. aiPostProcessSteps.aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space
  153. aiPostProcessSteps.aiProcess_SortByPType | // make 'clean' meshes which consist of a single typ of primitives
  154. (aiPostProcessSteps)0);
  155. // default model
  156. var path = "../../../../../test/models/3DS/test1.3ds";
  157. importer = new Importer();
  158. string[] args = Environment.GetCommandLineArgs();
  159. if (args.Length > 1) {
  160. path = args[1];
  161. }
  162. //var path = "man.3ds";
  163. scene = importer.ReadFile(path, flags);
  164. if (scene != null)
  165. {
  166. directory = Path.GetDirectoryName(path);
  167. CacheMaterials(scene.mMaterials);
  168. CacheMeshes(scene.mMeshes);
  169. SetupCamera(scene.mCameras);
  170. }
  171. else {
  172. MessageBox.Show("Failed to open file: " + path + ". Either Assimp screwed up or the path is not valid.");
  173. Application.Exit();
  174. }
  175. }
  176. private void CacheMeshes(aiMeshVector meshes) {
  177. var numMeshes = meshes.Count;
  178. meshCache = new Mesh[numMeshes];
  179. meshBoundsMin = new Vector3[numMeshes];
  180. meshBoundsMax = new Vector3[numMeshes];
  181. for (int i = 0; i < numMeshes; ++i) {
  182. var mesh = meshes[i];
  183. switch (mesh.mPrimitiveTypes) {
  184. case aiPrimitiveType.aiPrimitiveType_TRIANGLE:
  185. Vector3 min, max;
  186. meshCache[i] = CreateMesh(mesh, out min, out max);
  187. meshBoundsMin[i] = min;
  188. meshBoundsMax[i] = max;
  189. break;
  190. }
  191. }
  192. }
  193. private void CacheMaterials(aiMaterialVector materials) {
  194. var numMaterials = materials.Count;
  195. materialCache = new ExtendedMaterial[numMaterials];
  196. textureCache = new Dictionary<string, Texture>();
  197. for (int i = 0; i < numMaterials; ++i) {
  198. var material = materials[i];
  199. var dxExtendedMaterial = new ExtendedMaterial();
  200. var dxMaterial = new Material();
  201. dxMaterial.AmbientColor = material.Ambient.ToColorValue();
  202. dxMaterial.DiffuseColor = material.Diffuse.ToColorValue();
  203. dxMaterial.EmissiveColor = material.Emissive.ToColorValue();
  204. dxMaterial.SpecularColor = material.Specular.ToColorValue();
  205. dxMaterial.SpecularSharpness = material.ShininessStrength;
  206. dxExtendedMaterial.Material3D = dxMaterial;
  207. dxExtendedMaterial.TextureFilename = material.TextureDiffuse0;
  208. materialCache[i] = dxExtendedMaterial;
  209. var textureFilename = dxExtendedMaterial.TextureFilename;
  210. if (!string.IsNullOrEmpty(textureFilename) && !textureCache.ContainsKey(textureFilename)) {
  211. textureCache.Add(textureFilename, CreateTexture(textureFilename));
  212. }
  213. }
  214. }
  215. private Texture CreateTexture(string fileName) {
  216. var path = Path.Combine(directory, fileName);
  217. try {
  218. using (var data = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) {
  219. var dxTexture = Texture.FromStream(device, data, Usage.None, Pool.Managed);
  220. textureCache[path] = dxTexture;
  221. return dxTexture;
  222. }
  223. }
  224. catch (Exception) {
  225. return null;
  226. }
  227. }
  228. private Mesh CreateMesh(aiMesh aiMesh, out Vector3 min, out Vector3 max) {
  229. var numFaces = (int) aiMesh.mNumFaces;
  230. var numVertices = (int) aiMesh.mNumVertices;
  231. var dxMesh = new Mesh(numFaces, numVertices, MeshFlags.Managed | MeshFlags.Use32Bit,
  232. CustomVertex.PositionNormalTextured.Format, device);
  233. var aiPositions = aiMesh.mVertices;
  234. var aiNormals = aiMesh.mNormals;
  235. var aiTextureCoordsAll = aiMesh.mTextureCoords;
  236. var aiTextureCoords = (aiTextureCoordsAll!=null) ? aiTextureCoordsAll[0] : null;
  237. var dxVertices = new CustomVertex.PositionNormalTextured[numVertices];
  238. for (int i = 0; i < numVertices; ++i) {
  239. dxVertices[i].Position = aiPositions[i].ToVector3();
  240. if (aiNormals != null) {
  241. dxVertices[i].Normal = aiNormals[i].ToVector3();
  242. }
  243. if (aiTextureCoords != null) {
  244. var uv = aiTextureCoords[i];
  245. dxVertices[i].Tu = uv.x;
  246. dxVertices[i].Tv = uv.y;
  247. }
  248. }
  249. dxMesh.VertexBuffer.SetData(dxVertices, 0, LockFlags.None);
  250. var aiFaces = aiMesh.mFaces;
  251. var dxIndices = new uint[numFaces * 3];
  252. for (int i = 0; i < numFaces; ++i) {
  253. var aiFace = aiFaces[i];
  254. var aiIndices = aiFace.mIndices;
  255. for (int j = 0; j < 3; ++j) {
  256. dxIndices[i * 3 + j] = aiIndices[j];
  257. }
  258. }
  259. dxMesh.IndexBuffer.SetData(dxIndices, 0, LockFlags.None);
  260. var dxAttributes = dxMesh.LockAttributeBufferArray(LockFlags.None);
  261. // TODO: Set face material index for attributes
  262. dxMesh.UnlockAttributeBuffer(dxAttributes);
  263. var adjacency = new int[numFaces * 3];
  264. dxMesh.GenerateAdjacency(0.0f, adjacency);
  265. dxMesh.OptimizeInPlace(MeshFlags.OptimizeAttributeSort, adjacency);
  266. Geometry.ComputeBoundingBox(dxVertices, CustomVertex.PositionNormalTextured.StrideSize, out min, out max);
  267. return dxMesh;
  268. }
  269. //-------------------------------------------------------------------------------
  270. // Calculate the boundaries of a given node and all of its children
  271. // The boundaries are in Worldspace (AABB)
  272. // piNode Input node
  273. // min/max Receives the min/max boundaries
  274. // piMatrix Transformation matrix of the graph at this position
  275. //-------------------------------------------------------------------------------
  276. private void CalculateBounds(aiNode piNode, ref Vector3 min, ref Vector3 max, Matrix piMatrix) {
  277. Debug.Assert(null != piNode);
  278. var mTemp = piNode.mTransformation.ToMatrix();
  279. var aiMe = mTemp * piMatrix;
  280. var meshes = piNode.mMeshes;
  281. var numMeshes = meshes.Count;
  282. for (int i = 0; i < numMeshes; ++i) {
  283. var mesh = meshes[i];
  284. var meshMin = Vector3.TransformCoordinate(meshBoundsMin[mesh], aiMe);
  285. var meshMax = Vector3.TransformCoordinate(meshBoundsMax[mesh], aiMe);
  286. min.X = Math.Min(min.X, meshMin.X);
  287. min.Y = Math.Min(min.Y, meshMin.Y);
  288. min.Z = Math.Min(min.Z, meshMin.Z);
  289. max.X = Math.Max(max.X, meshMax.X);
  290. max.Y = Math.Max(max.Y, meshMax.Y);
  291. max.Z = Math.Max(max.Z, meshMax.Z);
  292. }
  293. var children = piNode.mChildren;
  294. var numChildren = children.Count;
  295. for (int i = 0; i < numChildren; ++i) {
  296. CalculateBounds(children[i], ref min, ref max, aiMe);
  297. }
  298. }
  299. protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
  300. HandleMouseInputLocal();
  301. Render();
  302. }
  303. private void Render() {
  304. device.Clear(ClearFlags.Target|ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
  305. device.BeginScene();
  306. SetupMatrices();
  307. if (scene != null && scene.mRootNode != null) {
  308. SetupLights(scene.mLights);
  309. RenderNode(scene.mRootNode, g_mWorldRotate);
  310. }
  311. device.EndScene();
  312. device.Present();
  313. }
  314. private void SetupMatrices() {
  315. var fovy = Geometry.DegreeToRadian(45.0f);
  316. var aspect = ((float)this.Width / (float)this.Height);
  317. device.Transform.Projection = Matrix.PerspectiveFovLH(fovy, aspect, g_zNear, g_zFar);
  318. device.Transform.View = g_sCamera.GetMatrix();
  319. }
  320. private void SetupCamera(aiCameraVector cameras) {
  321. // Get scene bounds
  322. var min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
  323. var max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
  324. CalculateBounds(scene.mRootNode, ref min, ref max, Matrix.Identity);
  325. // Projection Depth
  326. var size = (max-min);
  327. g_zFar = Math.Max(Math.Max(size.X, size.Y), size.Z) * 100;
  328. // Starting View Position
  329. var center = min + size * 0.5f;
  330. var lookAt = new Vector3(0,0,1); // Unit vector not coordinate
  331. var position = new Vector3(center.X, center.Y, center.Z-(size.Z*10));
  332. var up = new Vector3(0, 1, 0);
  333. if (cameras.Count > 0) {
  334. var camera = cameras[0];
  335. lookAt = camera.mLookAt.ToVector3();
  336. position = camera.mPosition.ToVector3();
  337. up = camera.mUp.ToVector3();
  338. }
  339. g_sCamera.vLookAt = lookAt;
  340. g_sCamera.vPos = position;
  341. g_sCamera.vUp = up;
  342. }
  343. private void SetupLights(aiLightVector lights) {
  344. var numLights = lights.Count;
  345. if (numLights == 0) {
  346. var light = device.Lights[0];
  347. light.Type = LightType.Directional;
  348. light.Diffuse = Color.LightGray;
  349. light.Direction = new Vector3(-1, -1, 1);
  350. light.Enabled = true;
  351. }
  352. else {
  353. // TODO: setup lights
  354. }
  355. // Enables lighting
  356. device.RenderState.Lighting = true;
  357. device.RenderState.Ambient = Color.DarkGray;
  358. device.RenderState.SpecularEnable = true;
  359. }
  360. private void RenderNode(aiNode node, Matrix parentMatrix) {
  361. var nodeMatrix = node.mTransformation.ToMatrix() * parentMatrix;
  362. var children = node.mChildren;
  363. var numChildren = node.mNumChildren;
  364. for (int i = 0; i < numChildren; ++i) {
  365. var child = children[i];
  366. RenderNode(child, nodeMatrix);
  367. }
  368. device.Transform.World = nodeMatrix;
  369. var meshes = node.mMeshes;
  370. var numMeshes = meshes.Count;
  371. for (int i = 0; i < numMeshes; ++i) {
  372. var meshId = (int)meshes[i];
  373. var mesh = scene.mMeshes[meshId];
  374. var materialId = (int)mesh.mMaterialIndex;
  375. var dxMaterial = materialCache[materialId];
  376. device.Material = dxMaterial.Material3D;
  377. Texture dxTexture = null;
  378. if (!string.IsNullOrEmpty(dxMaterial.TextureFilename)) {
  379. textureCache.TryGetValue(dxMaterial.TextureFilename, out dxTexture);
  380. }
  381. device.SetTexture(0, dxTexture);
  382. var dxMesh = meshCache[meshId];
  383. RenderMesh(dxMesh);
  384. }
  385. }
  386. private void RenderMesh(Mesh mesh) {
  387. for (int i = 0; i < mesh.NumberAttributes; ++i) {
  388. mesh.DrawSubset(i);
  389. }
  390. }
  391. private Device device;
  392. private PresentParameters presentParams;
  393. private Importer importer;
  394. private string directory;
  395. private aiScene scene;
  396. private Mesh[] meshCache;
  397. private ExtendedMaterial[] materialCache;
  398. private Dictionary<string, Texture> textureCache;
  399. private Vector3[] meshBoundsMin;
  400. private Vector3[] meshBoundsMax;
  401. private Camera g_sCamera = new Camera();
  402. private float g_zNear = 0.2f;
  403. private float g_zFar = 1000.0f;
  404. private Matrix g_mProjection = Matrix.Identity;
  405. private Matrix g_mWorldRotate = Matrix.Identity;
  406. }
  407. //-------------------------------------------------------------------------------
  408. // Position of the cursor relative to the 3ds max' like control circle
  409. //-------------------------------------------------------------------------------
  410. public enum EClickPos {
  411. // The click was inside the inner circle (x,y axis)
  412. Circle,
  413. // The click was inside one of tghe vertical snap-ins
  414. CircleVert,
  415. // The click was inside onf of the horizontal snap-ins
  416. CircleHor,
  417. // the cklick was outside the circle (z-axis)
  418. Outside
  419. };
  420. }