Dynamic Texture Example

Dynamic textures are a great feature. They allow you to do everything from blood-splats to tire-skids to, well, building entire image manipulation systems in blender.

In my case I wanted to have a vehicle leave marks in the mud:


This is a preliminary test that just leaves spherical trail-marks. At some point I may expand this to leaving tire-tracks, but I need some more inspiration for that!

The Blend:
TireTest.blend (526 KB)
Hit play and use the arrow keys

Description:
A node texture uses a generated texture map to mix between grass and mud. The same texture is mapped to the height. The location of the sphere is used as the position for the brush of the dynamic texture.

Code:

import bge
import math


IMAGE_SIZE = 512
IMAGE_RGB = b'\x88\x88\x88'

TRACK_SIZE = int(IMAGE_SIZE/8)
TRACK_ALPHA = 1
TRACK_SIGMA = 2.0
BLEND_MODE = bge.texture.IMB_BLEND_MIX

TARGET_OBJ = 'Cube'

def init(cont):
    cont.owner['brush'] = generate_brush()
    generate_tex(cont)
    
def generate_tex(cont):
    tex = bge.texture.Texture(cont.owner, 0, 0)
    tex.source = bge.texture.ImageBuff(IMAGE_SIZE, IMAGE_SIZE, 0)
    tex.source.load(IMAGE_RGB * (IMAGE_SIZE**2),IMAGE_SIZE,IMAGE_SIZE)
    
    tex.refresh(False)
    cont.owner['tex'] = tex

def generate_brush():
    brush = bytearray(TRACK_SIZE*TRACK_SIZE*4)
    ro = 2.0*TRACK_SIGMA**2
    for y in range(TRACK_SIZE):
        for x in range(TRACK_SIZE):
            pixel_num = y*TRACK_SIZE*4+x*4
            
            x_distance=abs(x-TRACK_SIZE/2) / TRACK_SIZE
            y_distance=abs(y-TRACK_SIZE/2) / TRACK_SIZE
            dist = math.sqrt(x_distance**2 + y_distance**2)
            alpha=int((255 - min(dist**0.6 * 255 * 2, 255)) * TRACK_ALPHA)
            
            col = [int((1 - math.cos(dist*math.pi * 2))*127)]*3
            brush[pixel_num:pixel_num+3] = col
            brush[pixel_num+3] = alpha
    return brush

def update(cont):
    brush = cont.owner['brush']
    
    pos = cont.owner.scene.objects[TARGET_OBJ].worldPosition 
    x = int(pos.x / cont.owner.worldScale.x * IMAGE_SIZE + IMAGE_SIZE / 2 - TRACK_SIZE / 2) 
    y = int(pos.y / cont.owner.worldScale.y * IMAGE_SIZE + IMAGE_SIZE / 2 - TRACK_SIZE / 2) 

    cont.owner['tex'].source.plot(brush,TRACK_SIZE,TRACK_SIZE,x,y,BLEND_MODE)
    cont.owner['tex'].refresh(False)

Attachments


Nice work, I also have discovered this feature for me for some time it’s a very nice thing.

Love it. Thank you.

Hurgh. I’m pretty sure I mentioned this method when you were trying to integrate PIL.
Yes you can load brushes from any format supported by imageFFmpeg(). Heck, you could even use video’s as brushes. Save images? Not sure. I can’t see why not.
And their is no easy way to solve painting across seams.
Also, go look what the word ‘barycentric’ means before you chuck it in random sentences.

I use barycentric transform to extract a UV cord in my paint application, I know exactly what it means…

You know, you don’t have to be so critical, and if you do want to be critical, please do it privately.

when I tried using this method, for some reason I was only getting a black buffer **

I think it was do to the method I was using to import the image. (bad math*)