Communication between objects

I don’t think so. That just resolves the Trait by its name.

Compiling the node tree to a Haxe file would be done as a part of the build process or node trees. There is the make_logic.py file that takes NodeTrees and compiles them to Haxe traits. We would have to add the logic to read the special nodes that indicate that the node defines a class and write out the Haxe file to take those nodes into account. Similar to how the function node already work.

1 Like

@zicklag Our actual brainstorming consists in trying to copy a usual POO approach on nodes and node trees.

If we reasoned differently by considering that a class instance is always a 3D object, for example an empty.

Then the methods are the traits added to the 3D object .
Inheritance takes place via the parenting of 3D objects.

The node Spwan Object is associated to the notion of Class constructor.
The node Remove Object is associated to the notion of Class destructor.

The scene could be associated to the notion of Package or maybe super-class/ virtual Class with already existing logic nodes to manage it.
image . Thus we have a way to group Class into a coherent set, and to provide a namespace for these elements.

Also as the Objects in a scene can be organized into named and nested collections, thus we could find here the notion of sub-package.

Objects (that is here for us with the meaning of the instantiation of a Class) are typically a member of one collection in a scene, but they can be put too in multiple collections, and we could make the link here too with the notion virtual class.

Collection could become a kind of virtual class that has already le logic nodes to use it image

1 Like

Thtat’s intriguing. It could be worth testing that out to see how it feels when using it.

1 Like

We could test with this first proposal :

  1. A Collection is a Container of Objects
  2. A Scene is a Container of Collections
  3. An Object is an Instance of a Class
  4. A Class is an Empty 3d object
  5. The Methods of a Class are defined by it’s Traits
  6. The Heritage is established with the Parenting

Thus for Containers, we have already things available like create empty container, add, delete, access to

Then for heritage between Classes :
When you spawn, after you do a Set Parent on this new object with the object from which it inherits (thus the empty that represents the parent Class)
Then when calling a Trait , that is a method of the class of this object, if this Trait doesn’t exist on this object, you do a get parent , again a get trait and the Call Function … which becomes equivalent to call the method of a Class parent.

That’s done for a first step in experimenting if this OOP with Armory “tient la route”.

This first step to test proposal is done by creating several Factory Class able to create instanciation of different Classes, taking the exemple of the Neural Network.

Here an exemple of result when printing the Neurones of each Layer, that is here the Container Collections of the scene.

![image|423x441]

Thus this first step shows that in this first OOP rules proposal :
1 + 2 is OK we have logic nodes to add a new collection in the active scene. Then Objects are added inside a collection simply by using an add Array logic node, with the Collection as the input Array.
3 + 4 + 5 is OK we have a Class Factory able to create 3D objects, where each object represents an instanciation of a Class. Each Factory class Methods are realized thanks to the logic node Function
made by @zicklag
6 is TBC as it needs now to be test further on, in order to confirm/define some rules to reproduce OOP inheritance.

I modify with 4. A Class is an Empty or a standard mesh object.

And always what’s amazing about Armory is that it’s very fast, even with a lot of neurons. Indeed, we could fear an additional cost of CPU time with a switch to OOP.
It seems to me that on the contrary with this first test, if I compare it with other realizations, it would allow to reduce the number of Logic Nodes, to use more native Blender possibilities and thus to gain even more in performance.
See therafter picture for a load test

image

For the next step, I will test a behavior well known in OOP, that is Observateur/Observable, and thus go deeper with point 6 of the OOP proposal for Armory.

Next step:

6 is now tested OK
Done by adding several “Observateurs” to an “Observable”, using a Method belonging to a child Class of the parent Class.

The technic used is as follow :

  • image The generic Class Neurone_0 has 2 children, that is 2 “Empty” Observateur and Observable

  • when I want to call the Method M_AjouterObservateur on an instance of the Neurone_0 (that is the objects created by the Factory of Neurones), I have to call the Method of it’s children Class Observable, that is in fact a specialized Class that has this Method. Thus all needed Observateurs become registered for this Neurone.

  • for example on the following system console picture, the method M_AjouterObservateur is called here with the following resulting print that shows that effectively the tab of the Observable, that is in this case for the neurone N0_1 (= Neurone 1 of the Layer Input ) register observateurs of the following Layer.
    image

:slot_machine:
After a few days of tests with OOP method for Armory SW dev … some news:

  • The new Neural Network made using this OOP approach is coming to life. See picture below

  • I discover using it that this OOP approach proposal gives great advantage in the lisibility of the application.

For example, when you clic on an object (that is a Class) into a Scene, as you see directly what are the Methods of it into the Armory Traits window, thus you kickly see what is the Logic Node Tree to select in order to modify the behavior of a Class. (for example here it’s about +15 traits and +12 Class, and the quantity of logic Nodes is divided by an approximate factor of +20 compared to a functional approach thanks to inheritance capabilities )

Hopping that you will find it usefull for you too and that you will soon give us some info on your own tests of this OOP proposed method for SW Dev within Armory.

News: I focus the design approach to get a best how-to with it for some things like to keep performances too in this OOP approach (as it was the case when using array of array of array in preceeding tests) that is to keep good alignment of datas in memory, keep cache friendly things and maximize the use of SIMD for example.

1 Like

Wow, that’s cool. Good job @Didier. Neat that you go that working and that you actually implemented it for your project.

@zicklag something useful at this stage would be to have feedback from those who have thoroughly tested/benchmarked the Kha.simd package :wink: but I fear that perhaps only @lubos is currently the best placed for this feedback when working on the Graphics2.hx for example?

Unfortunately simd only works on the hxcpp target, which Armory doesn’t use anymore. Development builds will use Krom and the production build will use the HL/C target so we won’t be getting any use out of it for Armory. It migh be possible to make simd for HL/C, but I wouldn’t know.

@zicklag At first sight, I’m looking for mainly reusing what’s available today in Armory/haxe…, so it’s easy to define for example custom iterators and iterable data types, and I focus on how to complete/provide/reuse a set of language abstractions/logic nodes/3D objects/… and optimizations for an object oriented high-performance computing in Armory.

Thus if the simd.hx is not interesting for all kind of platforms, it’s however I think an interesting example of a good way to organize things to make some cache-friendly things.

Looking for which strategy is best for an efficient OOP aproach in Armory will depend I think too as well on the hardware architecture/platform planned to use, but also the data access patterns+ dataset characteristics in the SW design.

Maybe @RobDangerous could give us advice in this domain on how to best use things like for example the haxe Map<K, V>(IMap<K, V>) , generics (see https://haxe.org/manual/type-system-generic.html) and iterators ?
It would be interesting to know for example how to get the equivalent of a hand-written SOA layout with Haxe, like when a compilator is able to make for us things like smart function inlining, constant folding, … in order to get a more efficient application.

Sorry guys, I don’t understand what you’re talking about. I can just tell you that the simd API does not need to be profiled because every function literally just creates a single intrinsic.

@RobDangerous it’s about mixing a standard OOP approach with a data driven one, that will take care of things like memory behavior, playing with the cache, data organization, … in order to get optimal performances. Thus maybe some things and astuces already available in haxe could be reused if well known and understood ?

Sorry, still don’t understand you. Also don’t really want to, I suppose, none of this looks like it would be something I’d like to think about.

@RobDangerous sorry for not being able to explain it better than by defining an object/data oriented design approach that takes advantage of the Armory 3D environment to facilitate development, but while focusing on performances that make the most of what exists. I thought that your activities on kha could be the source of good ideas about performance and I fully understand that you are not interested.

The kind of higher logic I see discussed here is nothing I actively think about because I’ve never seen it become an actual performance bottleneck. Can only give the advice to get rid of standard OOP ways of thinking.

maybe if I could give you an image : imagine that you design a game using a high quantity of particules … a simple class inheritance can already be prohibitive for the necessary performance/FPS during some intense loops (no contiguous allocation in memory; lot of cache misses… :boom:)

Not true, simple class inheritance incurs no performance costs. A extends B ends up being literally the same as manually writing a class C which contains all member vars of A and B.
But my general advice for lots of X is as follows:
For hundreds of X just use your game engine’s game logic things.
For thousands of X build it into your engine itself (there’s lots of talk nowadays for doing thousands of X with an engine’s high level features - but unless you’re using a closed source engine that’s just silly IMHO).
For millions of things compute it exclusively on the GPU (that’s how today’s fancier games handle particles and here’s a relevant example of that: https://www.youtube.com/watch?v=p2YyNKPlJrE).

1 Like

Thanks for these general tips for beginners, but I would like a more in-depth discussion on how we can make things easier with the concern of high performance, and thus organize our data and code with Haxe and Logic Nodes to handle the usual cases in game development where we have many items of the same type to update as soon as possible.
As an example on the type of experience sharing / discussion, what I would like to find in this post concerns the current SIMD materials which are extremely fussy about data layout. We can only achieve optimal performance by organizing our matrices in memory, but we still need to know how it works with Haxe ? ( managed efficiently by type of SIMD hardware? how are things optimized in Haxe? what should we do to facilitate performance for the most efficient memory configuration depending on the SIMD hardware used like we could have with pragma directives in certain langages ? focus on how our machine will execute code and data with Logic Nodes / Kha / Haxe …)

As I hinted before, working with kha.simd is the same as working with simd intrinsics in C. There’s nothing special to it at all. In general the only thing to keep in mind compared to C is that you can not create a contiguous array of class instances (just like in Java and many other languages) and that Float is a 64 bit type (that’s because JavaScript used to only support 64 bit floats and Kha’s FastFloat type was added for that reason).

@Didier You may also want to investigate the entity component system (ECS) design model as a competitor to the Object Oriented model. Here is a great article on how an ECS helps in the context of games:

https://kyren.github.io/2018/09/14/rustconf-talk.html

I know you aren’t really building a game, but the data-oriented design of an ECS can be great for performance and the way it breaks down the problem solving into components and systems is really useful for handling features as a project grows. I’m not sure how well that would fit in with designing larger software in logic nodes, but I figured I’d throw it out there.