Help getting image texture painting working in BGE

I am getting a buffer size error on line 144

External LIB PILLOW required
(going to be moving this to BGE and BPY eventually)

my images are
NewImage



and

RedBrush


import bge
import mathutils 
from PIL import Image
from bge import texture
from bge import logic
from mathutils import Vector
from bgl import Buffer, GL_BYTE
import itertools


def cmp(a, b): 
    return int((a > b) -(a < b))


def main():


    cont = bge.logic.getCurrentController()
    own = cont.owner


    MOA = cont.sensors['MouseOverAny']
    Left = cont.sensors['Mouse']
    MOA2 = cont.sensors['MouseOverColor']
    MOA3 = cont.sensors['MOA3']
    
    
    
    
    
    
    if 'Init' not in own:
        for child in own.children:
            if 'T1' in child:
                own['T1']=child
            if 'T2' in child:
                own['T2']=child    
        own['Camera']=bge.logic.getCurrentScene().objects['Camera']
        meshOb = bge.logic.getCurrentScene().objects['Plane']
        
        
        ####replace canvas texture with file#####
        obj = meshOb
        #object_texture = texture.Texture(obj, 0)
        #url = logic.expandPath("//cIRLIC2.png")
        #new_source = texture.ImageFFmpeg(url)
        #logic.texture = object_texture
       # logic.texture.source = new_source
        #logic.texture.refresh(False)
        
        #own['imageTex']=logic.texture.source
        #own['Back']=new_source
        #url = logic.expandPath("//RedBrush.png")
        #brush = texture.ImageFFmpeg(url)
        #own['Brush']=brush
        
        
        
        own['Init']=True
        
        mesh = meshOb.meshes[0]
        VL =[]
        PL =[]
        #### Build BVHTREE ######
        for m_index in range(len(mesh.materials)):
            for v_index in range(mesh.getVertexArrayLength(m_index)):
                vertex = mesh.getVertex(m_index, v_index)
                xYz = meshOb.worldPosition+(meshOb.worldOrientation.inverted()*vertex.XYZ)
                
                VL.append((xYz[0],xYz[1],xYz[2]) )
                
                
            for f_index in range(mesh.numPolygons):
                poly = mesh.getPolygon(f_index)
                L=[]
                v0 = poly.getVertexIndex(0)
                L.append(v0)
                v1 = poly.getVertexIndex(1)
                L.append(v1)
                v2 = poly.getVertexIndex(2)
                L.append(v2)
                if poly.v4:
                    v3=poly.getVertexIndex(3)
                    L.append(v3)
                PL.append(L)
                    
        
        own['Mesh']=mesh     
        own['OB'] = obj   
        B =mathutils.bvhtree.BVHTree.FromPolygons(VL, PL, all_triangles=False, epsilon=0.0)
        
        own['BVH']=B
    if MOA3.positive and Left.positive:
        
        v2 = MOA3.hitPosition-own.worldPosition
       
       
        ray =  own.rayCast(own.worldPosition+v2*300, own.worldPosition,0,'',0,0,2)
        #swapped BVHTree for reg game engine raycast with polyProxy2 (return uv)
        #print(ray[0])
        
        
        if ray[3]:
            obj = own['OB']
            mesh = own['Mesh']
            hitPosition = MOA3.hitPosition
           
            # calculate vectors from point hitPoint to vertices p1, p2 and p3:
            poly = ray[3] # hit face from ray
            #print(poly.v1)
            
            vertex_a = mesh.getVertex(0, poly.v1)
            vertex_b = mesh.getVertex(0, poly.v2)
            vertex_c = mesh.getVertex(0, poly.v3)
           
            hitPosition = MOA3.hitPosition
           
            marker = obj.scene.addObject('HL',own,1)
            marker.worldPosition = hitPosition
           
             # find the uv corresponding to point f (uv1/uv2/uv3 are associated to p1/p2/p3)
            obj_space_pos = obj.worldTransform.inverted() * MOA3.hitPosition
            uv= mathutils.geometry.barycentric_transform(obj_space_pos, vertex_a.XYZ, vertex_b.XYZ, vertex_c.XYZ,
                                                                        vertex_a.UV.to_3d(), vertex_b.UV.to_3d(), vertex_c.UV.to_3d())
                       
            own['UV']=str(uv)
            own['T1']['Text']=str(uv[0])
            own['T2']['Text']=str(uv[1])
            
            
            # we have UV lets draw!
            if 'IM' not in own:
                own['IM']=Image.open(logic.expandPath("//NewImage.png"))
                #own['IM'].convert("RGBA")
                own['Brush']=Image.open(logic.expandPath("//RedBrush.png"))
                
            background = own['IM']
            foreground = own['Brush']


            background.paste(foreground, (int(uv[0]), int(uv[1])), foreground)
            #background.show()
            im = background
            data = list(itertools.chain.from_iterable(im.getdata()))
            imbuf = Buffer(GL_BYTE, [len(data)], data)


            bge.logic.tex = texture.Texture(bge.logic.getCurrentScene().objects['Plane'], 0, 0)
            bge.logic.tex.source = texture.ImageBuff()
            bge.logic.tex.source.load(imbuf, im.size[0], im.size[1])


            bge.logic.tex.refresh(True)
                       
                   
        
    if MOA.positive:
        added=bge.logic.getCurrentScene().addObject('HL',own,1)
        added.worldPosition=MOA.hitObject.worldPosition
        added.worldPosition.z+=.01    
        
        
        
        
    if MOA.positive and Left.positive:
        #color a whole object
        MOA.hitObject.color = [ own['R'], own['G'], own['B'],own['A']]
                
        
        
        
        
    if MOA2.positive and Left.positive:
        # snag color from a object
        
        own['R']=MOA2.hitObject.color[0]
        own['G']=MOA2.hitObject.color[1]
        own['B']=MOA2.hitObject.color[2]
        own['A']=MOA2.hitObject.color[3]
         
                


main()



any help getting it painting would be appreciated

Attachments

BPRBrushTestUVBGE_Ray.blend (1.77 MB)

  • requires you to install PIL and then copy it from your python LIB -> site-packages and paste it in blender -> python *

working but slow, Moguri may have a solution up his sleeve though.

edit: thanks Youle and Agoose77 and Moguri and Kupoman and sdfgeoff.

MOA = cont.sensors[‘MouseOverAny’]
Left = cont.sensors[‘Mouse’]
MOA2 = cont.sensors[‘MouseOverColor’]
MOA3 = cont.sensors[‘MOA3’]

smack, smack, smack…

you know better than this

Yar, this file is the end resault of tinkering night by night without any plan in the end other then understanding how to do it right next time, it started off painting vertex colors =]

I do understand where the limitations are now and its not game logic, its that I need a way to cram a image into bge.buffer without passing heightwidthRGBA parts into the buffer,

Like instead pass a whole image at once, moguri was taking a look at the situation, and Tristan said if I need it, he may be able to add a argument to the image buffer video texture to accept a pil image as a input,

ok

Moguri says I can most likely use

bindcode = own.meshes[0].materials[0].getTextureBindcode(0)

which was added in 2.77
and pyopengl to pass the pil image to GLSL
however I have no idea what I am doing. :smiley:

probably going to need Youles help or someone.

Code I have -

import bgeimport mathutils
from PIL import Image
from bge import texture
from bge import logic
from mathutils import Vector
from bgl import Buffer, GL_BYTE




def cmp(a, b):
    return int((a > b) -(a < b))




def main():
    cont = bge.logic.getCurrentController()
    own = cont.owner


    MOA = cont.sensors['MouseOverAny']
    Left = cont.sensors['Mouse']
    MOA2 = cont.sensors['MouseOverColor']
    MOA3 = cont.sensors['MOA3']




    if 'Init' not in own:
        for child in own.children:
            if 'T1' in child:
                own['T1']=child
            if 'T2' in child:
                own['T2']=child


        own['Camera']=bge.logic.getCurrentScene().objects['Camera']
        meshOb = bge.logic.getCurrentScene().objects['Plane']


        ####replace canvas texture with file#####
        obj = meshOb




        own['Init']=True


        mesh = meshOb.meshes[0]
        VL =[]
        PL =[]
        #### Build BVHTREE ######
        for m_index in range(len(mesh.materials)):
            for v_index in range(mesh.getVertexArrayLength(m_index)):
                vertex = mesh.getVertex(m_index, v_index)
                xYz = meshOb.worldPosition+(meshOb.worldOrientation.inverted()*vertex.XYZ)


                VL.append((xYz[0],xYz[1],xYz[2]) )




            for f_index in range(mesh.numPolygons):
                poly = mesh.getPolygon(f_index)
                L=[]
                v0 = poly.getVertexIndex(0)
                L.append(v0)
                v1 = poly.getVertexIndex(1)
                L.append(v1)
                v2 = poly.getVertexIndex(2)
                L.append(v2)
                if poly.v4:
                    v3=poly.getVertexIndex(3)
                    L.append(v3)
                PL.append(L)




        own['Mesh']=mesh
        own['OB'] = obj
        B =mathutils.bvhtree.BVHTree.FromPolygons(VL, PL, all_triangles=False, epsilon=0.0)


        own['BVH']=B


    if MOA3.positive and Left.positive:
        v2 = MOA3.hitPosition-own.worldPosition


        ray =  own.rayCast(own.worldPosition+v2*300, own.worldPosition,0,'',0,0,2)
        #swapped BVHTree for reg game engine raycast with polyProxy2 (return uv)




        if ray[3]:
            obj = own['OB']
            mesh = own['Mesh']


            # calculate vectors from point hitPoint to vertices p1, p2 and p3:
            poly = ray[3] # hit face from ray
            #print(poly.v1)


            vertex_a = mesh.getVertex(0, poly.v1)
            vertex_b = mesh.getVertex(0, poly.v2)
            vertex_c = mesh.getVertex(0, poly.v3)


            hitPosition = MOA3.hitPosition


            marker = obj.scene.addObject('HL',own,1)
            marker.worldPosition = hitPosition


             # find the uv corresponding to point f (uv1/uv2/uv3 are associated to p1/p2/p3)
            obj_space_pos = obj.worldTransform.inverted() * MOA3.hitPosition
            uv= mathutils.geometry.barycentric_transform(obj_space_pos, vertex_a.XYZ, vertex_b.XYZ, vertex_c.XYZ,                              vertex_a.UV.to_3d(), vertex_b.UV.to_3d(), vertex_c.UV.to_3d())


            own['UV']=str(uv)
            #own['T1']['Text']=str(uv[0])
            #own['T2']['Text']=str(uv[1])


            # we have UV lets draw!
            if 'IM' not in own:
                own['IM'] = Image.open(logic.expandPath("//NewImage.png"))
                own['Brush'] = Image.open(logic.expandPath("//RedBrush.png"))




            brush = own['Brush']
            image = own['IM']


            width, height = image.size


            x_pos = round(uv[0] * width) - brush.width // 2
            y_pos = round((1-uv[1]) * height) - brush.height // 2


            image.paste(brush, (x_pos, y_pos), brush)
            own['IM'] = image


            # The rest is only for display (to rgb, and flip)
            image = image.convert("RGB")
            image = image.transpose(Image.FLIP_TOP_BOTTOM)


            surface = own['OB']


            img_data = image.tobytes()
            
            imbuf = Buffer(GL_BYTE, len(img_data), img_data)


            surface_texture = surface['_texture'] = texture.Texture(surface, 0)
            if 'Buff' not in own:
                own['Buff']=texture.ImageBuff()
                
            surface_texture.source = own['Buff']
            surface_texture.source.load(imbuf, width, height)
            surface_texture.refresh(True)


    if MOA.positive:
        added=bge.logic.getCurrentScene().addObject('HL',own,1)
        added.worldPosition=MOA.hitObject.worldPosition
        added.worldPosition.z+=.01


        if Left.positive:
            pass


    if MOA2.positive and Left.positive:
        # snag color from a object


        own['R']=MOA2.hitObject.color[0]
        own['G']=MOA2.hitObject.color[1]
        own['B']=MOA2.hitObject.color[2]
        own['A']=MOA2.hitObject.color[3]


main()



Code I need to use to cram PIL image into texture slot…


im = open(imageName)
try:
# get image meta-data (dimensions) and data
    ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBA", 0, -1)

except SystemError:
# has no alpha channel, synthesize one, see the
# texture module for more realistic handling
    ix, iy, image = im.size[0], im.size[1], im.tostring("raw", "RGBX", 0, -1)

# generate a texture ID
ID = bindcode = own['Ob'].meshes[0].materials[0].getTextureBindcode(0) 
# make it current
glBindTexture(GL_TEXTURE_2D, ID)
glPixelStorei(GL_UNPACK_ALIGNMENT,1)
# copy the texture into the current texture ID
glTexImage2D(GL_TEXTURE_2D, 0, 3, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)

and after I get this working

“im.tostring to get things going, but that probably involves a copy.”

Pow!

it needs testing and work… but it’s working :smiley:

UV based real time texture painting in the bge :smiley:

(about 7 ms logic in a 1024x1024 background and a 16x16 brush, code unoptimized)

Looks interesting.

use cases - real time destruction - bullet damage etc, real time tire tracks, etc,

I was thinking you could paint to a second uv layer that was unique to each item in game for real time damage,

also of course real time texture painting in engine - (see how it looks in game while you make it)
going to work on multi channel and color picking next.

I’m interested in using something like this for tire tracks and the wake that goes behind a boat. Any chance I could get access to your work to do some tests of my own?

sdfgeoff made a more simple system without all the dependencies on PIL

I will try and dig it up,
edit: https://blenderartists.org/forum/showthread.php?397651-Dynamic-Texture-Example&highlight=