Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Craft sphere texture

dave1707dave1707 Mod
in Codea Craft Posts: 6,929

Here’s an example of Craft spheres, one row without a texture and another row using a texture. Each face uses the texture. The columns show the spheres with higher and higher subdivisions. On the left is the image that’s being used. Try different images to see what you get. The sphere can be wrapped with a single texture, but it’s too involved to show here. I was able to wrap part of the sphere, but gave up trying to wrap the whole sphere because you can’t wrap an icosphere with a rectangular texture and have it work OK.

displayMode(FULLSCREEN)

function setup()
    assert(craft, "Please include Craft as a dependency")
    assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
    scene = craft.scene()
    skyMaterial=scene.sky.material
    skyMaterial.sky=color(158, 202, 223, 255)
    skyMaterial.horizon=color(98, 166, 114, 255)
    scene.sun.rotation=quat.eulerAngles(-90,0,0)
    v=scene.camera:add(OrbitViewer, vec3(0,0,0), 100, 0, 300)

    texture=readImage("Small World:Heart Flat")

    createSphere(vec3(12,30,0),0)
    createSphere(vec3(12,15,0),1)
    createSphere(vec3(12,0,0),2)
    createSphere(vec3(12,-15,0),3)
    createSphere(vec3(12,-30,0),4)

    createSphere1(vec3(-12,30,0),0)
    createSphere1(vec3(-12,15,0),1)
    createSphere1(vec3(-12,0,0),2)
    createSphere1(vec3(-12,-15,0),3)
    createSphere1(vec3(-12,-30,0),4)
end

function draw()
    update(DeltaTime)
    scene:draw()
    sprite(texture,100,HEIGHT/2,100)
    fill(255)
    text("No texture",WIDTH/2-120,HEIGHT-30)
    text("Use texture",WIDTH/2+120,HEIGHT-30)
    text("Texture used",100,HEIGHT/2+70)
end

function update(dt)
    scene:update(dt)
end

function createSphere(p,s)  
    pt=scene:entity()
    pt.position=vec3(p.x,p.y,p.z)
    pt.model = craft.model.icosphere(5,s,1)
    pt.material = craft.material("Materials:Standard")
end

function createSphere1(p,s)
    pt=scene:entity()
    pt.position=vec3(p.x,p.y,p.z)
    pt.model = craft.model.icosphere(5,s,1)
    uv={}   -- need to change the uvs for texture
    for z=1,#pt.model.uvs/3 do
        table.insert(uv,vec2(0,0))
        table.insert(uv,vec2(0,1))
        table.insert(uv,vec2(1,1))  
    end
    pt.model.uvs = uv
    pt.material = craft.material("Materials:Standard")
    pt.material.map=texture
end
«1

Comments

  • Posts: 795

    Looks great! Good job! But have to say that I didn’t really figured out what you meant with that dependency addition :)

  • Posts: 875

    @dave1707 - you are a star, I can see how you’ve done that, and I think there is a way of texturing an icosphere if we can get the formula used to derive the vertex points for the object.

    I had decided to go down the obvious route and get a sphere obj model and materials mtl file built for it to use any texture I need, then resizing as required. But I had difficulty finding a file and some of them won’t load or only load the obj file resulting in an untextured model. I assume @John’s loader within Craft is rightly built on the strict obj file model, but there are variants for different packages and I am useless with Blender.

    Using sphere = Craft.model(“sphere”) you can load any legal obj file, mtl file and texture. Perhaps we could get an obj file written using Codea -I have a feeling @Loopspace wrote one for his Craft demo - perhaps we could get that to write an obj file and mtl file to tabs in one of the textured sphere projects?

  • dave1707dave1707 Mod
    Posts: 6,929

    @Bri_G There is a way to texture it, maybe later I’ll have a demo showing it. The only problem is getting the right formula to do it automatically. For a 0 type icosphere, it has 20 faces or 60 vertices. For a type 4 icosphere it has 5120 faces and 15360 vertices. I can probably do 20 faces manually, but not the 5120 faces.

  • Posts: 875

    @dave1707 - I think you may be able to texture it by dropping perpendiculars onto the 2D texture from each vertex, it is needed for two faces and needs ordering for triangles. I’m looking for a formula now.

  • dave1707dave1707 Mod
    Posts: 6,929

    @Bri_G Here’s my latest example, coloring each face with a random color.

    function setup()
        assert(craft, "Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(158, 202, 223, 255)
        skyMaterial.horizon=color(98, 166, 114, 255)
        scene.sun.rotation=quat.eulerAngles(-90,0,0)
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 100, 0, 300)
        createSphere(vec3(0,30,0),0)
        createSphere(vec3(0,18,0),1)
        createSphere(vec3(0,6,0),2)
        createSphere(vec3(0,-6,0),3)
        createSphere(vec3(0,-18,0),4)
        createSphere(vec3(0,-30,0),5)
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()
    end
    
    function update(dt)
        scene:update(dt)
    end
    
    function createSphere(p,s)
        pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.model = craft.model.icosphere(5,s,1)
        pt.material = craft.material("Materials:Standard")
        cc={}
        for z=1,#pt.model.colors/3 do
            rc=color(math.random(255),math.random(255),math.random(255))
            table.insert(cc,rc)
            table.insert(cc,rc)
            table.insert(cc,rc)
        end
        pt.model.colors=cc
    end
    
  • edited March 14 Posts: 416

    Here's a texturing. It takes too long for the more detailed isosphere, my ipad pro is fine up to level 3, but takes a significant while with level 4. Of course, one could extract the coordinates and hard code them rather than calculating them each time. It's not great with level 0 as a dodecahedron doesn't have a meridian line.

    displayMode(FULLSCREEN)
    displayMode(OVERLAY)
    
    function setup()
        assert(craft, "Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(158, 202, 223, 255)
        skyMaterial.horizon=color(98, 166, 114, 255)
        scene.sun.rotation=quat.eulerAngles(-90,0,0)
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 100, 0, 300)
    
        texture=readImage("Cargo Bot:Crate Green 2")
        spheres = {}
        table.insert(spheres, createSphere(vec3(12,30,0),0))
        table.insert(spheres, createSphere(vec3(12,15,0),1))
        table.insert(spheres, createSphere(vec3(12,0,0),2))
        table.insert(spheres, createSphere(vec3(12,-15,0),3))
        table.insert(spheres, createSphere(vec3(12,-30,0),4))
    
        table.insert(spheres, createSphere1(vec3(-12,30,0),0))
        table.insert(spheres, createSphere1(vec3(-12,15,0),1))
        table.insert(spheres, createSphere1(vec3(-12,0,0),2))
        table.insert(spheres, createSphere1(vec3(-12,-15,0),3))
        table.insert(spheres, createSphere1(vec3(-12,-30,0),4))
        parameter.integer("angle",0,360,60)
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()
        sprite(texture,100,HEIGHT/2,100)
        fill(255)
        text("No texture",WIDTH/2-120,HEIGHT-30)
        text("Use texture",WIDTH/2+120,HEIGHT-30)
        text("Texture used",100,HEIGHT/2+70)
    end
    
    function update(dt)
        for k,v in ipairs(spheres) do
            v.rotation = quat.eulerAngles(0,angle,0)
        end
        scene:update(dt)
    end
    
    function createSphere(p,s)  
        local pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.rotation=quat.eulerAngles(0,90,0)
        pt.model = craft.model.icosphere(5,s,1)
        pt.material = craft.material("Materials:Standard")
        return pt
    end
    
    function createSphere1(p,s)
        local pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.model = craft.model.icosphere(5,s,1)
        local uv={}   -- need to change the uvs for texture
        local pa,pb,pc
        local ta,tb,tc,tm,tx
        tm,tx = 1,0
        local pha,phb,phc
        local u,v = vec3(0,1,0),vec3(math.cos(2*math.pi/3),0,math.sin(2*math.pi/3))
        for z=1,pt.model.indexCount,3 do
            pa = pt.model.positions[pt.model.indices[z]]
            pb = pt.model.positions[pt.model.indices[z+1]]
            pc = pt.model.positions[pt.model.indices[z+2]]
            ta, pha = longlat(u,v,pa)
            tb, phb = longlat(u,v,pb)
            tc, phc = longlat(u,v,pc)
            if ta > .9 and (tb < 0.5 or tc < .5) then
                ta = 0
            end
            if tb > .9 and (tc < 0.5 or ta < .5) then
                tb = 0
            end
            if tc > .9 and (ta < 0.5 or tb < .5) then
                tc = 0
            end
            if pha == 1 or pha == 0 then
                ta = (tb+tc)/2
            end
            if phb == 1 or phb == 0 then
                tb = (ta+tc)/2
            end
            if phc == 1 or phc == 0 then
                tc = (tb+ta)/2
            end
            uv[pt.model.indices[z]] = vec2(ta,pha)
            uv[pt.model.indices[z+1]] = vec2(tb,phb)
            uv[pt.model.indices[z+2]] = vec2(tc,phc)
        end
        pt.model.uvs = uv
        pt.material = craft.material("Materials:Standard")
        pt.material.map=texture
        return pt
    end
    
    function longlat(u,v,p)
        local p = p:normalize()
        local ph = math.asin(p:dot(u))/math.pi + .5
        local x,y = p:dot(v), p:dot(u:cross(v))
        local th = math.atan(y,x)/(2*math.pi)+.5
        return th, ph
    end
    
  • edited March 14 Posts: 875

    @dave1707 - just in the middle of expressing a view on how complex this model is when I saw the code from @LoopSpace and ran it. Cut it down to one sphere size 3 and textured with the EarthDay map. Wow!!!, I new it was feasible but the complexity makes it a very slow process and I think that’s why there’s been little progress/interest in complex textured models with Codea Craft. Thanks a lot for that @LoopSpace it’s nice to have a mathematician on this forum.

    I think it’s Back to the standard sphere obj model and texture to get what I need. Would have posted an image of the textured sphere from this code but can’t take a snapshot.

  • dave1707dave1707 Mod
    Posts: 6,929

    @LoopSpace Nice job. What looks really nice is using the texture "Surfaces:Stone Brick Height". I commented out the last one in each group so I only did 4 per group which took 18 seconds on my iPad Air. Can you give a quick explanation of how you calculate the uv table, not mathematically, but just what you’re doing. It looks like you might be taking the vertices and placing them on the texture map ( not literally ) and then calculating its position between 0 and 1 in width and height. Not exactly sure what’s going on in the longlat function.

  • Posts: 416

    @Bri_G I suspect that serialising the uv coordinates will speed it up considerably. There's quite a bit of calculation going on to figure out the coordinates and since the isosphere is the same each time, that could easily be done once for all. Maybe I'll gist them later.

    The advantage of the model that I have in my MeshExt class (which I posted about in this discussion) is that everything is defined in terms of latitude and longitude so the texture coordinates don't need any heavy calculation.

  • edited March 15 Posts: 875

    @LoopSpace - not a criticism, just an observation: I just run at level 3 for convenience in time, but I notice banding of the texture around the sphere. I presume this is due to overlap either inaccuracies in the calcs or slight positional inaccuracies in the individual textures. Can you see it?

    I run on an iPad Pro 10’’ it may be due to the retina screen.

  • dave1707dave1707 Mod
    Posts: 6,929

    @Bri_G @LoopSpace When I run this on my 12" iPad Pro at level 3 using texture=readImage("Surfaces:Stone Brick Height"), I see triangular faces on half of the sphere which might be the cause of the banding. The Stone Brick Height is a 1024x1024 image which gives a better resolution for the wrap. When I remove the 1 so pt.model = craft.model.icosphere(5,s,1) is pt.model = craft.model.icosphere(5,s) to show a smooth surface on the full icosphere, there is a bow tie effect at the seam. I don’t know if that can be corrected.

  • Posts: 875

    @dave1707 @LoopSpace good observation, I was going to report that there is a longitudinal seam visible, which on the rear of the sphere turns to a demarcation line between light and dark. Also when run at level 4 the banding seems reduced but the longitudinal band is more pronounced.

    I wonder if this is due to the difference between Codea and OpenGL in addressing textures - I think Codea starts at 1,1 and OpenGL at 0,0?

  • Posts: 416

    (Replying in order)

    @Bri_G I'm not sure what you mean by "banding". I'm on an iPad Pro too, and I don't see any obvious defects. Of course, using a texture that is meant for a rectangle on a sphere will lead to deformations, but that's due to geometry not to errors in calculation. That's not to say that there aren't any errors - I don't guarantee that - but I'd need to see a screenshot to know what you mean.

    @dave1707 The original code used the 1 flag meaning that the sphere was facetted, and that's the triangles that you're seeing. I also don't know if that's what @Bri_G means by banding. When you remove the 1 flag then it switches to a smooth sphere and that's a different kettle of fish (see below).

    @Bri_G A longitudinal seam will be visible if the texture doesn't tile, so @dave1707's suggestion of using the Stone Brick texture is a good one since those tile. Do you still see the seam with that? Any difference between Codea and OpenGL is irrelevant in this, by the way.

    Right, on to facetted vs smooth. With the facetted sphere, each vertex belongs to a unique triangle. This is because although the vertex position may repeat, the normals depend on the triangle. Therefore, you can't reuse an old vertex in a new triangle. This makes it easy to assign texture coordinates.

    With a smooth sphere, the normals now only depend on the vertex position and not on the triangle it is in. It is therefore possible to re-use vertices in different triangles. Most of the time this is okay, but when a vertex lies on the seam, it will need to have different texture coordinates depending on whether it is at the start of the texture or the end. For this to work, we need to duplicate the vertex.

    I'm working on this, and have it working for levels 0,1,2 but something goes wrong with levels 3 and 4 so I need to think a bit more carefully about my code!

    I've also serialised the coordinates for the facetted sphere, and with those then there's no observable slow-down in creating the spheres. Once we're happy with those then I'll upload them to github.

  • edited March 15 Posts: 875

    @LoopSpace - on the banding issue; if you look at @dave1707’s example with coloured triangles (levels 2 and 3) you can see bands of triangles which circle part of the sphere. Many in ‘parallel’, that’s in inverted commas as they are not truly parallel - many of the triangles in these bands are different sizes so the line is not truly straight. You can see these bands in the fully textured level 3 model.

    On the longitudinal seam - I saw it with the tiled Stone Brick Structure, which is 1024x1024. I didn’t see it with the EarthDay image (1024x512) but much of the image is dark, which also seemed to reduce the banding but not totally.

    A couple of things to throw into the discussion. I have seen references to isospheres based on the icosahedron which look to involve several parallel bands centred around the ‘equator’ with a different, more random, partial sphere at the top and bottom - are there different approaches to isospheres?

    Secondly, I am now thinking that this may be a lighting effect as when I reduced the sun’s x position from -90 to -32 the triangles became more prominent. Probably due to shadow effects on the sphere. Are all the points on the isosphere equidistant from its centre?

    On your point about the seam and different vertices - if the texture is truly tolerable/wrapable surely new vertices are not needed. How is the effect with levels 3 and 4 manifested?

    Let us know when you have serialised the vertices and placed them on github - keen to try that approach.

  • dave1707dave1707 Mod
    Posts: 6,929

    @Bri_G As to the construction of an icosphere, the lowest level is created by using 3 equal sized rectangles placed at right angles to each other and the corners of the rectangles are connected to form triangles. Each level above that is created by placing a smaller inverted triangle inside each larger triangle connected at the center of the lines of the larger triangle. The new points are pushed out so they are all the same distance from the center. That’s a visual way of creating the icospheres. There’s probably a nice formula that calculates every point for each of the different sized icosphere.

  • dave1707dave1707 Mod
    edited March 15 Posts: 6,929

    @Bri_G I ran across this link looking for a formula. Scroll to the bottom of this link. That’s for the level 0 icosphere, 20 sides. I don’t know if it can be expanded for the other levels. If I find something I’ll update this.

    https://geometrictools.com/Documentation/PlatonicSolids.pdf
    

    Here’s another link

    https://schneide.wordpress.com/2016/07/15/generating-an-icosphere-in-c/
    

    Here’s another link.

    http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
    
  • Posts: 875
    @dave1707 - you're going down the same road I went down, got about 5 of them logged in my browser bookmarks. It's all interesting stuff. iPads recharging, I'll compare notes when its up and running.
  • edited March 15 Posts: 875
    @dave1707 - just found this link, for personal use only. Textured Earth with full texture images. Will be playing with these also when we're fully charged:

    https://free3d.com/free-3d-models/obj-sphere

    You'll have to download this yourself according to licence - personal use only.
  • Posts: 416

    @Bri_G On the banding issue, do you just mean that you can see the individual triangles? If so, that's just to do with how the normals are defined. With a facetted model, each triangle is considered to be flat and reflects light accordingly. With a smooth model, the triangles are considered to be curved and reflect light accordingly (they aren't actually curved, but the light reflects as if they were). That's nothing to do with the texture.

    @dave1707:

    the lowest level is created by using 3 equal sized rectangles placed at right angles to each other and the corners of the rectangles are connected to form triangles

    That'll be an icosahedron, then.

    Now, with regard to the seam and the texture. Even with a tileable texture, you need to know which end of the texture the triangle should be cut from, and so a vertex on the seam could sometimes be at one end of the texture and sometimes at the other.

    Are either of you seeing a longitudinal seam on the facetted isosphere? It's definitely there on the smooth one, but I currently don't see it on the facetted one.

  • Posts: 875
    @LoopSpace - just realised what you mean, with a regular rectangular uv where the triangles just fit i.e. don't overlap (like we have used up to date) it's not a problem. The icosahedron model could overlap ?
  • dave1707dave1707 Mod
    Posts: 6,929

    @LoopSpace I’ve seen icosahedron mentioned as the 20 sided object. But since we're using Codea and the Codea code uses icosphere, I refer to the six objects (0 thru 5) as icospheres. I don’t know if the other objects (1 thru 5) have specific names or not.

  • Posts: 416

    @Bri_G Almost. It's not that the triangles overlap so much, just that they share vertices.

  • dave1707dave1707 Mod
    Posts: 6,929

    @LoopSpace I wonder if you used 3 images placed lengthwise would fix the problem. That way using the middle image and going off the left or right edge of the middle image, you would still have a correct image on the left or right to work with.

  • edited March 15 Posts: 875
    @dave1707 - I've used a similar system for another purpose but you need to cover all perimeter faces including the corners, something I was considering too.

    The size of the overlaps depends of the size of the triangles themselves.
  • Posts: 416

    No, that wouldn't work. The problem is with the interpolation: when the texture coordinates are at either end of the texture, then the interpolation fills the triangle with the entire texture rather than the wrap-around part.

    I think I need to draw y'all a picture ... but it's getting late so it'll have to be tomorrow now.

  • edited March 17 Posts: 875

    @dave1707 @LoopSpace - links to four images showing banding and longitudinal seam and finally image from @dave1707 mesh sphere earlier 3D textured sphere.

    See next post - had a lot of problems mainly due to image size.

  • Posts: 875

    @dave1707 @LoopSpace - I think the images above cause a problem with their physical size. I’ll replace them later after cutting and possibly shrinking (trying not to lose detail).

    Latest stupid question - going back to first principles, with a textured mesh, the routines used to date define vertices based on two triangle/rectangles with the vectors for the uv tied to the vertices using a clockwise rotation of vertex/vector points using an ordered progression across the mesh/texture so absolute orientation and location are determined for each point on the texture based against zero vec2(0,0) on the texture.

    On the ionosphere triangles are generated in a very prescribed, but diverse orientation relative to the the starting point. How do you allocate which point on the texture is the starting point and the order of progression in both points round the triangle and triangles round the texture/vertices? I’m just wondering if adjacent triangles could be misaligned. Secondly, with respect to lighting how does OpenGL determine light intensity etc on the triangles - is it based on normals?

    Finally the Earth image I linked to above is 4096x2048, I have loaded one up in @dave1707’s sphere viewer but I have reduced the size of the images by a factor of 4 to use more practically. A bump map, normals and cloud image is also given - I reduced them all. Now working ou how to use them in Craft.

  • Posts: 416

    @Bri_G Most spherical textures are based on a longitude/latitude system. So what I'm trying to do is figure out the longitude and latitude of a given vertex and assign that as the texture coordinates. What is causing the difficulty is that longitude 180 west is the same as longitude 180 east (this is what I've been calling "the seam"). So when we have a vertex on the seam, we have to decide whether it is part of an easterly triangle or a westerly one. I thought I'd figured this out, but it's proving complicated.

  • edited March 16 Posts: 875

    @LoopSpace -surely if you average the longitude components of the triangle versus the point in question, it will be predominantly left or right of the seam?

  • dave1707dave1707 Mod
    Posts: 6,929

    Here’s an example of what I was saying above by using multiple images side by side to get a single image. This isn’t exact, but it gives a close enough example. The top image has a grid in red that represents the icosphere grid. Just like the icosphere grid, the left and right edges don’t line up on a verticle line. You can slide the xx slider to move the red grid left and right. The bottom image is a result (not exact) of where the grid covers the upper images. If you cut out the bottom image and wrapped it around, it should be a continuous image no matter where you start with the upper images. Run this in landscape mode. A lot of the calculations are just hacked for this example.

    function setup()
        parameter.number("xx",0,.33,0)
        im1=readImage("SpaceCute:Rocketship")
        img=image(512,183)
        setContext(img)
        background(223, 209, 169, 255)
        sprite(im1,128,92)
        sprite(im1,128+256,92)
        setContext()
        m=mesh()
        m.vertices={
            vec2(100,0),vec2(100,183),vec2(0,183),
            vec2(100,0),vec2(200,183),vec2(100,183),
            vec2(100,0),vec2(200,0),vec2(200,183),
            vec2(200,0),vec2(300,0),vec2(200,183),    
            vec2(300,0),vec2(300,183),vec2(200,183),
            vec2(300,0),vec2(400,0),vec2(300,183)
        }
        m.texture=img
        m:setColors(255,255,255,255)
    end
    
    function draw()
        background(0)
        sprite(img,WIDTH/2,HEIGHT*.6)
        map()
        m.texCoords={
            vec2(xx+.165,0),vec2(xx+.165,1),vec2(xx+0,1),
            vec2(xx+.165,0),vec2(xx+.33,1),vec2(xx+.165,1),
            vec2(xx+.165,0),vec2(xx+.33,0),vec2(xx+.33,1),
            vec2(xx+.33,0),vec2(xx+.5,0),vec2(xx+.33,1),    
            vec2(xx+.5,0),vec2(xx+.5,1),vec2(xx+.33,1),
            vec2(xx+0,0),vec2(xx+.165,0),vec2(xx+0,1)    
        }
        translate(200,100)
        m:draw()
        translate()
    end
    
    function map()
        w=m.vertices
        stroke(255, 0, 0, 255)
        strokeWidth(2)
        for z=1,#m.vertices-1 do
            line(xx*340+w[z].x+120,w[z].y+370,xx*340+w[z+1].x+120,w[z+1].y+370)
        end
    end
    
  • edited March 16 Posts: 875

    @dave1707 @LoopSpace - I think you can see from @LoopSpace’s code with my mods. Built an image with border then used it as a texture for the sphere. Note the seam has gone. Reduce the x,y offsets in the txt:get() from 66,66 to 65,65 and the seam appears and even wider at 64,64.


    -- LoopSpaceTxt displayMode(FULLSCREEN) displayMode(OVERLAY) function setup() assert(craft, "Please include Craft as a dependency") assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency") sun = -32 -- parameter.integer("sun",0,-180,90) scene = craft.scene() skyMaterial=scene.sky.material skyMaterial.sky=color(158, 202, 223, 255) skyMaterial.horizon=color(98, 166, 114, 255) scene.sun.rotation=quat.eulerAngles(sun,0,0) v=scene.camera:add(OrbitViewer, vec3(0,0,0), 100, 0, 300) rx,ry = 1024+128,768+128 txt = image(rx,ry) pushMatrix() setContext(txt) background(122, 148, 211, 255) fill(230, 19, 19, 255) rectMode(CENTER) rect(rx/2,ry/2,1024,768) setContext() popMatrix() texture = txt:copy(66,66,1024,768) spheres = {} table.insert(spheres, createSphere1(vec3(-12,-30,0),3)) parameter.integer("angle",0,360,60) end function draw() update(DeltaTime) scene:draw() sprite(txt,100,HEIGHT/2,200,100) fill(255) text("No texture",WIDTH/2-120,HEIGHT-30) text("Use texture",WIDTH/2+120,HEIGHT-30) text("Texture used",100,HEIGHT/2+70) end function update(dt) for k,v in ipairs(spheres) do v.rotation = quat.eulerAngles(0,angle,0) end scene:update(dt) end function createSphere(p,s) local pt=scene:entity() pt.position=vec3(p.x,p.y,p.z) pt.rotation=quat.eulerAngles(0,90,0) pt.model = craft.model.icosphere(5,s,1) pt.material = craft.material("Materials:Standard") return pt end function createSphere1(p,s) local pt=scene:entity() pt.position=vec3(p.x,p.y,p.z) pt.model = craft.model.icosphere(5,s,1) local uv={} -- need to change the uvs for texture local pa,pb,pc local ta,tb,tc,tm,tx tm,tx = 1,0 local pha,phb,phc local u,v = vec3(0,1,0),vec3(math.cos(2*math.pi/3),0,math.sin(2*math.pi/3)) for z=1,pt.model.indexCount,3 do pa = pt.model.positions[pt.model.indices[z]] pb = pt.model.positions[pt.model.indices[z+1]] pc = pt.model.positions[pt.model.indices[z+2]] ta, pha = longlat(u,v,pa) tb, phb = longlat(u,v,pb) tc, phc = longlat(u,v,pc) if ta > .9 and (tb < 0.5 or tc < .5) then ta = 0 end if tb > .9 and (tc < 0.5 or ta < .5) then tb = 0 end if tc > .9 and (ta < 0.5 or tb < .5) then tc = 0 end if pha == 1 or pha == 0 then ta = (tb+tc)/2 end if phb == 1 or phb == 0 then tb = (ta+tc)/2 end if phc == 1 or phc == 0 then tc = (tb+ta)/2 end uv[pt.model.indices[z]] = vec2(ta,pha) uv[pt.model.indices[z+1]] = vec2(tb,phb) uv[pt.model.indices[z+2]] = vec2(tc,phc) end pt.model.uvs = uv pt.material = craft.material("Materials:Standard") pt.material.map=texture return pt end function longlat(u,v,p) local p = p:normalize() local ph = math.asin(p:dot(u))/math.pi + .5 local x,y = p:dot(v), p:dot(u:cross(v)) local th = math.atan(y,x)/(2*math.pi)+.5 return th, ph end
  • dave1707dave1707 Mod
    Posts: 6,929

    Here something interesting I ran across for icospheres 1 thru 5. When the sphere is rotated 31.7 degrees on the Y axis, the triangles line up to make a straight line that circles the icosphere. Here’s an example to show what I see. I draw a black line from the top of the screen to the bottom to make it easier to see the lineup. Drag your finger from top to bottom to rotate the sphere so you can see the lineup all the way around. By starting the texture at that line, it should wrap OK. Run this in landscape mode and change the 3 to the other values 1 thru 5. The 0 icosphere doesn’t work like this.

    function setup()
        parameter.number("angle",0,180,31.7)
        assert(craft,"Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(158, 202, 223, 255)
        skyMaterial.horizon=color(98, 166, 114, 255)
        scene.sun.rotation=quat.eulerAngles(-90,0,0)
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 15, 0, 300)
    
        createSphere(vec3(0,0,0),3) -- change the 3 to other values 1 thru 5
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()
        stroke(0,0,0)
        strokeWidth(2)
        line(WIDTH/2,0,WIDTH/2,HEIGHT)
    end
    
    function update(dt)
        scene:update(dt)
        pt.rotation = quat.eulerAngles(0,angle,0)
    end
    
    function createSphere(p,s)
        pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.model = craft.model.icosphere(5,s,1)
        pt.material = craft.material("Materials:Standard")
        cc={}
        for z=1,#pt.model.colors/3 do
            rc=color(math.random(255),math.random(255),math.random(255))
            table.insert(cc,rc)
            table.insert(cc,rc)
            table.insert(cc,rc)
        end
        pt.model.colors=cc
    end
    
    
  • Posts: 416

    Here we go. I made life hard for myself by not knowing how to set positions etc for models, but once I figured that out then the rest was okay.

    displayMode(FULLSCREEN)
    displayMode(OVERLAY)
    
    function setup()
        assert(craft, "Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(158, 202, 223, 255)
        skyMaterial.horizon=color(98, 166, 114, 255)
        scene.sun.rotation=quat.eulerAngles(-90,0,0)
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 100, 0, 300)
    
        texture=readImage("Surfaces:Stone Brick Height")
    
        spheres = {}
        local b=true
        -- table.insert(spheres, createSphere(vec3(12,30,0),0,b))
        table.insert(spheres, createSphere(vec3(12,15,0),1,b))
        table.insert(spheres, createSphere(vec3(12,0,0),2,b))
        table.insert(spheres, createSphere(vec3(12,-15,0),3,b))
        table.insert(spheres, createSphere(vec3(12,-30,0),4,b))
        b=nil
        table.insert(spheres, createSphere1(vec3(-12,30,0),0,b))
        table.insert(spheres, createSphere1(vec3(-12,15,0),1,b))
        table.insert(spheres, createSphere1(vec3(-12,0,0),2,b))
        table.insert(spheres, createSphere1(vec3(-12,-15,0),3,b))
        table.insert(spheres, createSphere1(vec3(-12,-30,0),4,b))
    
    
    
        parameter.integer("angle",0,360,300)
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()
        sprite(texture,100,HEIGHT/2,100)
        fill(255)
        text("No texture",WIDTH/2-120,HEIGHT-30)
        text("Use texture",WIDTH/2+120,HEIGHT-30)
        text("Texture used",100,HEIGHT/2+70)
    end
    
    function update(dt)
        for k,v in ipairs(spheres) do
            v.rotation = quat.eulerAngles(0,angle,0)
        end
        scene:update(dt)
    end
    
    function createSphere(p,s,b)
        local pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.rotation=quat.eulerAngles(0,90,0)
        pt.model = craft.model.icosphere(5,s,b)
        pt.material = craft.material("Materials:Standard")
        return pt
    end
    
    function createSphere1(p,s,b)
        local pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.model = craft.model.icosphere(5,s,b)
            local uv={}   -- need to change the uvs for texture
            local pts={}
            local pa,pb,pc
            local ta,tb,tc,tm,tx
            local pha,phb,phc
            local u,v = vec3(0,1,0),vec3((math.sqrt(5)-1)/2,0,-1):normalize()--/math.sqrt(5)
            local za,zb,zc
            local sa,sb,sc
            local nv = pt.model.vertexCount
            local inv = pt.model.vertexCount
    
            for z=1,pt.model.indexCount,3 do
                za,zb,zc = pt.model.indices[z],pt.model.indices[z+1],pt.model.indices[z+2]
                pa = pt.model.positions[za]
                pb = pt.model.positions[zb]
                pc = pt.model.positions[zc]
                ta, pha = longlat(u,v,pa)
                tb, phb = longlat(u,v,pb)
                tc, phc = longlat(u,v,pc)
                -- set defaults
                uv[za] = vec2(ta,pha)
                uv[zb] = vec2(tb,phb)
                uv[zc] = vec2(tc,phc)
    
                tm = {ta,tb,tc}
                table.sort(tm)
                -- See if the triangle wraps around the seam
                if tm[3] - tm[1] > tm[1] - tm[3] + 1 then
    
                    sa = math.floor(ta+.5) -- 0 or 1, depending on which seam is closest
                    sb = math.floor(tb+.5)
                    sc = math.floor(tc+.5)
    
                if (ta ~= .5 and tb ~= .5 and sa ~= sb) or (tc ~= .5 and tb ~= .5 and sb ~= sc) or (ta ~= .5 and tc ~= .5 and sc ~= sa) then
                    -- Need to ignore .5s
                    if ta ~= .5 and math.abs(sa - ta) > math.max(math.abs(sb - tb), math.abs(sc - tc)) then
                        -- a is furthest away from seam so use as basepoint
                        if tb ~= .5 and sb ~= sa then
                            -- shift point b
                            if not b then
                                table.insert(pts,zb)
                                nv = nv + 1
                                -- print(zb,nv,ta,tb,tc)
                                zb = nv
                                pt.model:index(z+1, zb)
                            end
                            tb = sa
                        end
                        if tc ~= .5 and sc ~= sa then
                            -- shift point c
                            if not b then
                                table.insert(pts,zc)
                                nv = nv + 1
                                -- print(zb,nv,ta,tb,tc)
                                zc = nv
                                pt.model:index(z+2,zc)
                            end
                            tc = sa
                        end
                    elseif tb ~= .5 and math.abs(sb - tb) > math.abs(sc - tc) then
                        -- b is furthest away
                        if ta ~= .5 and sa ~= sb then
                            -- shift point a
                            if not b then
                                table.insert(pts,za)
                                nv = nv + 1
                                -- print(zb,nv,ta,tb,tc)
                                za = nv
                                pt.model:index(z, za)
                            end
                            ta = sb
                        end
                        if tc ~= .5 and sc ~= sb then
                            -- shift point c
                            if not b then
                                table.insert(pts,zc)
                                nv = nv + 1
                                -- print(zb,nv,ta,tb,tc)
                                zc = nv
                                pt.model:index(z+2, zc)
                            end
                            tc = sb
                        end
                    else
                        -- c is furthest away
                        if tb ~= .5 and sb ~= sc then
                            -- shift point b
                            if not b then
                                table.insert(pts,zb)
                                nv = nv + 1
                                -- print(zb,nv,ta,tb,tc)
                                zb = nv
                                pt.model:index(z+1, zb)
                            end
                            tb = sc
                        end
                        if ta ~= .5 and sa ~= sc then
                            -- shift point a
                            if not b then
                                table.insert(pts,za)
                                nv = nv + 1
                                -- print(zb,nv,ta,tb,tc)
                                za = nv
                                pt.model:index(z, za)
                            end
                            ta = sc
                        end
                    end
                    -- print(ta,tb,tc)
                end
                end
                -- 
                if pha == 1 or pha == 0 then
                    ta = (tb+tc)/2
                end
                if phb == 1 or phb == 0 then
                    tb = (ta+tc)/2
                end
                if phc == 1 or phc == 0 then
                    tc = (tb+ta)/2
                end
                uv[za] = vec2(ta,pha)
                uv[zb] = vec2(tb,phb)
                uv[zc] = vec2(tc,phc)
            end
            if not b then
                pt.model:resizeVertices(nv)
                for k,v in ipairs(pts) do
                    pt.model:position(inv+k,pt.model.positions[v])
                    pt.model:normal(inv+k, pt.model.normals[v])
                    pt.model:color(inv+k, pt.model.colors[v])
    
                end
            end
    
            pt.model.uvs = uv
        for k=1,pt.model.vertexCount do
            ta,pa = longlat(u,v,pt.model.positions[k])
            pt.model:color(k,color(ta*127+127))
        end
    
        pt.material = craft.material("Materials:Standard")
        pt.material.map=texture
        return pt
    end
    
    function longlat(u,v,p)
        local p = p:normalize()
        local ph = math.asin(p:dot(u))/math.pi + .5
        local x,y = p:dot(v), p:dot(u:cross(v))
        local th = math.atan(y,x)/(2*math.pi)+.5
        return th, ph
    end
    

    The code needs a big clean-up! Also, I set the colours according to the longitude which in a real use case would be removed. This was so that I could more easily see where the seam was.

    Incidentally, @dave1707, the angle you spotted is where I put the seam. It’s embedded in the vector v.

  • dave1707dave1707 Mod
    Posts: 6,929

    @LoopSpace It looks like there’s a problem when using table.insert(spheres, createSphere1(vec3(-12,-15,0),3,b)) with b nil around angle 214 on the slider. Same way with level 4. Ran these by themselves, level 4 took forever on my iPad Air.

  • dave1707dave1707 Mod
    Posts: 6,929

    @LoopSpace Have you looked at the example World Generator. It looks like they texture a sphere using a cube map.

  • Posts: 875

    @LoopSpace - absolutely superb, used the Earth texture from the above reference and the finish on the sphere is fantastic.

  • Posts: 416

    @dave1707 try commenting out the loop:

    for k=1,pt.model.vertexCount do
            ta,pa = longlat(u,v,pt.model.positions[k])
            pt.model:color(k,color(ta*127+127))
        end
    

    Do you still see the problem? If so, can you describe it because I don’t know what you mean.

  • dave1707dave1707 Mod
    Posts: 6,929

    @LoopSpace Here’s a picture of what I was seeing. Commenting out the code above eliminated it.

  • Posts: 416

    @dave1707 Yes, so I added some colouring to help me see the structure of the sphere a little better, but that should be taken out of "final code". I left it in here so you could see where the seam is, and so where to look closely to see if it looks right.

    There do appear to still be a couple of triangles at the poles that aren't right.

    When it's right, we can serialise it into an overlay class on the isosphere and then it won't take nearly so long to initialise.

  • edited March 22 Posts: 416

    This appears to have fixed the poles (note that it still looks a bit off, but that’s because the texture is designed for cubes).

    displayMode(FULLSCREEN)
    displayMode(OVERLAY)
    
    function setup()
        assert(craft, "Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(158, 202, 223, 255)
        skyMaterial.horizon=color(98, 166, 114, 255)
        scene.sun.rotation=quat.eulerAngles(-90,0,0)
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 100, 0, 300)
    
        texture=readImage("Surfaces:Stone Brick Height")
    
        spheres = {}
        local b=true
        table.insert(spheres, createSphere(vec3(12,30,0),0,b))
        table.insert(spheres, createSphere(vec3(12,15,0),1,b))
        table.insert(spheres, createSphere(vec3(12,0,0),2,b))
        table.insert(spheres, createSphere(vec3(12,-15,0),3,b))
        table.insert(spheres, createSphere(vec3(12,-30,0),4,b))
        b=nil
        table.insert(spheres, createSphere1(vec3(-12,30,0),0,b))
        table.insert(spheres, createSphere1(vec3(-12,15,0),1,b))
        table.insert(spheres, createSphere1(vec3(-12,0,0),2,b))
        table.insert(spheres, createSphere1(vec3(-12,-15,0),3,b))
        table.insert(spheres, createSphere1(vec3(-12,-30,0),4,b))
    
        parameter.integer("angle",0,360,300)
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()
        sprite(texture,100,HEIGHT/2,100)
        fill(255)
        text("No texture",WIDTH/2-120,HEIGHT-30)
        text("Use texture",WIDTH/2+120,HEIGHT-30)
        text("Texture used",100,HEIGHT/2+70)
    end
    
    function update(dt)
        for k,v in ipairs(spheres) do
            v.rotation = quat.eulerAngles(0,angle,0)
        end
        scene:update(dt)
    end
    
    function createSphere(p,s,b)
        local pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.rotation=quat.eulerAngles(0,90,0)
        pt.model = craft.model.icosphere(5,s,b)
        pt.material = craft.material("Materials:Standard")
        return pt
    end
    
    function createSphere1(p,s,b)
        local pt=scene:entity()
        pt.position=vec3(p.x,p.y,p.z)
        pt.model = craft.model.icosphere(5,s,b)
        if isouvs and isouvs[s] and false then
            print("using saved uvs")
            pt.model.uvs = isouvs[s]
        else
            local uv={}   -- need to change the uvs for texture
            local pts={}
            local pa,pb,pc
            local ta,tb,tc,tm,tx
            local pha,phb,phc
            local u,v = vec3(0,1,0),vec3((math.sqrt(5)-1)/2,0,-1):normalize()
            local za,zb,zc
            local sa,sb,sc
            local da,db,dc
            local nv = pt.model.vertexCount
            local inv = pt.model.vertexCount
            for z=1,pt.model.indexCount,3 do
    
                za,zb,zc = pt.model.indices[z],pt.model.indices[z+1],pt.model.indices[z+2]
                pa = pt.model.positions[za]
                pb = pt.model.positions[zb]
                pc = pt.model.positions[zc]
    
                ta, pha = longlat(u,v,pa)
                tb, phb = longlat(u,v,pb)
                tc, phc = longlat(u,v,pc)
    
                -- set defaults
                uv[za] = vec2(ta,pha)
                uv[zb] = vec2(tb,phb)
                uv[zc] = vec2(tc,phc)
    
                tm = {ta,tb,tc}
                table.sort(tm)
                -- See if the triangle wraps around the seam
                if tm[3] - tm[1] > tm[1] - tm[3] + 1 then
    
                    sa = math.floor(ta+.5) -- 0 or 1, depending on which seam is closest
                    sb = math.floor(tb+.5)
                    sc = math.floor(tc+.5)
                    da = math.abs(sa - ta)
                    db = math.abs(sb - tb)
                    dc = math.abs(sc - tc)
                    if ta == .5 then
                        da = 0
                    end
                    if tb == .5 then
                        db = 0
                    end
                    if tc == .5 then
                        dc = 0
                    end
                    if (ta ~= .5 and tb ~= .5 and sa ~= sb) or (tc ~= .5 and tb ~= .5 and sb ~= sc) or (ta ~= .5 and tc ~= .5 and sc ~= sa) then
                        if da > math.max(db, dc) then
                            -- a is furthest away from seam so use as basepoint
                            if tb ~= .5 and sb ~= sa then
                                -- shift point b
                                if not b then
                                    table.insert(pts,zb)
                                    nv = nv + 1
                                    zb = nv
                                end
                                tb = sa
                            end
                            if tc ~= .5 and sc ~= sa then
                                -- shift point c
                                if not b then
                                    table.insert(pts,zc)
                                    nv = nv + 1
                                    zc = nv
                                end
                                tc = sa
                            end
                        elseif db > dc then
                            -- b is furthest away
                            if ta ~= .5 and sa ~= sb then
                                -- shift point a
                                if not b then
                                    table.insert(pts,za)
                                    nv = nv + 1
                                    za = nv
                                end
                                ta = sb
                            end
                            if tc ~= .5 and sc ~= sb then
                                -- shift point c
                                if not b then
                                    table.insert(pts,zc)
                                    nv = nv + 1
                                    zc = nv
                                end
                                tc = sb
                            end
                        else
                            -- c is furthest away
                            if tb ~= .5 and sb ~= sc then
                                -- shift point b
                                if not b then
                                    table.insert(pts,zb)
                                    nv = nv + 1
                                    zb = nv
                                end
                                tb = sc
                            end
                            if ta ~= .5 and sa ~= sc then
                                -- shift point a
                                if not b then
                                    table.insert(pts,za)
                                    nv = nv + 1
                                    za = nv
                                end
                                ta = sc
                            end
                        end
                    end
                end
    
                if pha == 1 or pha == 0 then
                    ta = (tb+tc)/2
                    if not b then
                        table.insert(pts,za)
                        nv = nv + 1
                        za = nv
                    end
                end
                if phb == 1 or phb == 0 then
                    tb = (ta+tc)/2
                    if not b then
                        table.insert(pts,zb)
                        nv = nv + 1
                        zb = nv
                    end
                end
                if phc == 1 or phc == 0 then
                    tc = (tb+ta)/2
                    if not b then
                        table.insert(pts,zc)
                        nv = nv + 1
                        zc = nv
                    end
                end
                pt.model:index(z, za)
                pt.model:index(z+1, zb)
                pt.model:index(z+2, zc)
                uv[za] = vec2(ta,pha)
                uv[zb] = vec2(tb,phb)
                uv[zc] = vec2(tc,phc)
    
    
            end
            --[[
            local tuv = {}
            for k,v in ipairs(uv) do
                table.insert(tuv, string.format("vec2(%f,%f)",v.x,v.y))
            end
            saveText("Project:Isosphere Textures " .. s,"uv[" .. s .. "] = {\n" .. table.concat(tuv,",\n") .. "\n}")
            --]]
            if not b then
                pt.model:resizeVertices(nv)
                -- print(nv,inv,#pts,#uv)
                for k,v in ipairs(pts) do
                    -- print(k,v)
                    pt.model:position(inv+k,pt.model.positions[v])
                    pt.model:normal(inv+k, pt.model.normals[v])
                    pt.model:color(inv+k, pt.model.colors[v])
    
                end
            end
            for k=1,nv do
                if not uv[k] then
                    print(k)
                end
            end
            pt.model.uvs = uv
            --[[
            for k=1,pt.model.vertexCount do
                ta = pt.model.uvs[k].x
                pt.model:color(k,color(ta*127+127))
            end
              ]]
        end
    
        pt.material = craft.material("Materials:Standard")
        pt.material.map=texture
        return pt
    end
    
    function longlat(u,v,p)
        local p = p:normalize()
        local ph = math.asin(p:dot(u))/math.pi + .5
        local x,y = p:dot(v), p:dot(u:cross(v))
        local th = math.atan(y,x)/(2*math.pi)+.5
        return th, ph
    end
    
  • dave1707dave1707 Mod
    Posts: 6,929

    @LoopSpace Where's local uvtab = readProjectTab("Library Craft:IsosphereTexture")

  • edited March 22 Posts: 416

    @dave1707 Sorry! That stuff should be commented out for the moment -- I'll edit the original post. That's the first steps toward serialising the uvs.

    There, that should work.

  • Posts: 875

    @LoopSpace - looks great, couldn't see a defect. Tried to save uvs to tab but would only print the header. I'll wait until you finalise the package.
    Thanks for the hard work - have you alerted @John to this?

  • dave1707dave1707 Mod
    Posts: 6,929

    Here’s my version of texturing an icosphere. I know I’m not doing it the correct way, but it’s fast. I can texture a level 5 icosphere in less than a second. Setting flat to true (shows small triangle on icosphere) seems to do the texture correct. Setting flat to false (shows a more rounded icosphere) still has a few problems. At level 5, it’s not too noticeable. I set up my own texture to make checking the texture wrap easier. Slide the flip slider to change between my texture and the Stone Brick texture. Also, change the level value (0 to 5) to change the level of the icosphere and rerun the code. I’m still working on fixing the rounded icosphere problem. There seems to be some triangles that aren’t set the way I think they should be. I don’t know how all the icosphere arrays work together, but that’s something to figure out. Use the angle slider to rotate the sphere, or slide your finger on the screen.

    function setup()
        parameter.integer("angle",-180,180,-90)
        parameter.boolean("flip",false)
        -- create test texture
        myImg=image(1024,512)
        setContext(myImg)
        background(170, 198, 223, 255)
        fill(255,0,0)
        ellipse(250,256,200,100)
        fill(0,255,0)
        ellipse(900,256,100,200)
        fill(255,255,0)
        rect(512,320,256,128)
        fill(0, 19, 255, 255)
        fontSize(25)
        text("This       is       my       texture       example,       move       image       slider       for       more.",512,490)
        text("This       is       my       texture       example,       move       image       slider       for      more.",512,22)
        fontSize(40)
        text("This is my texture example, move image slider for more.",512,512*.8)
        text("This is my texture example, move image slider for more.",512,512*.65)
        text("This is my texture example, move image slider for more.",512,512*.5)
        text("This is my texture example, move image slider for more.",512,512*.35)
        text("This is my texture example, move image slider for more.",512,512*.2)    
        stroke(0)
        strokeWidth(3)
        for z=0,1024,64 do
            line(z,0,z,512)
        end
        for z=0,512,32 do
            line(0,z,1024,z)
        end
        setContext()    
    
        texture1=myImg
        texture2=readImage("Surfaces:Stone Brick Height")
        texture=texture1
    
        level=1     --  0 thru 5
        flat=true  -- true (triangles) or false (rounded)
    
        assert(craft, "Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 250, 0, 600)
        createSphere()
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()
        if flip then
            texture=texture2
        else
            texture=texture1
        end
        sph.material.map=texture
        sprite(texture,WIDTH/2,140,texture.height/2)
    
    end
    
    function update(dt)
        scene:update(dt)
        sph.rotation = quat.eulerAngles(angle,0,0)
    end
    
    function createSphere()
        sph=scene:entity()
        sph.position=vec3(0,33,0)
        sph.model = craft.model.icosphere(60,level,flat)
        sph.material = craft.material("Materials:Standard")    
        sph.material.map=texture
        print("indexs",sph.model.indexCount)
        local temp={}  -- temp table for latitude and longitude
        local seam=0
        -- convert to latitude and longitude
        for a,b in pairs(sph.model.positions) do
            local c=b:normalize()
            local lat=math.deg(math.asin(c.z))+90
            local lon=math.deg(math.atan2(c.y,c.x))
            table.insert(temp,vec2(lon,lat))     -- save lat, lat in temp table
            -- get exact value of seam
            if lon//1==-149 then
                seam=lon
            end
        end  
        -- shift points on the left side of the seam to the right side  
        for a,b in pairs(temp) do        
            b.x=b.x+seam*-1
            if b.x<-.01 then
                b.x=b.x+360
            end
        end  
        -- shift individual points if needed   
        for z=1,#temp,3 do
            ax,bx,cx=temp[z].x,temp[z+1].x,temp[z+2].x        
            if ax>250 or bx>250 or cx>250 then
                if ax<.01 then 
                    temp[z].x=360
                end
                if bx<.01 then
                    temp[z+1].x=360
                end
                if cx<.01 then
                    temp[z+2].x=360
                end  
            end        
            ax,ay=temp[z].x,temp[z].y
            bx,by=temp[z+1].x,temp[z+1].y
            cx,cy=temp[z+2].x,temp[z+2].y
            if ay==0 or ay==180 then
                temp[z].x=(bx+cx)/2
            elseif by==0 or by==180 then
                temp[z+1].x=(ax+cx)/2
            elseif cy==0 or cy==180 then
                temp[z+2].x=(ax+bx)/2
            end
        end
        -- create uv table
        local uvTab={}    
        for a,b in pairs(temp) do
            table.insert(uvTab,vec2(b.x/360,b.y/180))
        end    
        sph.model.uvs=uvTab
    end
    
  • edited March 27 Posts: 875

    @dave1707 - excellent it is very fast rendering, level 3 seems to be the best compromise for my needs. It still suffers from the triangular banding effect, tested with EarthDay.png an most obvious on Sahara desert. Also, as with a few of the projects displayed one reflective triangle near the apex.

    I think Ignatz made a point on the triangular lighting effect on his WOT demo,
    He said the lighting was set for each triangle and he used a shader to normalise each pixel, see the following:

    https://coolcodea.wordpress.com/2015/10/16/239-wot-building-a-tank/

    That may resolve the problems with the icosphere, worth a look.

  • dave1707dave1707 Mod
    Posts: 6,929

    @Bri_G Was that level 3 flat=true, or flat=false. I haven’t seen any problems yet with flat=true for levels 1 thru 5.

  • Posts: 875

    @dave1707 - that was flat = true, switched to flat = false made the triangle/banding disappear and the single mirrored triangle at the apex became a small whit circle.

  • Posts: 875

    @dave1707 - the routine is certainly fast even at level 5. Just noticed when flat = false at level 3 there is something funny happening on the poles, one or two unrelated textured triangles. It’s not as noticeable when flat = true but the the triangles are pronounced with some banding.

  • dave1707dave1707 Mod
    Posts: 6,929

    @Bri_G How icospheres are drawn is kind of a mystery to me. There’s several arrays that are used, but they seem to be a little different when doing flat=true or flat=false. When flat=true, then you can see all the triangles that make up the icosphere. When flat=false, the icosphere has a rounded shape to it and the triangles are less noticeable at the higher levels. I’m still playing around with coloring individual triangles of the icospheres which might help with understanding how to get the texture to work in all instances.

Sign In or Register to comment.