indexing description: "[ A scene showing how to use procedural textures ]" date: "$Date$" revision: "$Revision$" class PROCEDURE_TEST_SCENE inherit EM3D_TRACKBALL_SCENE redefine make_scene, predraw, draw end GL_FUNCTIONS export {NONE} all end GLU_FUNCTIONS export {NONE} all end create make_scene feature {NONE} -- Initialisation make_scene is -- Initialise default values. local i, j: INTEGER mesh_size: INTEGER step_size: DOUBLE vert_x, vert_y, vert_step: DOUBLE tex_u, tex_v, tex_step: DOUBLE perlin_x, perlin_y, perlin_step: DOUBLE perlin_terrain_proc: EM_2D_PERLIN_NOISE height: DOUBLE height_factor: DOUBLE do camera_distance := 5.0 Precursor{EM3D_TRACKBALL_SCENE} -- Initialise normalized speed for rotation create speed speed.set_speed (50) create perlin.make create voronoi.make -- The simplest way to generate a texture could look like this: -- Create the generator -- create texture_generator.make (perlin) -- Specify where we want to evaluate the procedure -- texture_generator.set_evaluation_area (-5.0, -5.0, 5.0, 5.0) -- Specify the size of the texture (has to be something to the power of 2) -- texture_generator.set_texture_dimension (64, 64) -- Generate the texture -- texture_generator.generate_texture -- However, as generating a texture from a procedure can be a very time -- consuming task, we won't to all at once so we can already -- draw the scene while still generating. -- We do all the work in 'generate_textures' which does a little -- bit every time it's called -- TERRAIN -- The EM_PROCEDURES are simply procedures, and can be used for anything -- In this case, we use perlin noise to generate a random terrain create perlin_terrain_proc.make perlin_terrain_proc.set_seed (35) gl_new_list (perlin_terrain, em_gl_compile) mesh_size := 5 step_size := 1 / mesh_size vert_step := step_size tex_step := 0.5 / mesh_size perlin_step := 3.0 / mesh_size -- EM_PROCEDUREs give us values between 0 and 1.0 -- We scale that a little bit down height_factor := 0.25 -- We generate a mesh of gl_quads, and at every -- Point get the height by evaluating the -- Perlin noise procedure from i := -mesh_size until i > mesh_size - 1 loop from j := -mesh_size until j > mesh_size - 1 loop vert_x := i * step_size vert_y := j * step_size tex_u := (i + mesh_size) * (tex_step) tex_v := (j + mesh_size) * (tex_step) perlin_x := i * perlin_step perlin_y := j * perlin_step gl_begin(em_gl_quads) -- We always add 1 to the height to have the -- terrain at the top of the cube height := perlin_terrain_proc.evaluate_at ([perlin_x, perlin_y]) gl_tex_coord2d (tex_u, tex_v) gl_vertex3d (vert_x, 1 + height_factor * height, vert_y) height := perlin_terrain_proc.evaluate_at ([perlin_x, perlin_y + perlin_step]) gl_tex_coord2d (tex_u, tex_v + tex_step) gl_vertex3d (vert_x, 1 + height_factor * height, vert_y + vert_step) height := perlin_terrain_proc.evaluate_at ([perlin_x + perlin_step, perlin_y + perlin_step]) gl_tex_coord2d (tex_u + tex_step, tex_v + tex_step) gl_vertex3d (vert_x + vert_step, 1 + height_factor * height, vert_y + vert_step) height := perlin_terrain_proc.evaluate_at ([perlin_x + perlin_step, perlin_y]) gl_tex_coord2d (tex_u + tex_step, tex_v) gl_vertex3d (vert_x + vert_step, 1 + height_factor * height, vert_y) gl_end j := j + 1 end i := i + 1 end gl_end_list current_texture := none_done ensure then speed_not_void: speed /= Void end feature -- Access angle: DOUBLE -- Rotation angle in degrees speed: EM_NORMALIZED_SPEED -- Rotation speed for pyramid feature -- Drawing is_predraw: BOOLEAN predraw is -- Do the predraw things do is_predraw:=true -- Reset the aspect gl_matrix_mode_external (Em_gl_projection) gl_pop_matrix gl_matrix_mode_external (Em_gl_modelview) is_predraw:=false end draw is -- Draw scene. local delta: INTEGER do -- Continue generating textures generate_textures -- Move the object into the screen and rotate it -- Initial position: gl_scalef(1, -1, 1) gl_rotatef(45, 1, 0, 0) gl_rotatef(45, 0, 1, 0) -- Automatic rotation (only if user is not moving the object) gl_rotatef (angle, 0, 1, 0) delta := speed.get_delta if not rotate then if angle < 360 then angle := angle + delta else angle := 0 end end gl_enable (em_gl_texture_2d) if perlin_texture_1 /= void then perlin_texture_1.bind end gl_begin (em_gl_quads) gl_color3f(1, 0, 0) -- Red gl_tex_coord2d (0.0, 0.0) gl_vertex3d (-1, -1, -1) gl_tex_coord2d (1.0, 0.0) gl_vertex3d (-1, 1, -1) gl_tex_coord2d (1.0, 1.0) gl_vertex3d (-1, 1, 1) gl_tex_coord2d (0.0, 1.0) gl_vertex3d (-1, -1, 1) gl_end if perlin_texture_1 /= void then perlin_texture_1.unbind end if perlin_texture_2 /= void then perlin_texture_2.bind end gl_begin (em_gl_quads) gl_color3f(0, 1, 0) -- Green gl_tex_coord2d (1.0, 0.0) gl_vertex3d (-1, -1, 1) gl_tex_coord2d (1.0, 1.0) gl_vertex3d (1, -1, 1) gl_tex_coord2d (0.0, 1.0) gl_vertex3d (1, 1, 1) gl_tex_coord2d (0.0, 0.0) gl_vertex3d (-1, 1, 1) gl_end if perlin_texture_2 /= void then perlin_texture_2.unbind end if perlin_texture_3 /= void then perlin_texture_3.bind end gl_begin (em_gl_quads) gl_color3f (0, 0, 1) -- Blue gl_tex_coord2d (1.0, 0.0) gl_vertex3d (1, -1, -1) gl_tex_coord2d (1.0, 1.0) gl_vertex3d (1, 1, -1) gl_tex_coord2d (0.0, 1.0) gl_vertex3d (1, 1, 1) gl_tex_coord2d (0.0, 0.0) gl_vertex3d (1, -1, 1) gl_end if perlin_texture_3 /= void then perlin_texture_3.unbind end if voronoi_texture_1 /= void then voronoi_texture_1.bind end gl_begin (em_gl_quads) gl_color3f (1, 1, 0) -- Yellow gl_tex_coord2d (1.0, 0.0) gl_vertex3d (1, -1, -1) gl_tex_coord2d (1.0, 1.0) gl_vertex3d (1, 1, -1) gl_tex_coord2d (0.0, 1.0) gl_vertex3d (-1, 1, -1) gl_tex_coord2d (0.0, 0.0) gl_vertex3d (-1, -1, -1) gl_end if voronoi_texture_1 /= void then voronoi_texture_1.unbind end if voronoi_texture_2 /= void then voronoi_texture_2.bind end gl_color3f (1, 0, 1) -- Pink gl_call_list (perlin_terrain) if voronoi_texture_2 /= void then voronoi_texture_2.unbind end gl_disable (em_gl_texture_2d) gl_translatef (0, -1, 0) end generate_textures is -- generate all textures do -- This generates one texture after another -- The idea is that instead of generating whole the texture -- at once using -- texture_generator.generate_texture -- You can use the following: -- -- from -- texture_generator.start_generating_texture -- until -- texture_generator.is_finished -- loop -- texture_generator.continue_generating_texture -- end inspect current_texture when none_done then -- we haven't started, so lets start -- First texture (red): a simple perlin noise create texture_generator.make (perlin) texture_generator.set_evaluation_area ([-5.0, -5.0], [5.0, 5.0]) texture_generator.set_texture_dimension ([64, 64]) texture_generator.start_generating_texture current_texture := perlin_1 when perlin_1 then texture_generator.continue_generating_texture output_progress if texture_generator.is_finished then perlin_texture_1 := texture_generator.last_texture output_finished("perlin 1") -- Second texture (green): perlin noise with a different seed -- and less octaves -- (all other values remain the same) perlin.set_seed(27) perlin.set_num_of_octaves (2) texture_generator.start_generating_texture current_texture := perlin_2 end when perlin_2 then texture_generator.continue_generating_texture output_progress if texture_generator.is_finished then perlin_texture_2 := texture_generator.last_texture output_finished("perlin 2") -- Third texture (blue): Seamless perlin noise -- We set the width and height where the noise should repeat -- (all other values remain the same) perlin.set_repeat_at ([2, 4]) texture_generator.start_generating_texture current_texture := perlin_3 end when perlin_3 then texture_generator.continue_generating_texture output_progress if texture_generator.is_finished then perlin_texture_3 := texture_generator.last_texture output_finished("perlin 3") -- Fourth texture (yellow): a voronoi (width repetition) voronoi.set_repeat_at ([5, 4]) texture_generator.set_procedure (voronoi) texture_generator.start_generating_texture current_texture := voronoi_1 end when voronoi_1 then texture_generator.continue_generating_texture output_progress if texture_generator.is_finished then voronoi_texture_1 := texture_generator.last_texture output_finished("voronoi 1") -- Fifth image (pink, on terrain): voronoi using -- manhatten metric and a different seed voronoi.set_manhatten_metric (true) voronoi.set_seed (99) texture_generator.start_generating_texture current_texture := voronoi_2 end when voronoi_2 then texture_generator.continue_generating_texture output_progress if texture_generator.is_finished then voronoi_texture_2 := texture_generator.last_texture output_finished("voronoi 2") current_texture := all_done end when all_done then -- nothing to be done end ensure all_done: current_texture = all_done implies perlin_texture_1 /= void and perlin_texture_2 /= void and perlin_texture_3 /= void and voronoi_texture_1 /= void and voronoi_texture_2 /= void end output_progress is -- output some info about current progress of texture generation do io.put_string ((100 * texture_generator.progress).floor.out + "%%%N") end output_finished (a_texture_name: STRING) is -- output some info when a texture has finished generating do io.put_string("*** Finished texture " + a_texture_name + " ***%N") end feature -- Textures perlin_texture_1: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] perlin_texture_2: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] perlin_texture_3: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] voronoi_texture_1: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] voronoi_texture_2: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] feature -- Procedures perlin: EM_2D_PERLIN_NOISE voronoi: EM_2D_VORONOI feature -- Status information label: EM_LABEL feature -- Generator texture_generator: EM_2D_PROC_TEXTURE_GENERATOR current_texture: INTEGER -- Remember which texture we're working on none_done, perlin_1, perlin_2, perlin_3, voronoi_1, voronoi_2, all_done: INTEGER is unique perlin_terrain: INTEGER is 1 invariant speed_not_void: speed /= Void end