indexing
	description: "[
	
		3D counterpart to EM_TTF_FONT that provides Em3d with font rendering
		capabilities.

	]"
	author: ""
	date: "$Date$"
	revision: "$Revision$"

class
	EM3D_TTF_FONT

inherit

	EM3D_FONT

	EM_CONSTANTS
		export {NONE} all end

	EMGL_VIEW
		export {NONE} all end

	EMGLU_VIEW
		export {NONE} all end

	EMGL_SETTINGS
		export {NONE} all end

	EMGL_BASIC_RENDERER
		export {NONE} all end

create {EM3D_FONT_FACTORY}

	make_from_ttf

feature {NONE} -- Initialization

	make_from_ttf (a_ttf_font: EM_TTF_FONT) is
			-- Initialize `Current'.
		require
			font_not_void: a_ttf_font /= Void
		local
			i: INTEGER
		do
			font := a_ttf_font

			create glyph_cache.make (0, 255)
			from i := 0 until i > 255 loop
				glyph_cache[i] := Void
				i := i + 1
			end
		ensure
			set: font = a_ttf_font
		end

feature -- Basic operations

	cache_string (a_string: STRING) is
			--
		local
			i: INTEGER
			g: EM3D_TTF_GLYPH
		do
			from i := 1 until i > a_string.count loop
				g := glyph ((a_string @ i).code)
				check g /= Void end
				i := i + 1
			end
		end

	render_string (a_string: STRING; a_pos: EM_VECTOR3D; a_point_size: DOUBLE; a_color: EM_COLOR) is
			-- Render `a_string' at `a_pos', using current font with `a_point_size' and `a_color'
			-- subject to all OpenGL transformations.
			-- `a_pos' is start of the baseline.
			-- This implies that the OpenGL modelview and projection matrices
			-- are set to an appropriate transformation.
			-- Something like gluOrtho (0, w, h, 0) should give the expected
			-- results with regard to the 2D equivalent of this function:
			-- {EM_TTF_FONT}.draw_string
			-- The modelview transformation can also easily be set using
			-- {EM_COORDINATE_FRAME}
		local
			s: DOUBLE
		do
			emgl_matrix_mode (em_gl_modelview)
			emgl_push_matrix

			emgl_translated (a_pos.x, a_pos.y, a_pos.z)
			s := a_point_size / optimal_point_size
			emgl_scaled (s, s, s)

			-- Draw
			render_string_impl (a_string, a_color)

			emgl_pop_matrix
		end

	render_string_point_snapped (a_string: STRING; a_pos: EM_VECTOR3D; a_point_size: DOUBLE; a_color: EM_COLOR) is
			-- Same as `render_string', but tries to render `a_string' at integral coordinates to
			-- achieve better visual quality.
		local
			s: DOUBLE
		do
			emgl_push_matrix

			-- Round to integer coordinates
			emgl_translated (a_pos.x.rounded, a_pos.y.rounded, a_pos.z)
			s := a_point_size / optimal_point_size
			emgl_scaled (s, s, s)

			-- Draw
			render_string_impl (a_string, a_color)

			emgl_pop_matrix
		end

feature -- Measurements

	optimal_point_size: DOUBLE is
			-- Point size of the internally used ttf-font
		do
			Result := font.point_size.to_double
		end

	line_skip (a_point_size: DOUBLE): DOUBLE is
			-- Recommended line spacing for `a_point_size'
		do
			Result := scale (font.font_line_skip, a_point_size)
		end

	ascent (a_point_size: DOUBLE): DOUBLE is
			--Max ascent for `a_point_size'
		do
			Result := scale (font.font_ascent, a_point_size)
		end

	descent (a_point_size: DOUBLE): DOUBLE is
			-- Max descent for `a_point_size'
		do
			Result := scale (font.font_descent, a_point_size)
		end

	baseline_offset (a_point_size: DOUBLE): DOUBLE is
			-- Offset of baseline from top boundary for `a_point_size'
		do
			-- Gives best results. Tried adding bias to account for
			-- line skip, but result was not pleasing
			Result := ascent (a_point_size)
		end

	char_width (a_chr: CHARACTER; a_point_size: DOUBLE): DOUBLE is
			-- Width of `a_chr' for `a_point_size'
		do
			-- This also buffers the character texture
			-- One can argue whether this is a good or a bad thing...
			-- Personally, I think it is good. If we're interested
			-- in the metrics of a character, we're probably going
			-- to render it, anyway.
			Result := scale (glyph (a_chr.code).metrics.advance, a_point_size)
		end

	string_width (a_string: STRING; a_point_size: DOUBLE): DOUBLE is
			-- Width of `a_string' for `a_point_size'
		local
			i: INTEGER
			sx: DOUBLE
		do
			sx := 0.0

			-- Cannot use font.string_width because we have no kerning available
			-- when rendering glyphs seperately
			from i := 1 until i > a_string.count loop
				sx := sx + glyph ((a_string @ i).code).metrics.advance
				i := i + 1
			end

			Result := scale (sx, a_point_size)
		end

	string_height (a_string: STRING; a_point_size: DOUBLE): DOUBLE is
			-- Height of `a_string' for `a_point_size' (single line always)
		do
			Result := line_skip (a_point_size)
		end

feature {NONE} -- Implementation

	render_string_impl (a_string: STRING; a_color: EM_COLOR) is
			-- Render `a_string' with `a_color', assuming that transformation has
			-- already been set up.
		local
			i: INTEGER
		do
			emgl_push_attrib (em_gl_color_buffer_bit | em_gl_depth_buffer_bit | em_gl_enable_bit)

			emgl_depth_mask (em_gl_false)
			emgl_disable (em_gl_cull_face)
			emgl_enable (em_gl_blend)
			emgl_blend_func (em_gl_src_alpha, em_gl_one_minus_src_alpha)
			emgl_enable (em_gl_texture_2d)
			emgl_color4ub (a_color.red, a_color.green, a_color.blue, a_color.alpha)

			emgl_normal3d (0.0, 0.0, -1.0)
			from i := 1 until i > a_string.count loop
				glyph ((a_string @ i).code).render
				i := i + 1
			end

			emgl_pop_attrib
		end

	scale (fm: DOUBLE; a_point_size: DOUBLE): DOUBLE
			-- Scale `fm' from `optimal_point_size' to `a_point_size'
		do
			Result := fm * a_point_size / optimal_point_size
		end

	font: EM_TTF_FONT
			-- 2D counterpart

	glyph_cache: ARRAY [EM3D_TTF_GLYPH]
			-- Cache that stores glyphs for rendering

	glyph (code: INTEGER): EM3D_TTF_GLYPH is
			-- Return glyph associated with `code'
		require
			valid_index: glyph_cache.lower <= code and code <= glyph_cache.upper
		local
			g: EM3D_TTF_GLYPH
		do
			g := glyph_cache[code]
			if g = Void then
				create g.make (code, font)
				glyph_cache[code] := g
			end
			Result := g
		end

invariant

	properly_initialized: font /= Void and glyph_cache /= Void

end