public OBJFileParser Create(int recursionLevel) { this.geometry = new OBJFileParser(); this.middlePointIndexCache = new Dictionary <long, int>(); this.index = 0; // create 12 vertices of a icosahedron var t = (1.0 + Math.Sqrt(5.0)) / 2.0; addVertex(new Point(-1, t, 0)); addVertex(new Point(1, t, 0)); addVertex(new Point(-1, -t, 0)); addVertex(new Point(1, -t, 0)); addVertex(new Point(0, -1, t)); addVertex(new Point(0, 1, t)); addVertex(new Point(0, -1, -t)); addVertex(new Point(0, 1, -t)); addVertex(new Point(t, 0, -1)); addVertex(new Point(t, 0, 1)); addVertex(new Point(-t, 0, -1)); addVertex(new Point(-t, 0, 1)); // create 20 triangles of the icosahedron var faces = new List <Tri>(); // 5 faces around point 0 faces.Add(new Tri(0, 11, 5)); faces.Add(new Tri(0, 5, 1)); faces.Add(new Tri(0, 1, 7)); faces.Add(new Tri(0, 7, 10)); faces.Add(new Tri(0, 10, 11)); // 5 adjacent faces faces.Add(new Tri(1, 5, 9)); faces.Add(new Tri(5, 11, 4)); faces.Add(new Tri(11, 10, 2)); faces.Add(new Tri(10, 7, 6)); faces.Add(new Tri(7, 1, 8)); // 5 faces around point 3 faces.Add(new Tri(3, 9, 4)); faces.Add(new Tri(3, 4, 2)); faces.Add(new Tri(3, 2, 6)); faces.Add(new Tri(3, 6, 8)); faces.Add(new Tri(3, 8, 9)); // 5 adjacent faces faces.Add(new Tri(4, 9, 5)); faces.Add(new Tri(2, 4, 11)); faces.Add(new Tri(6, 2, 10)); faces.Add(new Tri(8, 6, 7)); faces.Add(new Tri(9, 8, 1)); // refine triangles for (int i = 0; i < recursionLevel; i++) { var faces2 = new List <Tri>(); foreach (var tri in faces) { // replace triangle by 4 triangles int a = getMiddlePoint(tri.v1, tri.v2); int b = getMiddlePoint(tri.v2, tri.v3); int c = getMiddlePoint(tri.v3, tri.v1); faces2.Add(new Tri(tri.v1, a, c)); faces2.Add(new Tri(tri.v2, b, a)); faces2.Add(new Tri(tri.v3, c, b)); faces2.Add(new Tri(a, b, c)); } faces = faces2; } // done, now add triangles to mesh Point origin = new Point(0, 0, 0); foreach (var tri in faces) { // Create the point normals for the smoothing Vector n0 = (geometry.Verticies[tri.v1] - origin).Normalize(); Vector n1 = (geometry.Verticies[tri.v2] - origin).Normalize(); Vector n2 = (geometry.Verticies[tri.v3] - origin).Normalize(); // texture map coordinates Point t0 = new Point((geometry.Verticies[tri.v1].X + 1) / 2, (geometry.Verticies[tri.v1].Y + 1) / 2, 0); Point t1 = new Point((geometry.Verticies[tri.v2].X + 1) / 2, (geometry.Verticies[tri.v2].Y + 1) / 2, 0); Point t2 = new Point((geometry.Verticies[tri.v3].X + 1) / 2, (geometry.Verticies[tri.v3].Y + 1) / 2, 0); // Create the texture maps for each vertex SmoothTriangle st = new SmoothTriangle(geometry.Verticies[tri.v1], geometry.Verticies[tri.v2], geometry.Verticies[tri.v3]); st.AddNormals(n0, n1, n2); st.AddTexture(t0, t1, t2); this.geometry.AddToCurrentGroup(st); } return(this.geometry); }
static void Main(string[] args) { IcoSphereCreator dome = new IcoSphereCreator(); OBJFileParser g = dome.Create(1); }
///------------------------------------------------------------------------------------------------- /// <summary> Main entry-point for this application. </summary> /// /// <remarks> Kemp, 11/19/2018. </remarks> /// /// <param name="args"> An array of command-line argument strings. Not used.</param> ///------------------------------------------------------------------------------------------------- static void Main(string[] args) { bool show_help = false; bool mirror = false; bool center = false; bool bbox = false; bool nm = false; bool np = false; string outputFile = ""; uint canvasX = 200; uint canvasY = 200; uint rotx = 0; uint roty = 0; uint rotz = 0; double cameraZ = 0; double cameraY = 0; double myFOV = 0; bool serial = false; var pa = new OptionSet() { { "o|output=", "the {NAME} of the output PMM file.", v => outputFile = v }, { "W|width=", "The width in pixels of the output image.", (uint v) => canvasX = v }, { "H|height=", "The height in pixels of the output image.", (uint v) => canvasY = v }, { "h|help", "show this message and exit", v => show_help = v != null }, { "x|xrotation=", "The rotation around the x axis in units of PI/2.", (uint v) => rotx = v }, { "y|yrotation=", "The rotation around the y axis in units of PI/2.", (uint v) => roty = v }, { "z|zrotation=", "The rotation around the z axis in units of PI/2.", (uint v) => rotz = v }, { "cz|cameraz=", "The angle of rotation of the camera in the upward direction in radians.", (double v) => cameraZ = v }, { "cy|cameray=", "The angle of rotation of the camera in the right-ward direction in radians.", (double v) => cameraY = v }, { "fov|fieldofview=", "The field of view of the camera (zoom).", (double v) => myFOV = v }, { "s|serial", "Use only one processor to render.", v => serial = v != null }, { "M|mirror", "Mirror image on X", v => mirror = v != null }, { "C|center", "Center group at the origin", v => center = v != null }, { "B|boundingbox", "Display bounding box of object", v => bbox = v != null }, { "n|nomove", "Don't move object to first quadrant", v => nm = v != null }, { "p|noplane", "Don't show the plane", v => np = v != null } }; List <string> extra; try { extra = pa.Parse(args); } catch (OptionException e) { Console.Write("RenderTriangleMesh: "); Console.WriteLine(e.Message); Console.WriteLine("Try `RenderTriangleMesh --help' for more information."); return; } if (show_help) { Console.WriteLine("RenderTriangleMesh [Options] inputFileName"); Console.WriteLine("Options: "); pa.WriteOptionDescriptions(Console.Out); return; } /// The only positional parameter is the file name to process if (extra.Count != 1) { Console.Write("RenderTriangleMesh: exactly one input file name permitted."); return; } string ifn = extra[0]; string ifp = Path.GetDirectoryName(ifn); if (outputFile == "") { outputFile = Path.Combine(ifp, Path.GetFileNameWithoutExtension(ifn)); } Console.WriteLine("Started: " + DateTime.Now.ToString()); World w = new World(); /// Read the triangular mesh and color all the triangles red. OBJFileParser p = new OBJFileParser(ifn, OBJFileParser.TriangleType.Smooth, mirror); Color red = new Color(1, 0, 0); Color blue = new Color(0, 0, 1); Color gray = new Color(0.5, 0.5, 0.5); /// collect what was parsed into a single group Group g = new Group(); //Group a = Axes.Generate(25,5,new Color(1,0,0)); //w.AddObject(a); //w.AddObject(BoundingBox.Generate(a,new Color(1,1,0))); foreach (Group gp in p.Groups) { g.AddObject(gp); } Console.WriteLine("Objects in default: " + g.Children.Count().ToString()); //ColorShapes(g, gray); /// Transform the group containing the triangular mesh to correspond to our world and camera view point. /// Move it back to origin, rotate it, and return it to where it was. if (center) { g.Transform = (Matrix)(MatrixOps.CreateTranslationTransform(0 - (g.Bounds.MinCorner.X + (g.Bounds.MaxCorner.X - g.Bounds.MinCorner.X) / 2), 0 - (g.Bounds.MinCorner.Y + (g.Bounds.MaxCorner.Y - g.Bounds.MinCorner.Y) / 2), 0 - (g.Bounds.MinCorner.Z + (g.Bounds.MaxCorner.Z - g.Bounds.MinCorner.Z) / 2)) * g.Transform); } if (rotx != 0) { g.Transform = (Matrix)(MatrixOps.CreateRotationXTransform(rotx * (Math.PI / 2)) * g.Transform); } if (roty != 0) { g.Transform = (Matrix)(MatrixOps.CreateRotationYTransform(roty * (Math.PI / 2)) * g.Transform); } if (rotz != 0) { g.Transform = (Matrix)(MatrixOps.CreateRotationZTransform(rotz * (Math.PI / 2)) * g.Transform); } /// Move the whole group into the first quadrant if (!nm) { g.Transform = (Matrix)(MatrixOps.CreateTranslationTransform(0 - g.Bounds.MinCorner.X, 0 - g.Bounds.MinCorner.Y, 0 - g.Bounds.MinCorner.Z) * g.Transform); w.AddObject(g); } /// add bounding box if (bbox) { Group bb = BoundingBox.Generate(g, blue); w.AddObject(bb); } /// three planes make up the background. if (!np) { Plane floor = new Plane(); floor.Material.Pattern = new Checked3DPattern(new Color(1, 1, 1), new Color(0.75, 0.75, 0.75)); floor.Material.Pattern.Transform = MatrixOps.CreateScalingTransform(2, 2, 2); w.AddObject(floor); Plane wallz = (Plane)floor.Copy(); wallz.Transform = (Matrix)(MatrixOps.CreateRotationXTransform(Math.PI / 2) * MatrixOps.CreateTranslationTransform(0, 0, 0)); //w.AddObject(wallz); Plane wallx = (Plane)floor.Copy(); wallx.Transform = (Matrix)(MatrixOps.CreateRotationZTransform(Math.PI / 2) * MatrixOps.CreateTranslationTransform(0, 0, 0)); //w.AddObject(wallx); } Console.WriteLine("Bounds of object: " + g.Bounds.ToString()); w.AddLight(new LightPoint(new Point(50000, 70000, -50000), new Color(0.5, 0.5, 0.5))); // w.AddLight(new LightPoint(new Point(-50000, 70000, -50000), new Color(0.2, 0.2, 0.2))); // w.AddLight(new RTLightPoint(new RTPoint(0, 10, -10), new Color(0.5, 0.5, 0.5))); /// Render the image Console.WriteLine("Now rendering ..."); Point cameraPoint = CalcCameraPoint(g, cameraY, cameraZ); Point cameraPointsAt = new Point(g.Bounds.MinCorner.X + (g.Bounds.MaxCorner.X - g.Bounds.MinCorner.X) / 2, g.Bounds.MinCorner.Y + (g.Bounds.MaxCorner.Y - g.Bounds.MinCorner.Y) / 2, g.Bounds.MinCorner.Z + (g.Bounds.MaxCorner.Z - g.Bounds.MinCorner.Z) / 2); double cameraFOV = myFOV == 0 ? CalcCameraFOV(cameraPoint, cameraPointsAt, g) : myFOV; Camera camera = new Camera(canvasX, canvasY, cameraFOV); camera.Transform = MatrixOps.CreateViewTransform(cameraPoint, cameraPointsAt, new RayTracerLib.Vector(0, 1, 0)); Console.WriteLine("CameraPoint = " + cameraPoint.ToString() + "CameraPointsAt = " + cameraPointsAt.ToString()); Console.WriteLine("CameraFOV = " + cameraFOV.ToString()); Canvas image; if (serial) { image = w.Render(camera); } else { image = w.ParallelRender(camera); } Console.WriteLine("Now writing output ..."); /* * String ppm = image.ToPPM(); * String ppmname = ("ToPPM.ppm"); * System.IO.File.WriteAllText(outputFile, ppm); */ image.WritePNG(outputFile + ".png"); Console.WriteLine("Finished: " + DateTime.Now.ToString()); Console.Write("Press Enter to finish ... "); Console.Read(); }