Skip to content

Terrain 3D Scattered Models update - #2356

Merged
Bouh merged 8 commits into
GDevelopApp:mainfrom
Eldarduil:main
Nov 10, 2026
Merged

Bouh merged 8 commits into
GDevelopApp:mainfrom
Eldarduil:main

Conversation

@Eldarduil

@Eldarduil Eldarduil commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Sorry for the wall of text, but I felt like I need to tell everything. Thank you for your understandings.

Reasons:

  1. it is impossible to scatter objects properly touching the terrain mesh.
  2. it is not possible to use Z+ up models.
  3. it is not possible to do proper object replacing with a copy, because lack of angle and scale expressions.

Changes:

v0.3.0

Bouh did add 2 drop down properties that allows user to change which point of the 3d model to use to place copies and option for if the model is Y+ up or Z+ Up model.
Can be seen in this forum post: https://forum.gdevelop.io/t/terrain-3d-scattered-models-does-not-use-object-origin/78811/1

Using the model origin solves the issues that asymmetric object's wrong trunk collision positions and floating model on slopes. and allowing to use Zup models.

v0.3.1 (here is my changes and additions)

Height Property is changed to Scale Factor:

If a User wants to match the 3D model and Scattered models dimensions this makes it easier. Because in 3D model object Scale Factor = 100 means model's original size. You set the scale factor to 100 in both Scattered Models and in the 3D model and you are good to go. This helps for easy workflow.

In the old version it feels odd that I need to feed the Height of the object to the system if I want matching sizes. There are no 3D systems in gdevelop uses height of the object as main dimension property as I can think of. Consistency across all gdevelop features is important.

So with that The changes in the code uses the _getScaleFactor instead of Height calculations. Except matrices, raycast, fade shader etc. continue to read copy.height . so no harm done to the other systems in the code.

Two new Expression functions added:

NearestCopyAngle & NearestCopyScale are needed for exactly swaping the instancedmesh copy with the regular object. With these new expressions seemless replacement can be done. Previously it was very disappointing that you can place the object only at the copy position. when I first see the object that I create doesn't even matches the angle and size, it creates a very cheap game feeling and it is unacceptable while the code has all the data about the copy. anyway now we have access to that data.

One new extension action is added:

It is called ReplaceNearestCopyWithObject . it does the replacement actions automatic for the user. (if you see the examples events it is basically applying the copy's transform to the created new 3d model one by one. nothing fancy. just removes the manual labour for creating series of actions.
It needs to be extension action, because Custom object functions cannot create scene objects. they can only create child objects of the custom object.

I have done these changes with in-engine AI assistance. Since now it can read the extensions it is good for pin pointing the locations in the code and helping to understand structure of the system. Also I am familiar with these type of system because I have made Scatter On Surface community extension which is very similar.

Something to note:

When you use the bbox bottom center for scattering the objects and when you want to swap it with 3d model. The 3D model origin point and object center should be chosen bottomcenterZ to have a seamless swapping. which is not ideal because now you gonna have problematic physics3D shape positions and calculations etc.

I am not sure giving user a option to use object bottom center for scattering is wise choice. Not even giving it on default. it shouldn't be given at all. Because it causes problems than solving it.

A terrain object is almost never flat, so there will be always floating copies if you use the bottom center. Even if you have a proper 3d model lets say a flat bottomed rock, you want to sink a little bit inside the terrain to avoid floating bottom. so in the modelling part of the object you sink the model a little bit lower than the origin point. But when you come to the engine, the engine suggest you to use bottom center on default and it completely pushes the object up again completely overrides your floating prevention in the modelling part which is accepted design principle in game development. So using bbox bottom center is awfully wrong. Every time people needs to fix that by switching to "use model origin" option. I can't think of any use case for bottom center scattering. even in top down game it will show the shadows offset from the object base for the objects that are at the slopes and has asymmetric shapes.

Another thing is in-engine AI keeps telling store installed extension (terrain3d) is read-only. which AI can't edit unless you duplicate. User can Edit, but you get "hey update this extension or skip it" popup when you change something in the extension.

Future update suggestions:

It would be good to have an option to rotate the objects along the slope face normals. Like "Align Rotation" option in Blender's Scatter on surface modifier. Of course with that we need new rotation expressions for the copies.

Scatter models from Object groups. allows nonclipping scattering while having variety of 3D models

ORM (AO/Rough/Metal) texture support for Terrain textures.

Example project

Terrain Scatter Fix.zip

In the example; You can see both 3d models one is coming from the LeafyForest prefab (Common_Tree_1, Y+ up) the other one is from Quaternius assets that I gave it a custom name (Tree_Asym_Z_01, Z+ up). In both examples the seamless object swapping is perfect. Common_Tree_1 is dynamic Physics object. Tree_Asym_Z_01 is static physics object, giving examples of two different approach what can be done after replacing the object.

Notice that Common_Tree_1 has 0 Z rotation in the object properties. that is important for the matching replacement. Gdevelop brings any 3D model with X: 90, Y: 0 , Z: 90 rotations to fix the Y up models to Z up orientation. that extra Z rotation is not accounted in the Scattered model data. so if it left z: 90 it will not match the angle of the copy. Which we shouldn't even think to fix or change things. This should be users responsibility to properly setup their 3D models. Gdevelop already messing up with the user by automaticly rotating the 3d models. The engine assumes every model is Y+ up while the 3d engine itself is Zup. that is another topic for another day :P

AI usage

  • [+] I used an AI to generate some part of the code.

Property change: Height to Scale Factor
Added two expression functions for gettin copy angle and scale
Added one extension action for automating the object replacement with the copy
@Eldarduil
Eldarduil requested a review from a team as a code owner November 8, 2026 17:35
Bouh added 7 commits November 9, 2026 17:34
Put back the official short description and the store origin, replaced by the ones of a local editable copy.
ReplaceNearestCopyWithObject created the object on the base layer.
It is the height in the model file units, while the other sizes are relative to a height of 1.
The Size property is back as a deprecated property, 0 by default. When it is greater than 0 (objects made with v0.2.0), copies keep this fixed height and their trunk collisions as before. Otherwise, the scale factor is used. NearestCopyScale works in both cases.
Without any copy, NearestCopyScale returns 0 and the action created an object with a size of 0 at the given position.
Store prefabs and older objects keep their height of a copy, now multiplied by the scale factor instead of ignoring it (100% keeps their look).
@Bouh
Bouh merged commit 1713d34 into GDevelopApp:main Nov 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants