Results 1 to 16 of 16

Thread: Pixelate3D

  1. #1

    Pixelate3D

    Hi, everyone.

    Here's my "version 1.0" Pixelation script. Also attached are two images showing the basic functionality.

    Of course, Suzanne isn't the best example - cambo mentioned that it is not a closed mesh because the eyes are non manifold. But the script mostly works for Suzanne. For any fully closed mesh, however, it should work without any errors.

    The GUI allows you to choose the "brick" sizing in all three axes, to choose the destination layer for the bricks, and whether or not you want the original object removed.

    Note that it can be quite slow. I received a few suggestions from cambo for speed ups that I'll look into next.

    Some of the key parts of this code are from cambo, with minor/tiny modifications.


    BEFORE:


    AFTER:


    Code:
    #!BPY
    
    """
    Name: 'Pixelate'
    Blender: 241
    Group: 'Mesh'
    Tooltip: '3D Pixelation of Meshes'
    """
    
    __author__ = "David Levine"
    __url__ = ("blender", "elysiun")
    __version__ = "1.0 02/24/06"
    
    __bpydoc__ = """\
    This script takes closed meshes and 'pixelates' them in 3D.
    
    Usage:
    
    Select the mesh and run this script.  You can specify the size of
    the blocks used.  You can specify the destination layer for those
    blocks.  You can also specify that the original object is removed.
    Upon hitting the PIXELATE button, a 'blocky' version of your original
    mesh will be created.
    
    Thanks to: cambo, RipSting, JM Soler
    """
    
    from Blender import *
    from Blender.BGL import *
    from Blender.Draw import *
    from Blender.Noise import *
    
    def ptInFaceXYBounds(f, pt):
        co= f.v[0].co
        xmax= xmin= co.x
        ymax= ymin= co.y
    
        co= f.v[1].co
        xmax= max(xmax, co.x)
        xmin= min(xmin, co.x)
        ymax= max(ymax, co.y)
        ymin= min(ymin, co.y)
    
        co= f.v[2].co
        xmax= max(xmax, co.x)
        xmin= min(xmin, co.x)
        ymax= max(ymax, co.y)
        ymin= min(ymin, co.y)
    
        if len(f.v)==4: 
            co= f.v[3].co
            xmax= max(xmax, co.x)
            xmin= min(xmin, co.x)
            ymax= max(ymax, co.y)
            ymin= min(ymin, co.y)
            
        # Now we have the bounds, see if the point is in it.
        if\
        pt.x < xmin or\
        pt.y < ymin or\
        pt.x > xmax or\
        pt.y > ymax&#58;
            return False # point is outside face bounds
        else&#58;
            return True # point inside.
            
    def pointInsideMesh&#40;ob, pt&#41;&#58;
        obMat = Mathutils.Matrix&#40;ob.matrixWorld&#41;
        obInvMat = obMat.invert&#40;&#41;
        obSpaceVec = pt* obInvMat
        obSpacePt = Mathutils.Vector&#40;obSpaceVec&#91;0&#93;, obSpaceVec&#91;1&#93;, obSpaceVec&#91;2&#93;&#41;
    
        Intersect = Mathutils.Intersect # 2 less dict lookups.
        Vector = Mathutils.Vector
    
        ray = Vector&#40;0,0,-1&#41;
    
        def faceIntersect&#40;f&#41;&#58;
            isect= Intersect&#40;f.v&#91;0&#93;.co, f.v&#91;1&#93;.co, f.v&#91;2&#93;.co, ray, obSpacePt, 1&#41; # Clipped.
            if not isect and len&#40;f.v&#41; == 4&#58;
                isect= Intersect&#40;f.v&#91;0&#93;.co, f.v&#91;2&#93;.co, f.v&#91;3&#93;.co, ray, obSpacePt, 1&#41; # Clipped.
    
            if isect and isect.z > obSpacePt.z&#58; # This is so the ray only counts if its above the point. 
                return True
            else&#58;
                return False
    
        me= ob.getData&#40;mesh=1&#41;
    
        # Here we find the number on intersecting faces, return true if an odd number &#40;inside&#41;, false &#40;outside&#41; if its true.
        return len&#40;&#91;None for f in me.faces if ptInFaceXYBounds&#40;f, obSpacePt&#41; if faceIntersect&#40;f&#41;&#93;&#41; % 2
    
    def findminmaxxyz&#40;obj&#41;&#58;
        minx= 999999999
        maxx= -999999999
        miny= 999999999
        maxy= -999999999
        minz= 999999999
        maxz= -999999999
        boundBox= obj.getBoundBox&#40;&#41;
        for v in boundBox&#58;
            minx= min&#40;minx, v.x&#41;
            maxx= max&#40;maxx, v.x&#41;
            miny= min&#40;miny, v.y&#41;
            maxy= max&#40;maxy, v.y&#41;
            minz= min&#40;minz, v.z&#41;
            maxz= max&#40;maxz, v.z&#41;
        minV= Mathutils.Vector&#40;minx,miny,minz&#41;
        maxV= Mathutils.Vector&#40;maxx,maxy,maxz&#41;
        return &#91;minV,maxV&#93;
    
    def makeblock&#40;name='blockModel',mat=None,min=&#91;-1.0,-1.0,-1.0&#93;,max=&#91;1.0,1.0,1.0&#93;&#41;&#58;
        vertices_list=&#91;
            &#91;min&#91;0&#93;,min&#91;1&#93;,min&#91;2&#93;&#93;,
            &#91;min&#91;0&#93;,max&#91;1&#93;,min&#91;2&#93;&#93;,
            &#91;max&#91;0&#93;,max&#91;1&#93;,min&#91;2&#93;&#93;,
            &#91;max&#91;0&#93;,min&#91;1&#93;,min&#91;2&#93;&#93;,
            &#91;min&#91;0&#93;,min&#91;1&#93;,max&#91;2&#93;&#93;,
            &#91;min&#91;0&#93;,max&#91;1&#93;,max&#91;2&#93;&#93;,
            &#91;max&#91;0&#93;,max&#91;1&#93;,max&#91;2&#93;&#93;,
            &#91;max&#91;0&#93;,min&#91;1&#93;,max&#91;2&#93;&#93;
        &#93;
        faces_list=&#91;
            &#91;0,1,2,3&#93;,
            &#91;4,5,6,7&#93;,
            &#91;0,4,7,3&#93;,
            &#91;1,2,6,5&#93;,
            &#91;0,1,5,4&#93;,
            &#91;3,7,6,2&#93;
        &#93;
        blockobj= Object.New&#40;'Mesh',name&#41;
        Scene.getCurrent&#40;&#41;.link&#40;blockobj&#41;
        blockobj.layers = &#91;NumberL.val&#93;
        blockmesh= blockobj.getData&#40;&#41;
        for v in vertices_list&#58;
            vert= NMesh.Vert&#40;v&#91;0&#93;,v&#91;1&#93;,v&#91;2&#93;&#41;
            blockmesh.verts.append&#40;vert&#41;
        for f in faces_list&#58;
            meshface= NMesh.Face&#40;&#41;
            for vv in f&#58;
                meshface.append&#40;blockmesh.verts&#91;vv&#93;&#41;
            blockmesh.faces.append&#40;meshface&#41;
        if mat&#58;
            blockmesh.addMaterial&#40;mat&#41;
        blockmesh.update&#40;&#41;
        return blockobj
    
    def faceCent&#40;f&#41;&#58;
        x= y= z= 0
        for v in f.v&#58;
            x+= v.co&#91;0&#93;
            y+= v.co&#91;1&#93;
            z+= v.co&#91;2&#93;
        return Mathutils.Vector&#40;&#91;x/len&#40;f.v&#41;, y/len&#40;f.v&#41;, z/len&#40;f.v&#41;&#93;&#41;
    
    def nearestFace&#40;ob, pt&#41;&#58;
        obMat= Mathutils.Matrix&#40;ob.matrixWorld&#41;
        obInvMat= obMat.invert&#40;&#41;
        obSpaceVec= pt* obInvMat
        obSpacePt= Mathutils.Vector&#40;obSpaceVec&#91;0&#93;, obSpaceVec&#91;1&#93;, obSpaceVec&#91;2&#93;&#41;
        mesh= ob.getData&#40;&#41;
        closedist= 999999999
        for f in mesh.faces&#58;
            fc = faceCent&#40;f&#41;
            dist = obSpacePt - fc
            if &#40;dist.length < closedist&#41;&#58;
                closedist = dist.length
                closeface = f
        return closeface
    
    def pix&#40;&#41;&#58;
        editmode= Window.EditMode&#40;&#41;
        if editmode&#58;
            Window.EditMode&#40;0&#41;
        Window.WaitCursor&#40;1&#41;
        Window.DrawProgressBar&#40;0.0, ''&#41;
        count= 1
        spacingx= NumberX.val
        spacingy= NumberY.val
        spacingz= NumberZ.val
        objectList= Object.GetSelected&#40;&#41;
        for obj in objectList&#58;
            if obj.getType&#40;&#41; == 'Mesh'&#58;
                minmax= findminmaxxyz&#40;obj&#41;
                x= minmax&#91;0&#93;.x
                while x < &#40;minmax&#91;1&#93;.x - &#40;spacingx/4.0&#41;&#41;&#58;
                    progress= &#40;x - minmax&#91;0&#93;.x&#41; / &#40;minmax&#91;1&#93;.x - minmax&#91;0&#93;.x&#41;
                    Window.DrawProgressBar&#40;progress, "Pixelating..."&#41;
                    y= minmax&#91;0&#93;.y
                    while y < &#40;minmax&#91;1&#93;.y - &#40;spacingy/4.0&#41;&#41;&#58;
                        z= minmax&#91;0&#93;.z
                        while z < &#40;minmax&#91;1&#93;.z - &#40;spacingz/4.0&#41;&#41;&#58;
                            posvec= Mathutils.Vector&#40;&#91;x + &#40;spacingx/2.0&#41;,y + &#40;spacingy/2.0&#41;,z + &#40;spacingz/2.0&#41;, 1.0&#93;&#41;
                            if pointInsideMesh&#40;obj, posvec&#41;&#58;
                                nearFaceMaterial= nearestFace&#40;obj, posvec&#41;.materialIndex
                                if len&#40;obj.getData&#40;&#41;.getMaterials&#40;&#41;&#41; > 1&#58;
                                    nextblock= makeblock&#40;'block' + str&#40;count&#41;,obj.getData&#40;&#41;.getMaterials&#40;&#41;&#91;nearFaceMaterial&#93;,Mathutils.Vector&#40;&#91;x,y,z&#93;&#41;,Mathutils.Vector&#40;&#91;x + spacingx,y + spacingy,z + spacingz&#93;&#41;&#41;
                                else&#58;
                                    nextblock= makeblock&#40;'block' + str&#40;count&#41;,None,Mathutils.Vector&#40;&#91;x,y,z&#93;&#41;,Mathutils.Vector&#40;&#91;x + spacingx,y + spacingy,z + spacingz&#93;&#41;&#41;
                                count= count + 1
                            z= z + spacingz
                        y= y + spacingy
                    x= x + spacingx
        if Replace.val == 1&#58;
            Scene.getCurrent&#40;&#41;.unlink&#40;obj&#41;
        Window.DrawProgressBar&#40;1.0, ''&#41;
        Window.WaitCursor&#40;0&#41;
        if editmode&#58;
            Window.EditMode&#40;1&#41;
        Window.RedrawAll&#40;&#41;
    
    Replace= Create&#40;0&#41;
    NumberZ= Create&#40;0.20&#41;
    NumberY= Create&#40;0.20&#41;
    NumberX= Create&#40;0.20&#41;
    NumberL= Create&#40;1&#41;
    
    EVENT_NOEVENT = 1
    EVENT_PIXELATE = 2
    
    def draw&#40;&#41;&#58;
        global Button, Replace, NumberZ, NumberY, NumberX, NumberL
        global EVENT_NOEVENT, EVENT_PIXELATE
        glClearColor&#40;0.753, 0.753, 0.753, 0.0&#41;
        glClear&#40;GL_COLOR_BUFFER_BIT&#41;
        glColor3f&#40;0.000, 0.000, 0.000&#41;
        glRasterPos2i&#40;108, 54&#41;
        Text&#40;'Select this toggle to completely replace the original mesh.'&#41;
        glRasterPos2i&#40;108, 86&#41;
        Text&#40;'Z Spacing &#40;0.05 to 10.00&#41;'&#41;
        glRasterPos2i&#40;108, 118&#41;
        Text&#40;'Y Spacing &#40;0.05 to 10.00&#41;'&#41;
        glRasterPos2i&#40;108, 150&#41;
        Text&#40;'X Spacing &#40;0.05 to 10.00&#41;'&#41;
        glRasterPos2i&#40;108, 182&#41;
        Text&#40;'Layer for New Pixels &#40;1 to 20&#41;'&#41;
        Button&#40;'Pixelate!', EVENT_PIXELATE, 16, 10, 83, 23, ''&#41;
        Replace= Toggle&#40;'Replace', EVENT_NOEVENT, 16, 42, 83, 23, Replace.val, ''&#41;
        NumberZ= Number&#40;'', EVENT_NOEVENT, 16, 74, 83, 23, NumberZ.val, 0.05, 10.05, ''&#41;
        NumberY= Number&#40;'', EVENT_NOEVENT, 16, 106, 83, 23, NumberY.val, 0.05, 10.05, ''&#41;
        NumberX= Number&#40;'', EVENT_NOEVENT, 16, 138, 83, 23, NumberX.val, 0.05, 10.05, ''&#41;
        NumberL= Number&#40;'', EVENT_NOEVENT, 16, 170, 83, 23, NumberL.val, 1, 20, ''&#41;
    
    def event&#40;evt, val&#41;&#58;
        if &#40;evt == QKEY and not val&#41;&#58; Exit&#40;&#41;
    
    def bevent&#40;evt&#41;&#58;
        if evt == EVENT_PIXELATE&#58;
            pix&#40;&#41;
        Redraw&#40;&#41;
    
    Register&#40;draw, event, bevent&#41;



  2. #2
    Great fun - if you add a cylinder on top of all the blocky "pixels"(as an option) you'll have a "3D Lego-lizer"

    Tin2tin



  3. #3
    Member vliegtuig's Avatar
    Join Date
    Feb 2005
    Location
    Beer Capital of the World
    Posts
    1,371
    Hey, wow this is cool!
    P



  4. #4
    Member Jedrzej_s's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    272
    Nice ! Hehehe... But I have error in console and it hasn't worked in Blender 2.41:

    SyntaxError: invalid token
    File "Pixelate.py", line 58
    if\
    ^
    SyntaxError: invalid token

    P.S. I have installed Python 2.4 and Win XP Pro.



  5. #5
    Originally Posted by Jedrzej_s
    Nice ! Hehehe... But I have error in console and it hasn't worked in Blender 2.41:

    SyntaxError: invalid token
    File "Pixelate.py", line 58
    if\
    ^
    SyntaxError: invalid token

    P.S. I have installed Python 2.4 and Win XP Pro.
    you can delete all the '\' at the end of a line and move the next line here. i.e.
    Code:
    if\ 
        pt.x < xmin or\ 
        pt.y < ymin or\ 
        pt.x > xmax or\ 
        pt.y > ymax&#58;
    to
    Code:
    if pt.x < xmin or pt.y < ymin or pt.x > xmax or pt.y > ymax&#58;



  6. #6
    Member Jedrzej_s's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    272
    THX Oyster !!! Now work !

    curiouskangaroo <- You should improve your code so as Oyster showed !

    Greetings



  7. #7
    Originally Posted by Jedrzej_s
    THX Oyster !!! Now work !

    curiouskangaroo <- You should improve your code so as Oyster showed !

    Greetings
    no, no, that is not a fault of curiouskangaroo, hehe



  8. #8
    Member Jedrzej_s's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    272
    I don't understand... ??? Why it doesn't work earlier on my PC ??? Strange...



  9. #9
    Member Ryz's Avatar
    Join Date
    Jan 2006
    Location
    Montréal, Canada
    Posts
    179
    Very cool script! Thanks. 8)



  10. #10
    Thanks for the responses, everyone!

    Originally Posted by tin2tin
    Great fun - if you add a cylinder on top of all the blocky "pixels"(as an option) you'll have a "3D Lego-lizer"

    Tin2tin
    Yes, this had occurred to me. Actually, that was my original goal, but I decided to make the script a little more general. For example, single-stud Lego bricks aren't perfect cubes but are slightly taller. But since you can adjust the size of the bricks in the GUI, you can account for that. The other thing, of course, is that building a LEGO model entirely out of single-stud bricks is pretty inefficient. I'm working on an update to use larger bricks where possible. The final challenge would be to convert the model colors to their closest LEGO-equivalents, or perhaps even do some three-dimensional dithering.

    Originally Posted by Jedrzej_s
    I don't understand... ??? Why it doesn't work earlier on my PC ??? Strange...
    I'm afraid I can't help you here. The backslash is a valid continuation character in Python (I think... this was my first project in Python), and the script works as posted on my machine.



  11. #11
    Member Jedrzej_s's Avatar
    Join Date
    Mar 2005
    Location
    Poland
    Posts
    272
    When you will use Oysters code then this script will use on my and on your computers !!! Try !!! If You don't change code maybe somebody else will have same trouble as I...



  12. #12
    Member ideasman42's Avatar
    Join Date
    Mar 2004
    Location
    Australia
    Posts
    5,338
    It would be interesting to...
    * Have random block rotation.
    * Use blocks that have a distance between eachother. could do some quasi volumetric stuff.
    * Make the script use linked duplicate objects. so the user could select 2 objects. the active would be the pixelate mesh. The inactive would be used for the pixel.

    hah. Now Im thinking of particles for bbrush
    Podcast * dotfiles * My Wiki * Blender/Stackexchange
    ideasman42<at>gmail.com



  13. #13
    Originally Posted by Jedrzej_s
    When you will use Oysters code then this script will use on my and on your computers !!! Try !!! If You don't change code maybe somebody else will have same trouble as I...
    One thing you should check is that there is no space character after the backslash in your copy of the script. That prevents Python from interpreting it as a valid line-continuation character. The backslash must be the LAST character on the line.



  14. #14
    Originally Posted by cambo
    It would be interesting to...
    * Have random block rotation.
    * Use blocks that have a distance between eachother. could do some quasi volumetric stuff.
    * Make the script use linked duplicate objects. so the user could select 2 objects. the active would be the pixelate mesh. The inactive would be used for the pixel.

    hah. Now Im thinking of particles for bbrush
    Hi, cambo. I like #1 and #3 as options - would be fun. Can you explain #2 a bit more? I'm not sure I follow when you talk about blocks having a distance between each other. Do you mean have interstitial spaces between the blocks?



  15. #15
    Member ideasman42's Avatar
    Join Date
    Mar 2004
    Location
    Australia
    Posts
    5,338
    Originally Posted by curiouskangaroo
    Originally Posted by cambo
    It would be interesting to...
    * Have random block rotation.
    * Use blocks that have a distance between eachother. could do some quasi volumetric stuff.
    * Make the script use linked duplicate objects. so the user could select 2 objects. the active would be the pixelate mesh. The inactive would be used for the pixel.

    hah. Now Im thinking of particles for bbrush
    Hi, cambo. I like #1 and #3 as options - would be fun. Can you explain #2 a bit more? I'm not sure I follow when you talk about blocks having a distance between each other. Do you mean have interstitial spaces between the blocks?
    Imagine you have blocks filling a mesh. and each block is quite small- like a bit of dust, you could then render and it would look like fog/smoke of there were enough. the thicker areas of the object would be more dense.

    Some randomization would help the effect look less patterned. (like dithering)
    - You would need to experement with different materials and brush sizes- but you could definetly do some cool stuff with transperent materials also.
    Podcast * dotfiles * My Wiki * Blender/Stackexchange
    ideasman42<at>gmail.com



  16. #16
    Member Octopus4's Avatar
    Join Date
    Jun 2005
    Location
    Jordan
    Posts
    4,531
    wow, interesting.
    iOS Developer, Game Developer & Blender Artist
    www.deyaeldeen.com



Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •