3d problems: mesh data not re-usable?

I finally found what was not working in my program. I’ll explain it here, please confirm if it is correct. I was creating A mesh ms by setting

ms=mesh()
ms.vertices = vertices 
ms.texCoords = tc

And up to that point it was ok. But then, during the draw(), i was reading the mesh data back from ms to reprocess them.

verts = ms.vertices

And now the results were messed up, like the top and bottom were inverted for instance. I finally tried to change my code not to use the ms.vertices table, but the original vertices table instead, and the problem was solved. So i have come to the conclusion that the data from the mesh should not be read back, because they are somewhat ‘‘corrupted’’ (willingly, i assume) when used by the mesh variable. do you confirm this is correct? If true, i think that should be mentionned in the documentation: i have lost the whole week trying to find where my bug was…

No, it is designed to be readable but this could be a bug. If you can reproduce the issue with some small amount of code, post that and I’ll try to fix it.

Here is some code that show the bug: the bottom image is ok, the top is inverted! The problem is in Sphere:setHeight(). Sorry if the code is messy, but i started from my prog and removed 95% of it: last time i started from scratch to recreate a bug, i couldn’t reproduce it…


--# Main
-- project "sphere" JMV38
   displayMode(FULLSCREEN)

function setup()
    url2 = "http://www.jmv38.net23.net/images/earthHeight.jpg"
    url1 = "http://www.jmv38.net23.net/images/earthDay.jpg"
    local sunDir = vec3(-1,0.3,0)
    local color0 = color(255, 255, 255, 255)
    planet1 = Sphere({  
            nx = 80, ny = 40 ,            -- mesh definition
            c1 = color0 , c2 = color0 ,    -- mesh colors
            cx=-200, cy=100, cz=-10  ,         -- sphere center    
            height=200, width=400,    -- flat image size
            --r = 100      ,         -- radius of the sphere
            url = url1,        -- texture image url
            urlHeight = url2,    -- height image
            heightFactor = 20,
            showBug = true,
    })
    planet2 = Sphere({  
            nx = 80, ny = 40 ,            -- mesh definition
            c1 = color0 , c2 = color0 ,    -- mesh colors
            cx=-200, cy=-300, cz=-10  ,         -- sphere center    
            height=200, width=400,    -- flat image size
            --r = 100      ,         -- radius of the sphere
            url = url1,        -- texture image url
            urlHeight = url2,    -- height image
            heightFactor = 20,
            showBug = false,
    })
end

function draw()
    perspective(45)
    camera(0,0,1000,    0,0,0,    0,1,0)
    background(14, 14, 14, 255)    -- clean background
    planet1:draw()
    planet2:draw()
end





--# Sphere
Sphere = class()

function Sphere:init(input)
    self.showBug = input.showBug
    -- spatial position 
    self.pos = input.pos or vec3(0,0,0)
    -- angular position, defined by angles around x,y,z axis
    self.angles = input.angles or vec3(0,0,0)
    -- spatial position of sphere
    self.cx = input.cx or 0
    self.cy = input.cy or 0
    self.cz = input.cz or 0
    -- angular position of sphere, defined by angles around x,y,z axis
    self.ax = input.ax or 0
    self.ay = input.ay or 0
    self.az = input.az or 0
    -- size
    self.width = input.width
    self.height = input.height
    -- mesh definition
    self.nx = input.nx    -- number of triangles in x
    self.ny = input.ny    -- and in y
    self.c1 = input.c1    -- 2 color() objects, to see the triangles
    self.c2 = input.c2
    self.optimized = input.meshOptimize    -- boolean
    -- sphere decoration
    self.url = input.url    -- texture as a url (text)
    self.urlHeight = input.urlHeight
    self.heightFactor = input.heightFactor or 20

    -- create flat mesh and colors
    local vertices,colors,tc = {},{},{}

    tc,colors = self:simpleMesh({ nx=self.nx, ny=self.ny, c1=self.c1, c2=self.c2 })

    -- give a size to the mesh
    if self.width and self.height then
        vertices = self:flat({tc=tc, width=self.width, height=self.height})
    end

    -- create the mesh itself
    self.tc = tc
    self.ms = mesh()
    self.ms.vertices = vertices
    self.ms.colors = colors
    self.ms.texCoords = tc
        
    -- add the texture from internet
    if self.url then 
        self:load( self.url ) -- this will not be instantaneous!
    end
    
    -- add height map
    if self.urlHeight then
        self:loadHeightMap(self.urlHeight)
    end
    
end

function Sphere:flat(args)
    local tc, w, h = args.tc, args.width, args.height
    local vertices = {}
    local i,v,x,y,z
    for i,v in ipairs(tc) do
        x,y,z = v[1]*w, v[2]*h, 0
        vertices[i] = vec3(x,y,z)
    end
    return vertices
end
    
function Sphere:simpleMesh(input)
    -- create the mesh tables
    local vertices = {}
    local colors = {}
    local texCoords = {}
    --local w,h = img.width/10, img.height/10
    local k = 0
    local s = 1
    -- create a rectangular set of triangles
    local x,y
    local nx,ny = input.nx,input.ny
    local opt = input.opt
    local sx, sy = 1/nx, 1/ny
    local color1 = input.c1
    local color2 = input.c2
    local center = vec3(1,0.5,0)
    for y=0,ny-1 do
      for x=0,nx-1 do
        vertices[k+1] = vec2( sx*x    , sy*y    )
        vertices[k+2] = vec2( sx*(x+1), sy*y    )
        vertices[k+3] = vec2( sx*(x+1), sy*(y+1))
        vertices[k+4] = vec2( sx*x    , sy*y    )
        vertices[k+5] = vec2( sx*x    , sy*(y+1))
        vertices[k+6] = vec2( sx*(x+1), sy*(y+1))
        colors[k+1] = color1 
        colors[k+2] = color1 
        colors[k+3] = color1 
        colors[k+4] = color2 
        colors[k+5] = color2 
        colors[k+6] = color2 
        k = k + 6    
      end
    end   
    return vertices,colors
end

function Sphere:setHeight(input)
    -- change each vector length according to heightmap
    local verts = input.verts
--    local tc = input.tc
    local hmap = input.heightmap
    local w,h = self.width,self.height
    local s = self.radius
    local z,x,y,r,g,b,a,vx,vy,vz,cx,cy
    local tc = {}
    -- this his here i trigger the bug ###################################
    if self.showBug then tc = self.ms.texCoords
    else tc = self.tc end
    
    for i,v in ipairs(tc) do
        cx,cy = v.x , v.y
        x,y = math.floor(cx * w) , math.floor(cy * h)
        if x<1 then x=1 end
        if x>w then x=w end
        if y<1 then y=1 end
        if y>h then y=h end
        r,g,b,a = hmap:get(x,y)
        z = g/255*self.heightFactor
        verts[i] = vec3(x,y,z)
    end    
    return verts
end

function Sphere:loadHeightMap(url)
    http.request(url, 
        function(theImage, status, head) 
            self:setHeightMap(theImage, status, head)
        end
        )
end

function Sphere:setHeightMap(theImage, status, head)
    self.ms.vertices = self:setHeight({
        verts = self.ms.vertices,
        tc = self.ms.texCoords,
        heightmap = theImage,
        })
end

function Sphere:load(url)
 --   map = nil
    http.request(url, 
        function(theImage, status, head) 
            self:setTexture(theImage, status, head)
        end
        )
end

function Sphere:setTexture(theImage, status, head)
    self.texture = theImage
    self.ms.texture = theImage
end

function Sphere:draw()
    local s
    pushMatrix()
    pushStyle()
    translate(self.cx,self.cy,self.cz)
    self.ms:draw()  
    popStyle()
    popMatrix()    

end

.@John : any feedback? From what you’ve seen is it a codea bug or a bug of myself? Thanks.

@Jvm38

I was playing with the code and I commented out --planet2:draw in function draw() so I was only showing the top image. I also changed urlHeight=url2 to urlHeight=url1 for planet1. Sometimes the top image displayed correctly and then after a few seconds it would flip. Other times it would just display inverted from the start. So sometimes it’s starting out right. Don’t know if you were seeing that or not.

.@dave1707 on my side it was always wrong. The delay is due to the fact that the mesh is updated first naked, then when the texture map arrives, then again when the height map arrives, these processes are asynchronous. The first draw may be ok because it start from fresh data, but for the height map i always start from the mesh data, and then the second draw is corrupted. Since i assumed the bug is because i restart some computations from the mesh.vertices, i’ve just stored a copy of it, and i never got the problem again. Of course i may run into memory problems later…

I think this code shows the bug:


-- texCoords Test

function setup()
    m = mesh()
    v = {}
    tc = {}
    for i = 1, 6 do
        v[i] = vec2(math.random(WIDTH), math.random(HEIGHT))
        tc[i] = vec2(math.random(), math.random())
    end
    m.vertices = v
    m.texCoords = tc
    for i = 1, 6 do
        print("---- "..i.." ----")
        print(tc[i])
        print(m.texCoords[i])
        print(tc[i].y + m.texCoords[i].y) -- Output: 1
    end
end

function draw()
    background(0)    
end

u, v is returned as u, 1 - v.

Thank you @mpilgrem for simplifying the code. Thanks to your code, I’ve checked that the vertices are ok. So the problem is only in the mesh.texCoords table, which inverts top and bottom.

Some more: the mesh.colors are normalized to 1, so if you read them, remember to do *255.
I also checked the texture image: it’s ok to read it back.

-- texCoords Test

function setup()
    m = mesh()
    v = {}
    tc = {}
    c = {}
    for i = 1, 6 do
        v[i] = vec3(math.random(), math.random(),math.random())
        tc[i] = vec2(math.random(), math.random())
        c[i] = color(math.random(255), math.random(255), math.random(255), 255)
    end
    m.vertices = v
    m.texCoords = tc
    m.colors = c
    local r,g,b,a,col
    for i = 1, 6 do
        print("---- "..i.." ----")
--        print(tc[i])
--        print(m.texCoords[i])
--        print(v[i])
--        print(m.vertices[i])
        print(c[i])
        col = m.colors[i]
        r,g,b,a = col.r, col.g, col.b, col.a
        print(r..","..g..","..b..","..a)
        r,g,b,a = r*255, g*255, b*255, a*255
        print(r..","..g..","..b..","..a)
    end
end

function draw()
    background(0)    
    perspective(45)
    camera(0,0,3,    0.5,0.5,0.5,    0,1,0)
    m:draw()
end