Summary
Wind animation on trees generated with Procedural Vegetation Editor is incorrect. Individual branches bend along inconsistent axes, and some branches bend in the opposite direction from the wind. This differs significantly from the Dynamic Wind behavior shown in the Unreal Engine 5 Witcher IV Tech Demo, where branches deform consistently in the wind direction.
I investigated the engine and plugin source code and found that Dynamic Wind relies on a specific skeleton axis convention. The local +X axis of every deforming bone must represent its forward direction, pointing along the branch from the joint toward its child or endpoint. This convention is assumed by the shader and is not derived from bone positions or validated at runtime.
According to the Dynamic Wind shader source (Engine/Plugins/Experimental/DynamicWind/Shaders/DynamicWindEval.usf), the bind-pose rotation is used to transform the bone’s local +X axis into its forward direction:
AdjustedBoneForward =
QuatRotateVector(CurrentBoneData.BindPoseRotation, float3(1.0f, 0.0f, 0.0f));
The compute shader then uses this forward direction to calculate the bending axis for each bone:
WindHorRotVector =
normalize(cross(AdjustedBoneForward, SectionWindDirection));
This means that when a bone’s local axes remain aligned with world space instead of the bone itself, the shader interprets that bone as pointing along global +X regardless of the actual branch direction. The resulting cross product produces an incorrect bending axis. Depending on the branch’s position and hierarchy, it may bend sideways, twist around the trunk, or move against the wind. The sample trees generated by Procedural Vegetation Editor are using these world-aligned local bone orientations.
What Type of Bug are you experiencing?
World Creation Tools
Steps to Reproduce
Enable the “Procedural Vegetation Editor” and “Dynamic Wind” plugins. Activate “Nanite foliage” in project settings.
Open the example map located under:
/ProceduralVegetationEditor/SampleAssets/Maps
The incorrect wind animation is already visible on the trees in this map. Some branches bend along inconsistent axes or appear to twist around the trunk.
Open any sample Skeletal Mesh used by this map, for example one located under:
/ProceduralVegetationEditor/SampleAssets/StarterContent/DeciduousTree_01
Select several branch bones and compare the transform gizmo in Local and World coordinate modes. Their local axes remain aligned with world space instead of following the direction of each branch.
The same issue can be reproduced by generating and exporting a new skeletal tree with Procedural Vegetation Editor.
Expected Result
For every bone, the local +X axis should point along the bone toward its child or endpoint. Secondary axes should also remain reasonably consistent along each chain. Under Dynamic Wind, the trunk and branches should bend coherently in the wind direction without branches reversing direction or twisting around the trunk.
Observed Result
The generated and included sample skeletons have local bone axes aligned with world space rather than with their corresponding branches. Dynamic Wind therefore calculates incorrect bending axes. Branches bend in inconsistent directions, and some move against the wind. The problem becomes especially noticeable at wind speeds of approximately 50 or higher, where branches appear to rotate or twist around the trunk.
Affects Versions
5.8
Platform(s)
Windows
Upload an image
Additional Notes
I created a small diagnostic Python workaround that corrects the bone orientations and demonstrates the expected Dynamic Wind behavior. The script uses Unreal Engine’s native SkeletonModifier. it does not modify the original asset.
To run it:
Enable the Skeletal Mesh Editing Tools plugin.
In the Content Browser, select exactly one Skeletal Mesh. For example, select a mesh inside:
/ProceduralVegetationEditor/SampleAssets/StarterContent/DeciduousTree_01
Open Output Log, switch to Python mode, and execute the following code:
import unreal
OUTPUT_SUFFIX = “_XOriented”
def main():
if not hasattr(unreal, “SkeletonModifier”):
raise RuntimeError(
“SkeletonModifier is unavailable. Enable the Skeletal Mesh Editing Tools plugin.”
)
selected = [
asset
for asset in unreal.EditorUtilityLibrary.get_selected_assets()
if isinstance(asset, unreal.SkeletalMesh)
]
if len(selected) != 1:
raise RuntimeError("Select exactly one Skeletal Mesh in the Content Browser.")
source = selected[0]
source_package = source.get_path_name().split(".", 1)[0]
output_folder = source_package.rsplit("/", 1)[0]
target_name = f"{source.get_name()}{OUTPUT_SUFFIX}"
target_path = f"{output_folder}/{target_name}"
if unreal.EditorAssetLibrary.does_asset_exist(target_path):
raise RuntimeError(f"Experiment asset already exists: {target_path}")
asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
target = asset_tools.duplicate_asset(target_name, output_folder, source)
if not isinstance(target, unreal.SkeletalMesh):
raise RuntimeError(f"Could not duplicate {source.get_path_name()} to {target_path}")
modifier = unreal.SkeletonModifier()
if not modifier.set_skeletal_mesh(target):
raise RuntimeError(f"Could not initialize SkeletonModifier for {target_path}")
bones = list(modifier.get_all_bone_names())
if not bones:
raise RuntimeError(f"No bones found in {target_path}")
options = unreal.OrientOptions(
primary=unreal.OrientAxis.POSITIVE_X,
secondary=unreal.OrientAxis.POSITIVE_Y,
use_plane_as_secondary=True,
orient_children=True,
)
if not modifier.orient_bones(bones, options):
raise RuntimeError(f"Bone orientation failed for {target_path}")
if not modifier.commit_skeleton_to_skeletal_mesh():
raise RuntimeError(f"Skeleton commit failed for {target_path}")
if not unreal.EditorAssetLibrary.save_loaded_asset(
target, only_if_is_dirty=False
):
raise RuntimeError(f"Could not save {target_path}")
unreal.EditorAssetLibrary.sync_browser_to_objects([target_path])
unreal.log(f"Created +X-oriented experiment: {target_path}")
unreal.log_warning(
"UE Python cannot set SkeletonFactory.TargetSkeletalMesh. "
"Right-click the selected mesh and choose Skeleton > Create Skeleton."
)
main()
The script creates a sibling duplicate with the _XOriented suffix. It preserves the mesh geometry, skin weights, materials, and Dynamic Wind data while reorienting the bones so that their local +X axes follow the bone chains.
One additional manual step is required:
Select the generated _XOriented Skeletal Mesh.
Right-click it and select Skeleton → Create Skeleton.
Replace the original mesh reference with the corrected copy in the relevant PCG setup.
Regenerate the PCG instances.
The resulting tree has the same geometry and wind settings, but its branches bend coherently with the wind instead of twisting around the trunk or moving in opposing directions. This provides a direct A/B comparison and confirm that the incorrect bind-pose bone orientation is the cause of the problem.

