Icosphere

@dave1707 - thanks for the update. Working fine with changes above. At 32,767 you might think that is a reasonably high number - but if you are drawing a scene that could be prohibitive. Although - is that number the limit for just one object. Could you display several high vertices spheres, textured at the highest level.

Just changes the s1 definition to:

s1=icoSphere(vec3(0,0,0),100,level,false,lines,1,image)

Working with objects with complex surface detail could be a problem though.

@Bri_G Modified my code to draw 2 icospheres with lines. Depending on the level of each icosphere, I could get both to draw all lines or 1 to draw all lines and the second only partial lines, or one to draw partial and the second none. So the limit isn’t by each icosphere, but the total scene.

@dave1707 - here’s what I managed with your demo. Added three spheres, switching to lines makes third sphere disappear and central sphere has lines + texture present. Probably needs a little more definition but limit in vertices is restrictive.

@Bri_G The drawLines routine was originally written for 1 icosphere, so I’m not sure what sphere or combination it’s using since you’re drawing 3. The sphere that’s disappearing is the one where the lines should be drawn at, but they’re at the middle one. The above code shouldn’t be used for more than 1 icosphere unless it’s totally rewritten.

@dave1707 - thanks, kinda figured each sphere needed it’s own complete environment . My main aim was to see if there spheres would display. I’ll tidy it up but I suspect the vertices limitation would still cause an issue.

Having said that the planet terrain demo for Craft does look quite complicated an may exceed the vertices limit. So there could be a way round the limitation. I’ll take another look at that (before the footy starts).

@Bri_G I don’t think the limit is for the vertices but for the debug:lines. I think that has the 32,768 limit. The Planet 3D looks complicated, but it’s not. The planet is just a level 1 to level 5 icosphere based on a slider that’s distorted. The other things are simple objects placed around the planet.

@dave1707 - thanks for the feedback.

@Bri_G The more I play with this, the weirder it gets. Here are 5 icospheres. Move the “lines” slider to show the lines. Not all of the lines are shown which I expect because there’s more than 32,768 lines. But here’s where the odd stuff starts to happen. Move the “slow” slider so it starts to draw the lines for each of the icospheres slowly. Everything goes OK until the running total reaches 32,768. After that, as the lines of the left icospheres are being drawn, the lines of the right icosphere are now un-drawing. That will continue until all the lines of the left icospheres are drawn. After that the count keeps increasing for the rightmost icosphere, but since we’re above 32,768 nothing is drawn anymore. Not sure about the un-drawing.

viewer.mode=STANDARD

function setup() 
    font("Courier") 
    parameter.watch("fps")
    textMode(CORNER)
    totalLines=0
    fill(255)
    parameter.boolean("lines",false)
    parameter.boolean("slow",false)
    sTab={}
    assert(OrbitViewer, "Please include Cameras as a dependency")        
    assert(icoSphere, "Please include IcoCreate as a dependency")            
    scene = craft.scene()
    scene.ambientColor=color(255)
    v=scene.camera:add(OrbitViewer, vec3(0,0,0), 150, 0, 3000)
    v.ry=180  
    image=readImage(asset.builtin.Surfaces.Basic_Bricks_AO)
    table.insert(sTab,icoSphere(vec3(-50,0,0),10,4,false,false,1,image))
    table.insert(sTab,icoSphere(vec3(-25,0,0),10,4,false,false,1,image))
    table.insert(sTab,icoSphere(vec3(0,0,0),10,3,false,false,1,image))
    table.insert(sTab,icoSphere(vec3(25,0,0),10,1,false,false,1,image))
    table.insert(sTab,icoSphere(vec3(50,0,0),10,5,false,false,1,image))
    tl=true
    for a,b in pairs(sTab) do
        totalLines=totalLines+#b.model.indices
    end
end

function draw()
    fps=1//DeltaTime
    update(DeltaTime)
    scene:draw()
    tl=0
    for a,b in pairs(sTab) do
        if lines then
            b.material.opacity=0
            drawLines(b)
        else
            b.material.opacity=1
            b.nbr=0
        end
        str=string.format("Level  %2d  %8d   %8d",b.level,#b.model.positions,b.nbr)
        text(str,100,HEIGHT-a*40-50) 
        tl=tl+b.nbr
    end
    str=string.format("Total    %10d %10d",totalLines,tl)
    text(str,100,HEIGHT-20)
end

function update(dt)
    scene:update(dt)
    for a,b in pairs(sTab) do
        b.rotation = quat.eulerAngles(-90,0,0)
    end
end

function drawLines(s)
    s.material.blendMode=NORMAL
    local sd=scene.debug
    local pTab=s.model.positions
    local iTab=s.model.indices
    if not slow then
        for z=1,#s.model.indices,3 do
            local p1=pTab[iTab[z]]+s.position
            local p2=pTab[iTab[z+1]]+s.position
            local p3=pTab[iTab[z+2]]+s.position
            sd:line(p1,p2,color(255))
            sd:line(p1,p3,color(255))
            sd:line(p2,p3,color(255))
        end
    else
        if s.nbr<=#s.model.indices-3 then
            s.nbr=s.nbr+3
        end
        for z=1,s.nbr,3 do
            local p1=pTab[iTab[z]]+s.position
            local p2=pTab[iTab[z+1]]+s.position
            local p3=pTab[iTab[z+2]]+s.position
            sd:line(p1,p2,color(255))
            sd:line(p1,p3,color(255))
            sd:line(p2,p3,color(255))
        end
    end
end

function icoSphere(pos,size,level,flat,inside,material,image)
    local s=scene:entity()
    s.position=pos
    s.model=craft.model.icosphere(size,level,flat)  -- create icosphere
    if material==1 or material>3 or material==nil then
        s.material=craft.material(asset.builtin.Materials.Basic) 
    elseif material==2 then
        s.material=craft.material(asset.builtin.Materials.Standard) 
    else
        s.material=craft.material(asset.builtin.Materials.Specular) 
    end  
    if image~=nil then 
        icoTexture(s,inside,image)
    else
        for z=1,#s.model.colors do
            s.model:color(z,color(math.random(255),math.random(255),math.random(255)))
        end
    end
    s.nbr=0
    s.level=level
    return s
end

function icoTexture(sph,inside,img)
    local seam=0
    local ax,bx,cx,ay,by,cy,c,lat,lon  
    local posTab={}
    local pTab,cTab,nTab,llTab,uvTab,colTab,norTab,iTab={},{},{},{},{},{},{},{}
    local deg=math.deg
    local asin=math.asin
    local atan2=math.atan2
    sph.material.map=img 
    
    -- create tables for rounded icospheres        
    if not flat then
        iTab=sph.model.indices
        pTab=sph.model.positions
        cTab=sph.model.colors
        nTab=sph.model.normals  
        for a,b in pairs(iTab) do
            posTab[a]=pTab[b]
            colTab[a]=cTab[b]
            norTab[a]=nTab[b]
            iTab[a]=a
        end
    end 
    
    -- convert sphere positions to latitude and longitude
    for a,b in pairs(posTab) do
        c=b:normalize()
        lat=deg(asin(c.z))+90
        lon=deg(atan2(c.y,c.x))
        llTab[a]=vec2(lon,lat)      -- save lon, lat in table
        if lon//1==-149 then        -- get exact value of seam
            seam=lon
        end
    end 
    
    -- shift points on the left side of the seam to the right side 
    for a,b in pairs(llTab) do        
        b.x=b.x-seam
        if b.x<-.01 then
            b.x=b.x+360
        end
    end 
    
    -- shift individual points of triangle if needed  
    for z=1,#llTab,3 do
        ax,ay=llTab[z].x,llTab[z].y
        bx,by=llTab[z+1].x,llTab[z+1].y
        cx,cy=llTab[z+2].x,llTab[z+2].y
        if ax>250 or bx>250 or cx>250 then
            if ax<.01 then 
                ax=360
            end
            if bx<.01 then
                bx=360
            end
            if cx<.01 then
                cx=360
            end  
        end  
        if ay==0 or ay==180 then
            ax=(bx+cx)*.5
        elseif by==0 or by==180 then
            bx=(ax+cx)*.5
        elseif cy==0 or cy==180 then
            cx=(ax+bx)*.5
        end  
        
        -- create uv table
        uvTab[z]=vec2(ax/360,ay/180)
        uvTab[z+1]=vec2(bx/360,by/180)
        uvTab[z+2]=vec2(cx/360,cy/180)
    end 
    
    -- reset tables 
    sph.model.uvs=uvTab
    
    -- flat shows the triangles that make the sphere
    if not flat then
        sph.model.indices=iTab
        sph.model.positions=posTab
        sph.model.colors=colTab
        sph.model.normals=norTab
    end
    
    --update indices table for inside view    
    if inside then
        iTab=sph.model.indices
        for z=#sph.model.indices,1,-1 do
            iTab[#iTab+1]=iTab[z]
        end
        sph.model.indices=iTab
    end
end