@dave1707 All my writings on Codea are listed on this section of my website. Haven’t written anything for a while, but am happy to take requests …
@LoopSpace Thanks for the link. I have it bookmarked and will look thru it later.
Here’s my latest texture code. I don’t like it because it’s sloppy and has hard coded values. This works for both flat (triangles) and rounded (smooth) icospheres. It handles the bad triangles that were showing up in the previous rounded icospheres. The higher the level, the better the texture looks. It will do flat and rounded level 5 icospheres in less than a half second on my iPad Air, doing both the outside and inside texture. I’m starting to understand the problems with texturing the icospheres and hopefully in future versions, I’ll be able to cut the size of the code and remove the hard coded values. I changed the texture map from my other versions just to give a better view of the texturing. Also, the lower level icospheres don’t texture as smooth at the poles as I want and maybe I’ll get those fixed too.
Code deleted, see updated code below.
@dave1707 - found a funny!! Set the size of the sphere to 800 and run, You are inside the sphere and there is a smaller untextured sphere inside coloured grey. The inside sphere responds to the two finger shrink/enlarge in the opposite way to the big sphere that you are inside. Intriguing, is the inner sphere the sphere you added for internal viewing? it looks like the second sphere may not be necessary - just the inversion of the normals as described by @LoopSpace.
@Bri_G That has something to do with the limit of the camera. If you add
v.camera.farPlane=3000 after v=scene.camera:… and before createSphere() then you don’t get it. Without the farPlane, then you’re looking into dead space.
@dave1707 - thinking about it, it makes sense, if you build a reverse sphere inside another, as part of that sphere, and you increase the size of the outer sphere; then the inner sphere will move in the opposite direction ie towards the centre and hence shrink.
@Bri_G I’m not building a reverse sphere, all I’m doing is allowing you to see the texture of the original sphere from the inside. Depending on the direction that you define the 3 points of the triangle defines which direction you can view the texture from. So the indices table has the values winding in one direction and I add the table to itself reading the table in the reverse order. So the indices table has the points winding in both directions which allows you to see the texture from both sides.
@dave1707 - your right but isn’t that just hiding the other sphere, took it down to a setting of 1440 for the farPlane and still there. At 1500 it’s gone. Just feel it’s more unnecessary work for the graphic processors than needed, and curious to know what it is.
@Bri_G There isn’t another sphere, just the one I create. What you’re seeing is a hole in the viewing area because of the way the camera works. As you get closer to the hole, it gets bigger and looks like you’re looking at a black sphere. @Simeon or @John will have to explain it because they’re the ones who wrote Codea. All I know is the farPlane fixes it because it allows the camera to see farther away.
@Bri_G Here’s my latest texture code. I think I have all the kinks worked out for all levels both triangular and smooth. It’s a lot simpler than what I had before. With my other code I didn’t understand everything that was happening, but as I wrote more and more versions, I started to get an understanding of what was going on. Even though the code is smaller, it’s a little slower. Here are approximate times in seconds on my iPad Air for all levels and shapes.
EDIT: I think this version takes care of the poles for all levels except 0 which doesn’t have a pole but a flat surface.
EDIT: I made changes to the code below to increase the speed. On my iPad Air, the level 5 smooth icosphere that took .662 now takes .58 seconds. Not much of an increase, but an increase. On my iPad Pro, it takes .25 seconds.
Level Triangular Smooth
0 .088 .090
1 .088 .088
2 .096 .094
3 .122 .123
4 .210 .230
5 .591 .662
--displayMode(FULLSCREEN)
-- created by dave1707
function setup()
-- 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)
stroke(255,0,0)
strokeWidth(8)
line(0,0,1024,512)
line(0,512,1024,0)
strokeWidth(4)
stroke(255,255,0)
line(4,0,4,512)
line(1020,0,1020,512)
line(0,508,1024,508)
line(0,4,1024,4)
fill(0, 19, 255, 255)
fontSize(25)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde",512,490)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde",512,22)
fontSize(40)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.8)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.65)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.5)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.35)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",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()
-- end test texture
level=5 -- 0 thru 5
flat=false -- true (triangles) or false (rounded)
insideView=true -- show inside view of sphere
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), 350, 0, 600)
createSphere()
end
function draw()
update(DeltaTime)
scene:draw()
sprite(myImg,WIDTH*.85,100,300)
end
function update(dt)
scene:update(dt)
sph.rotation = quat.eulerAngles(-90,0,0)
end
function createSphere()
sph=scene:entity()
sph.position=vec3(0,0,0)
sph.model = craft.model.icosphere(100,level,flat)
sph.material = craft.material("Materials:Standard")
sph.material.map=myImg
local seam=0
local ax,bx,cx,ay,by,cy,c,lat,lon
local posTab=sph.model.positions
local pTab,cTab,nTab,llTab,uvTab,colTab,norTab,iTab={},{},{},{},{},{},{},{}
local deg=math.deg
local asin=math.asin
local atan2=math.atan2
-- 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
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 insideView 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
@dave1707 - wow, that’s superb, I can’t see how you are going to improve that. Very neat finishes at the top and bottom of the sphere. I’m sure that I found a reference in which the sphere was built largely as an icosphere but had the usual rectangular/triangles for the top and bottom.
I assume generation of the craft icosphere vertices is rapid and the slow times in the Codea Craft are due to texturing, is that true?
@dave1707 With each iteration you are approaching my code …
The poles are tricky because of their interaction with the seam. Since the poles officially lie on the seam, the triangles involved in the poles will pass a simple test for being on the seam, but not all those triangles should be shifted. It took me a few iterations to figure that one out.
@Bri_G As for improvements, I would like to get rid of the hard coded values and condense the one section of code that’s used by them. The level 0 icosphere is created by Codea with a flat surface at the top and bottom, whereas the level 1 thru 5 have the point at the top and bottom. So a level 0 icosphere wouldn’t texture at the top and bottom very good unless more calculations are done to rotate it first to get the point at the top and bottom. The time for Codea to create a blank level 5 icosphere is .10 seconds. Using my code to texture a level 5 icosphere both inside and outside takes .36 seconds. That’s on my iPad Air. @LoopSpace I’m not having trouble with the poles. The problem I’m having is with the rounded icospheres because so many of the points are shared with other values. When one of those points need to be shifted, then new values for the 3 points of the triangle need to be created and I have to be careful how they’re created.
Here’s an updated version. I removed the hard coded values. I still don’t like the way the level 0 icosphere looks, but why do a level 0 when the other levels are fast. There are still some minor pole problems, but they’re not that noticeable.
displayMode(FULLSCREEN)
function setup()
-- 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)
stroke(255,0,0)
strokeWidth(8)
line(0,0,1024,512)
line(0,512,1024,0)
strokeWidth(4)
stroke(255,255,0)
line(4,0,4,512)
line(1020,0,1020,512)
line(0,508,1024,508)
line(0,4,1024,4)
fill(0, 19, 255, 255)
fontSize(25)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde",512,490)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde",512,22)
fontSize(40)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.8)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.65)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.5)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",512,512*.35)
text("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",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()
-- end test texture
level=1 -- 0 thru 5
flat=true -- true (triangles) or false (rounded)
insideView=true -- show inside view of sphere
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), 350, 0, 600)
createSphere()
end
function draw()
update(DeltaTime)
scene:draw()
sprite(myImg,WIDTH*.85,100,300)
end
function update(dt)
scene:update(dt)
sph.rotation = quat.eulerAngles(-90,0,0)
end
function createSphere()
sph=scene:entity()
sph.position=vec3(0,0,0)
sph.model = craft.model.icosphere(100,level,flat)
sph.material = craft.material("Materials:Standard")
sph.material.map=myImg
local llTab={} -- latitude, longitude table
local uvTab={} -- uvs table
local seam=0
local ax,bx,cx,ay,by,cy,c,lat,lon
-- convert sphere positions to latitude and longitude
local posTab=sph.model.positions
local norTab=sph.model.normals
local colTab=sph.model.colors
local indTab=sph.model.indices
--for a,b in pairs(sph.model.positions) do
for a,b in pairs(posTab) do
c=b:normalize()
lat=math.deg(math.asin(c.z))+90
lon=math.deg(math.atan2(c.y,c.x))
table.insert(llTab,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 point of 3 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)/2
elseif by==0 or by==180 then
bx=(ax+cx)/2
elseif cy==0 or cy==180 then
cx=(ax+bx)/2
end
-- create uv table
table.insert(uvTab,vec2(ax/360,ay/180))
table.insert(uvTab,vec2(bx/360,by/180))
table.insert(uvTab,vec2(cx/360,cy/180))
end
-- fix bad triangles on rounded icospheres
if not flat then
for z=1,#indTab,3 do
v1=indTab[z] v2=indTab[z+1] v3=indTab[z+2]
u1=uvTab[v1] u2=uvTab[v2] u3=uvTab[v3]
if u1.x<.005 or u1.x>.995 or u2.x<.005 or
u2.x>.995 or u3.x<.005 or u3.x>.995 then
v=0
if uvTab[v1].x>.5 then
v=v+1
end
if uvTab[v2].x>.5 then
v=v+2
end
if uvTab[v3].x>.5 then
v=v+4
end
xxx=0
if v==1 or v==6 then
xxx=z
elseif v==2 or v==5 then
xxx=z+1
elseif v==3 or v==4 then
xxx=z+2
end
if level==1 and (v==3 or v==4) then
for m=0,1 do
local val=indTab[z+m]
table.insert(uvTab,vec2(1-uvTab[val].x,uvTab[val].y))
table.insert(posTab,posTab[val])
table.insert(colTab,colTab[val])
table.insert(norTab,norTab[val])
indTab[z+m]=#uvTab
end
elseif xxx>0 then
val=indTab[xxx]
table.insert(uvTab,vec2(1-uvTab[val].x,uvTab[val].y))
table.insert(posTab,posTab[val])
table.insert(colTab,colTab[val])
table.insert(norTab,norTab[val])
indTab[xxx]=#uvTab
end
end
end
end
sph.model.indices=indTab
sph.model.uvs=uvTab
sph.model.positions=posTab
sph.model.colors=colTab
sph.model.normals=norTab
-- update indices table for inside view
if insideView then
local ind=sph.model.indices
for z=#sph.model.indices,1,-1 do
table.insert(ind,ind[z])
end
sph.model.indices=ind
end
end