private static KdNode MakeLeaf(List <IShape> shapes) { return(new KdNode(KdNodePlane.NoPlane, PosVector.NewDefault(), shapes, null, null)); }
/* * Using Surface Area Heuristic (SAH) for finding best split pane * SAH = 0.5 * voxel_surface_area * number_of_objects_in_voxel * splitted_SAH = split_cost * + 0.5 * left_voxel_surface_area * number_of_objects_in_left_voxel * + 0.5 * right_voxel_surface_area * number_of_objects_in_right_voxel * Finding coordinate of split plane (XY, XZ or YZ) which minimizing SAH * If can't find optimal split plane - returns NONE * see: http://stackoverflow.com/a/4633332/653511 */ private static (KdNodePlane, PosVector) FindPlane(List <IShape> shapes, BoundingBox bbox, int depth) { var finalPlane = KdNodePlane.NoPlane; var finalPos = PosVector.NewDefault(); if (depth >= MaxTreeDepth || shapes.Count <= NumObjectsInLeaf) { return(KdNodePlane.NoPlane, PosVector.NewDefault()); } double hx = bbox.BoxMax.X - bbox.BoxMin.X; double hy = bbox.BoxMax.Y - bbox.BoxMin.Y; double hz = bbox.BoxMax.Z - bbox.BoxMin.Z; // calculate square of each side of initial bounding box double sxy = hx * hy; double sxz = hx * hz; double syz = hy * hz; double ssum = sxy + sxz + syz; // normalize square of each side of initial bounding box to satisfy following relationship: // sxy + sxz + syz = 1 sxy = sxy / ssum; sxz = sxz / ssum; syz = syz / ssum; int maxSplits = 5; // max splits of bounding box double splitCost = 5.0; // assum that at beginning best SAH has initial bounding box // SAH = 0.5 * square * objectsCount // square of initial bounding box is sxy + sxz + syz = 1 double bestSah = Convert.ToDouble(shapes.Count); // initial bounding box doesn't have split plane finalPlane = KdNodePlane.NoPlane; var currentSplitCoord = PosVector.NewDefault(); // find split surface which have the least SAH // trying to minimize SAH by splitting across XY plane double sSplit = sxy; double sNonSplit = sxz + syz; for (int i = 1; i < maxSplits; i++) { double l = Convert.ToDouble(i) / Convert.ToDouble(maxSplits); double r = 1.0 - l; // current coordinate of split surface currentSplitCoord = new PosVector(currentSplitCoord.X, currentSplitCoord.Y, bbox.BoxMin.Z + l * hz); var(vl, vr) = SplitBoundingBox(bbox, KdNodePlane.XY, currentSplitCoord); var currentSah = (sSplit + l * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vl)) + (sSplit + r * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vr)) + splitCost; if (currentSah < bestSah) { bestSah = currentSah; finalPlane = KdNodePlane.XY; finalPos = currentSplitCoord; } } // trying to minimize SAH by splitting across XZ plane sSplit = sxz; sNonSplit = sxy + syz; for (int i = 1; i < maxSplits; i++) { double l = Convert.ToDouble(i) / Convert.ToDouble(maxSplits); double r = 1.0 - l; // current coordinate of split surface currentSplitCoord = new PosVector(currentSplitCoord.X, bbox.BoxMin.Y + l * hy, currentSplitCoord.Z); var(vl, vr) = SplitBoundingBox(bbox, KdNodePlane.XZ, currentSplitCoord); var currentSah = (sSplit + l * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vl)) + (sSplit + r * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vr)) + splitCost; if (currentSah < bestSah) { bestSah = currentSah; finalPlane = KdNodePlane.XZ; finalPos = currentSplitCoord; } } // trying to minimize SAH by splitting across YZ plane sSplit = syz; sNonSplit = sxy + sxz; for (int i = 1; i < maxSplits; i++) { double l = Convert.ToDouble(i) / Convert.ToDouble(maxSplits); double r = 1.0 - l; // current coordinate of split surface currentSplitCoord = new PosVector(bbox.BoxMin.X + l * hy, currentSplitCoord.Y, currentSplitCoord.Z); var(vl, vr) = SplitBoundingBox(bbox, KdNodePlane.YZ, currentSplitCoord); var currentSah = (sSplit + l * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vl)) + (sSplit + r * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vr)) + splitCost; if (currentSah < bestSah) { bestSah = currentSah; finalPlane = KdNodePlane.YZ; finalPos = currentSplitCoord; } } return(finalPlane, finalPos); }
public static NffParserResult ParseFile(string path, int numThreads, int rayTraceDepth, int resolutionX, int resolutionY) { var background = new Background(new ColorVector(0.0, 0.0, 0.0), 0.0); var lights = new List <Light>(); var shapes = new List <Shape>(); var cameraAt = PosVector.NewDefault(); var cameraFrom = PosVector.NewDefault(); var cameraUp = PosVector.NewDefault(); var lookingFor = LookingFor.Instruction; var currentMaterial = new SolidMaterial(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, new ColorVector(0.0, 0.0, 0.0)); var polyVectors = new List <PosVector>(); var currentItemCounter = 0; var lines = File.ReadAllLines(path); foreach (var line in lines) { var split = line.Split(' ', '\t'); switch (lookingFor) { case LookingFor.Instruction: { var instruction = split[0]; if (instruction == "b") { // background color background = new Background(new ColorVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])), 0.0); } else if (instruction == "v") { // viewpoint location lookingFor = LookingFor.ViewpointFrom; } else if (instruction == "l") { // positional light var colorVector = split.Length == 7 ? new ColorVector(double.Parse(split[4]), double.Parse(split[5]), double.Parse(split[6])) : new ColorVector(1.0, 1.0, 1.0); lights.Add( new PointLight(new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])), colorVector)); } else if (instruction == "f") { // println!("reading f: {}", num); // object material properties // "f" red green blue Kd Ks Shine T index_of_refraction // Kd Diffuse component // Ks Specular // Shine Phong cosine power for highlights // T Transmittance (fraction of contribution of the transmitting ray). // Usually, 0 <= Kd <= 1 and 0 <= Ks <= 1, though it is not required that Kd + Ks = 1. Note that transmitting objects (T > 0) are considered to have two sides for algorithms that need these (normally, objects have one side). // todo: i don't think i'm assigning the correct values into my solidmaterial yet currentMaterial = new SolidMaterial( 0.0, // kAmbient double.Parse(split[4]), // kDiffuse double.Parse(split[5]), // kSpecular double.Parse(split[7]), // kReflection double.Parse(split[8]), // kTransparent double.Parse(split[8]), // refraction -- todo: which is which here? double.Parse(split[6]), // gloss new ColorVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])) ); } else if (instruction == "c") { // cone or cylinder } else if (instruction == "s") { // sphere shapes.Add(new SphereShape( new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])), double.Parse(split[4]), currentMaterial )); } else if (instruction == "p") { // polygon currentItemCounter = int.Parse(split[1]); polyVectors = new List <PosVector>(); lookingFor = LookingFor.Polygon; } else if (instruction == "pp") { // polygon patch } else if (instruction == "#") { // comment } } break; case LookingFor.Polygon: { if (currentItemCounter > 0) { currentItemCounter--; polyVectors.Add(new PosVector(double.Parse(split[0]), double.Parse(split[1]), double.Parse(split[2]))); } if (currentItemCounter == 0) { if (polyVectors.Count >= 3) { var firstVert = polyVectors[0]; var prevVert = polyVectors[1]; var thisVert = polyVectors[2]; shapes.Add(new TriangleShape(firstVert, prevVert, thisVert, currentMaterial, currentMaterial)); for (var i = 3; i < polyVectors.Count; i++) { prevVert = thisVert; thisVert = polyVectors[i]; shapes.Add(new TriangleShape(firstVert, prevVert, thisVert, currentMaterial, currentMaterial)); } } lookingFor = LookingFor.Instruction; } } break; case LookingFor.ViewpointFrom: { cameraFrom = new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])); lookingFor = LookingFor.ViewpointAt; } break; case LookingFor.ViewpointAt: { cameraAt = new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])); lookingFor = LookingFor.ViewpointUp; } break; case LookingFor.ViewpointUp: { cameraUp = new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])); lookingFor = LookingFor.ViewpointAngle; } break; case LookingFor.ViewpointAngle: { // todo: implement lookingFor = LookingFor.ViewpointHither; } break; case LookingFor.ViewpointHither: { // todo: implement lookingFor = LookingFor.ViewpointResolution; } break; case LookingFor.ViewpointResolution: { //resolutionX = int.Parse(split[1]); //resolutionY = int.Parse(split[2]); lookingFor = LookingFor.Instruction; } break; } } return(new NffParserResult(Scene.Create(background, shapes, lights), new RenderData(resolutionX, resolutionY, rayTraceDepth, numThreads, true), new Camera(cameraFrom, cameraAt, cameraUp, 50.0))); }