Live Texture Painting Script

Forums: Live Texture Painting Script
Forum for OctaneRender Lua scripting examples, discussion and support.

Live Texture Painting Script

Postby stratified » Mon Jan 06, 2014 11:14 pm

stratified Mon Jan 06, 2014 11:14 pm
Hi all,

In release candidate 1.25, we replaced the IMAGE_LABEL by the BITMAP component. The BITMAP component is like a paint canvas that can react to mouse events so you can write some pretty cool scripts with it. The bitmap is low-level (pixel level) so it may be a bit cumbersome. But because it's so low level it would allow you to do some pretty powerful stuff (e.g. creating your own components, draw paths, display the scene, ...).

In this example we do live texture painting. Just start rendering something, select a texture node and start the script. Now you should by able to paint some stuff on your selected texture. You can also use it to paint a texture environment. This is a proof-of-concept, not a production stable script!

First, the selected texture's buffer is loaded in the bitmap. Then we show the bitmap in a window and start listening for mouse events. When the mouse is dragged on the bitmap, we draw a red square in the bitmap and update the texture's buffer with the red square. Important here is the call to octane.changemanager.update(), each time you want to see your changes reflected in the current render, you should call this function. (The idea is that you make all your changes and then call this function once to optimize things).

Here's the code:

Code: Select all
-- Live texture painting script.
-- Proof of concept, don't use this to launch nuclear missiles!
--
-- @version Live Texture Painting
-- @author  Thomas Loockx
-- @version 0.2


-- Retrieves the colour of a texel.
local function getTexPixel(tex, x, y)
    local ix = 4 * ((x-1) + tex.size[1] * (y-1)) + 1
    return { tex.texels[ix], tex.texels[ix+1], tex.texels[ix+2], tex.texels[ix+3] }
end

-- Sets a pixel in our texel data structure.
local function setTexPixel(tex, x, y, cl)
    -- bounds check
    if x > tex.size[1] or x < 1 then return end
    if y > tex.size[1] or y < 1 then return end

    local ix = 4 * ((x-1) + tex.size[1] * (y-1)) + 1
   
    -- bounds check on the index
    if (ix > #tex.texels) then return end

    tex.texels[ix]   = cl[1]
    tex.texels[ix+1] = cl[2]
    tex.texels[ix+2] = cl[3]
    tex.texels[ix+3] = cl[4]
end


-- get all the info about the selected texture
local TEX = {}
TEX.node = octane.project.getSelection()[1]
assert(TEX.node and TEX.node:getProperties().type == octane.NT_TEX_IMAGE, "please select an image texture node")
TEX.size   = TEX.node:getAttribute(octane.A_SIZE)
TEX.texels = TEX.node:getAttribute(octane.A_BUFFER)
print(string.format("texture %d x %d, buffer len %d", TEX.size[1], TEX.size[2], #TEX.texels))

-- clear the filename attributes. This tricks Octane into thinking that the texture
-- came directly from the image buffer.
TEX.node:clearAttribute(octane.A_FILENAME, false)
TEX.node:clearAttribute(octane.A_PACKAGE, false)

function mouseCallback(bitmap, event, x, y, dx, dy)
    if event == octane.gui.eventType.MOUSE_DOWN or event == octane.gui.eventType.MOUSE_DRAG then
    print(x, y)
        -- paint brush settings
        local thickness = 5
        local colour    = { 255, 0, 0, 255 }
       
        for i=x,x+thickness do
            for j=y,y+thickness do
                bitmap:setPixel(i, j, colour)
                setTexPixel(TEX, i, TEX.size[2] - j - 1, colour)
            end
        end

        -- update the texture node
        TEX.node:setAttribute(octane.A_BUFFER, TEX.texels)
        -- update the render engine
        octane.changemanager.update()
    end
end

-- create a plain vanilla bitmap same size as the texture
local bitmap = octane.gui.create
{
    type             = octane.gui.componentType.BITMAP,
    width            = TEX.size[1],
    height           = TEX.size[2],
    opacity          = 0.5,
    backgroundColour = { 255, 255, 255, 0 },
    mouseCallback    = mouseCallback,
}

-- fill the bitmap with the texture colours
for x=1,TEX.size[1] do
    for y=1,TEX.size[2] do
        -- we need to flip the texture to display it properly
        bitmap:setPixel(x, TEX.size[2]-y, getTexPixel(TEX, x, y))
    end
end


local group = octane.gui.create
{
    type     = octane.gui.componentType.GROUP,
    rows     = 1,
    cols     = 1,
    children = { bitmap },
    border   = false,
    inset    = { 5 },
    padding  = { 5 },
}

local window = octane.gui.create
{
    type     = octane.gui.componentType.WINDOW,
    children = { group },
    width    = group:getProperties().width,
    height   = group:getProperties().height,
    text     = "Texture Painter",
}

window:showWindow()


Here's a result of my artistic skills:

paint.jpg
texture painting


texture_paint.png
my skills


Some ideas for improvement:

  • Modify the brush colour, shape and size (1.25 features a colour swatch as well to pick a colour).
  • Allow saving of the texture
  • Don't screw up the original texture
  • Make it work for HDRI images.
  • It's slow for larger textures.
  • ...

cheers,
Thomas
Last edited by stratified on Tue Jan 07, 2014 3:21 am, edited 3 times in total.
User avatar
stratified
OctaneRender Team
OctaneRender Team
 
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

Re: Live Texture Painting Script

Postby grimm » Mon Jan 06, 2014 11:20 pm

grimm Mon Jan 06, 2014 11:20 pm
I'm thinking QA production tool? Looks like fun. :)

Jason
Linux Mint 20 x64 | Nvidia GTX 980 4GB (displays) RTX 2070 8GB| Intel I7 5820K 3.8 Ghz | 32Gb Memory | Nvidia Driver 460.56
User avatar
grimm
Licensed Customer
Licensed Customer
 
Posts: 1321
Joined: Wed Jan 27, 2010 8:11 pm
Location: Spokane, Washington, USA

Re: Live Texture Painting Script

Postby Tugpsx » Mon Jan 06, 2014 11:44 pm

Tugpsx Mon Jan 06, 2014 11:44 pm
Guys I think you found King Solomon's mine when you added Lua scripting to Octane, can't wait to see the goodies that get created with this and maybe some interesting add-ons.
Thanks again.
Dell Win Vista 32 | NVIDIA Quadro NVS135M | Core2 Duo T7500 2.20GHz | 4GB
CyberPowerPC Win 7 64| NVIDIA GTX660Ti (3G) GTX480(1.5G) | 16GB
Tugpsx
Licensed Customer
Licensed Customer
 
Posts: 1145
Joined: Thu Feb 04, 2010 8:04 pm
Location: Chicago, IL

Re: Live Texture Painting Script

Postby stratified » Tue Jan 07, 2014 12:02 am

stratified Tue Jan 07, 2014 12:02 am
Yeah, we hope to see some killer apps in the future ;)

cheers,
Thomas
User avatar
stratified
OctaneRender Team
OctaneRender Team
 
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

Re: Live Texture Painting Script

Postby pixelrush » Tue Jan 07, 2014 12:57 am

pixelrush Tue Jan 07, 2014 12:57 am
Very nifty. Can you make size pressure sensitive for a Wacom tablet? A simple circular brush with opacity is ok. :) 3d markups would be cool.
Yeah, I know what Uncle Roeland is going to say... :roll:
i7-3820 @4.3Ghz | 24gb | Win7pro-64
GTS 250 display + 2 x GTX 780 cuda| driver 331.65
Octane v1.55
User avatar
pixelrush
Licensed Customer
Licensed Customer
 
Posts: 1618
Joined: Mon Jan 11, 2010 7:11 pm
Location: Nelson, New Zealand

Re: Live Texture Painting Script

Postby UnCommonGrafx » Tue Jan 07, 2014 2:04 am

UnCommonGrafx Tue Jan 07, 2014 2:04 am
That's awfully cool. Made me pay attention to what the future holds for octane.
Perhaps this is a benefit to being 'late to the party' of renderers: out of the box can actually occur now that the box has been clearly defined.
i7-4770K, 32gb ram, windows 8.1, GTX Titan and gt 620 for display
UnCommonGrafx
Licensed Customer
Licensed Customer
 
Posts: 199
Joined: Wed Mar 13, 2013 9:14 pm

Re: Live Texture Painting Script

Postby stratified » Tue Jan 07, 2014 2:52 am

stratified Tue Jan 07, 2014 2:52 am
pixelrush wrote:Very nifty. Can you make size pressure sensitive for a Wacom tablet? A simple circular brush with opacity is ok. :) 3d markups would be cool.
Yeah, I know what Uncle Roeland is going to say... :roll:


pressure sensitive is not going to work right now. There's not enough info in the mouse event to do that. You could modify the brush size with the scroll wheel (or use that to zoom on the texture).

Or you can do something more funky like creating some pictures of shapes (triangle, circle, square, ...). Display those in a bitmap on the component and then when clicked change the shape of the brush.

cheers,
Thomas
User avatar
stratified
OctaneRender Team
OctaneRender Team
 
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand

Re: Live Texture Painting Script

Postby pixelrush » Tue Jan 07, 2014 3:08 am

pixelrush Tue Jan 07, 2014 3:08 am
Well my tablet has a touch strip I can use for scroll purposes so we aren't defeated yet ;)
Please do that. Brush size <-> scroll wheel.
Funk mode brush swapping sounds way cool. 8-)
i7-3820 @4.3Ghz | 24gb | Win7pro-64
GTS 250 display + 2 x GTX 780 cuda| driver 331.65
Octane v1.55
User avatar
pixelrush
Licensed Customer
Licensed Customer
 
Posts: 1618
Joined: Mon Jan 11, 2010 7:11 pm
Location: Nelson, New Zealand

Re: Live Texture Painting Script

Postby Tugpsx » Tue Jan 07, 2014 4:30 am

Tugpsx Tue Jan 07, 2014 4:30 am
Interesting, I wonder if this procedure could be applied to a painted path scenario (although the math behind it may get interesting) since moving the brush in 3 space and tracking the paths in an array and using those as waypoint rails for a camera system is turning Octane into way more than we would bargain for.

Just more crazy ideas.
Dell Win Vista 32 | NVIDIA Quadro NVS135M | Core2 Duo T7500 2.20GHz | 4GB
CyberPowerPC Win 7 64| NVIDIA GTX660Ti (3G) GTX480(1.5G) | 16GB
Tugpsx
Licensed Customer
Licensed Customer
 
Posts: 1145
Joined: Thu Feb 04, 2010 8:04 pm
Location: Chicago, IL

Re: Live Texture Painting Script

Postby stratified » Tue Jan 07, 2014 4:39 am

stratified Tue Jan 07, 2014 4:39 am
Yep, that's possible. It would require a lot of work, all you can do in a bitmap is load pictures, capture mouse events or manipulate pixels. So it's powerful but really low level. It's like a step back to 80's game programming (not that I would now) ...

You could start with drawing in a camera path in 2D. This means that the camera would move in the same plane.

If somebody's keen, I think it's interesting to create a 2D draw library on top of this to draw primitives like circles, arcs, rectangles, ... We might add them in the future but we don't have the bandwidth right now.

cheers,
Thomas
User avatar
stratified
OctaneRender Team
OctaneRender Team
 
Posts: 945
Joined: Wed Aug 15, 2012 6:32 am
Location: Auckland, New Zealand
Next

Return to Lua Scripting


Who is online

Users browsing this forum: No registered users and 7 guests

Thu Mar 28, 2024 9:38 pm [ UTC ]