Hi again, and thanks to everyone for the help.
Because “edj” is interested in my project, I’ve attached two screenshots. Thanks for your interest. Please note that this is a work in progress and has been for several years now 
There’s still an old/new menu, etc. I’m constantly trying to figure out the best way to do things.
I started with cubes because I want to draw a house using fewer triangles if possible. And I don’t necessarily want to use LOD, since I have other problems as well. One of them is that I have no idea about 3D math. That’s why I usually can’t follow the explanations in the forum. But the actual programming behind the 3D, the complex processes of a city with people, is just fun for me.
So, a house can be small at first. It’s a cube with a texture. Then it can reach different sizes (to accommodate more people). But it’s still a cube, just with different textures for different floors. I thought this would be the fastest way to draw small/large houses. In the background, I have a few different scenes, but always in pairs. I always fill the cubes in the non-visible scene with threads and when it’s finished I switch it. The scenes depend on what they are supposed to show: a scene with no shadows (for distant objects), with shadows, transparent, with or without collision, and one for the landscape. For the landscape, I once asked how the engine landscape can be used with “holes,” since I have tunnels and the landscape needs to be somewhat adaptable when placing objects on it. Since I have no idea how to do this with the built-in landscape, I just used my old code and draw it myself. But then there were other things I didn’t know how to solve.
Anyway: until a 200x200 world is full (one cell = one house, if there’s space), I have hundreds of cubes for the houses. Also trees, and then a few thousand people running around, but I only show those that are nearby and can be displayed at the current speed.
Since the houses can “grow,” the scenes are constantly changing. So, I need to be able to fill, change, or delete scenes. But at the moment, everything is simply redrawn in the non-visible scene and then switched.
The user can select a house and have it enlarged. Then it’s in a “build” mode. So that also changes as well.
For the trees, I use a kind of LOD: far away, only two cubes are used.
But since you can also see the whole world from above, at least I’ve turned off the grass, but with all those cubes, that’s still not enough. And at the very beginning with the engine, I tried using billboards for distant objects (with individual scenes), but that was VERY slow. Maybe I’d do it better today, but I still don’t quite trust it.
I also once tried to load grass patches as 3D objects and render them multiple times. That was so slow. Probably done wrong, but I gave up on that.
Shaders: I once tried to use them to make the landscape look better. But I never really figured out how, since I draw everything myself.
To the question: “cubes: Do they just lie in place statically?” The people are moving all the time, the rest mostly stays in the same place if it doesn’t change. Trees can also be felled. And I also have moving transport objects.
Unfortunately, I can’t just make a small runnable demo version, but a cube is drawn approximately like this:
procedure AddPoint(dx, dy, dz: single);
begin
sceneStackOpponents[opp,sceneNr].coords[sceneStackOpponents[opp,sceneNr].whatScene].FdPoint.Items.Add(
Vector3(scale * (ix + dx), scale * (iy + dy), scale * (iz + dz)));
Inc(sceneStackOpponents[opp,sceneNr].index[sceneStackOpponents[opp,sceneNr].whatScene]);
sceneStackOpponents[opp,sceneNr].indexFaceSet[sceneStackOpponents[opp,sceneNr].whatScene].
FdCoordIndex.Items.Add(sceneStackOpponents[opp,sceneNr].index[sceneStackOpponents[opp,sceneNr].whatScene]);
end;
procedure AddTex(aValue: integer; rotation: integer);
procedure CalculateTextureCoordinates(row, column: integer; out texCoords: array of single);
begin
texLeft := column * SubTextureSize / TextureWidth;
texRight := (column + 1) * SubTextureSize / TextureWidth;
texTop := row * SubTextureSize / TextureWidth;
texBottom := (row + 1) * SubTextureSize / TextureWidth;
case rotation of
90: // 90°
begin
texCoords[0] := texLeft; texCoords[1] := texBottom;
texCoords[2] := texLeft; texCoords[3] := texTop;
texCoords[4] := texRight; texCoords[5] := texTop;
texCoords[6] := texRight; texCoords[7] := texBottom;
.....
end;
procedure AddTex(aValue: integer; rotation: integer);
procedure RightQuad(x, y, z: single);
begin
for i := 0 to verticalRepeat - 1 do
begin
// Unten: entranceTex, Oben: tex
if i = verticalRepeat - 1 then
currentTex := entranceTex
else
currentTex := tex;
if currentTex = 0 then
Continue;
AddPoint(x, y - i * segmentHeight, z);
AddPoint(0, y - i * segmentHeight, z);
AddPoint(0, y - (i + 1) * segmentHeight, z);
AddPoint(x, y - (i + 1) * segmentHeight, z);
..
end;
FrontQuad(sx, sy, sz);
BackQuad(sx, sy, sz);
RightQuad(sx, sy, sz);
LeftQuad(sx, sy, sz);
I hope this makes sense. One cube routine is rotatable for the people who walk around. Others are not, etc. But because I’ve been trying out different things for quite a while, the routines are probably not that efficient.
Sorry for the long explanation, but you asked 
If necessary, I’ll just keep going as before; I still have a few ideas for how it might get faster.
But if I had a small demo with add, change, and remove of a cube, multiply over GPU, I would be able to try that. 
Thanks to everyone for reading and for the help.