private void Init(TreeSkeleton skeleton) { // Get branch transforms Matrix[] transforms = new Matrix[skeleton.Branches.Count]; skeleton.CopyAbsoluteBranchTransformsTo(transforms); // Create the vertices and indices numlines = skeleton.Branches.Count; numvertices = numlines * 2; VertexPositionColor[] vertices = new VertexPositionColor[numvertices]; short[] indices = new short[numlines * 2]; int vidx = 0; int iidx = 0; for (int i = 0; i < skeleton.Branches.Count; i++) { TreeBranch branch = skeleton.Branches[i]; indices[iidx++] = (short)vidx; indices[iidx++] = (short)(vidx + 1); vertices[vidx++] = new VertexPositionColor(transforms[i].Translation, Color.White); vertices[vidx++] = new VertexPositionColor(Vector3.Transform(new Vector3(0, branch.Length, 0), transforms[i]), Color.White); } // Create buffers vbuffer = new VertexBuffer(device, VertexPositionColor.VertexDeclaration, numvertices, BufferUsage.None); vbuffer.SetData <VertexPositionColor>(vertices); ibuffer = new IndexBuffer(device, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None); ibuffer.SetData <short>(indices); // Create the effect effect = new BasicEffect(device); }
/// <summary> /// Moves forward while painting a branch here. /// </summary> /// <param name="length">Length of the new branch. The current scale will be applied to this.</param> /// <param name="radiusEndScale">How much smaller the ending radius should be. The equation is: StartRadius * RadiusEndScale = EndRadius.</param> /// <remarks> /// The crayon always moves along its local Y-axis, which is initially upwards. /// </remarks> public void Forward(float length, float radiusEndScale) { // Run the constraints if (constraints != null && !constraints.ConstrainForward(this, ref length, ref radiusEndScale)) { return; } // Create the branch var branch = new TreeBranch( state.Rotation, length * state.Scale, GetRadiusAt(state.ParentIndex, state.ParentPosition) * state.RadiusScale, GetRadiusAt(state.ParentIndex, state.ParentPosition) * state.RadiusScale * radiusEndScale, state.ParentIndex, state.ParentPosition); branch.BoneIndex = state.ParentBoneIndex; skeleton.Branches.Add(branch); branchTransforms.Add(GetTransform()); // Set newest branch to parent state.ParentIndex = skeleton.Branches.Count - 1; // Rotation is relative to the current parent, so set to identity // to maintain original orientation state.Rotation = Quaternion.Identity; // Move to the end of the branch state.ParentPosition = 1.0f; // Move radius scale back to one, since the radius will now be relative to the new parent state.RadiusScale = 1.0f; }
/// <summary> /// Returns the radius of a given branch at the height. /// </summary> private float GetRadiusAt(int parentIndex, float f) { if (parentIndex == -1) { return(128.0f); } TreeBranch branch = skeleton.Branches[parentIndex]; return(branch.StartRadius + f * (branch.EndRadius - branch.StartRadius)); }
/// <summary> /// Sets the EndRadius to 0.0f on all branches without children. /// This is automatically called by the TreeGenerator. /// </summary> public void CloseEdgeBranches() { // Create a map of all the branches to remember if it is a parent or not var parentmap = new bool[branches.Count]; for (int i = branches.Count - 1; i >= 0; --i) { int parent = branches[i].ParentIndex; if (parent != -1) { parentmap[parent] = true; } if (!parentmap[i]) { TreeBranch branch = branches[i]; branch.EndRadius = 0.0f; branches[i] = branch; } } }
/// <summary> /// Moves forward while painting a branch here. /// </summary> /// <param name="length">Length of the new branch. The current scale will be applied to this.</param> /// <param name="radiusEndScale">How much smaller the ending radius should be. The equation is: StartRadius * RadiusEndScale = EndRadius.</param> /// <remarks> /// The crayon always moves along its local Y-axis, which is initially upwards. /// </remarks> public void Forward(float length, float radiusEndScale) { // Run the constraints if (constraints != null && !constraints.ConstrainForward(this, ref length, ref radiusEndScale)) return; // Create the branch TreeBranch branch = new TreeBranch( state.Rotation, length * state.Scale, GetRadiusAt(state.ParentIndex, state.ParentPosition) * state.RadiusScale, GetRadiusAt(state.ParentIndex, state.ParentPosition) * state.RadiusScale * radiusEndScale, state.ParentIndex, state.ParentPosition); branch.BoneIndex = state.ParentBoneIndex; skeleton.Branches.Add(branch); branchTransforms.Add(GetTransform()); // Set newest branch to parent state.ParentIndex = skeleton.Branches.Count - 1; // Rotation is relative to the current parent, so set to identity // to maintain original orientation state.Rotation = Quaternion.Identity; // Move to the end of the branch state.ParentPosition = 1.0f; // Move radius scale back to one, since the radius will now be relative to the new parent state.RadiusScale = 1.0f; }
/// <summary> /// Places the end of a bone here, unless the bone level would become too high, or too many bones have been added already. /// The bone's origin is at the previously added bone (as seen from the current state). /// </summary> /// <param name="delta">Amount to adjust the current bone level by</param> /// <remarks> /// Branches added between this and the previously added bone will be controlled by the new bone. /// /// The current bone is part of the crayon's state, like position and rotation, and is affected by PushState and PopState. /// </remarks> public void Bone(int delta) { if (state.BoneLevel > boneLevels + delta || skeleton.Bones.Count == MaxBones) { return; } // Get index of the parent int parent = state.ParentBoneIndex; // Get the parent's absolute transform Matrix parentTransform = Matrix.Identity; Matrix parentInverseTransform = Matrix.Identity; float parentLength = 0.0f; if (parent != -1) { parentTransform = skeleton.Bones[parent].ReferenceTransform; parentInverseTransform = skeleton.Bones[parent].InverseReferenceTransform; parentLength = skeleton.Bones[parent].Length; } // Find the starting and ending point of the new bone Vector3 targetLocation = GetTransform().Translation; Vector3 fromLocation = parentTransform.Translation + parentTransform.Up * parentLength; // Direction of the bone's Y-axis Vector3 directionY = Vector3.Normalize(targetLocation - fromLocation); // Choose arbitrary perpendicular X and Z axes Vector3 directionX; Vector3 directionZ; if (directionY.Y < 0.50f) { directionX = Vector3.Normalize(Vector3.Cross(directionY, Vector3.Up)); directionZ = Vector3.Normalize(Vector3.Cross(directionX, directionY)); } else { directionX = Vector3.Normalize(Vector3.Cross(directionY, Vector3.Backward)); directionZ = Vector3.Normalize(Vector3.Cross(directionX, directionY)); } // Construct the absolute rotation of the child Matrix childAbsoluteTransform = Matrix.Identity; childAbsoluteTransform.Right = directionX; childAbsoluteTransform.Up = directionY; childAbsoluteTransform.Backward = directionZ; childAbsoluteTransform.Translation = fromLocation; // Calculate the relative transformation Matrix relativeTransformation = childAbsoluteTransform * parentInverseTransform; Quaternion rotation = Quaternion.CreateFromRotationMatrix(relativeTransformation); // Create the new bone var bone = new TreeBone(); bone.ReferenceTransform = childAbsoluteTransform; bone.InverseReferenceTransform = Matrix.Invert(bone.ReferenceTransform); bone.Length = Vector3.Distance(fromLocation, targetLocation); bone.Rotation = rotation; bone.ParentIndex = parent; bone.Stiffness = skeleton.Branches[state.ParentIndex].StartRadius; // 1.0f; // TODO: Set stiffness according to radius bone.EndBranchIndex = state.ParentIndex; // Add the bone to the skeleton skeleton.Bones.Add(bone); // Set this bone as the parent int endIndex = (state.ParentBoneIndex == -1 ? -1 : skeleton.Bones[state.ParentBoneIndex].EndBranchIndex); int boneIndex = state.ParentBoneIndex = skeleton.Bones.Count - 1; state.BoneLevel -= delta; // Update the bone index on branches int branchIndex = state.ParentIndex; while (branchIndex != endIndex) { TreeBranch branch = skeleton.Branches[branchIndex]; branch.BoneIndex = boneIndex; skeleton.Branches[branchIndex] = branch; branchIndex = branch.ParentIndex; } }