GD++: a boilerplate-free language that targets C++ specifically for Godot

Some Godot tutorials mention that developing with C++ is hard, often going so far as to say, “if you’re crazy enough to write your entire game with C++, …” What I find is that it’s not C++ itself that slows you down (though admittedly that happens too, to a smaller degree) but mostly the toil associated with:

  1. A boilerplate-to-code ratio that is far too high
  2. A lack of consistency in build tooling and dependency management

I’ve been frustrated with this situation myself, which is why I created a tool specifically for writing GDExtension with C++. It’s a programming language, or rather, a DSL, called GD++: GitHub - caphindsight/gdpp: Develop Godot projects in C++ with clean syntax and minimal boilerplate. · GitHub

The declaration syntax resembles GDScript almost entirely, with the same “just drop a file anywhere in the project” ease of use. The implementation is “flavoured C++” (the GD++ transpiler rewrites certain expressions before passing the result to the C++ compiler).

There are many nice features coming out of the box (e.g., @onthread, @profile, externs, etc.). I can’t enumerate all of them here, so please take a look at the README on GitHub.

Related: As a first demonstration of the language, I created a procedural foliage add-on, Foliage3D: GitHub - caphindsight/Foliage3D: Interactive procedural foliage for Godot 4 · GitHub. It was originally written by hand in GDScript and was rewritten in GD++ in a single pass by an AI bot. It immediately compiled and worked without issues, which I believe is due to how similar the declaration scopes of the two languages are. This rewrite helped speed up foliage generation times by ~5-10x, at no functionality cost.

5 Likes

Wow - that terrain generation is very good, well done.

Looks useful. I have thought of a feature request for the foilage system - instanced submeshes. So each tree could have instanced branches, or just duplicate MeshInstance3D objects (alt + D in blender) as Godot probably just renders those using instanced draw calls anyway.

So each tree could be a .scn or .tscn file instead of a .res file. This way tree models that use the same geometry for each branch/leafy chunk would use less VRAM and might and so could allow for faster renders with more complexity.

@StateCraft thank you! High praise coming from someone working on terrain generation professionally.

@pizza_delivery_man do you mean for the closest chunks, or further away chunks, or something intermediate? Note that chunks that are far away are not instanced scenes - they are rendered as a single multimesh.

Edit: after re-reading, I think you meant to allow the same mesh to be placed multiple times for one asset. I think we can support it somewhat easily. Do you want to send me a PR, and get some exposure to the new language?

Yeah i mean for the closest chunks and maybe for the intermediate chunks. Furthest chunks might be a low lod like bilboard quads.

Not exactly. I mean when the tree is a single mesh stored in a tres then it uses more memory on the GPU than when it is multiple meshes like this …

node3d

  • Trunk
  • Leafy_branch
  • Leafy_branch

Edit: this post sums up the idea:

then theres more space on the GPU for other things.

How is my “the same mesh drawn multiple times on one asset” not a valid summary? From what I understand, you are suggesting building an asset (say, a tree) out of copies of the same mesh (branch with different transforms). When rendering a foliage multi mesh, we can place transforms for all branches into the same buffer, instead of placing transforms for individual trees each containing a copy of every branch, like it works today. Or am I still missing the point?

Yeah but we dont have to do that because Godot already collects duplicates into instance buffers internally, all we need to do is draw a TSCN or SCN file for the lowest LOD’s at close range instead of a RES file.

I think that may lead to using too many nodes in the scene.

I never ran into the issue. The node count is user responsibility and is just another optimization parameter. I never had node count as a bottleneck, probably because I use the BVH. Dont worry ill just hire somebody on fiver if i need it.

Tbf I could see this being used instead of gdscript, it looks good to work with

Could this be used to convert GDScript resource classes into corresponding C++ classes? That’d be immensely useful.

Absolutely! In fact, if your resource classes simply define exported fields and don’t provide any code, you most likely don’t have to convert anything. GD++ is very similar to GDScript in the declaration scope, so GDScript might just work :slight_smile:

Please let me know if you try this on your project.

If you want plain C++ classes, without using GD++ / its build system, you can run the gd++ trans command on gd++ sources to generate the C++ headers and sources from them. But generated code is not nearly as nice as plain gd++, of course.

2 Likes

I’ll give it a try.

EDIT: Oh, it’s “ai”. I gues I won’t then. Sorry.

Yeah, it’s way too big of a project to take on all alone without help. Vibecoding the compiler was the only way to finish the project in weeks, instead of years. It’s tested though, and there is nontrivial code written in the language that works.

1 Like

I think it’s so impressively similar to GDscript while still compiling to C++ that, from what I can tell, it’s not far off from just being GDscript that compiles to C++. A tool to do this would be game changing, and I think it would be awesome if you could pull that off. Might be easier said than done though :rofl:

Nice work, would love to see how this progresses in the future!

Haha thank you, it’s a bit far from just “compile GDScript to C++”, unfortunately. I don’t think that is achievable fully, compiling a scripting language into strongly typed code might not be possible due to information loss. The best bet is probably a good quality JIT compiler, which is what I believe the long-term goal for GDScript is.

In this project, the goal is to provide a GDScript-inspired language for the bindings generator. Basically to speed up C++ development.

1 Like

Of course it’s possible. Cython has been doing this for C for almost two decades now.

1 Like

Well, of course it’s technically possible to do. Just embed all Variant checks everywhere you see any variable. My point, that I admit I can’t articulate properly, is that it necessarily has a performance hit. I am only familiar with the concept of cython, haven’t actually worked with it, but I’m sure its runtime has a nontrivial performance penalty.

Cython with dynamic typing is certainly slower than Cython with static typing, but it’s still faster than plain interpreted Python. Or rather, it’s faster than the default Python implementation from python.org. I don’t know how it compares to the PyPy, the just-in-time Python compiler.

And GDScript already has optional static typing.