Howdy, Stranger!

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

Converting meshes to models

edited December 9 in Codea Craft Posts: 457

I have quite a lot of code that is heavily invested in meshes which I'd like to adapt to use with Craft, so I came up with a little helper class that can be used as a drop-in for a mesh with a converter to a model for use with Craft.

Latest version can be found on github

PseudoMesh = class()

function PseudoMesh:init()
    self.vertices = {}
    self.texCoords = {}
    self.normals = {}
    self.colors = {}
    self.size = 0
end

function PseudoMesh:vertex(k,v)
    if v then
        self.vertices[k] = v
    else
        return self.vertices[k]
    end
end

function PseudoMesh:normal(k,v)
    if v then
        self.normals[k] = v
    else
        return self.normals[k]
    end
end

function PseudoMesh:texCoord(k,v)
    if v then
        self.texCoords[k] = v
    else
        return self.texCoords[k]
    end
end

function PseudoMesh:color(k,v)
    if v then
        self.colors[k] = v
    else
        return self.colors[k]
    end
end

function PseudoMesh:resize(k)
    self.size = k
end

function PseudoMesh:invertNormals()
    for k,v in ipairs(self.normals) do
        self.normals[k] = -v
    end
end

function PseudoMesh:toModel()
    local m = craft.model()
    local i = {}
    local n = #self.vertices
    for k=1,n,3 do
        if (self.vertices[k+1] - self.vertices[k]):cross(self.vertices[k+2] - self.vertices[k]):dot(self.normals[k+1] + self.normals[k+2] + self.normals[k]) < 0 then
            table.insert(i,k)
            table.insert(i,k+1)
            table.insert(i,k+2)
        else
            table.insert(i,k)
            table.insert(i,k+2)
            table.insert(i,k+1)
        end
    end
    m.positions = self.vertices
    m.normals = self.normals
    m.uvs = self.texCoords
    m.colors = self.colors
    m.indices = i
    return m
end

Use as:

m = PseudoMesh()
-- set up m as if it were a mesh
e = scene:entity()
e.model = m:toModel()

The invertNormals is because meshes would look the same from both sides but models are viewable from one only, so it may be that ones mesh is inside out. This flips it round.

As an example, I've adapted my old Roller Coaster code to Craft using this technique (@Simeon, @John would you consider replacing the old code with a Crafty update?).

«1

Comments

  • SimeonSimeon Admin Mod
    Posts: 5,054

    @LoopSpace would there be value in having something like this built-in?

    We would totally love to replace your Roller Coaster with a Craft-version! Share the zip here.

  • em2em2
    Posts: 194

    @Simeon or keep both, for reference?

  • edited October 2017 Posts: 1,400

    Hi @LoopSpace

    Thanks for the prompt on inversion of normals with meshes, I mentioned this in a recent dialogue with @dave1707 on a 3D demo with Craft. I noticed it whilst building a skybox.

    With respect to your above code - is this intended to be within project conversion ? So you add the new tab with a few mods and it runs with the in-house conversion of the mesh to an in-house obj model? If so very neat !!!

    Would this allow you to write to an .obj model file from a built up mesh?

    On the classic Roller Coaster demo a revision as a mesh to update to the latest Codea spec (didn’t you write your own Vec3() code?) and the equivalent Craft demo would be great. Good examples are always welcome.

    On 3D objects only having one face is that true, I always wondered if it was lack of light source inside models?

    Craft is fantastic and will take me a little time to digest properly. But, meshes appear more flexible if a little bit harder to create.

    Can meshes and models(Craft) be used together or do the Craft scenes use the mesh and overwrite it?

  • Posts: 1,400

    @LoopSpace ,

    Just thinking - your code may allow us to build our own model conversion utils. There is already a Codea ply loader.

  • JohnJohn Admin Mod
    Posts: 587
    In regards to model loading / saving. I’m planning to add a loader that supports dozens of file formats for both saving and loading as well as bones, morph targets, etc.
  • Posts: 457

    @Simeon I think there could be value in having something like the PseudoMesh class available, but I'm not sure if it's useful for it to be built in. I'm using it to convert old code over to Craft, but I'm not sure how much I'll use it for new code. At the moment, I tend to think in terms of meshes so my instincts are all centred on that. As I get more used to Craft, I'll probably be able to switch more easily between models and meshes.

    I could put together a sample project with PseudoMesh as the non-Main tab, then it could be included via the project import feature.

    Just in case my code above is of any use to you (or anyone else) ...

    I hereby place the above PseudoMesh code in the public domain to the extent governable by law. Explicitly, I place it under the CC0.

    Note Regarding Attribution

    Code that is placed in the public domain does not require attribution. However, if you have found it useful, the best way to say "Thank you" is to point others to it. So if you wish to acknowledge the source of the code, a link or bare URL text pointing to http://loopspace.mathforge.org would be nice.

    I have a Craft version of the Roller Coaster almost ready - it currently pulls in some other code via cmodule so I just need to make it into a self-contained project.

    @Bri_G Yes, the intention is to make it easy to convert from meshes to models. The above is not enough, but certainly helps me a lot. I have quite a lot of legacy code which I'd like to be able to use with Craft, for example my Mesh Extension Library adds lots of common shapes to the mesh object. Via the PseudoMesh, I can use it all with models as well without faffing around modifying the code.

    Your mention of the Vec3 code takes me back! Story of my life as regards Codea:

    • October 2011 asking this question on the Apple Stack Exchange led me to Codify (as it was known then).

      I don't remember what questions I emailed to Simeon but clearly the answer satisfied me that I'd be able to do what I wanted. Interesting that I was a bit wary about learning lua!

    • December 2011 Write a shape explorer including my own font renderer, UI, touch handler, 3D capability ... then watch as over the years Codea implements just about all of those features itself.

      I like to kid myself that some of what I did with Codea in the early days inspired Simeon and co to implement stuff natively.

    Codea is simply amazing and made programming actually joyful rather than just how to get some task done. From fonts to meshes to shaders and now craft, it's been a bit of a roller coaster journey itself!

    Sorry ... I'm waxing lyrical. @Bri_G got me in a nostalgic mood. I'd better stop before I start going on about the early days with Bortels and Fred and Nat ...

  • Posts: 1,400

    @LoopSpace
    Nostalgia - I assume Nat is @Ignatz, there are many names you could add but I’m still using @Jmv38 code for spherical mesh generation, for my skybox. Codea is making great strides but some of the older coders code is still solid!!!

  • Posts: 457

    @Bri_G No, Nat is not Ignatz. Ignatz joined in 2013. I'm talking about 2011 - the really old days. Back then, we didn't even have text, let alone meshes and shaders. Not even 3D - we had to write our own 3D-to-2D projection stuff back in those days. I regard Ignatz as a youngster in Codea terms!

    But now you've really got me going. I've started on "Back in my day ... "!

  • SimeonSimeon Admin Mod
    Posts: 5,054

    @LoopSpace your font renderer is exactly what made me implement native text rendering! I got to see how valuable it was and so felt motivated to include it. A lot of Codea's features get implemented like that.

  • @LoopSpace in the comments of your sphere mesh, you hint that it could be possible (but not yet implemented) to smooth a faceted sphere. In order to keep the fps high i use a low number of facets. Would a smoothed sphere with low number of facets still be ‘fast’? If so, could you be persuaded to implement the smooth sphere option? Of course the correct texturing of the sphere should be retained. The in-built craft spheres do not seem to apply the texture correctly.

  • Hi @piinthesky,

    Simply put: don't believe everything you read! Looking through the code, that has been implemented (if you trace through the code at https://github.com/loopspace/Codea-Library-Graphics/blob/master/MeshExt.lua then line 1318 is the key line: f is what the faceted option has become and this defines the normal accordingly.

    Faceting vs smoothing is all about the normals and has no effect on where the texture coordinates are (though it will affect the shading). I'm pretty sure that my texture coordinates are in the right place.

    The default appears to be smooth, so if you have a sphere with my code it ought to be smooth. Let me know if not.

  • @LoopSpace you are right i should not believe what i read! i set faceted true and i can clearly see the difference. Interestingly with faceted true my texture misbehaves-it seems to rotate around the sphere as i view the sphere from different angles (works ok for the default faceted false).

    i was hoping to get the ‘profile’ of the sphere more circular but i guess only increasing the number of facets can help with that (but too slow).

    Setting up my 2000 craft/pseudomesh spheres take a lot of time. Can you think of a clever way to speed that up?

  • dave1707dave1707 Mod
    edited December 2018 Posts: 7,923

    @piinthesky Are you trying to draw 2000 craft spheres with a texture on them. How long does it take you to draw your 2000 spheres.

  • @dave1707 drawing is not so bad (fps around 20-30ish with everything else going on). Setting up takes 14.2secs for 1800 craft spheres on ipad pro 12.9’’. It was slow enough i introduced a coroutine to display an animation during the setup (coroutine was disabled for the timing quoted above).

  • dave1707dave1707 Mod
    edited December 2018 Posts: 7,923

    @piinthesky I’m working on something right now that draws 2000 level 2 craft spheres (smooth) with textures at 22 fps on my iPad Air. It takes approx 7 seconds to create them. I’m tidying up the code now. I’ll try it on my iPad Pro and see what the times are.

  • dave1707dave1707 Mod
    Posts: 7,923

    @piinthesky On my iPad Pro it takes 3 seconds to load and runs at 54 fps.

  • @dave1707 mine are craft models made via the pseudomesh with number=6. i am going to try copying the vertices and normals from the first sphere and use in the subsequent spheres. this should avoid a lot of calculations.

  • dave1707dave1707 Mod
    Posts: 7,923

    @piinthesky That’s how I got mine to run faster. I created all the sphere tables first and then use them when I created the other spheres. I looked at the code from the link above, but when I saw it was 1609 lines, I closed it. Too many lines for me to look at.

  • @dave1707 it worked, down from 14 secs to 1 sec -great!

  • @piinthesky Are you creating a mesh and converting it to a model for every single sphere? I'm pretty sure that you don't need to do that. I'll need to experiment, but I think that once the model is created then you can use it for as many entities as you like.

  • @LoopSpace ahhh, yes i was stupid! Just using the same model for all sphere.entities works and is even faster.

  • @piinthesky That's good to know. I still don't feel that I have completely figured out the basics of craft.

    Every time I think I've understood what's going on in Codea, they add new features! "Fortunately", none of my iPads will do Face AR so I can't play with that.

  • JohnJohn Admin Mod
    Posts: 587

    I was going to mention it but it looks like you guys already figured it out. You can use the same model for as many entities as you like :smile:

    @LoopSpace what stuff would you say you find the most difficult to grasp with Craft?

  • dave1707dave1707 Mod
    Posts: 7,923

    @John if you’re asking questions about Craft, would you happen to have something like a flowchart that shows how Craft is layed out. For instance you start with craft.scene and from there you have aaa bbb ccc . From aaa bbb ccc you have other things etc. I don’t have a good understanding of how Craft is layed out.

  • Posts: 1,400
    @John - pardon my ignorance but is there a simple way of scaling a model (around its core central point) so individual spheres as mentioned above could be modified quickly?

    On the same theme is there a simple way of moving a model by reference to its core central point?

    I'm about to play with a few model ideas and you could save me a little time.
  • JohnJohn Admin Mod
    Posts: 587

    @dave1707 That's a good idea, I'll see what I can do

    @Bri_G, I assume you mean the pivot point when you say core central point. All models pivot around zero, which for most things is right in the middle (cubes, sphere, in particular).
    You can use myEntity.scale = vec3(0.5, 0.5, 0.5) to scale something down to 50% size. Each entry in vec3 is a different axis, so if you want to scale non-uniformly then you can use different amounts. For positioning you can use myEntity.position and rotation using myEntity.rotation.
    If you want to modify where the centre of a model is there are a few options:

    • modify where the centre is by changing the model vertices
    • manually adjust the position you are setting
    • use an empty entity as a parent and adjust the position of the child entity

    The 3rd option is nice in that scaling the parent will scale the child as if you had adjusted the model's pivot point.

    Here’s an example of scaling and positioning some spheres:

    -- Scale Test
    
    -- Use this function to perform your initial setup
    function setup()
        print("Hello Scaling!")
    
        scene = craft.scene()
        -- Move the camera back and rotate 180 degrees to face the center of the world
        scene.camera.z = 15
        scene.camera.rotation = quat.eulerAngles(0,180,0)
    
        sphere = craft.model("Primitives:Sphere")
    
        -- Models come with a default material loaded from their file (usually Materials:Specular)
        sphere:getMaterial().diffuse = color(213, 175, 12, 255)
    
        spheres = {}
    
        -- Create 10 entities that all use the same sphere model
        for i = 1, 10 do 
            local s = scene:entity()
            s.model = sphere
            -- Adjust position to keep them spaced evenly relative to the center of the world
            s.position = vec3((-10 * 0.5 + i), 0, 0)
            table.insert(spheres, s)
        end
    
        -- I forgot to document this but models have a bounds object
        -- Bounds includes an offset and size, min and max location 
        -- which is in local space relative to the vertices in the model
        print("Bounds (offset, size): ", sphere.bounds.offset, sphere.bounds.size)
    
    end
    
    -- This function gets called once every frame
    function draw()
    
        -- Update sphere scale based on position and time using sine wave
        local v = vec3(1,1,1)
        for i = 1, #spheres do 
            spheres[i].scale = v * math.abs(math.sin(i*0.25 + ElapsedTime) * 0.5)
        end
    
        scene:update(DeltaTime)
        scene:draw()    
    end
    
  • @John while we are on the subject of scaling! I reported previously there is an bug with scaling when in AR mode-if the scaling is small all hell breaks lose! I made a video to convince you....

  • JohnJohn Admin Mod
    Posts: 587

    @piinthesky How are you doing the scaling, can you show me the code?

  • Posts: 1,400
    @John - thanks for the feedback and example, I want to move models in a figure of 8 path away from the screen then back rotating through 360 as it moves. Should be able to figure it out now. Thanks.
  • @John here is the AR example i modified for scaling. Myscene is the parent of each block. Myscene is then scaled in the parameter slider.....

    -----------------------------------------
    -- AR
    -- Written by John Millard
    -----------------------------------------
    -- Description:
    -- A basic Augmented Reality demo using ARKit internally.
    -- Use scene.ar to setup and pause AR* mode.
    --
    -- * Please note that only devices with an A9 processor or above support AR.
    --   This is an iOS 11 only feature.
    -----------------------------------------
    
    
    displayMode(FULLSCREEN)
    
    function setup()
        -- Create a new craft scene
        scene = craft.scene()
        scene.sun:get(craft.light).intensity = 0.7
        myscene=scene:entity()
        parameter.number("sceneScale",0,2,1, function(c) myscene.scale=vec3(1,1,1)*c end)
    
        if craft.ar.isSupported then
            -- Enable AR session
            scene.ar:run()
    
            -- Keep a list of detected planes
            planes = {}
    
            -- Option to turn plane detection on and off
            parameter.boolean("PlaneDetection", true, function(b)
                scene.ar.planeDetection = b
            end)
    
            -- Option to draw any detected planes using camera rendering mask
            parameter.boolean("DrawPlanes", true, function(b)
                local c = scene.camera:get(craft.camera)
                if b then
                    c.mask = ~0
                else
                    c.mask = 1
                end
            end)
    
            parameter.boolean("DrawPointCloud", true)
    
            local grid = readImage("Project:GridWhite")
    
            scene.ar.didAddAnchors = function(anchors)
                for k,v in pairs(anchors) do
                    local p = scene:entity():add(Plane, v, grid)
                    planes[v.identifier] = p
                end
            end
    
            scene.ar.didUpdateAnchors = function(anchors)
                for k,v in pairs(anchors) do
                    local p = planes[v.identifier]
                    p:updateWithAnchor(v)
                end
            end
    
            scene.ar.didRemoveAnchors = function(anchors)
                for k,v in pairs(anchors) do
                    local p = planes[v.identifier]
                    p.entity:destroy()
                    planes[v.identifier] = nil
                end
            end   
    
            trackingState =
            {
                [AR_NOT_AVAILABLE] = "Not Available",
                [AR_LIMITED] = "Limited",
                [AR_NORMAL] = "Normal"
            }
    
            cross = image(16,16)
            setContext(cross)
            pushStyle()
            fill(255, 198, 0, 255)
            noStroke()
            rectMode(CENTER)
            rect(cross.width/2, cross.height/2, 3, cross.height)
            rect(cross.width/2, cross.height/2, cross.width, 3)
            popStyle()
            setContext()
    
        end
        parameter.action("ar pause", function()  scene.ar:pause() end)
        parameter.action("ar run", function()  scene.ar:run() end)
    end
    
    function update(dt)
        scene:update(dt)   
    end
    
    -- Called automatically by codea 
    function draw()
        update(DeltaTime)
    
        -- Draw the scene
        scene:draw()    
    
        local status = nil
        if craft.ar.isSupported then
            status = trackingState[scene.ar.trackingState]
    
            if DrawPointCloud then
                local c = scene.camera:get(craft.camera)
                for k,v in pairs(scene.ar.points) do
                    local p = c:worldToScreen(v)
                    sprite(cross, p.x, p.y)
                end
            end
        else
            status = "AR Not Supported"
        end
        fill(255, 255, 255, 255)
        text(status, WIDTH/2, HEIGHT - 50)
    
    end
    
    function touched(touch)
        if craft.ar.isSupported and touch.state == BEGAN then
            local results = scene.ar:hitTest(
                vec2(touch.x, touch.y),
                AR_EXISTING_PLANE_CLIPPED)
    
            for k,v in pairs(results) do
                local e = scene:entity()
                e.parent=myscene
                local cube = e:add(Cube, v.position + vec3(0,0.5,0), 0.1)
                break
            end
        end
    end
    
  • @john you could also add an ar.pause() and ar.run() to the above to see the problem with restarting after a pause.

  • Posts: 419

    @John did you get a chance to look into the AR scaling issue-illustrated in the video above? I suspect this is the origin of many of the unstable AR problems i am experiencing.

    I also think applyTorque does not work in AR for some reason-can you confirm or not?

    It appears you fixed the ar.pause/ar.run issue?

  • SimeonSimeon Admin Mod
    Posts: 5,054

    @piinthesky I think @John did fix the ar.pause / run issue. We'll look into the scaling soon

  • JohnJohn Admin Mod
    Posts: 587

    @piinthesky Can I ask what your intended effect is for globally scaling the AR world and applying physics at the same time? Scaling multiple physics objects arbitrary and having them behave is tricky, and even engines like Unity don't necessarily handle this perfectly.

    I can see that there's definitely a problem with physics scaling as it's causing non-uniform warping and the physics isn't syncing correctly - so I'm attempting to figure out what's causing that. It shouldn't have anything to do with AR since that only effects the camera/view transform and nothing else.

  • Posts: 419

    @john i am (still!) developing a neutrino telescope app, see video here...

    I am now working on an AR view of it, see here...

    The AR view applies a global scaling to make it fit in the real world (it is a kilometer cubed in real size). When i try to implement the submarine in AR as a dynamic body, it displays crazily -a bit like the cubes do in the video in the earlier post. It seems dynamic bodies and scaling and AR are not happy together at the moment.

    I use a dynamic body with applyForce and applyTorque to control the submarine movement (this does works in normal 3D). If you think the prospects of fixing this are dim, i could probably switch back to using a kinematic body and handle the flight mechanics using the ignatz/loopspace flight class.

    image.jpeg 21.6K
  • SimeonSimeon Admin Mod
    Posts: 5,054

    @piinthesky that's amazing, do you actually work with a neutrino telescope?

  • edited January 13 Posts: 419

    yes, its 2,500m deep at the bottom of the Mediterranean Sea and studies your sky in Australia! (the neutrinos pass through the Earth no problem), follow me on twitter!

  • Posts: 419
    @LoopSpace when i try to use an open ended cone from your 3d pseudoMesh library, which is then converted to a craft model with ‘toModel()’. I observe that only the inside of the cone is ever visible. Surprisingly, if i apply ‘invertNormals()’ it does not seem to have any effect? Am i doing something wrong? How can i see the exterior surface of the open ended cone model?
  • Posts: 457

    @piinthesky Are you doing "invertNormals" before converting to a model?

    Can you post a small example?

  • edited December 3 Posts: 419

    @LoopSpace here is my test code. I am trying to make a spotlight beam effect using a 3d cone. Need to add dependencies on your PseudoMesh class and the orbitViewer class (in. Examples). You will see the cone disappears at the same viewer angles for both the invertedNormals and non-inverted normals? Set useTransparency to true get the effect of the light fading with distance (sort of).

    -- Test spotlight beam using cone
    
    -- Use this function to perform your initial setup
    function setup()
    
    -- Create a new craft scene    
        scene = craft.scene()   
    
    --orbital viewer
        scene.camera:add(OrbitViewer, vec3(0,0,0), 400, 0, 2000)
        orv=scene.camera:get(OrbitViewer)   
    
    --texture for cone for transparency
        useTransparency=false
        if useTransparency then  
            coneTexture=image(255,255)
            setContext(coneTexture)
                for i=1,255 do
                for j=1,255 do
                    mycol=color(46, 218, 240, j)
                    coneTexture:set(i,j,mycol )
                end
                end
            setContext()
        end
    
    
        coneHeight=1   
        coneBase=1
    
        local coneMesh = PseudoMesh()
        coneMesh:addCylinder({
                    ends=0,
                    axis=vec3(0,1,0),
                    height=coneHeight,
                    startRadius=coneBase,
                    endRadius=0, 
                    centre=vec3(0,-coneHeight/2,0),
                    size=30,   --number of facets
                    })
    --    coneMesh:invertNormals()
        coneModel= coneMesh:toModel()     
    
    --  
        local coneMesh2 = PseudoMesh()
        coneMesh2:addCylinder({
                    ends=0,
                    axis=vec3(0,1,0),
                    height=coneHeight,
                    startRadius=coneBase,
                    endRadius=0, 
                    centre=vec3(0,-coneHeight/2,0),
                    size=30,   --number of facets
                    })
        coneMesh2:invertNormals()
        coneModel2= coneMesh2:toModel()     
    
    
        cone1=scene:entity()
        cone1.model =coneModel
        cone1.scale=vec3(1,1,1)*50
        cone1.material = craft.material("Materials:Standard")
        if useTransparency then
            cone1.material.map=coneTexture
            cone1.material.diffuse=color(255, 0, 37, 255)
            cone1.material.opacity=0.8
            cone1.material.blendMode=NORMAL   
        end  
    
        cone2=scene:entity()
        cone2.model =coneModel2
        cone2.scale=vec3(1,1,1)*50
        cone2.material = craft.material("Materials:Standard")
        cone2.position=vec3(0,100,0)
        if useTransparency then
            cone2.material.map=coneTexture
            cone2.material.diffuse=color(255, 0, 37, 255)
            cone2.material.opacity=0.8
            cone2.material.blendMode=NORMAL    
        end 
    --]]   
    
    end
    
    function update(dt)  
        scene:update(dt)
    end   
    
    function draw()
        update(DeltaTime)
        scene:draw()   
    end
    
    
    function touched(touch)  
        orv:touched(touch)
    end
    
  • Posts: 457

    @piinthesky The problem is that you are using addCylinder to add a cone. Since addCylinder is designed for cylinders, it expects there to be a circle at either end and so all of the normals are calculated using how each triangle relates to the circle at its end. By making the ending radius 0, you are scupperring this calculation. So many of your normals are 0 (or possibly NaN).

    The simplest solution is to make the end radius small but non-zero. I get a reasonable looking cone with end radius 0.1, but 0.01 also works.

    There is an addCone method, but that is more general and needs more input. What this does is take a set of points, thought of as a sample of points along a path, and constructs what is known as the "topological cone" on that set of points. This involves connecting each given point to an apex (which must also be specified). As it is more general, you have to construct the points explicitly.

  • Posts: 419

    @LoopSpace Ahha, eureka! Yes, setting the end radius to a non-zero small value does the trick. By drawing two cones at the same location one with 'normal' normals, the other with 'inverted' normals and setting useTransparency=true gives pretty much the effect i was looking for. Thanks.

    Currently, the transparency of the texture increases as function of distance from the apex. I would also like that the transparency increases as a function of distance from the cone axis i.e. soften the 'edges' of the light beam cone-can you think of a way to do that?

  • dave1707dave1707 Mod
    Posts: 7,923

    Thought I’d try my hand at making a transparent cone that moves thru a cube of spheres. I took one of my tube programs and modified it to make a cone. When you run this, it’s not a true cone just to make it easier to see what it looks like as you rotate the cubed spheres. To see a full cone, comment out the line conePoint=2 at the start of the code so that conePoint has a value of 1000.

    displayMode(FULLSCREEN)
    
    function setup()
        conePoint=1000
        conePoint=2
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()    
        v=scene.camera:add(OrbitViewer,vec3(0,0,0), 2000, 0, 2000)
        v.camera.farPlane=3000
        v.rx,v.ry=10,-45
        cone(vec3(400,0,0),vec3(0,0,0),200,40)
        for x=-5,5 do   -- create cube of spheres
            for y=-5,5 do
                for z=-5,5 do
                    createSphere(vec3(x*60,y*60,z*60))
                end
            end
        end
        x,y,z=400,100,0 -- cone starting position
    end
    
    function draw()
        update(DeltaTime)
        scene:draw() 
    end
    
    function update(dt)
        scene:update(dt)
        x=x-.5  -- move cone thru spheres
        cn.position=vec3(x,y,z) -- change cone position
    end
    
    function cone(p1,p2,dia,sides)
        local pos,ind,nor,col={},{},{},{}
        local rp=vec3(100,100,100)
        local v1=rp-p1
        local r1=v1:cross(p2-p1)
        local s1=r1:cross(p2-p1)
        local n
        r1,s1=r1:normalize(),s1:normalize() 
        for a=0,719,360/sides do
            n = r1 * math.cos(math.rad(a)) + s1 * math.sin(math.rad(a))
            n=n*dia
            if a>359 then
                table.insert(pos,n/conePoint + p2)    -- add p2 to last loop
            else
                table.insert(pos,n + p1)    -- add p1 to loop
            end
            table.insert(nor,n)
        end    
        local o,p={1,2,3,4,5,6,3,2,1,6,5,4},{}
        for z=1,#pos-sides do
            p[1],p[2],p[3],p[4],p[5],p[6]=z,z+1,z+sides+1,z,z+sides+1,z+sides
            if z%sides==0 then
                p[2]=z-sides+1
                p[3]=z+1
                p[5]=z+1
            end
            for t=1,12 do
                table.insert(ind,p[o[t]])
            end
        end 
        for z=1,#pos do
            table.insert(col,color(255))
        end    
        cn=scene:entity()
        cn.model = craft.model()
        cn.model.positions=pos
        cn.model.indices=ind
        cn.model.colors=col
        cn.model.normals=nor
        cn.material=craft.material("Materials:Basic")
        cn.material.blendMode = NORMAL
        cn.material.opacity=.5    
    end
    
    function createSphere(p)
        local s=scene:entity()
        s.position=vec3(p.x,p.y,p.z)
        s.model = craft.model.icosphere(8,3)
        s.material = craft.material("Materials:Specular")
        s.material.diffuse=color(255,0,0)
    end
    
  • edited December 6 Posts: 419

    @dave1707, thanks that is interesting-i always thought that with craft models only either the inner or outer surfaces can be visible, but here both are visible! @LoopSpace why then are the PseudoMesh models only visible on one of the surfaces?

    @dave1707 i tried to use my coneTexture (see the code i uploaded earlier) on your implementation of the cone, but it does not seem to work? Using coneTexture was a way to make the intensity of the 'spot light' fade as a function of distance from the apex.

  • dave1707dave1707 Mod
    Posts: 7,923

    @piinthesky Not sure why texture isn’t working. In other code, I use something like cn.material.map=img and I get the img texture. Maybe because I’m not using a built in model or maybe there’s something else that needs to be set. I don’t fully understand everything that needs to be set.

  • dave1707dave1707 Mod
    Posts: 7,923

    @piinthesky Here’s another version where a map can be used. I created a white gradient image to use so the cone gets darker the farther away from the cube. I don’t know if this is any better for you.

    displayMode(FULLSCREEN)
    
    function setup()
        img=image(256,256)  -- create gradiant texture image
        setContext(img)
        for x=0,256 do
            for y=0,256 do
                img:set(x,y,x,x,x)
            end
        end
    
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(255, 255, 255, 255)
        skyMaterial.horizon=color(255, 255, 255, 255)
        v=scene.camera:add(OrbitViewer,vec3(px,100,pz), 1000, 0, 10000)
        v.camera.farPlane=10000
        pos={}
        for z=0,360,20 do
            x=math.cos(math.rad(z))*50
            y=math.sin(math.rad(z))*50
            table.insert(pos,vec3(x,y,800))
            x=math.cos(math.rad(z))*5
            y=math.sin(math.rad(z))*5
            table.insert(pos,vec3(x,y,25))
        end
        for z=1,#pos-2,2 do
            p1=pos[z]
            p2=pos[z+1]
            p3=pos[z+3]
            p4=pos[z+2]
            createTile(p1,p2,p3,p4)
        end
        z=#pos-1
        p1=pos[z]
        p2=pos[z+1]
        p3=pos[2]
        p4=pos[1]
        createTile(p1,p2,p3,p4)
        createCube(vec3(0,0,0))
    end
    
    function draw()
        update(DeltaTime)
        scene:draw() 
    end
    
    function update(dt)
        scene:update(dt)
    end
    
    function createTile(p1,p2,p3,p4)
        local c=color(255,255,255,255)
        local pt=scene:entity()
        pt.model = craft.model()
        pt.model.positions={p1,p3,p4,p1,p2,p3}
        pt.model.indices={1,2,3,4,5,6,6,5,4,3,2,1}
        pt.model.colors={c,c,c,c,c,c}
        pt.model.uvs={vec2(0,0),vec2(1,1),vec2(0,1),vec2(0,0),vec2(1,0),vec2(1,1)}
        pt.material = craft.material("Materials:Basic")  
        pt.material.blendMode = NORMAL
        pt.material.opacity=.8  
        pt.material.map=img
        --pt.material.map=readImage("Blocks:Error") 
    end
    
    function createCube(p)
        local s=scene:entity()
        s.position=vec3(p.x,p.y,p.z)
        s.model = craft.model.cube(vec3(50,50,50))
        s.material = craft.material("Materials:Specular")
        s.material.diffuse=color(255,0,0)
    end
    
  • Posts: 1,400

    @piinthesky @dave1707 - just looking at this from an external point of view the cone seems to look artificial - that is it looks like a solid object. There was a post by @ignatz where he was building a 2d game where he made a flaming torch for mazes which produced an in beam high colour out of beam graduated darker effect - link below. I think you may need a shader for this where all in beam objects are shown in true colour fading offset towards the perimeter rather than building an object.

    https://codea.io/talk/discussion/5614/2d-sidescrolling-toolkit#latest

  • Posts: 457

    @piinthesky The model in @dave1707 's code is visible from both sides because each triangle is included twice, once with the vertices clockwise and once anticlockwise. This makes it visible from both sides. You could achieve the same effect with my code by adding a second copy of the cone with inverted normals. Speaking of which, the normals in @dave1707 's code aren't quite right - they don't take into account the slant of the cone sides.

    As to the transparency, I have to admit that I'm not really sure what you are trying to accomplish overall so can't advise on the best solution, but distance from the cone axis is proportional to distance from the apex so if you can fade appropriately from the apex then you'll be fine. If this is simulating a light cone, then it should fade with the inverse square of the distance to the apex. But changes in transparency don't always look the same to the eye as they do in the code so you'd just have to experiment.

  • Posts: 1,400
    @piinthesky - just picking up on my earlier point. The image you produce depends on the perspective with regard to viewing point. Do you need a sideways view of a light source moving through the environment or are you needing a viewpoint behind the light source which moves with the camera?

    The latter could be simulated by a graduated screen just in front of the camera i.e. make the background darker with the centre focus brighter.

    One thing you need to do is brighten the image which is at the end of the cone i.e.the broad end (target for the beam), if you are viewing from the side.
  • edited December 7 Posts: 1,400

    @piinthesky - just watched your video again and note that the craft you use is not shaded so it looks like you don’t have a light source in the project although you have produced a lot of interesting light effects.

    How have you generated your ‘world’ ? Are you using Craft, if you are then you could add light sources to shade and illuminate to produce the effect you need.

Sign In or Register to comment.