// constructor public Sphere(Position3 position, double radius, bool useNormalMap, CreatePositionDelegate createPosition, CreateTerrainNodeVertexBufferDelegate createTerrainNodeVertexBuffer, bool isSphere) { this.position = position; this.Radius = radius; this.useNormalMap = useNormalMap; this.createPosition = createPosition; this.createTerrainNodeVertexBuffer = createTerrainNodeVertexBuffer; this.isSphere = isSphere; Initialize(); }
public static double CreatePositionSphere(ref Position3 spherePosition, ref Position3 cubePosition, ref Vector2 patchPosition, double radius, out Position3 outPosition) { // the input position is a point on the surface of a unit sphere // by default we just want to move it out to the sphere radius // descendant classes will do more interesting things, like add some perlin noise // to it for terrain outPosition = spherePosition * radius; return 0; }
public static double CreatePositionPlanet(ref Position3 spherePosition, ref Position3 cubePosition, ref Vector2 patchPosition, double radius, out Position3 outPosition) { Position3 p = cubePosition * 1.2; double h0 = Math.Abs(Noise.fBm(p.X, p.Y, p.Z, 3)) / 10.0; double h1 = Noise.Turbulence(p.X, p.Y, p.Z, 3, 2.0, 0.5); double h2 = Noise.HybridMultiFractal(p.X, p.Y, p.Z, 14, 1.854143, h0, 0.60); double h = h1 * h2; h *= 60; outPosition = spherePosition * (radius + h); return h; }
/// <summary> /// Determine if the node needs to be split based on camera distance and field of view /// </summary> /// <param name="cameraPosition">Planet-space camera position </param> /// <param name="fieldOfView">Camera's field of view</param> /// <returns>True if the terrain node needs to be split</returns> private bool NeedsSplit(Position3 planetPosition, Position3 cameraPosition, float fieldOfView) { // limit splitting if (isSphere && level >= Constants.MaxAtmosphereNodeLevel) return false; if (!isSphere && level >= Constants.MaxTerrainNodeLevel) return false; // get distance between camera and terrain node center - both are already in planet space float viewDistance = GetViewDistance(planetPosition, cameraPosition); // TODO : splitting not working properly when zooming in/out - seems to be the opposite of what's needed // calculate screen space error float k = 1024.0f * 0.5f * (float)Math.Tan(fieldOfView * 0.5f); float screenSpaceError = (geometricError / viewDistance) * k; // return true if the screen space error is greater than the max return (screenSpaceError > maxScreenSpaceError); }
/// <summary> /// Get distance from camera /// </summary> /// <param name="planetPosition">Position of planet center</param> /// <param name="cameraPosition">Camera position in planet space</param> /// <returns></returns> private float GetViewDistance(Position3 planetPosition, Position3 cameraPosition) { //return (float)Position3.Distance(this.Position, cameraPosition); // need to get the camera position in patch-space to compare the distance Vector3 v = (cameraPosition - this.Position).AsVector3; // translate to object space by adding patch position to planet position then subtracting from the camera position //Vector3 v = (cameraPosition - (planetPosition + this.position)).AsVector3; float minDistance = 999999999999; closestPosition = new Position3(vertexBuffer.Vertices[MapVertexIndex(Constants.PatchWidth / 2, Constants.PatchHeight / 2)].Position); // center center CheckDistance(Constants.PatchWidth / 2, Constants.PatchHeight / 2, ref v, ref minDistance); // bottom left CheckDistance(0, 0, ref v, ref minDistance); // bottom center CheckDistance(Constants.PatchWidth / 2, 0, ref v, ref minDistance); // bottom right CheckDistance(Constants.PatchWidth - 1, 0, ref v, ref minDistance); // top left CheckDistance(0, Constants.PatchHeight - 1, ref v, ref minDistance); // top center CheckDistance(Constants.PatchWidth / 2, Constants.PatchHeight - 1, ref v, ref minDistance); // top right CheckDistance(Constants.PatchWidth - 1, Constants.PatchHeight - 1, ref v, ref minDistance); // left center CheckDistance(0, Constants.PatchHeight / 2, ref v, ref minDistance); // right center CheckDistance(Constants.PatchWidth - 1, Constants.PatchHeight / 2, ref v, ref minDistance); // transform to planet-space closestPosition += this.Position; // return closest distance return (float)Math.Sqrt(minDistance); }
public void Subtract(ref Position3 V1, ref Position3 V2) { X = V1.X - V2.X; Y = V1.Y - V2.Y; Z = V1.Z - V2.Z; }
public void Update(Position3 cameraPosition, float fieldOfView) { // update nodes, allowing them to split if required front.Update(position, cameraPosition, fieldOfView); back.Update(position, cameraPosition, fieldOfView); left.Update(position, cameraPosition, fieldOfView); right.Update(position, cameraPosition, fieldOfView); top.Update(position, cameraPosition, fieldOfView); bottom.Update(position, cameraPosition, fieldOfView); }
/// <summary> /// Calculate angle from camera to horizon /// </summary> /// <param name="cameraPosition">Planet space camera position</param> /// <returns>Horizon angle in radians</returns> public float CalculateHorizonAngle(Position3 cameraPosition) { double result = MathHelper.ToRadians(300); double h = cameraPosition.Length(); if (h > radius) { result = Math.Acos(radius / h); result += MathHelper.ToRadians(5); // add 5 degrees to account for mountains on the horizon // if we're fairly far away from the planet we need to account // for the very large patch size of the 6 faces that make up the planet h = h - radius - 9; if (h > 1000) result += MathHelper.ToRadians(50); } return (float)result; }
public void Clone(Movement movement) { Orientation = movement.Orientation; fLocalPosition = movement.fLocalPosition; UpdateMovement(0); }
/// <summary> /// Split this node's children /// </summary> /// <param name="cameraPosition">Planet-space camera position</param> /// <param name="fieldOfView">Camera's field of view</param> private void SplitChildren(Position3 planetPosition, Position3 cameraPosition, float fieldOfView) { if (HasChildren) { children[0].Split(planetPosition, cameraPosition, fieldOfView); children[1].Split(planetPosition, cameraPosition, fieldOfView); children[2].Split(planetPosition, cameraPosition, fieldOfView); children[3].Split(planetPosition, cameraPosition, fieldOfView); } }
public Vector3[] GetHeightAndNormal(Position3 position, out double height, out Vector3 normal) { // we know position is over this node, now we need to find which triangle within the node // normalize the position so it points out from the planet center towards the actual position position.Normalize(); // get the direction vector Position3 direction3 = position; Vector3 direction = position.AsVector3; // get a position 1km under the planet surface, in planet space position *= radius - 1; // translate to patch space position -= this.Position; // get ray from position to planet center, translate to patch space Ray ray = new Ray(position.AsVector3, direction); float? intersection = null; float u = 0; float v = 0; Vector3 v0 = Vector3.Zero; Vector3 v1 = Vector3.Zero; Vector3 v2 = Vector3.Zero; Vector3 v4 = Vector3.Zero; Vector3 n0 = Vector3.Zero; Vector3 n1 = Vector3.Zero; Vector3 n2 = Vector3.Zero; Vector3 n4 = Vector3.Zero; int triangleIndex = 0; int quadIndex = 0; int index; int y; int x; // now we need to loop through each triangle and see if the ray intersects for (int i = 0; i < TerrainNodeIndexBuffer.IndexCount; i += 3) { index = TerrainNodeIndexBuffer.IndexData[i]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v0 = vertexBuffer.Vertices[index].Position; n0 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[i + 1]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v1 = vertexBuffer.Vertices[index].Position; n1 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[i + 2]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v2 = vertexBuffer.Vertices[index].Position; n2 = vertexBuffer.Vertices[index].Normal; Tools.RayIntersectsTriangle(ref ray, ref v0, ref v1, ref v2, out intersection, out u, out v); if (intersection != null) { triangleIndex = i; quadIndex = i; if (quadIndex % 6 != 0) quadIndex -= 3; break; } } Globals.TriangleIndex = triangleIndex; if (intersection == null) { height = radius; normal = direction; return null; } // Now that we've calculated the indices of the corners of our cell, and // where we are in that cell, we'll use bilinear interpolation to calculuate // our height. This process is best explained with a diagram, so please see // the accompanying doc for more information. // First, calculate the heights on the bottom and top edge of our cell by // interpolating from the left and right sides. // get the vertices for the quad containing this triangle index = TerrainNodeIndexBuffer.IndexData[quadIndex + 0]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v0 = vertexBuffer.Vertices[index].Position; n0 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[quadIndex + 1]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v1 = vertexBuffer.Vertices[index].Position; n1 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[quadIndex + 2]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v2 = vertexBuffer.Vertices[index].Position; n2 = vertexBuffer.Vertices[index].Normal; index = TerrainNodeIndexBuffer.IndexData[quadIndex + 4]; y = index / Constants.PatchHeight; x = index % Constants.PatchWidth; index = (y + 1) * patchColumns + (x + 1); v4 = vertexBuffer.Vertices[index].Position; n4 = vertexBuffer.Vertices[index].Normal; /* Vector3 top, bottom, p, topNormal, bottomNormal, n; if (triangleIndex % 6 == 0) { // we're in triangle A - the top left triangle top = Vector3.Lerp(v0, v1, u); bottom = Vector3.Lerp(v2, v4, u); p = Vector3.Lerp(top, bottom, v); topNormal = Vector3.Lerp(n0, n1, u); bottomNormal = Vector3.Lerp(n2, n4, u); n = Vector3.Lerp(topNormal, bottomNormal, v); n.Normalize(); } else { // we're in triangle B - the bottom right triangle // this means u and v are positions within *that* triangle, so we need to do our lerping differently // we're in triangle A - the top left triangle // TODO : except u and v are acting very odd! they don't seem to be correct // let's try getting the quad all at once, then doing the Ray check on each triangle in the quad top = Vector3.Lerp(v0, v1, u); bottom = Vector3.Lerp(v2, v4, u); p = Vector3.Lerp(top, bottom, v); topNormal = Vector3.Lerp(n0, n1, u); bottomNormal = Vector3.Lerp(n2, n4, u); n = Vector3.Lerp(topNormal, bottomNormal, v); n.Normalize(); } */ // intersection gives us the length on the ray of the exact intersection position, // which gives us the exact height value to use Position3 vp = this.Position + (position + (direction3 * (double)intersection)); height = vp.Length(); // but now, how do we get the normal from that? let's start with just averaging the normals normal = (n0 + n1 + n2 + n4) * 0.25f; Vector3[] result = new Vector3[5]; result[0] = v0; result[1] = v1; result[2] = v2; result[3] = v4; result[4] = (vp - this.Position).AsVector3; return result; }
/// <summary> /// Generate terrain mesh for this node /// </summary> public void GenerateMesh(TerrainNodeSplitItem item) { // the minus one here is correct: e.g. 3x3 vertices labeled 0, 1, 2: v0.x = 0, v1.x = 0.5, v2.x = 1. The increment = 1 / (3 - 1) = 0.5 double horizontalStep = bounds.Width / (Constants.PatchWidth - 1); double verticalStep = bounds.Height / (Constants.PatchHeight - 1); float horizontalTextureStep = 1.0f / (Constants.PatchWidth - 1); float verticalTextureStep = 1.0f / (Constants.PatchHeight - 1); float MinX, MinY, MinZ; float MaxX, MaxY, MaxZ; // initialize min and max vertex tracking MinX = MinY = MinZ = 999999; MaxX = MaxY = MaxZ = -999999; // get matrix used to transform vertices to the proper cube face Matrix faceMatrix = CubeFaces.GetFace(bounds.Face).FaceMatrix; // create vertex storage using user supplied delegate vertexBuffer = createTerrainNodeVertexBuffer(item.HeightData.Length); //int cubeVertexIndex = 0; //CubeVertices = new Vector2[item.HeightData.Length]; hasMeshBorder = false; int vertexIndex = 0; int Rows = Constants.PatchHeight; int Columns = Constants.PatchWidth; if (item.HeightData.Length > Constants.PatchWidth * Constants.PatchHeight) { hasMeshBorder = true; Rows += 2; Columns += 2; } patchRows = Rows; patchColumns = Columns; float v = 0; double y = bounds.Bottom; if (hasMeshBorder) { v -= verticalTextureStep; y -= verticalStep; } for (int hy = 0; hy < Rows; hy++) { float u = 0; double x = bounds.Left; if (hasMeshBorder) { u -= horizontalTextureStep; x -= horizontalStep; } for (int hx = 0; hx < Columns; hx++) { // create the vertex position and rotate it to the appropriate face Position3 cubePosition = new Position3(x, y, 1); Position3 spherePosition = Tools.CubeToSphereMapping(x, y, 1); spherePosition.Transform(ref faceMatrix); cubePosition.Transform(ref faceMatrix); // transform the vertex based on the user defined delegate double height; Position3 finalPosition; Vector2 patchPosition = new Vector2(hx, hy); if (item == null) CreatePosition(ref spherePosition, ref cubePosition, ref patchPosition, out finalPosition, out height); else { height = item.HeightData[hy * Columns + hx]; if (height < 0) height = 0; finalPosition = spherePosition * (radius + height); TranslateToPatchSpace(ref finalPosition); } VertexPositionNormalTextureHeight vertex = new VertexPositionNormalTextureHeight(); vertex.Position = (Vector3)finalPosition; vertex.Height = (float)height; vertex.Normal = (Vector3)spherePosition; vertex.TextureCoordinate = new Vector2(u, v); vertex.Tangent = Vector4.Zero; vertexBuffer.Vertices[vertexIndex++] = vertex; // track min and max coordinates, but only for the vertices that will be in the final mesh if (hx >= 1 && hx < Columns - 1 && hy >= 1 && hy < Rows - 1) { if (vertex.Position.X < MinX) MinX = vertex.Position.X; if (vertex.Position.Y < MinY) MinY = vertex.Position.Y; if (vertex.Position.Z < MinZ) MinZ = vertex.Position.Z; if (vertex.Position.X > MaxX) MaxX = vertex.Position.X; if (vertex.Position.Y > MaxY) MaxY = vertex.Position.Y; if (vertex.Position.Z > MaxZ) MaxZ = vertex.Position.Z; } x += horizontalStep; u += horizontalTextureStep; } y += verticalStep; v += verticalTextureStep; } // create min and max bounding vertices, in patch-space minVertex = new Vector3(MinX, MinY, MinZ); maxVertex = new Vector3(MaxX, MaxY, MaxZ); // calculate normals and tangents CalculateNormals(); CalculateTangents(); // save vertex buffer changes - this effectively copies the raw data to a vertex buffer on the device vertexBuffer.CommitChanges(Constants.PatchWidth, Constants.PatchHeight); }
// face space method, using ray intersection in planet space public TerrainNode FindNodeUnderPosition(Position3 position, out Vector3[] triangle, out double height, out Vector3 normal) { triangle = null; height = 0; normal = Vector3.Zero; Position3 originalPosition = position; // normalize the position so it points out from the planet center towards the actual position position.Normalize(); // get the direction vector Vector3 direction = position.AsVector3; // get a position 1km under the planet surface position *= radius - 1; // get ray from position to planet center, translate to patch space Ray ray = new Ray((position - this.Position).AsVector3, direction); Vector3 p1 = MinVertex; Vector3 p2 = MaxVertex; // first do a bounding box/ray intersection test BoundingBox box = new BoundingBox(MinVertex, MaxVertex); // if the ray doesn't intersect the bounding box then it can't intersect terrain node if (ray.Intersects(box) == null) return null; // we have a potential match - do mesh collision to be sure Vector3[] t = this.GetHeightAndNormal(originalPosition, out height, out normal); // if no mesh collision then we're done if (t == null) return null; // if the position is over this node and this is a leaf node then we have what we want if (!HasChildren) { triangle = t; return this; } // otherwise we want to recurse into the children to find the leaf node for (int i = 0; i < children.Length; i++) { TerrainNode node = children[i].FindNodeUnderPosition(position, out triangle, out height, out normal); if (node != null) return node; } // if we get here then we have a problem - the position is allegedly above this node, which means // it should also be above one of this node's children, but we didn't find a child // return this; if (Globals.Game.Graphics.IsFullScreen) Globals.Game.Graphics.ToggleFullScreen(); throw new Exception("Unable to find leaf node."); }
public Position3(Position3 V) { X = V.X; Y = V.Y; Z = V.Z; }
public void SubtractNormalize(ref Position3 V1, ref Vector3 V2) { X = V1.X - V2.X; Y = V1.Y - V2.Y; Z = V1.Z - V2.Z; double L = 1 / Math.Sqrt(X * X + Y * Y + Z * Z); X *= L; Y *= L; Z *= L; }
public void SubtractFrom(ref Position3 V1) { X = V1.X - X; Y = V1.Y - Y; Z = V1.Z - Z; }
public void Subtract(ref Position3 V1) { X -= V1.X; Y -= V1.Y; Z -= V1.Z; }
/// <summary> /// Split or unsplit nodes and child nodes as required /// </summary> /// <param name="cameraPosition">Planet-space camera position</param> /// <param name="fieldOfView">Camera's field of view</param> private void Split(Position3 planetPosition, Position3 cameraPosition, float fieldOfView) { // if we're already splitting then we don't want to do any other split processing until the thread is complete // this node will continue to be the leaf node in this branch so it will continue to draw until the // splitting is completed if (Splitting) { // if we no longer need to split, but we're still queued up for splitting then cancel it - the thread // will see this when it gets to the the node in the queue and won't do anything if (!NeedsSplit(planetPosition, cameraPosition, fieldOfView)) CancelSplitting = true; return; } // if this node needs to be split then check its children or queue up a split if (NeedsSplit(planetPosition, cameraPosition, fieldOfView)) { // if it already has children then we've already split, so recurse through the children if (HasChildren) { SplitChildren(planetPosition, cameraPosition, fieldOfView); } // otherwise we need to be split and have no children, so queue up the child node to the node splitter queue thread else { // setting this will cause drawing code to pretend this is a leaf node until the split is complete // it will also become non-splittable during this time CancelSplitting = false; Splitting = true; QueueNodeGeneration(); } } // if we don't need to be split then we can remove child nodes else { // if our children are all leaf nodes then we no longer need them, so get rid of them and this node is now a leaf node if (ChildrenAreLeafNodes()) ClearChildren(); // if we do have non-leaf children then recurse down until we find a node whose children are leaves, and remove those children // the lower level children will continue to be drawn until they've been cleared out, at only 1 level per frame if (HasChildren) SplitChildren(planetPosition, cameraPosition, fieldOfView); } }
/// <summary> /// Split all the way down to leaf nodes before generating the actual terrain. /// The deepest node with data will still be displayed until all the leaf nodes /// are ready. /// </summary> /// <param name="planetPosition"></param> /// <param name="cameraPosition"></param> /// <param name="fieldOfView"></param> private void Split2(Position3 planetPosition, Position3 cameraPosition, float fieldOfView) { }
/// <summary> /// Translate sphere-space position to patch-space /// </summary> /// <param name="position">Sphere-space position</param> public void TranslateToPatchSpace(ref Position3 position) { position.Subtract(ref this.position); }
public void UpdateMovement(float elapsed) { fRotationalVelocity += (fRotationalAcceleration * elapsed); fRotationTime += elapsed; while (fRotationTime >= (1.0f / 60.0f)) { fRotationalVelocity *= fRotationalDrag; fRotationTime -= (1.0f / 60.0f); fRotationalAcceleration = Vector3.Zero; } float Yaw = MathHelper.ToRadians(fRotationalVelocity.Y * elapsed); float Pitch = MathHelper.ToRadians(fRotationalVelocity.X * elapsed); float Roll = MathHelper.ToRadians(fRotationalVelocity.Z * elapsed); // create a rotation quaternion based on orientation changes - this is the amount to rotate Quaternion Rotation = Quaternion.CreateFromYawPitchRoll(Yaw, Pitch, Roll); // add the rotation to the current rotation quaternion fOrientation *= Rotation; UpdateOrientation(); // apply linear acceleration to velocity fLinearVelocity += fLinearAcceleration * elapsed; fLinearTime += elapsed; while (fLinearTime >= (1.0f / 60.0f)) { fLinearVelocity *= fLinearDrag; fLinearTime -= (1.0f / 60.0f); fLinearAcceleration = Vector3.Zero; } fLocalPosition.Add(fLinearVelocity); Position = fLocalPosition + AttachedPosition; // update matrices UpdateWorldMatrix(); }
public void Update(Position3 planetPosition, Position3 cameraPosition, float fieldOfView) { Split(planetPosition, cameraPosition, fieldOfView); }
public void LookAt(Position3 Target) { Target -= Position; SetWorldMatrix(Matrix.CreateLookAt(Vector3.Zero, (Vector3)Target, (Vector3)fUp)); }
/// <summary> /// Calculate the sphere-space position of the patch by determining where the center vertex lies on the sphere /// </summary> private void CalculatePatchPosition(TerrainNodeSplitItem item) { // find the cube position of the center vertex double x = bounds.Left + bounds.Width / 2; double y = bounds.Bottom + bounds.Height / 2; // get the matrix used to rotate the a position to the appropriate cube face Matrix faceMatrix = CubeFaces.GetFace(bounds.Face).FaceMatrix; // create the vertex position in cube-space and rotate it to the appropriate face Position3 cubePosition = new Position3(x, y, 1); cubePosition.Transform(ref faceMatrix); // get the vertex position on the unit sphere and rotate it to the appropriate face Position3 spherePosition = Tools.CubeToSphereMapping(x, y, 1); spherePosition.Transform(ref faceMatrix); spherePosition.Normalize(); Vector2 patchPosition = new Vector2(Constants.PatchWidth / 2, Constants.PatchHeight / 2); if (item == null) { // transform the vertex based on the user defined delegate - this returns the height value of the point on the sphere createPosition(ref spherePosition, ref cubePosition, ref patchPosition, radius, out position); } else { double h = item.HeightData[(int)((patchPosition.Y + 1) * (Constants.PatchWidth + 2) + (patchPosition.X + 1))]; if (h < 0) h = 0; position = spherePosition * (radius + h); } }
public TerrainNode FindNodeUnderPosition(Position3 position, out Vector3[] triangle, out double height, out Vector3 normal) { TerrainNode result = null; result = front.FindNodeUnderPosition(position, out triangle, out height, out normal); if (result == null) result = back.FindNodeUnderPosition(position, out triangle, out height, out normal); if (result == null) result = left.FindNodeUnderPosition(position, out triangle, out height, out normal); if (result == null) result = right.FindNodeUnderPosition(position, out triangle, out height, out normal); if (result == null) result = top.FindNodeUnderPosition(position, out triangle, out height, out normal); if (result == null) result = bottom.FindNodeUnderPosition(position, out triangle, out height, out normal); return result; }
public void AddNormalize(ref Position3 V1, ref Vector3 V2) { X = V1.X + V2.X; Y = V1.Y + V2.Y; Z = V1.Z + V2.Z; double L = 1 / Math.Sqrt(X * X + Y * Y + Z * Z); X *= L; Y *= L; Z *= L; }
private void CheckDistance(int x, int y, ref Vector3 v, ref float minDistance) { VertexPositionNormalTextureHeight p = vertexBuffer.Vertices[y * patchColumns + x]; // MapVertexIndex(x, y)]; float d = Vector3.DistanceSquared(p.Position, v); if (d < minDistance) { minDistance = d; closestPosition = new Position3(p.Position); } }
public bool Equals(Position3 P) { // if (P == null) return false; return (X == P.X && Y == P.Y && Z == P.Z); }
/// <summary> /// Create a patch-space position by using the user supplied delegate. /// </summary> /// <param name="inPosition">Input position, located on the surface of a unit sphere</param> /// <param name="outPosition">Output position, moved out to the sphere surface, or altered with noise, as determined by the user supplied delegate</param> private void CreatePosition(ref Position3 spherePosition, ref Position3 cubePosition, ref Vector2 patchPosition, out Position3 outPosition, out double height) { height = createPosition(ref spherePosition, ref cubePosition, ref patchPosition, radius, out outPosition); TranslateToPatchSpace(ref outPosition); }
public void Add(ref Position3 V1, ref Position3 V2) { X = V1.X + V2.X; Y = V1.Y + V2.Y; Z = V1.Z + V2.Z; }