0 votes

I'm trying to randomly place trees on procedural ground. The ground has randomly placed rocks on it and I want to place the trees where there aren't any rocks. I have tried to use intersectray() downwards at random spots and only place trees if the returned collider is the groundcollider. The trees are placed perfectly on the ground but ignores all of the rocks (places trees inside of rocks).

The project is structured such that the world consist of chunks. The chunks are quads created within the four points nw, ne, se, sw. The chunks first creates the ground using ArrayMesh with a StaticBody using ConcavePolygonShape. On the ground procedural rocks are placed using a script similar to this one, but doesn't care about colliding with other objects. The rocks are deformed spheres using ArrayMesh and similar collisions to the ground. They are on the same collision layers, and my KinematicBody character can collide with them all perfectly. The next step is to place the trees, which is the script down below. One length unit represents 1 meter, and the character is 2 meters tall.

extends Spatial

const TREE_RESOURCE = preload("res://Scenes/Terrain/Chunk/Component Scenes/Tree/Tree.tscn")

func setup_trees(nw,ne,se,sw, tree_density: float, chunk_size: float, noise: OpenSimplexNoise, ground_collider: Object):
    # just a random number
    var trees_rand_offset = noise.period * PI

var max_trees = chunk_size * tree_density
var min_trees = max_trees / 2
var noise_sample_point = global_transform.origin + Vector3.DOWN * trees_rand_offset
var noise_sample = noise.get_noise_3dv(noise_sample_point)
var tree_amount = round(min_trees + (max_trees - min_trees) * abs(noise_sample))
print(tree_amount)

var space_state = get_world().direct_space_state

# Create tree_amount of trees at random positions based on noise
for tree in range(tree_amount):
    # Use random weights to get position
    var vertical_weigth_sample_point = global_transform.origin + Vector3.DOWN * trees_rand_offset * ((tree * 10) + 3.33)
    var horizontal_weigth_sample_point = global_transform.origin + Vector3.DOWN * trees_rand_offset * ((tree * 10) + 6.67)
    var vertical_weigth = abs(noise.get_noise_3dv(vertical_weigth_sample_point)) * trees_rand_offset
    var horizontal_weigth = abs(noise.get_noise_3dv(horizontal_weigth_sample_point)) * trees_rand_offset
    vertical_weigth = fmod(vertical_weigth, 1)
    horizontal_weigth = fmod(horizontal_weigth, 1)

    var nw_weight = (1 - vertical_weigth) * (1 - horizontal_weigth)
    var ne_weight = (1 - vertical_weigth) * horizontal_weigth
    var se_weight = vertical_weigth * horizontal_weigth
    var sw_weight = vertical_weigth * (1 - horizontal_weigth)

    var tree_position = (nw * nw_weight + ne * ne_weight + se * se_weight + sw * sw_weight)

    # Raycast setup
    var raycast_length = 10.0
    var raycast_from = tree_position + (Vector3.UP * (raycast_length / 2))
    var raycast_to = tree_position + (Vector3.DOWN * (raycast_length / 2))

    var result = space_state.intersect_ray(raycast_from, raycast_to)

    # Only places trees on ground.
    if result.empty() == false and result.collider == ground_collider:
        var tree_instance = TREE_RESOURCE.instance()
        add_child(tree_instance)
        # build_tree(pos: Vector3, noise: OpenSimplexNoise, tree_type, tree_maturity, ground_collider)
        tree_instance.build_tree(result.position, noise, 0, 0.5, ground_collider)

The trees are currently only white pillars. Placed perfectly on the ground but ignores the grey rocks. Clipping the camera through the rocks shows the trees are placed right on the ground inside the rocks.
enter image description here
My code says to only place the trees right on the ground. But why is it ignoring my rocks? They are on the same layers. To make sure it isn't about the colliders face directions I raised the rocks to force the rays all the way through them. But it yielded the same result.
enter image description here
I also tested to ignore the result.collider == ground_collider check:

if result.empty() == false:
        var tree_instance = TREE_RESOURCE.instance()
        add_child(tree_instance)
        # build_tree(pos: Vector3, noise: OpenSimplexNoise, tree_type, tree_maturity, ground_collider)
        tree_instance.build_tree(result.position, noise, 0, 0.5, ground_collider)

The results were interesting. Rays seem to be colliding with thin air.
enter image description here
Anyone got a good reason for why this is happening? All of this happens on the same frame. As stated earlier, the ground and the rocks are using the same collision masks and collision layers. The chunks scene trees are:
enter image description here
And a rocks:
enter image description here
The rocks and trees and children of the Rocks and Trees Spatials respectively.

Thanks in advance!

Godot version 3.3.2
in Engine by (12 points)

Does the trees and the rocks have different collision layers? If that's the case then maybe that's the reason why the trees don't detect the rocks, have you set the trees' masks to detect the rocks' collision layer?

The trees don't have their own collisions yet. They are just CSGBox objects. The intersectray( ) that determines the placement has the default collidermask without exceptions. As shown when it is used near the bottom of the script. The ground and the rocks are both on the same collision layer 1 and collision mask 1. Their two static bodies are identical in the inspector.

Maybe try putting the trees first then the rocks? Can't guarantee if it would work but it doesn't hurt to try...
Another proposition is to wrap the rocks with an Area Node (keep it simple, like a square or something), and call it an "unspawn area", basically to prevent trees from spawning when they raycast on it.

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.