lighttroupe/luz

View on GitHub
engine/actor_shape.rb

Summary

Maintainability
A
0 mins
Test Coverage
class ActorShape < Actor
    virtual

    description 'Base class for shapes'

    attr_reader :shape

    def render
        shape { |options| render_with_options(options) }        # shape expected to yield array of x,y pairs, or array of arrays: [x1,y1,x2,y2,...]
    end

    def render_with_options(options)
        options = {:type => :polygon, :shape => []}.merge(options)
        options[:shape] = [options[:shape]] unless options[:shape][0].is_a?(Array)

        case options[:type]
        when :triangle_strip
            render_triangle_strip(options)
        when :polygon
            tesselate_polygon(options)
        else
            raise "unhandled shape type '#{options[:type]}'"
        end
    end

private

    POINTS_PER_VERTEX = 2

    def render_triangle_strip(options)
        options[:shape].each { |shape|
            GL.Begin(GL::TRIANGLE_STRIP) # GL::LINE_LOOP
                0.step(shape.size-1, POINTS_PER_VERTEX) { |points_index|
                    v = shape[points_index, POINTS_PER_VERTEX]
                    tc = texture_coord(v)
                    GL.TexCoord(tc[0] + RADIUS, -(tc[1] - RADIUS))
                    GL.Vertex(*v)
                }
            GL.End
        }
    end

    def tesselate_polygon(options)
        # Use a (cached) tessalator to generate (OpenGL-compatible) *convex* polygons
        # from our possibly (non-OpenGL-compatible) concave shape.
        unless $tessalator
            $tessalator = GLU.NewTess

            #puts "#{v.collect { |f| sprintf('%4.2f', f)}.join(',')} => #{tc.collect { |f| sprintf('%4.2f', f)}.join(',')}" ;
            # Add callbacks.  All do pretty much the minimum.
            GLU.TessCallback($tessalator, GLU::TESS_BEGIN, proc { |shape_type| GL.Begin(shape_type) }) # add GL.Color(rand,rand,rand) before Begin to see shapes
            GLU.TessCallback($tessalator, GLU::TESS_EDGE_FLAG, proc { |e| GL.EdgeFlag(e) })        #             [(v[0] + RADIUS), -(v[1] - RADIUS)]
            GLU.TessCallback($tessalator, GLU::TESS_VERTEX, proc { |v| v = vertex(v) ; tc = texture_coord(v) ; GL.TexCoord(tc[0] + RADIUS, -(tc[1] - RADIUS)) ; GL.Vertex(v) })
            GLU.TessCallback($tessalator, GLU::TESS_COMBINE, proc { |coords, vertex_data, weight| combine(coords, vertex_data, weight) })
            GLU.TessCallback($tessalator, GLU::TESS_END, proc { GL.End })
            GLU.TessCallback($tessalator, GLU::TESS_ERROR, proc { |error_code| puts "Tessellation Error: #{GLU.ErrorString(error_code)}" })
        end

        #GL.FrontFace(GL::CCW)
        GLU.TessBeginPolygon($tessalator, nil)
        options[:shape].each { |shape|
            next if shape.size < 3
            GLU.TessBeginContour($tessalator)
                # step to every other index (0,2,4,etc.)
                0.step(shape.size-1, POINTS_PER_VERTEX) { |points_index|
                    v = shape[points_index, POINTS_PER_VERTEX]        # gets [x,y]
                    GLU.TessVertex($tessalator, v, v)
                    #v3d = v + [z_width]
                    #GLU.TessVertex($tessalator, v3d, v3d)
                }
            GLU.TessEndContour($tessalator)
        }
        GLU.TessEndPolygon($tessalator)
    end

    def texture_coord(v)
        v
    end

    def vertex(v)
        v
    end

    def combine(coords, vertex_data, weight)
        coords[0,2]
    end
end