STK-BLENDER
#1
Wink 
Discussion space related to the STK addon in Blender.

I have gained experience and a good command of Blender (dev script python) and I am currently working on a node editor for launching tests of different 3D artist projects from blender.
An advanced version already exists on my personal repository https://github.com/LLS-lopos/stk-blender/tree/master and I am currently rewriting it properly in https://github.com/LLS-lopos/stk-blender...stk-origin for the main version of the addon.
All constructive remarks are welcome with great pleasure (even negative criticism). 
If anything seems interesting to add/modify, please report it.
I am also willing to perform other tasks related to the existing code (improvements and other things).

# Left image: the version currently being rewritten
# Right image: the test version


Attached Files Thumbnail(s)
       
Reply
#2
Added a configuration file to automate the zip compression of addons such as release Latest (https://github.com/LLS-lopos/stk-blender...addon.yaml)

For the 2.79/2.8 addons, a manual release would be needed for each one, and then the addons could be downloaded directly from the official website.
Reply
#3
Not sure if this is relevant, but I wrote a small script to render out the existing carts from the spm files to png in a few different orientations. I just did this for inspiration while I was sketching, and confirmation that I was keeping with existing styles and standards when I was playing around with new kart ideas.

I am busy trying to line up a new gig right now, so the past week, and probably the next month, any of my contributions are gonna go on hold. But, if anyone else wants the script or the files, lemme know!

[Image: yJNMdnk.png]
Reply
#4
(10-01-2026, 04:24 PM)kadajett Wrote: Not sure if this is relevant, but I wrote a small script to render out the existing carts from the spm files to png in a few different orientations. I just did this for inspiration while I was sketching, and confirmation that I was keeping with existing styles and standards when I was playing around with new kart ideas.

I am busy trying to line up a new gig right now, so the past week, and probably the next month, any of my contributions are gonna go on hold. But, if anyone else wants the script or the files, lemme know!
Interesting project (it's quite surprising that the wheel and texture are missing).
Reply
#5
Wheels are added separately by the game. If you look into a kart's folder, you'll see it has separate .spm files for the wheels, like "wheel-front-left.spm".

I believe that it's related to allowing them to rotate (like wheels and of course left and right for the front wheels) independently from the main kart chassis.
Reply
#6
The wheels are already there, they are just below the karts. If those are the SPM files of the karts then they will appear in the middle, since the kart xml has their positions and not the SPM itself
Reply
#7
   
node for graphics configuration

   
Illustration of the info node which allows visualization of the commands created (sudo does not appear on Windows, start as administrator if needed)

   

The nodes are currently functional according to Alayan's recommendations (if I haven't been too distracted LOL)

   

all available nodes

VERT is functioning as originally intended.

ORANGE (medium/partial completion)

RED present/not functional/without significant consequence (currently being implemented)


Tests can be performed by retrieving the zipped file from here. -> https://github.com/LLS-lopos/stk-blender...tag/master
Reply
#8
   
update
Reply
#9
https://github.com/supertuxkart/stk-blender/pull/45

Adds a fix related to a change in the Blender API for exporting Blender particle systems.
I encourage you to test this modification
----
Important Message and Personal Notice

I've always had problems with the particle system in SuperTuxKart; sometimes it works and sometimes it doesn't (I don't understand why, but the blend file often breaks).
Should we use the cache for better results and a backup of the particle system put in place?

Personally, I use geo-nodes for the same task and I have never had a problem.
It creates my instance scattering and with "make instances real" I don't worry about potential errors related to data loss in the particle system.
---
If you have any problems, don't hesitate to mention them in this chat room.
Reply
#10
Sven confirmed your change works for Cocoa Temple so I merged it. Well done! (I know you still have your other PRs sitting around, I'll get to them...)

(10-04-2026, 12:25 AM)LLS_forum Wrote: I've always had problems with the particle system in SuperTuxKart; sometimes it works and sometimes it doesn't (I don't understand why, but the blend file often breaks).
Should we use the cache for better results and a backup of the particle system put in place?

Personally, I use geo-nodes for the same task and I have never had a problem.
It creates my instance scattering and with "make instances real" I don't worry about potential errors related to data loss in the particle system.

I'm not familiar with the technical merits or demerits of either method. I suppose that's something for people using Blender to create tracks in STK to discuss and decide. Maybe @Sven has some opinion on the matter.
Reply
#11
The problem is that the particle system breaks down strangely and systematically. @Alayan
I have a backup method that's not very practical but functional (thanks to Sven, without him it would have taken me 3 months to decide to do it XD)

   

   

   

   

---
Code:
# Keep a copy for any changes
# The copy must have activated "Switch to point instance enabled" for "GeoNodeParticle".

import bpy, subprocess, pprint, random
subprocess.run("clear")

obj_particle = bpy.context.selected_objects
n_point = [] # data (object_point/location/rotation/scale/proxy)

def separate_point():
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.separate(type='LOOSE')
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')

def check_n_point():
    for i, obj in enumerate(bpy.context.selected_objects, 1):
        rotation = 0
        scale = 1
        location = (obj.location.x, obj.location.y, obj.location.z)
        n_point.append([obj, location, rotation, scale])
    pprint.pprint(n_point)

def new_scale():       
    for i in n_point:
        a = random.uniform(0.6, 1)
        i[3] = a

def new_rotation():
    for i in n_point:
        a = random.uniform(-6.28319, 6.28319)
        i[2] = a

def attribute_proxy_to_point(obj):
    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.object.select_pattern(pattern=obj, case_sensitive=True, extend=False)
    fix = 1
    for n in range(len(n_point)):
        copy = bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":True, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "orient_type":'GLOBAL', "orient_matrix":((0, 0, 0), (0, 0, 0), (0, 0, 0)), "orient_matrix_type":'GLOBAL', "constraint_axis":(False, False, False), "mirror":False, "use_proportional_edit":False, "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "use_proportional_connected":False, "use_proportional_projected":False, "snap":False, "snap_elements":{'INCREMENT'}, "use_snap_project":False, "snap_target":'CLOSEST', "use_snap_self":True, "use_snap_edit":True, "use_snap_nonedit":True, "use_snap_selectable":False, "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "gpencil_strokes":False, "cursor_transform":False, "texture_space":False, "remove_on_cancel":False, "view2d_edge_pan":False, "release_confirm":False, "use_accurate":False, "use_automerge_and_split":False})
        n_point[n].append(bpy.data.objects[obj+"."+str(fix).zfill(3)])
        fix += 1
    for i in n_point:
        i[4].location = i[0].location
       
def modify_rotation_proxy():
    for i in n_point:
        i[4].rotation_euler.z = i[2]

def modify_scale_proxy():
    for i in n_point:
        i[4].scale.xyz = i[3]

def clear_point():
    for i in n_point:
        obj = bpy.data.objects.get(i[0].name)
        if obj:
            bpy.ops.object.select_all(action='DESELECT')
            obj.select_set(True)
            bpy.context.view_layer.objects.active = obj
            bpy.ops.object.delete()
    for i in n_point:
        obj = bpy.data.objects.get(i[4].name)
        if obj:
            obj.name = obj.name+"-geo-node"

separate_point()
check_n_point()
new_scale()
new_rotation()
attribute_proxy_to_point("boxe22") # Name of your object
modify_rotation_proxy()
modify_scale_proxy()
clear_point()

#pprint.pprint(n_point)
Reply
#12
I started writing this documentation for io_artists

.zip   Doc Node Editor STK RUNNER (io_artists).zip (Size: 146.87 KB / Downloads: 3)
language: FR/EN
Reply
#13
I have just started a small experiment, adding shader nodes for all currently available types from the property parameters in the shader editor.

I'm testing a node using only the Blender configuration and a copy using the shaders present in the SuperTuxKart source code via GLSL injection.

EEVEE
   
CYCLES
   


---
I don't know how to change the size of an "attachment" image. Cry
Reply
#14
(11-05-2026, 01:56 AM)LLS Wrote: I don't know how to change the size of an "attachment" image. Cry

The trick is pretty simple:
(1) Retrieve the URL of the attachment, for example: https://forum.supertuxkart.net/attachment.php?aid=136
(2) Use the insert image option and put the attachment's URL in.
(3) (Optionally) Specify a width/height

The attachment id are not some secret, it's just a sequentially increasing number and once you upload your attachment you can easily see the attachment's number, so you don't even really need to copy the attachment's URL, you can easily get it by changing the attachment id at the end of the base URL that could be bookmarked.
Reply
#15
For nitro export, do we need to adjust and handle the case where there is only 1 nitro, to automatically add the second one???

Code:
def saveNitroEmitter(self, f, lNitroEmitter, path):
    if len(lNitroEmitter) > 2:
        self.report({'WARNING'}, " %d nitro emitter specified. Up to 2 are allowed." % len(lNitroEmitter))
        return
    if len(lNitroEmitter) > 0:
        f.write('  <nitro-emitter>\n')
        letters = ['a', 'b']
        for i, nitro in enumerate(lNitroEmitter):  # i is object index
            f.write('    <nitro-emitter-%s position = "%f %f %f" />\n' \
                    % (letters[i], nitro.location.x, nitro.location.z, nitro.location.y))
            if i == 1:
                f.write('  </nitro-emitter>\n')
    if len(lNitroEmitter) == 1:
        f.write('    <nitro-emitter-b position = "%f %f %f" />\n' \
                    % (lNitroEmitter[0].location.x, lNitroEmitter[0].location.z, lNitroEmitter[0].location.y))
        f.write('  </nitro-emitter>\n')

or perhaps we will only be able to have one ???

Code:
def saveNitroEmitter(self, f, lNitroEmitter, path):
    if len(lNitroEmitter) > 2:
        self.report({'WARNING'}, " %d nitro emitter specified. Up to 2 are allowed." % len(lNitroEmitter))
        return
    if len(lNitroEmitter) > 0:    
        f.write('  <nitro-emitter>\n')
        f.write('    <nitro-emitter-a position = "%f %f %f" />\n' \
                % (lNitroEmitter[0].location.x, lNitroEmitter[0].location.z, lNitroEmitter[0].location.y))
        f.write('    <nitro-emitter-b position = "%f %f %f" />\n' \
                % (lNitroEmitter[1].location.x, lNitroEmitter[1].location.z, lNitroEmitter[1].location.y))
        f.write('  </nitro-emitter>\n')
    else:
        f.write('  <nitro-emitter>\n')
        f.write('    <nitro-emitter-a position = "%f %f %f" />\n' \
                % (lNitroEmitter[0].location.x, lNitroEmitter[0].location.z, lNitroEmitter[0].location.y))
        f.write('  </nitro-emitter>\n')
Reply
#16
You mean when exporting a kart?

I guess the best way to know is to actually get a kart with only one nitro emitter and see what happens when you try to use it in-game... Does the game handle it gracefully or does it generate some error message? A single nitro-emitter doesn't necessarily look wrong with the right kart design, but in that case putting both nitro emitters in the same spot would probably work too...
Reply
#17
(15-05-2026, 07:30 PM)Alayan Wrote: I guess the best way to know is to actually get a kart with only one nitro emitter and see what happens when you try to use it in-game... Does the game handle it gracefully or does it generate some error message? A single nitro-emitter doesn't necessarily look wrong with the right kart design, but in that case putting both nitro emitters in the same spot would probably work too...

With the old code, the export was broken; handling the case (lNitroEmitter) == 1 fixes the export problem.
Reply
#18
My question was about what happens when you try to use that kart with one emitter in STK.
Reply
#19
(16-05-2026, 03:31 PM)Alayan Wrote: My question was about what happens when you try to use that kart with one emitter in STK.

If, despite everything, you have a kart with only one nitro emitter, the game will create a second one in the very center of the kart.

file kart_model.cpp

line 199-216
Code:
    if(const XMLNode *wheels_node=node.getNode("wheels"))
    {
        loadWheelInfo(*wheels_node, "front-right", 0);
        loadWheelInfo(*wheels_node, "front-left",  1);
        loadWheelInfo(*wheels_node, "rear-right",  2);
        loadWheelInfo(*wheels_node, "rear-left",   3);
    }

    m_nitro_emitter_position[0] = Vec3 (0,0.1f,0);
    m_nitro_emitter_position[1] = Vec3 (0,0.1f,0);
    m_has_nitro_emitter = false;

    if(const XMLNode *nitroEmitter_node=node.getNode("nitro-emitter"))
    {
        loadNitroEmitterInfo(*nitroEmitter_node, "nitro-emitter-a", 0);
        loadNitroEmitterInfo(*nitroEmitter_node, "nitro-emitter-b", 1);
        m_has_nitro_emitter = true;
    }
Reply
#20
The solution of placing the second emitter in the same position as the first emitter is also the idea I was considering. The change has been merged in the Blender scripts.
Reply


Forum Jump: