Problem with Windswept Procedural tree

I am new to Unity and trying my first project. In the project I need a specific type of windswept tree that players climb so I want the tree as realistic as possible. The Unity-generated tree produces square branches for the smaller ones. I wanted something more realistic. I grabbed “Procedural Tree” (Unity Asset Store - The Best Assets for Game Making) from the asset store. It makes nice trees, round branches,… but not windswept. I went into the code and tried to modify the rotations to preferentially have the limbs bend in a specific world direction. I can’t seem to get the rotations correct. I can bend the branches but the orthogonal rings don’t rotate (the branches turn inside out) or I can bend them in the local system and get corkscrew trees - interesting but not what I need. I have never worked with quaternions so that it where I am messed up.

It is clear that my use of transform.InverseTransformDirection is incorrect. I have tried it using quaternions instead of vector components, rotating the quaternion toward an object. switching the entire code to vectors,… not having much luck. I have contacted wasabimole who wrote the code a couple of times but haven’t heard back from them.

Any help would be greatly appreciated. What I need are suggestions on the best and simplest way to do this and hints to understand what is happening so I can do it myself in the future.

Thank you in advance.

The recursive branch code with my modifications is:

// Add ring vertices
for (var n = 0; n <= NumberOfSides; n++, ang += angInc) 
{
	var r = ringShape[n] * radius;
	offset.x = r * Mathf.Cos(ang); // Get X, Z vertex offsets
	offset.z = r * Mathf.Sin(ang);
	vertexList.Add(position + quaternion * offset); // Add Vertex position
	uvList.Add(texCoord); // Add UV coord
	texCoord.x += textureStepU;
}

if (lastRingVertexIndex >= 0) // After first base ring is added ...
{
	// Create new branch segment quads, between last two vertex rings
	for (var currentRingVertexIndex = vertexList.Count - NumberOfSides - 1; currentRingVertexIndex < vertexList.Count - 1; currentRingVertexIndex++, lastRingVertexIndex++) 
	{
		triangleList.Add(lastRingVertexIndex + 1); // Triangle A
		triangleList.Add(lastRingVertexIndex);
		triangleList.Add(currentRingVertexIndex);
		triangleList.Add(currentRingVertexIndex); // Triangle B
		triangleList.Add(currentRingVertexIndex + 1);
		triangleList.Add(lastRingVertexIndex + 1);
	}
}

// Do we end current branch?
radius *= RadiusStep;
if (radius < MinimumRadius || vertexList.Count + NumberOfSides >= MaxNumVertices + 65000) // End branch if reached minimum radius, or ran out of vertices
{
	// Create a cap for ending the branch
	vertexList.Add(position); // Add central vertex
	uvList.Add(texCoord + Vector2.one); // Twist UVs to get rings effect
	for (var n = vertexList.Count - NumberOfSides - 2; n < vertexList.Count - 2; n++) // Add cap
	{
		triangleList.Add(n);
		triangleList.Add(vertexList.Count - 1);
		triangleList.Add(n + 1);
	}
	return; 
}

// Code added for windswept tree
var x = (transform.InverseTransformDirection(0f,0f,1f).x);
var y = (transform.InverseTransformDirection(0f,0f,1f).y);
var z = (transform.InverseTransformDirection(0f,0f,1f).z);
position += quaternion * new Vector3(x, (y + SegmentLength * Mathf.Sqrt (Mathf.Sqrt(radius))), z);
transform.Rotate(x, y, z);
// Code added for windswept tree

x += (Random.value - 0.5f) * Twisting;
y += 0f;
z += (Random.value - 0.5f) * Twisting;

transform.Rotate(x, y, z);

// Continue current branch (randomizing the angle)
texCoordV += 0.0625f * (SegmentLength + SegmentLength / radius);

lastRingVertexIndex = vertexList.Count - NumberOfSides - 1;
Branch(transform.rotation, position, lastRingVertexIndex, radius, texCoordV); // Next segment

// Do we branch?
if (vertexList.Count + NumberOfSides >= MaxNumVertices || Random.value > BranchProbability) return;

// Yes, add a new branch
transform.rotation = quaternion;
x = Random.value * 70f - 35f;
x += x > 0 ? 10f : -10f;
y = 0f;
z = Random.value * 70f - 35f;
z += z > 0 ? 10f : -10f;
transform.Rotate(x, 0f, z);

Branch(transform.rotation, position, lastRingVertexIndex, radius, texCoordV);

}

I just had a look at your added code. Well, there are a few things strange / wrong. InverseTransformDirection transforms a worldspace direction into a local space direction. “localspace” inside the branch however means local to the current section as he’s adjusting the transform position / rotation for each step. This is reverted when the tree creation is finished. He basically uses the Transform as temp position / rotation marker.

So your x,y and z values would now contain the local space equivalent of the world space vector (0,0,1). I’m not sure why you rotate that vector by the quaternion value as this would basically revert the vector back to (0,0,1).

You then shift the next branch step by this vector which would still make a bit of sense. However using the same values in Rotate makes not much sense. The vector is a normalized vector while Rotate() takes angles in degrees.

I’ve just downloaded the original asset from the assetstore and added “some code” for a “wind” parameter. The script now has an additional “windStrength” variable and a vector to define the direction of the wind. That vector is in local space of the tree object (not local to each branch). I also added an “worldWindSource” Transform variable which allows you to assign an empty gameobject. If a gameobject is provided the script will use the objects forward vector as wind direction and convert it into local space.


The modified script can be found here.


I enclosed all changes with comments like this:

// <windswept>
  the changes
// </windswept>

So if you want to know what i changed, just look out for those or use the search function and search for “”.