Howdy, Stranger!

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

Scrolling a large mesh

edited March 7 in Questions Posts: 1,863

Hi All,

I’ve been looking at scrolling a large mesh by selecting a section for display and changing the coordinates on the mesh itself so only part of the mesh is visible.@dave1707 has demonstrated scrolling on a flat plane with a single texture stretched over the plane. It works for flat planes. I would also like to mask out the surrounding area so the plane appears like a table-top view.

@Ignatz did something with his 3D racing course but filled the screen with the view.

Anyone delved into this area?

Tagged:

Comments

  • Posts: 758

    what do you mean by changing the coords? tell us more about the idea? and maybe the purpose.

  • edited March 7 Posts: 1,863
    @RonJeffries - I have scrolled images by taking a grid and reading a subset by using an offset from x and y zeros. But, it's just a 2D image. I'd like to know if it's possible to build a mesh, which could introduce 3D, and scroll using a subset and offsets in a similar way to 2D. Your comments on flood filling with chunks sounded promising. I think that Craft will have the capabilities to do this but I'm slow in picking up Craft.

    It's basically like displaying a map with a magnifying glass and moving the map around looking for features.
  • Posts: 758

    the voxel stuff is pretty nice if you can live with the minecraft look. here's an article based on dave's example, with some pics.

  • JohnJohn Admin Mod
    Posts: 639

    @RonJeffries - I appreciate the amount of work you put into that article and unravelling the mysteries of the threaded voxel terrain generator.

    What I could potentially do is add a craft.noise.custom noise generator, that takes in a Lua function and runs that when :getValue(x, y, z) is called, which could then be passed to volume:setWithNoise() allowing you to populate a volume fairly easily

    Potential Example:

    local myVolume = scene:entity():add(craft.volume, 100, 25, 100)
    local air = 0
    local dirt = myVolume:blockID("dirt") -- Assuming block types have already been defined
    local myNoise = craft.noise.custom(function(x, y, z) 
      if y > 0.5 then 
        return air
      else
        return dirt
      end
    end)
    myVolume:setWithNoise(myNoise)
    
  • JohnJohn Admin Mod
    Posts: 639

    @Bri_G It seems like what you want is a terrain system. This can be done by making a series of square chunks, which you make visible depending on whether or not they are visible to the camera (via frustum culling).

    Craft already does frustum culling, although depending on the map size you would need to disable entities that are not visible for performance reasons.

    Generally for generic terrain you'll want to make each section a grid mesh, where you vary the y (or height) to get the shape you want.

  • Posts: 565

    @Bri_G i think you have used @Ignatz terrain generation, is that not okay? I use his code, but replace the mesh with a @LoopSpace pseudoMesh so that it is compatible with the craft framework.

  • Posts: 1,863

    @piinthesky - I have looked at the @ignatz code and generally found it really good I sometimes struggle to adapt and get lost or distracted - so I tend to shelve partially finished projects. When you mention the @Ignatz terrain code which particular code are you referring to - he has demonstrated a walk through village, a racing game, a battle at sea and a tank battle - all very well presented?

    On the @Loopspace PseudoMesh I have tried to get that working but haven’t been able to integrate it into my own code. I was hoping to build a mesh to .obj exporter so that I could convert old mesh objects to Craft loadable objects.

  • Posts: 565

    I use this from @Ignatz modified to use PseudoMesh from @LoopSpace.
    I think it from Ignatz project with the trees (but not used here).

    function terrain_setup()
        print("in terrain setup")
        rad=math.pi/180
        col=color(138, 92, 36, 255)
        meshTable={}
        meshTable[1]={}
        meshTable[2]={}   
     --   AddLevel(-500,0,0,1000,1000,gravel,.1)
        HM={} --holds height maps, one for each terrain area we build
        --add a terrain area --NEW
    -- x,z = bottom left corner
    -- y = height around the edges
    -- w,d = width and depth of area to be terrained (pixels)
    -- p = pixels per tile
    -- f = minimum height (pixels), can be lower than y
    -- h = max height (pixels)
    -- r = whether to randomise terrain each run (true/false)
    -- n = noise level (bigger numbers result in bumpy terrain, smaller gives smoother results)
    -- tbl = table of heights, if provided
    -- img = image or string name of image (do not include library name)
    -- sc = image scaling factor (see above) 0-1
    -- hm = height map storing heights of each point in 
    -- ns = the fraction by which noise level increases for each tile as we go deeper into the terrain
    --      eg a value of 0.3 means that the outside ring of tiles has nil noise (as always, so these tiles
    --      exactly meet the surface around), the next ring of tiles has noise of 0.3 of the full level,
    --      the third ring of tiles has noise of 0.6, and so on, up to a maximum of 1.0. The bigger ns, the
    --      the more you will get cliffs at the edges of the terrain
        terrainWidthOver2=1000
        terrainDepthOver2=1000
        AddTerrain( {x=-terrainWidthOver2, y=0.0, z=-terrainWidthOver2,  
                    width=terrainWidthOver2*2,depth=terrainDepthOver2*2,
                    tileSize=25,minY=0,maxY=50,random=false,
                    noiseLevel=0.05,noiseStep=0.3,sc=0.11,
    --                img=gravel,
                    heightMaps=HM} )
    
        terrainEnt=scene:entity()
        terrainEnt.active=True
    
        terrainEnt.position=offset
    --     terrainEnt.position=vec3(0,0,0)
        local tmodel=meshTable[1][1]:toModel()
        terrainEnt.model=tmodel
    
     --terrainEnt.material = craft.material("Materials:Standard")
     terrainEnt.material = craft.material.preset("Surfaces:Desert Cliff")         
     --    terrainEnt.material = craft.material.preset("Surfaces:Stone Brick")         
    --   terrainEnt.material.map = gravel    
     --  terrainEnt.material.map =readImage("Project:sand-seamless"):copy(1,1,512,512)
       terrainEnt.material.map =readImage("Project:TexturesCom_MuddySand2_2x2_1K_albedo")
    
    --    print("vertex, index count",terrainEnt.model.vertexCount, indexCount)
    
    end
    
    -- x,z = bottom left corner
    -- y = height around the edges
    -- w,d = width and depth of area to be terrained (pixels)
    -- p = pixels per tile
    -- f = minimum height (pixels), can be lower than y
    -- h = max height (pixels)
    -- r = whether to randomise terrain each run (true/false)
    -- n = noise level (bigger numbers result in bumpy terrain, smaller gives smoother results)
    -- tbl = table of heights, if provided
    -- img = image or string name of image (do not include library name)
    -- sc = image scaling factor (see above) 0-1
    -- hm = height map storing heights of each point in 
    -- ns = the fraction by which noise level increases for each tile as we go deeper into the terrain
    --      eg a value of 0.3 means that the outside ring of tiles has nil noise (as always, so these tiles
    --      exactly meet the surface around), the next ring of tiles has noise of 0.3 of the full level,
    --      the third ring of tiles has noise of 0.6, and so on, up to a maximum of 1.0. The bigger ns, the
    --      the more you will get cliffs at the edges of the terrain
    
    function AddTerrain(myarg)
        local x,y,z,w,d,p,f,h,r,n,tbl,--img,
        sc,hm,ns=
        Params({"x","y","z","width","depth","tileSize","minY","maxY","random","noiseLevel",
             "heightTable",
    --    "img",
            "sc", "heightMaps","noiseStep"},myarg) 
    
       local nw,nd=math.floor(w/p),math.floor(d/p) --number of tiles in each direction
    
        --if no table of heights provided, generate one with noise
        if tbl==nil then
            n=n or 0.2 --default noise level
            tbl1,tbl2={},{} 
            --noise starts at 0 on the outside, increases by the step as we move inward, max of 1
            --noise is nil at edge, increases by 30% per tile inwards, to 100% 
            local min,max=9999,0
            --if user wants a random result each time. add a random number to the noise results
            if r==true then rnd=math.random(1,10000) else rnd=0 end
            for i=1,nw+1 do
                tbl1[i],tbl2[i]={},{}
                for j=1,nd+1 do
                    --noise fraction for this tile, based on how close to the edge it is
                    --this formula counts how many rows in from the edge this tile is
     --               tbl1[i][j]=math.min(i-1,j-1,nw+1-i,nd+1-j)
      --              tbl1[i][j]=10-math.min(i-1,j-1,nw+1-i,nd+1-j)    --added by me
                    tbl1[i][j]=20-j*2   --added by me
                    if tbl1[i][j]<0 then tbl1[i][j]=0.0 end        --added by me
                    tbl1[i][j]=tbl1[i][j]*ns
     --               print(i,j,  math.min(i-1,j-1,nw+1-i,nd+1-j) )
                    --the noise itself
                    tbl2[i][j]=noise(rnd+i*n, rnd+j*n) 
                    --keep track of min and max values of noise
                    if tbl2[i][j]<min then min=tbl2[i][j] end
                    if tbl2[i][j]>max then max=tbl2[i][j] end
                end
            end
            --now go back through the whole array and scale it
            --we know the user wants values between f and h
            --we know our noise varies between min and max
            --so now we pro rate all our values so they vary between f and h, based on the noise values
            for i=1,nw+1 do
                for j=1,nd+1 do
                    local f1=y
                    --pro rate
                    local f2=f+(h-f)*(tbl2[i][j]-min)/(max-min)
                    --now apply noise fraction, to reduce noise around the edges
                    tbl2[i][j]=f1*(1-tbl1[i][j])+f2*tbl1[i][j]
                end
            end       
        end
        --store details for later use in determining height
        table.insert(hm,{x1=x,z1=z,x2=x+w,z2=z+d,p=p,t=tbl2})
    
        --create the vectors and mesh
     --   print("sc",sc)
    
     --   local wx, wz=img.width*sc, img.height*sc    --connecred with bug   
        local wx,wz= 400,400
        v,t,c={},{},{}
    --    col=color(208, 160, 42, 255)
    
        for i=1,nw do
            for j=1,nd do
                local x1,x2=x+(i-1)*p,x+i*p  local x3,x4=x2,x1
                local y1,y2,y3,y4=tbl2[i][j],tbl2[i+1][j],tbl2[i+1][j+1],tbl2[i][j+1]
                local z1,z3=z+(j-1)*p,z+j*p  local z2,z4=z1,z3
                v[#v+1]=vec3(x1,y1,-z1) t[#t+1]=vec2(x1/wx,z1/wz)   c[#c+1]=col--bottom left
                v[#v+1]=vec3(x2,y2,-z2) t[#t+1]=vec2(x2/wx,z2/wz)   c[#c+1]=col--bottom right
                v[#v+1]=vec3(x3,y3,-z3) t[#t+1]=vec2(x3/wx,z3/wz)   c[#c+1]=col--top right
                v[#v+1]=vec3(x3,y3,-z3) t[#t+1]=vec2(x3/wx,z3/wz)   c[#c+1]=col--top right
                v[#v+1]=vec3(x4,y4,-z4) t[#t+1]=vec2(x4/wx,z4/wz)   c[#c+1]=col--top left
                v[#v+1]=vec3(x1,y1,-z1) t[#t+1]=vec2(x1/wx,z1/wz)   c[#c+1]=col--bottom left
    
            end 
        end
        local norms=CalculateNormals(v)
        local m=PseudoMesh()
    --    local m=mesh()
    --    m.texture=img
        m.vertices=v
        m.normals=norms
    --    m:setColors(color(255))
        m.colors=c
        m.texCoords=t 
        m.pos=vec3(x,y,z)
        print("terrain x y z",x,y,z)
    --    m.shader = shader(autoTilerShader.vertexShader, autoTilerShader.fragmentShader)  
    --    m.shader=mega()
    
        table.insert(meshTable[1],m)
    end
    
    --calculates height at x,z
    function HeightAtPos(x,z)
        --identify the location and calculate height
        local h=0 --set default as 0
        local i,v
        for i,v in pairs(HM) do  --look in each terrain area
            if x>=v.x1 and x<=v.x2 and z>=v.z1 and z<=v.z2 then --if in this area...
                --calc which square we are in
                local mx=1+math.floor((x-v.x1)/v.p)
                local mz=1+math.floor((z-v.z1)/v.p)
                --use bilinear interpolation (most common method) for interpolating 4 corner values
    --            local px,pz=(x-v.x1)/v.p-mx,(z-v.z1)/v.p-mz
    
                local px,pz=1+(x-v.x1)/v.p-mx,1+(z-v.z1)/v.p-mz
    --            output.clear()
    --            print(x,z)
    --            print (mx+px,mz+pz)
    
                h=      v.t[mx][mz]*     (1-px)*(1-pz)+
                        v.t[mx+1][mz]*   px*(1-pz)+                 
                        v.t[mx][mz+1]*   (1-px)*pz+
                        v.t[mx+1][mz+1]* px*pz
                break
            end
        end
        return h
    end
    
  • Posts: 565

    Need these as well

    function AddItem(x,y,z,w,i,r) --centred on x
        y=HeightAtPos(x,z)
        local h=i.height*w/i.width
        local m=PseudoMesh()
        m.texture=i
        local v,t,c={},{},{}
        v,t=AddImage(-w/2,0,0,w,h,0,v,t,c)
        m.pos=vec3(x,y,z)
        if r~=nil then m.ang=r else m.rotate=true end
        m.vertices=v
     --   m:setColors(color(255))
        m.colors=c
        m.texCoords=t 
        table.insert(meshTable[2],m)
    end
    
    function AddImage(x,y,z,w,h,d,v,t,c)
        v[#v+1]=vec3(x,y,z)  t[#t+1]=vec2(0,0)  c[#c+1]=color(255)
        v[#v+1]=vec3(x+w,y,z-d)  t[#t+1]=vec2(1,0)   c[#c+1]=color(255)
        v[#v+1]=vec3(x+w,y+h,z-d)  t[#t+1]=vec2(1,1)   c[#c+1]=color(255)
        v[#v+1]=vec3(x+w,y+h,z-d)  t[#t+1]=vec2(1,1)   c[#c+1]=color(255)
        v[#v+1]=vec3(x,y+h,z)  t[#t+1]=vec2(0,1)   c[#c+1]=color(255)
        v[#v+1]=vec3(x,y,z)  t[#t+1]=vec2(0,0)   c[#c+1]=color(255)
        return v,t,c
    end
    
    function AddLevel(x,y,z,w,d,i,s)
        local m=PseudoMesh()
    --    m.texture=i
        local v,t,c={},{},{}
        local nx,nz=w/i.width/s,d/i.height/s
        v[#v+1]=vec3(x,y,-z)  t[#t+1]=vec2(0,0) c[#c+1]=color(255)
        v[#v+1]=vec3(x+w,y,-z)  t[#t+1]=vec2(nx,0) c[#c+1]=color(255)
        v[#v+1]=vec3(x+w,y,-z-d)  t[#t+1]=vec2(nx,nz) c[#c+1]=color(255)
        v[#v+1]=vec3(x+w,y,-z-d)  t[#t+1]=vec2(nx,nz) c[#c+1]=color(255)
        v[#v+1]=vec3(x,y,-z-d)  t[#t+1]=vec2(0,nz) c[#c+1]=color(255)
        v[#v+1]=vec3(x,y,-z)  t[#t+1]=vec2(0,0) c[#c+1]=color(255)
        m.vertices=v
     --   m:setColors(color(255))
        m.colors=c
        m.texCoords=t 
        m.pos=vec3(x,y,z)
    --    m.shader = shader(autoTilerShader.vertexShader, autoTilerShader.fragmentShader)
        table.insert(meshTable[1],m)
    end
    
    function Params(list,tbl) --tbl is params
    if type(tbl)=="table" then --was named table
            print("type is table")
            local t={} --match the named params with the list provided
            for n,v in pairs(tbl) do
                for i=1,#list do
                   if n==list[i] then 
                        print(list[i],v)
                        t[i]=v 
                        break 
                    end 
                end     
            end  
            return unpack(t)
      else --was an unnamed list, return it as is
         return unpack(tbl) 
      end
    end
    
    
    function CalculateNormals(vertices)
        --this assumes flat surfaces, and hard edges between triangles
        local norm = {}
        for i=1, #vertices,3 do --calculate normal for each set of 3 vertices
            local n = ((vertices[i+1] - vertices[i]):cross(vertices[i+2] - vertices[i])):normalize()
            norm[i] = n --then apply it to all 3
            norm[i+1] = n
            norm[i+2] = n
        end
        return norm
    end  
    
  • edited March 8 Posts: 1,863
    @piinthesky - thanks for the posts, well annotated and will take a little digesting. I've been trolling the net for terrain links for a week or so (have to make a list of references) all good info but describing principles isn't as good as presenting well annotated working code
  • Posts: 1,863
    @Simeon - just reading through a number of posted code examples and find that sometimes the annotations can detract from reading code when added to Codea. I like the idea of collapsible functions to tidy code whilst developing and wonder if it would be possible to try applying it to comments so that we can have annotations local but collapsible.

    I have examples where annotations have covered a couple of iPad screen pages. I tend to number big annotations and append a numbered list in a tab to use for that purpose.
  • Posts: 758

    @John the noise.custom would be nifty and possibly useful. I would imagine it wouldn't be terribly hard to do. I'd have (first) used it as a debug tool to learn what was going on, but I can see it might be useful for other purposes. It might even be possible to use it to hook things like tree generators directly into a noise tree. That's over my head at the moment but it would certainly open some doors.

  • Posts: 1,863

    @piinthesky - first impressions - woah, so many options. Busy trying to put my images in place - so far got it up and running no errors but no image at the moment. Dig deeper!!!!!

  • Posts: 495

    @RonJeffries If you let me know what the problem was with PseudoMesh I could take a look for you.

  • Posts: 758

    thanks @LoopSpace but it wasn't me with a pseudo mesh prob.

  • edited March 10 Posts: 1,863

    @LoopSpace - Hi, I made a comment of not being able to integrate Pseudomesh into a project - I don't think the issue was with Pseudomesh it was more a lack of understanding on my part how to integrate it into my code. My intention was to use Pseudomesh to essentially build .obj files from a mesh in order to save the .obj and .mtl files for use in the Craft environment directly. It was some time ago when Craft had just been launched and I was looking for a way to upgrade my projects to Craft. In the end I searched the internet for similar free .obj models and found a few to play with. Thanks for the interest not sure if conversion is relevant now although Craft coding isn't as prominent as I thouht it would be and there may be a few interested members.

  • Posts: 495

    Sorry ... lost track of who's who in the thread.

Sign In or Register to comment.