racer-svn Mailing List for Racer
Status: Alpha
Brought to you by:
jlegg
You can subscribe to this list here.
2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(42) |
Oct
(23) |
Nov
(32) |
Dec
(78) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2010 |
Jan
(55) |
Feb
(28) |
Mar
(16) |
Apr
(2) |
May
|
Jun
(43) |
Jul
(2) |
Aug
(10) |
Sep
(14) |
Oct
(2) |
Nov
|
Dec
|
From: <jl...@us...> - 2010-10-10 16:34:07
|
Revision: 349 http://racer.svn.sourceforge.net/racer/?rev=349&view=rev Author: jlegg Date: 2010-10-10 16:34:01 +0000 (Sun, 10 Oct 2010) Log Message: ----------- Fix editor crash when loading a file. Modified Paths: -------------- trunk/libtrack/Lighting.cpp Modified: trunk/libtrack/Lighting.cpp =================================================================== --- trunk/libtrack/Lighting.cpp 2010-10-10 16:14:58 UTC (rev 348) +++ trunk/libtrack/Lighting.cpp 2010-10-10 16:34:01 UTC (rev 349) @@ -157,13 +157,30 @@ void Lighting::free_shaders() const { - glUseProgram(0); + // If there is no OpenGL context, glDelete* could crash. + // Lighting can be destroyed without an OpenGL context in the editor + // because loading a track replaces the default Lighting with + // one found in a file. It can't do it in the initaliser list + // because old files don't have Lighting and require the default + // constructor instead of the stream constructor. + // So, even though the documentation states glDeleteShader(0) has + // no effect, we check for 0 first. if (m_shader_program_handle) { + // The lighting is only destroyed when the track is, so shaders + // are not needed. Go back to fixed function. + glUseProgram(0); + // delete the shader glDeleteProgram(m_shader_program_handle); } - glDeleteShader(m_vertex_shader_handle); - glDeleteShader(m_fragment_shader_handle); + if (m_vertex_shader_handle) + { + glDeleteShader(m_vertex_shader_handle); + } + if (m_fragment_shader_handle) + { + glDeleteShader(m_fragment_shader_handle); + } } /** Print the shader or program's compile log to standard output. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-10-10 16:15:08
|
Revision: 348 http://racer.svn.sourceforge.net/racer/?rev=348&view=rev Author: jlegg Date: 2010-10-10 16:14:58 +0000 (Sun, 10 Oct 2010) Log Message: ----------- Give parts of the track have multiple levels of detail. MultiDrawableMesh will select a level of detail depending on the distance. The selection is affected by a quality setting that GameScene adjusts to try to keep the frame rate about 60Hz. Each of the themes has extra levels of detail added for the most used meshes. Modified Paths: -------------- trunk/libtrack/AxisAlignedBoundingBox.cpp trunk/libtrack/AxisAlignedBoundingBox.h trunk/libtrack/Mesh/Makefile.am trunk/libtrack/OcclusionTester.h trunk/libtrack/Segment.cpp trunk/libtrack/Segment.h trunk/libtrack/Track.cpp trunk/libtrack/TrackMesh.h trunk/libtrack/path/PathEdge.cpp trunk/libtrack/path/PathEdge.h trunk/libtrack/path/PathVertex.cpp trunk/libtrack/path/PathVertex.h trunk/python/racer_theme_export.py trunk/racer/Engine/CarCamera.cpp trunk/racer/Engine/GameScene.cpp trunk/racer/Engine/LoadScene.h trunk/racer/data/Makefile.am trunk/racer/data/theme1/theme1 trunk/racer/data/theme1/theme1.blend trunk/racer/data/theme2/theme2 trunk/racer/data/theme2/theme2.blend trunk/racer/data/theme4/theme4 trunk/racer/data/theme4/theme4.blend Added Paths: ----------- trunk/libtrack/Mesh/MultiDrawableMesh.cpp trunk/libtrack/Mesh/MultiDrawableMesh.h trunk/racer/data/theme1/cap_end_g.png trunk/racer/data/theme1/cap_start_g.png trunk/racer/data/theme1/jump_g.png trunk/racer/data/theme1/left_90_g.png trunk/racer/data/theme1/right_90_g.png trunk/racer/data/theme1/start_g.png trunk/racer/data/theme1/straight_g.png trunk/racer/data/theme1/straight_h.png trunk/racer/data/theme1/straight_i.png trunk/racer/data/theme1/straight_j.png trunk/racer/data/theme2/Double_straight_g.png trunk/racer/data/theme2/Double_straight_h.png trunk/racer/data/theme2/Double_straight_i.png trunk/racer/data/theme2/From_double_g.png trunk/racer/data/theme2/From_tube_g.png trunk/racer/data/theme2/Intersection_g.png trunk/racer/data/theme2/Start_g.png trunk/racer/data/theme2/Straight_g.png trunk/racer/data/theme2/Straight_h.png trunk/racer/data/theme2/To_double_g.png trunk/racer/data/theme2/To_tube_g.png trunk/racer/data/theme2/Tube_g.png trunk/racer/data/theme2/Tube_h.png trunk/racer/data/theme4/bare_g.png trunk/racer/data/theme4/bare_h.png trunk/racer/data/theme4/bare_i.png trunk/racer/data/theme4/end-cap_g.png trunk/racer/data/theme4/from-bare_g.png trunk/racer/data/theme4/join_g.png trunk/racer/data/theme4/jump_g.png trunk/racer/data/theme4/left-90_g.png trunk/racer/data/theme4/quad-join_g.png trunk/racer/data/theme4/quad-split_g.png trunk/racer/data/theme4/quad_g.png trunk/racer/data/theme4/quad_h.png trunk/racer/data/theme4/quad_i.png trunk/racer/data/theme4/right-90_g.png trunk/racer/data/theme4/split_g.png trunk/racer/data/theme4/start-cap_g.png trunk/racer/data/theme4/start_g.png trunk/racer/data/theme4/straight_g.png trunk/racer/data/theme4/straight_h.png trunk/racer/data/theme4/straight_i.png trunk/racer/data/theme4/straight_j.png trunk/racer/data/theme4/straight_k.png trunk/racer/data/theme4/thin_g.png trunk/racer/data/theme4/to-bare_g.png trunk/racer/data/theme4/tube-entry_g.png trunk/racer/data/theme4/tube-exit_g.png trunk/racer/data/theme4/tube_g.png Removed Paths: ------------- trunk/racer/data/theme1/cap_end.png trunk/racer/data/theme1/cap_start.png trunk/racer/data/theme1/jump.png trunk/racer/data/theme1/left_90.png trunk/racer/data/theme1/right_90.png trunk/racer/data/theme1/start.png trunk/racer/data/theme1/straight.png trunk/racer/data/theme2/Double_straight.png trunk/racer/data/theme2/From_double.png trunk/racer/data/theme2/From_tube.png trunk/racer/data/theme2/Intersection.png trunk/racer/data/theme2/Start.png trunk/racer/data/theme2/Straight.png trunk/racer/data/theme2/To_double.png trunk/racer/data/theme2/To_tube.png trunk/racer/data/theme2/Tube.png trunk/racer/data/theme4/bare.png trunk/racer/data/theme4/end-cap.png trunk/racer/data/theme4/from-bare.png trunk/racer/data/theme4/join.png trunk/racer/data/theme4/jump.png trunk/racer/data/theme4/left-90.png trunk/racer/data/theme4/quad-join.png trunk/racer/data/theme4/quad-split.png trunk/racer/data/theme4/quad.png trunk/racer/data/theme4/right-90.png trunk/racer/data/theme4/split.png trunk/racer/data/theme4/start-cap.png trunk/racer/data/theme4/start.png trunk/racer/data/theme4/straight.png trunk/racer/data/theme4/thin.png trunk/racer/data/theme4/to-bare.png trunk/racer/data/theme4/tube-entry.png trunk/racer/data/theme4/tube-exit.png trunk/racer/data/theme4/tube.png Modified: trunk/libtrack/AxisAlignedBoundingBox.cpp =================================================================== --- trunk/libtrack/AxisAlignedBoundingBox.cpp 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/AxisAlignedBoundingBox.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -2,7 +2,7 @@ * @brief Implement the Track::AxisAlignedBoundingBox class. * @author James Legg */ -/* Copyright © 2009 James Legg. +/* Copyright © 2009, 2010 James Legg. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -102,6 +102,12 @@ return max - min; } +btScalar AxisAlignedBoundingBox::get_volume() const +{ + btVector3 extent = get_extent(); + return extent.x() * extent.y() * extent.z(); +} + AxisAlignedBoundingBox AxisAlignedBoundingBox::transform(btTransform transform) const { // find the AABB of the 8 transoformed corners of the original AABB. Modified: trunk/libtrack/AxisAlignedBoundingBox.h =================================================================== --- trunk/libtrack/AxisAlignedBoundingBox.h 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/AxisAlignedBoundingBox.h 2010-10-10 16:14:58 UTC (rev 348) @@ -2,7 +2,7 @@ * @brief Declare the Track::AxisAlignedBoundingBox class. * @author James Legg */ -/* Copyright © 2009 James Legg. +/* Copyright © 2009, 2010 James Legg. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -85,6 +85,9 @@ /// Get the length of the bounding box in each direction. btVector3 get_extent() const; + /// Calculate the volume of the bounding box. + btScalar get_volume() const; + /** Find the minimum axis aligned bounding box of a transformed bounding * box. * The box may not be minimal for it's original contents, because the Modified: trunk/libtrack/Mesh/Makefile.am =================================================================== --- trunk/libtrack/Mesh/Makefile.am 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/Mesh/Makefile.am 2010-10-10 16:14:58 UTC (rev 348) @@ -1,5 +1,6 @@ noinst_LTLIBRARIES = libmesh.la libmesh_la_SOURCES = BulletMesh.cpp BulletMesh.h\ DrawableMesh.cpp DrawableMesh.h\ - MeshFaces.cpp MeshFaces.h + MeshFaces.cpp MeshFaces.h\ + MultiDrawableMesh.cpp MultiDrawableMesh.h libmesh_la_CPPFLAGS = $(debug_specific_CFLAGS) $(libtrack_CFLAGS) Added: trunk/libtrack/Mesh/MultiDrawableMesh.cpp =================================================================== --- trunk/libtrack/Mesh/MultiDrawableMesh.cpp (rev 0) +++ trunk/libtrack/Mesh/MultiDrawableMesh.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -0,0 +1,204 @@ +/** @file libtrack/Mesh/MultiDrawableMesh.cpp + * @brief Implement the Track::MultiDrawableMesh class. + * @author James Legg + */ +/* Copyright © 2010 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#include "MultiDrawableMesh.h" +#include <Debug.h> + +namespace Track +{ + +/// The quality of meshes selected by MultiDrawable. +/// Higher values have more triangles, and should take longer to render. +btScalar global_quality = 10.0; + +MultiDrawableMesh::MultiDrawableMesh(DrawableMesh::RenderMode render_mode) + : m_render_mode(render_mode) +{ +} + +MultiDrawableMesh::MultiDrawableMesh(std::istream & stream) + : m_render_mode(DrawableMesh::RM_SOLID) +{ + unsigned int count; + stream >> count; + for (unsigned int index = 0; index < count; index++) + { + boost::shared_ptr<Texture> texture(new Texture(stream)); + DrawableMesh mesh(stream, m_render_mode); + add_mesh(mesh, texture); + } +} + +MultiDrawableMesh::~MultiDrawableMesh() +{ +} + +void MultiDrawableMesh::draw() const +{ + // We don't know the scale. + // Draw the highest level of detail. + assert(!m_meshes.empty()); + const MeshLOD & lod = *m_meshes.begin(); + if (m_render_mode == DrawableMesh::RM_SOLID) lod.texture->bind(); + lod.mesh.draw(); +} + +void MultiDrawableMesh::make_cache() const +{ + // cache all meshes + for (std::vector<MeshLOD>::const_iterator it = m_meshes.begin(); + it != m_meshes.end(); + it++) + { + it->mesh.make_cache(); + it->texture->make_cache(); + } +} + +void MultiDrawableMesh::set_render_mode(DrawableMesh::RenderMode new_render_mode) +{ + for (std::vector<MeshLOD>::iterator it = m_meshes.begin(); + it != m_meshes.end(); + it++) + { + it->mesh.set_render_mode(new_render_mode); + } + m_render_mode = new_render_mode; +} + +void MultiDrawableMesh::conditional_draw(const OcclusionTester & occlusion_tester) const +{ + // draw at a sensible resolution if it is on screen. + // Get the bounds of the highest level of detail and check it is + // on the screen first. + assert(!m_meshes.empty()); + const DrawableMesh & highest = m_meshes.begin()->mesh; + if (occlusion_tester(highest.get_bounds()) == OcclusionTester::VS_OUT) + { + // completely off screen, don't draw. + return; + } + // must be at least partially on screen. + draw(occlusion_tester.camera_position.distance2(middle)); +} + +void MultiDrawableMesh::draw(btScalar distance2) const +{ + assert(!m_meshes.empty()); + // Work out what sort of quality we want to use. + // quality should be proportional area of the screen occupied, so + // it is proportional to area_scale and inversely proportional the + // square of the distance from the camera. + btScalar quality = area_scale / (distance2); + quality *= global_quality; + // now pick a suitable mesh. + // There are more meshes at a distance than close by, so start scanning from the back. + for (std::vector<MeshLOD>::const_reverse_iterator it = m_meshes.rbegin(); + it != m_meshes.rend(); + it++) + { + // detailed enough? + if (it->triangle_density > quality) + { + // this is good enough. + if (m_render_mode == DrawableMesh::RM_SOLID) + { + it->texture->bind(); + } + it->mesh.draw(); + return; + } + } + // We could go more detailed than the highest level, but there isn't + // such a mesh available. Just use the highest detailed mesh. + if (m_render_mode == DrawableMesh::RM_SOLID) + { + m_meshes.begin()->texture->bind(); + m_meshes.begin()->mesh.draw(); + } +} + +AxisAlignedBoundingBox MultiDrawableMesh::get_bounds() const +{ + assert(!m_meshes.empty()); + return bounds; +} + +void MultiDrawableMesh::add_mesh(const DrawableMesh & mesh, boost::shared_ptr<Texture> texture) +{ + m_meshes.push_back(MeshLOD(mesh, texture)); + bounds |= mesh.get_bounds(); + if (m_meshes.size() == 1) + { + // first mesh + // find some properties that will be useful later. + AxisAlignedBoundingBox aabb = mesh.get_bounds(); + middle = (aabb.get_min() + aabb.get_max()) / 2.0; + // area scale determines the importance of the mesh. + //area_scale = aabb.get_volume(); + // Use the surface area at the highest detail level. + area_scale = 0; + for (unsigned int f_i = 0; f_i < mesh.get_number_of_faces(); f_i++) + { + const MeshFaces::Face & face = mesh.get_face(f_i); + btVector3 q = mesh.get_vertex_pos(face.fv1.vertex_index); + btVector3 r = mesh.get_vertex_pos(face.fv2.vertex_index); + btVector3 s = mesh.get_vertex_pos(face.fv3.vertex_index); + // technically the area is half of this, but we just want it + // to be to a uniform scale so it doesn't matter. + area_scale += ((q-r).cross(s-r)).length(); + } + } + if (m_meshes.size() > 1) + { + // check they are in a sensible order + int last_faces = m_meshes.begin()->mesh.get_number_of_faces() + 1; + for (std::vector<MeshLOD>::iterator it = m_meshes.begin(); + it != m_meshes.end(); + it++) + { + int this_faces = it->mesh.get_number_of_faces(); + if (last_faces <= this_faces) + { + std::cerr << "MultiDrawableMesh has levels of detail in a weird order:\n"; + std::cerr << "A mesh with " << this_faces << " triangles is supposidly lower detail than a mesh with " << last_faces << " triangles.\n"; + } + last_faces = this_faces; + } + } +} + +const DrawableMesh & MultiDrawableMesh::get_level(unsigned int index) const +{ + assert(index < m_meshes.size()); + return m_meshes[index].mesh; +} + +boost::shared_ptr<Texture> MultiDrawableMesh::get_tex_level(unsigned int index) const +{ + assert(index < m_meshes.size()); + return m_meshes[index].texture; +} + +unsigned int MultiDrawableMesh::get_num_levels() const +{ + return m_meshes.size(); +} + +MultiDrawableMesh::MeshLOD::MeshLOD(const DrawableMesh & mesh, boost::shared_ptr<Texture> texture) + : mesh(mesh) + , texture(texture) + , triangle_density(float(mesh.get_number_of_faces()) / mesh.get_bounds().get_volume()) +{ + //DEBUG_MESSAGE("triangle_density is " << triangle_density); +} + +} // namespace Track. Added: trunk/libtrack/Mesh/MultiDrawableMesh.h =================================================================== --- trunk/libtrack/Mesh/MultiDrawableMesh.h (rev 0) +++ trunk/libtrack/Mesh/MultiDrawableMesh.h 2010-10-10 16:14:58 UTC (rev 348) @@ -0,0 +1,147 @@ +/** @file libtrack/Mesh/MultiDrawableMesh.h + * @brief Declare the Track::MultiDrawableMesh class. + * @author James Legg + */ +/* Copyright © 2010 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#ifndef LIBTRACK_MULTIDRAWABLEMESH_H +#define LIBTRACK_MULTIDRAWABLEMESH_H + +#include "DrawableMesh.h" +#include "../Texture.h" + +#include <istream> + +namespace Track +{ + +extern btScalar global_quality; + +/** Group of several related meshes. + * Replaces a single mesh with several levels of detail that represent + * the same thing. Drawing dynamically picks a mesh with reasonable + * detail. + * + * Add meshes with the add_mesh() function. + */ +class MultiDrawableMesh : public AABBDrawable +{ +public: + + /** Construct an empty group of meshes. + * @param render_mode The method of displaying the mesh. You can set this + * later with set_render_mode() instead. + */ + MultiDrawableMesh(DrawableMesh::RenderMode render_mode = DrawableMesh::RM_SOLID); + + /** Construct from data in a stream. + * Moves the stream on to the end of the MultiDrawableMesh. + */ + MultiDrawableMesh(std::istream & stream); + + virtual ~MultiDrawableMesh(); + + /** Render the mesh using OpenGL commands. + * Expects the texture and matrices to be set up already. + * Must be called outside of a glBegin/glEnd pair. + * Finishes with no change to the matrices, outside of a glBegin/glEnd pair. + * Precondition: At least one mesh must have been added. + */ + virtual void draw() const; + + /** Load the meshes into OpenGL to speed up their first draw. + * Requires the OpenGL context that will be used to draw active. + * Only needs to be called once, before draw is called. + * Calling it after draw is called or multiple times has no additional + * effect. + */ + virtual void make_cache() const; + + /** Control how the mesh is displayed. + * If the new RenderMode is the same as the previous, there is no effect. + * Otherwise this will unset the cache created by a previous call to + * draw() or make_cache(), if any. + * The deafult RenderMode is DrawableMesh::RenderMode::RM_SOLID. + * @param new_render_mode The method used by draw() or make_cache(). + */ + void set_render_mode(DrawableMesh::RenderMode new_render_mode); + + /** Draw a suitable mesh for the view frustrum. + * Precondition: At least one mesh must have been added. + */ + virtual void conditional_draw(const OcclusionTester & occlusion_tester) const; + + /** Draw an appropriate mesh considering the distance from the camera. + * Precondition: At least one mesh must have been added. + * @param distance2 The square of the distance between the mesh's + * centre and the camera. + */ + void draw(btScalar distance2) const; + + /** Return the axis aligned bounding box of the first (most detailed) mesh. + * Precondition: At least one mesh must have been added. + */ + virtual AxisAlignedBoundingBox get_bounds() const; + + /** Add a mesh to the set. + * Call this with all the meshes, in order of most detailed to least + * detailed. + * @param mesh Mesh to add. It will be copied. + * @param texture Shared pointer to the texture to bind before + * drawing the mesh. + */ + void add_mesh(const DrawableMesh & mesh, boost::shared_ptr<Texture> texture); + + /** Get a mesh that has been previously added. + * @param index the index of the mesh to return. The mesh added + * first has index 0. The index must be less than the number + * returned by get_num_levels(). + */ + const DrawableMesh & get_level(unsigned int index) const; + + /** Get a shared pointer to a texture for a mesh that has been added. + * @param index The index of the mesh who's texture should be + * returned. The first mesh added has index 0. index must be less + * than the result of get_num_levels(). + */ + boost::shared_ptr<Texture> get_tex_level(unsigned int index) const; + + /** Return the number of mesh levels added. + * This starts at 0, and increases by one for each call to + * add_mesh(). + */ + unsigned int get_num_levels() const; +protected: + /** Structure to hold a mesh and its level of detail. + */ + struct MeshLOD + { + MeshLOD(const DrawableMesh & mesh, boost::shared_ptr<Texture> texture); + DrawableMesh mesh; + boost::shared_ptr<Texture> texture; + // The number of triangles per cubed spacial unit. + float triangle_density; + }; + /// The meshes to show. + std::vector<MeshLOD> m_meshes; + + AxisAlignedBoundingBox bounds; + + /// Method for drawing the meshes + DrawableMesh::RenderMode m_render_mode; + + /// The position of the centre of the bounding box. + btVector3 middle; + + /** Number approximately proportional the number of pixels covered. + */ + btScalar area_scale; +}; + +} // namespace Track + +#endif /*LIBTRACK_MULTIDRAWABLEMESH_H*/ Modified: trunk/libtrack/OcclusionTester.h =================================================================== --- trunk/libtrack/OcclusionTester.h 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/OcclusionTester.h 2010-10-10 16:14:58 UTC (rev 348) @@ -38,8 +38,11 @@ virtual ~OcclusionTester(); /** Plane normal vectors. * A point p is in view if p dot plane_vector[i] < plane_distance[i] for all planes i. + * Plane 0 should be the front plane. */ btVector3 plane_vectors[5]; + /// The position of the camera + btVector3 camera_position; /** Plane distances from the origin. */ btScalar plane_distances[5]; Modified: trunk/libtrack/Segment.cpp =================================================================== --- trunk/libtrack/Segment.cpp 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/Segment.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -15,10 +15,8 @@ { Segment::Segment(std::istream & source) - // load the texture for the graphics - : texture(source) // load the meshes - , graphics_mesh(source) + : graphics_mesh(source) , ai_floor_mesh(source) , physics_wall_mesh(source) , physics_floor_mesh(source) @@ -69,7 +67,7 @@ return *(connections[index]); } -const TrackMesh<DrawableMesh> & Segment::get_graphics_mesh() const +const TrackMesh<MultiDrawableMesh> & Segment::get_graphics_mesh() const { return graphics_mesh; } @@ -99,12 +97,6 @@ return physics_floor_mesh.get_minimum_y(); } - -const Texture & Segment::get_texture() const -{ - return texture; -} - bool Segment::edges_allowed() const { return m_edges_allowed; Modified: trunk/libtrack/Segment.h =================================================================== --- trunk/libtrack/Segment.h 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/Segment.h 2010-10-10 16:14:58 UTC (rev 348) @@ -21,7 +21,6 @@ #include "TrackMesh.h" #include "Mesh/DrawableMesh.h" -#include "Texture.h" #include "SegmentConnection.h" namespace Track @@ -74,7 +73,7 @@ /** Get the TrackMesh used for the graphics. */ - const TrackMesh<DrawableMesh> & get_graphics_mesh() const; + const TrackMesh<MultiDrawableMesh> & get_graphics_mesh() const; /** Get the TrackMesh used for the ai's floor navigation. */ @@ -97,14 +96,10 @@ /// Get the lowest y position of the vertices in the physics floor mesh. btScalar get_minimum_y() const; - /// Get the texture to bind before drawing the graphics_mesh. - const Texture & get_texture() const; - /// Return true if the mesh could be used along an edge. bool edges_allowed() const; protected: - Texture texture; - TrackMesh<DrawableMesh> graphics_mesh; + TrackMesh<MultiDrawableMesh> graphics_mesh; TrackMesh<> ai_floor_mesh; TrackMesh<> physics_wall_mesh; TrackMesh<> physics_floor_mesh; Modified: trunk/libtrack/Track.cpp =================================================================== --- trunk/libtrack/Track.cpp 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/Track.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -405,7 +405,7 @@ * be used interchangably (i.e. draw() is solid, draw(RM_WIREFRAME) is * wireframe.) */ - const_cast<DrawableMesh&>(theme.get_segment(index).get_graphics_mesh().get_faces()).set_render_mode(DrawableMesh::RM_WIREFRAME); + const_cast<MultiDrawableMesh&>(theme.get_segment(index).get_graphics_mesh().get_faces()).set_render_mode(DrawableMesh::RM_WIREFRAME); } } Modified: trunk/libtrack/TrackMesh.h =================================================================== --- trunk/libtrack/TrackMesh.h 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/TrackMesh.h 2010-10-10 16:14:58 UTC (rev 348) @@ -2,7 +2,7 @@ * @brief Declare the Track::TrackMesh class. * @author James Legg */ -/* Copyright © 2009 James Legg. +/* Copyright © 2009, 2010 James Legg. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -12,6 +12,7 @@ #define LIBTRACK_TRACKMESH_H_ #include "Mesh/MeshFaces.h" +#include "Mesh/MultiDrawableMesh.h" #include "PieceDistortion.h" #include "SegmentConnection.h" #include <Debug.h> @@ -100,15 +101,8 @@ , mesh(source) { // Calculate the length. - std::size_t number_of_vertices = mesh.get_num_vertices(); - for (std::size_t vertex_index = 0; - vertex_index < number_of_vertices; - vertex_index++) - { - btScalar y = mesh.get_vertex_pos(vertex_index).getY(); - if (minimum_y > y) minimum_y = y; - if (maximum_y < y) maximum_y = y; - } + minimum_y = mesh.get_bounds().get_min().y(); + maximum_y = mesh.get_bounds().get_max().y(); length = maximum_y - minimum_y; /// @todo calculate the maximum bend. DEBUG_MESSAGE("Loaded Track::TrackMesh"); @@ -138,6 +132,7 @@ } template <class T> +inline T TrackMesh<T>::get_distorted_faces(PieceDistortion piece_distortion) const { T result; @@ -152,6 +147,32 @@ return result; } +// specialization for MultiDrawableMesh +template <> +inline +MultiDrawableMesh TrackMesh<MultiDrawableMesh>::get_distorted_faces(PieceDistortion piece_distortion) const +{ + MultiDrawableMesh result; + for (unsigned int lod = 0; + lod < mesh.get_num_levels(); + lod++) + { + DrawableMesh out_mesh_level; + const DrawableMesh &in_mesh_level = mesh.get_level(lod); + for (unsigned int vert_index = 0; + vert_index < in_mesh_level.get_num_vertices(); + vert_index++) + { + out_mesh_level.add_vertex(piece_distortion(in_mesh_level.get_vertex_pos(vert_index))); + } + // now copy the faces. + out_mesh_level.copy_faces(in_mesh_level); + // and copy the entire thing to the group of mesh levels. + result.add_mesh(out_mesh_level, mesh.get_tex_level(lod)); + } + return result; +} + template <class T> const T & TrackMesh<T>::get_faces() const { @@ -159,6 +180,7 @@ } template <class T> +inline T TrackMesh<T>::get_distorted_faces(btTransform transform) const { T result; @@ -173,6 +195,32 @@ return result; } +// specialization for MultiDrawableMesh +template <> +inline +MultiDrawableMesh TrackMesh<MultiDrawableMesh>::get_distorted_faces(btTransform transform) const +{ + MultiDrawableMesh result; + for (unsigned int lod = 0; + lod < mesh.get_num_levels(); + lod++) + { + DrawableMesh out_mesh_level; + const DrawableMesh &in_mesh_level = mesh.get_level(lod); + for (unsigned int vert_index = 0; + vert_index < in_mesh_level.get_num_vertices(); + vert_index++) + { + out_mesh_level.add_vertex(transform(in_mesh_level.get_vertex_pos(vert_index))); + } + // now copy the faces. + out_mesh_level.copy_faces(in_mesh_level); + // and copy the entire thing to the group of mesh levels. + result.add_mesh(out_mesh_level, mesh.get_tex_level(lod)); + } + return result; } +} + #endif /*TRACKMESH_H_*/ Modified: trunk/libtrack/path/PathEdge.cpp =================================================================== --- trunk/libtrack/path/PathEdge.cpp 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/path/PathEdge.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -134,9 +134,10 @@ PieceDistortion transform(*this, i, segment->get_length(), segment->get_minimum_y()); - meshes.push_back(boost::shared_ptr<DrawableMesh>( - new DrawableMesh(segment->get_graphics_mesh().get_distorted_faces(transform), render_mode) + meshes.push_back(boost::shared_ptr<MultiDrawableMesh>( + new MultiDrawableMesh(segment->get_graphics_mesh().get_distorted_faces(transform)) )); + meshes.back()->set_render_mode(render_mode); graphics_bounds |= meshes.back()->get_bounds(); } @@ -222,7 +223,7 @@ return btTransform(rotation, translation); } -const boost::shared_ptr<DrawableMesh> PathEdge::get_graphics_mesh(std::size_t index) const +const boost::shared_ptr<MultiDrawableMesh> PathEdge::get_graphics_mesh(std::size_t index) const { assert(index < number_of_repetions); return meshes[index]; @@ -400,7 +401,6 @@ { // draw everything assert(segment); - segment->get_texture().bind(); for (unsigned int i = 0; i < number_of_repetions; i++) { meshes[i]->draw(); @@ -414,13 +414,12 @@ switch (vs) { case OcclusionTester::VS_IN: - // All on screen, draw everything. - draw(); - break; case OcclusionTester::VS_PARTIAL: - // Partially visible, check each mesh individually. + // At least partially visible. + // We use conditional_draw on each mesh individually even if + // completely visible, because MultiDrawableMesh selects + // meshes based on the distance in the occlusion_tester. assert(segment); - segment->get_texture().bind(); for (unsigned int i = 0; i < number_of_repetions; i++) { meshes[i]->conditional_draw(occlusion_tester); @@ -465,7 +464,6 @@ void PathEdge::make_cache() const { - segment->get_texture().make_cache(); for (unsigned int i = 0; i < number_of_repetions; i++) { meshes[i]->make_cache(); Modified: trunk/libtrack/path/PathEdge.h =================================================================== --- trunk/libtrack/path/PathEdge.h 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/path/PathEdge.h 2010-10-10 16:14:58 UTC (rev 348) @@ -136,7 +136,7 @@ * get_number_of_repetitions(). * @todo write similar functions for the other meshes. */ - const boost::shared_ptr<DrawableMesh> get_graphics_mesh(std::size_t index) const; + const boost::shared_ptr<MultiDrawableMesh> get_graphics_mesh(std::size_t index) const; virtual bool is_here(btVector3 start, btVector3 stop, btScalar radius) const; @@ -217,7 +217,7 @@ std::vector<boost::shared_ptr<EditAssist::TrackAttachmentHandle> > m_attachment_handles; btScalar length; unsigned int number_of_repetions; - std::vector<boost::shared_ptr<DrawableMesh> > meshes; + std::vector<boost::shared_ptr<MultiDrawableMesh> > meshes; const Path * path; std::size_t start_vertex_index; std::size_t finish_vertex_index; Modified: trunk/libtrack/path/PathVertex.cpp =================================================================== --- trunk/libtrack/path/PathVertex.cpp 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/path/PathVertex.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -300,17 +300,40 @@ m_transform = btTransform(angle, position); if(segment) { - m_bounds = segment->get_graphics_mesh().get_faces().get_bounds().transform(m_transform); + // Finding an accurate graphics mesh bounding box improves the + // level of detail selection: + // get the bounding box of the transformed graphics mesh. + m_bounds = segment->get_graphics_mesh().get_distorted_faces(m_transform).get_bounds(); + // update data for other meshes: m_nav_mesh = segment->get_ai_mesh().get_distorted_faces(m_transform); m_nav_mesh.set_source(false, get_name()); m_navigation_graph = m_nav_mesh.get_connectivity(); } } +void PathVertex::conditional_draw(const OcclusionTester &occlusion_tester) const +{ + assert(segment); + // test if it is at least partially on the screen + if (occlusion_tester(m_bounds) != OcclusionTester::VS_OUT) + { + glPushMatrix(); + btScalar mat[16]; + mat[15] = 1.0; + m_transform.getOpenGLMatrix(mat); + glMultMatrixf(mat); + // draw a suitable mesh for this distance. + segment->get_graphics_mesh().get_faces().draw( + // square of the distance between this and the camera + occlusion_tester.camera_position.distance2(position) + ); + glPopMatrix(); + } +} + void PathVertex::draw() const { assert(segment); - segment->get_texture().bind(); glPushMatrix(); btScalar mat[16]; mat[15] = 1.0; Modified: trunk/libtrack/path/PathVertex.h =================================================================== --- trunk/libtrack/path/PathVertex.h 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/libtrack/path/PathVertex.h 2010-10-10 16:14:58 UTC (rev 348) @@ -157,6 +157,11 @@ virtual AxisAlignedBoundingBox get_bounds() const; // from Drawable, to make class a not an abstract AABBDrawable. virtual void draw() const; + // from Drawable, overridden in AABDrawable + /** Draw an appropriate mesh considering the distance to the camera. + * Checks if the bounding box is in view, if not no action is taken. + */ + virtual void conditional_draw(const OcclusionTester &occlusion_tester) const; protected: unsigned int file_version; /// The position of the vertex Modified: trunk/python/racer_theme_export.py =================================================================== --- trunk/python/racer_theme_export.py 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/python/racer_theme_export.py 2010-10-10 16:14:58 UTC (rev 348) @@ -29,6 +29,13 @@ # - A mesh object name ending in _w is used for wall collisions. # - A mesh object name ending in _f is used for floor physics, so it can be # driven on. +# additionally, lower level of detail meshes can be provided for the +# graphics meshes. These should have the base name with _h, _i, _j, used +# in order and consecutively. (_h is a mesh slightly lower in detail +# than _g, _i is lower in detail than _h, etc, until no more levels are +# provided.) +# Each graphics mesh at each level of detail needs a texture, which +# should have the name as the mesh object it relates to followed by .png # # The points where edges can be connected are given by the positions of # curves objects with a name ending in _0 to _9. The same prefix as the other @@ -114,10 +121,26 @@ out.write('%i ' % (len(piece_names))) curve_data_names = {} for piece in piece_names: - # texture placeholder - out.write("%s " % format_string(texture_path + '/' + piece + ".png")) - #each required mesh. (graphics / ai / walls / floor). - for name in [piece + x for x in mesh_codes_b]: + # Write the graphics meshes. + # Find the graphics levels of detail created + lods = {} + # most detailed level first + for name in [piece + '_' + x for x in "ghijklmnopqrstuv"]: + if name in export: + lods[name] = export[name] + # the number of levels of detail we found + out.write('%i ' % len(lods.keys())) + # Each graphics mesh level of detail + for name in [piece + '_' + x for x in "ghijklmnopqrstuv"]: + if name in export: + # texture placeholder + ##@todo get the texture assigned to the mesh, and complain earlier if there is more than one. + # currently we just take combine the guessed path, the name of the object, and ".png". + out.write("%s " % format_string(texture_path + '/' + name + ".png")) + write_mesh(out, lods[name]) + + #Each required mesh except for graphics. (ai / walls / floor). + for name in [piece + x for x in ['_a', '_w', '_f']]: #print " Writing " + name write_mesh(out, export[name]) #human readable name Modified: trunk/racer/Engine/CarCamera.cpp =================================================================== --- trunk/racer/Engine/CarCamera.cpp 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/racer/Engine/CarCamera.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -153,6 +153,8 @@ btVector3 bottom_normal = -sideways.cross(btTransform(btQuaternion(sideways, half_fovy))(front_normal)).normalized(); occlusion_tester.plane_vectors[4] = bottom_normal; occlusion_tester.plane_distances[4] = (-location).dot(bottom_normal); + + occlusion_tester.camera_position = centre; } const btVector3 & CarCamera::get_location() const Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/racer/Engine/GameScene.cpp 2010-10-10 16:14:58 UTC (rev 348) @@ -19,6 +19,7 @@ #include "../MainLoop.h" #include "Audio.h" +#include <libtrack/Mesh/MultiDrawableMesh.h> #include <libtrack/Texture.h> #include <libtrack/StartingPosition.h> @@ -322,6 +323,43 @@ m_total_time += milliseconds_elapsed; #endif fps = 1000.0 / float(milliseconds_elapsed); + /*Set limits for quality adjustment. + * We want to ensure a reasonable enough quality for the surrounding + * track so the player can see the walls they are approching even + * with a slow refresh rate. + * Also, if facing empty space on a fast machine, the quality could + * increase way to much, making it very slow to get back to sensible + * value when they turn round. + * Within the limits, quality is adjusted to target 60fps. + */ + btScalar max_quality = 10000000000.0; // 10 billion + ///@todo adjust this per theme? + ///@todo Keep this proportional to viewport size? + btScalar min_quality = 1.5; + /**@todo See if the rate of adaption should be adjusted. + * Too high, and it might cause a lot of flickering, + * to low and it might cause slow refresh rates for too long. + */ + if (fps > 60.0) + { + Track::global_quality *= 1.0 + (fps-60.0) * 0.015625; + if (Track::global_quality > max_quality) + { + Track::global_quality = max_quality; + } + } + else if (fps < 60.0) + { + Track::global_quality *= 1.0 - ((60.0 - fps) * 0.015625); + if (Track::global_quality < min_quality) + { + Track::global_quality = min_quality; + } + } + if (m_total_frames % 64 == 0) ///@todo remove- debug + { + DEBUG_MESSAGE("Quality level is " << Track::global_quality); + } if (paused) { if (m_pause_menu.want_to_quit()) Modified: trunk/racer/Engine/LoadScene.h =================================================================== --- trunk/racer/Engine/LoadScene.h 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/racer/Engine/LoadScene.h 2010-10-10 16:14:58 UTC (rev 348) @@ -126,7 +126,7 @@ track = boost::shared_ptr<Track::Track>(new Track::Track(*track_file, *theme)); track->set_filename(track_filename); - // load theme textures used in the track. + // Cache meshes and textures needed for the track. const Track::Path & path = track->get_path(); typedef boost::graph_traits<Track::Path::Graph>::edge_iterator EdgeIterator; std::pair<EdgeIterator, EdgeIterator> edge_range; @@ -135,7 +135,6 @@ edge_range.first++) { const Track::PathEdge & edge = path.graph[*(edge_range.first)]; - edge.segment->get_texture().make_cache(); edge.make_cache(); } typedef boost::graph_traits<Track::Path::Graph>::vertex_iterator VertexIterator; @@ -145,7 +144,6 @@ vertex_range.first++) { const Track::PathVertex & vertex = path.graph[*(vertex_range.first)]; - vertex.get_segment()->get_texture().make_cache(); vertex.get_segment()->get_graphics_mesh().get_faces().make_cache(); } Modified: trunk/racer/data/Makefile.am =================================================================== --- trunk/racer/data/Makefile.am 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/racer/data/Makefile.am 2010-10-10 16:14:58 UTC (rev 348) @@ -24,13 +24,16 @@ sound/engine.wav\ sound/slide.wav\ theme1/theme1\ -theme1/cap_end.png\ -theme1/cap_start.png\ -theme1/jump.png\ -theme1/left_90.png\ -theme1/right_90.png\ -theme1/start.png\ -theme1/straight.png\ +theme1/cap_end_g.png\ +theme1/cap_start_g.png\ +theme1/jump_g.png\ +theme1/left_90_g.png\ +theme1/right_90_g.png\ +theme1/start_g.png\ +theme1/straight_g.png\ +theme1/straight_h.png\ +theme1/straight_i.png\ +theme1/straight_j.png\ theme1/sky.png.-x\ theme1/sky.png.+x\ theme1/sky.png.-y\ @@ -38,15 +41,19 @@ theme1/sky.png.-z\ theme1/sky.png.+z\ theme2/theme2\ -theme2/Double_straight.png\ -theme2/From_double.png\ -theme2/Intersection.png\ -theme2/Straight.png\ -theme2/To_tube.png\ -theme2/From_tube.png\ -theme2/Start.png\ -theme2/To_double.png\ -theme2/Tube.png\ +theme2/Double_straight_g.png\ +theme2/From_double_g.png\ +theme2/Intersection_g.png\ +theme2/Straight_g.png\ +theme2/To_tube_g.png\ +theme2/From_tube_g.png\ +theme2/Start_g.png\ +theme2/To_double_g.png\ +theme2/Tube_g.png\ +theme2/Double_straight_h.png\ +theme2/Double_straight_i.png\ +theme2/Straight_h.png\ +theme2/Tube_h.png\ theme2/sky.png.-x\ theme2/sky.png.+x\ theme2/sky.png.-y\ @@ -60,25 +67,33 @@ theme4/sky.png.-z\ theme4/sky.png.+z\ theme4/theme4\ -theme4/bare.png\ -theme4/end-cap.png\ -theme4/from-bare.png\ -theme4/join.png\ -theme4/jump.png\ -theme4/left-90.png\ -theme4/quad.png\ -theme4/quad-join.png\ -theme4/quad-split.png\ -theme4/right-90.png\ -theme4/split.png\ -theme4/start.png\ -theme4/start-cap.png\ -theme4/straight.png\ -theme4/thin.png\ -theme4/to-bare.png\ -theme4/tube.png\ -theme4/tube-entry.png\ -theme4/tube-exit.png\ +theme4/bare_g.png\ +theme4/end-cap_g.png\ +theme4/from-bare_g.png\ +theme4/join_g.png\ +theme4/jump_g.png\ +theme4/left-90_g.png\ +theme4/quad_g.png\ +theme4/quad-join_g.png\ +theme4/quad-split_g.png\ +theme4/right-90_g.png\ +theme4/split_g.png\ +theme4/start_g.png\ +theme4/start-cap_g.png\ +theme4/straight_g.png\ +theme4/thin_g.png\ +theme4/to-bare_g.png\ +theme4/tube_g.png\ +theme4/tube-entry_g.png\ +theme4/tube-exit_g.png\ +theme4/bare_h.png\ +theme4/bare_i.png\ +theme4/quad_h.png\ +theme4/quad_i.png\ +theme4/straight_h.png\ +theme4/straight_i.png\ +theme4/straight_j.png\ +theme4/straight_k.png\ tracks/0\ tracks/1\ tracks/2\ Deleted: trunk/racer/data/theme1/cap_end.png =================================================================== (Binary files differ) Copied: trunk/racer/data/theme1/cap_end_g.png (from rev 298, trunk/racer/data/theme1/cap_end.png) =================================================================== (Binary files differ) Deleted: trunk/racer/data/theme1/cap_start.png =================================================================== (Binary files differ) Copied: trunk/racer/data/theme1/cap_start_g.png (from rev 298, trunk/racer/data/theme1/cap_start.png) =================================================================== (Binary files differ) Deleted: trunk/racer/data/theme1/jump.png =================================================================== (Binary files differ) Copied: trunk/racer/data/theme1/jump_g.png (from rev 298, trunk/racer/data/theme1/jump.png) =================================================================== (Binary files differ) Deleted: trunk/racer/data/theme1/left_90.png =================================================================== (Binary files differ) Copied: trunk/racer/data/theme1/left_90_g.png (from rev 298, trunk/racer/data/theme1/left_90.png) =================================================================== (Binary files differ) Deleted: trunk/racer/data/theme1/right_90.png =================================================================== (Binary files differ) Copied: trunk/racer/data/theme1/right_90_g.png (from rev 298, trunk/racer/data/theme1/right_90.png) =================================================================== (Binary files differ) Deleted: trunk/racer/data/theme1/start.png =================================================================== (Binary files differ) Copied: trunk/racer/data/theme1/start_g.png (from rev 298, trunk/racer/data/theme1/start.png) =================================================================== (Binary files differ) Deleted: trunk/racer/data/theme1/straight.png =================================================================== (Binary files differ) Copied: trunk/racer/data/theme1/straight_g.png (from rev 298, trunk/racer/data/theme1/straight.png) =================================================================== (Binary files differ) Added: trunk/racer/data/theme1/straight_h.png =================================================================== (Binary files differ) Property changes on: trunk/racer/data/theme1/straight_h.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/racer/data/theme1/straight_i.png =================================================================== (Binary files differ) Property changes on: trunk/racer/data/theme1/straight_i.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/racer/data/theme1/straight_j.png =================================================================== (Binary files differ) Property changes on: trunk/racer/data/theme1/straight_j.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: trunk/racer/data/theme1/theme1 =================================================================== --- trunk/racer/data/theme1/theme1 2010-09-25 02:29:03 UTC (rev 347) +++ trunk/racer/data/theme1/theme1 2010-10-10 16:14:58 UTC (rev 348) @@ -1,4 +1,4 @@ -6 1 22 data/theme1/sky.png.+z 1 22 data/theme1/sky.png.-z 1 22 data/theme1/sky.png.-x 1 22 data/theme1/sky.png.+x 1 22 data/theme1/sky.png.-y 1 22 data/theme1/sky.png.+y 7 1 24 data/theme1/right_90.png 297 +6 1 22 data/theme1/sky.png.+z 1 22 data/theme1/sky.png.-z 1 22 data/theme1/sky.png.-x 1 22 data/theme1/sky.png.+x 1 22 data/theme1/sky.png.-y 1 22 data/theme1/sky.png.+y 7 1 1 26 data/theme1/right_90_g.png 297 0.0 -6.0 -0.875 4.25 -6.0 -0.125 -4.25 -6.0 -0.125 @@ -1525,7 +1525,7 @@ 16 0.0 0.0 1.0 0 0 17 0.0 0.0 1.0 0 0 16 0.0 0.0 1.0 0 0 -18 1 8 right_90 2 0 0.0 -6.0 0.0 0.0 0.0 1.0 1.9470718370939721e-07 0 -6.0 0.0 0.0 0.0 0.0 0.7071068286895752 0.70710676908493042 1 24 data/theme1/straight.png 231 +18 1 8 right_90 2 0 0.0 -6.0 0.0 0.0 0.0 1.0 1.9470718370939721e-07 0 -6.0 0.0 0.0 0.0 0.0 0.7071068286895752 0.70710676908493042 4 1 26 data/theme1/straight_g.png 231 0.0 4.0 -0.87449997663497925 0.0 -4.0 -0.87449997663497925 -4.25 -4.0 -0.12449999898672104 @@ -2166,7 +2166,543 @@ 218 1.0 0.0 0.0 0.91336256265640259 0.87412755191326141 123 1.0 0.0 0.0 0.95212608575820923 0.83360244333744049 218 1.0 0.0 0.0 0.91378390789031982 0.75029680132865906 -122 10 +122 1 26 data/theme1/straight_h.png 122 +0.0 4.0 -0.87449997663497925 +0.0 -4.0 -0.87449997663497925 +-4.25 -4.0 -0.12449999898672104 +4.25 -4.0 -0.12449999898672104 +-4.25 4.0 -0.12449999898672104 +4.25 4.0 -0.12449999898672104 +-4.0 4.0 0.00050000101327896118 +-4.0 -4.0 0.00050000101327896118 +4.0 -4.0 0.00050000101327896118 +4.0 4.0 0.00050000101327896118 +-4.0 4.0 0.43799999356269836 +4.0 4.0 0.43799999356269836 +-4.25 4.0 0.43799999356269836 +4.25 4.0 0.43799999356269836 +-4.25 -4.0 0.43799999356269836 +4.25 -4.0 0.43799999356269836 +0.0 0.0 -0.87449997663497925 +-4.25 0.0 -0.12449999898672104 +4.25 0.0 -0.12449999898672104 +-4.0 0.0 0.00050000101327896118 +4.0 0.0 0.0 +-4.25 0.0 0.43799999356269836 +4.25 0.0 0.43799999356269836 +-4.25 1.34375 0.43799999356269836 +4.25 1.34375 0.43799999356269836 +-4.0 1.34375 0.43799999356269836 +4.0 1.34375 0.43799999356269836 +4.0 0.0 0.43799999356269836 +4.0 -4.0 0.43799999356269836 +-4.0 -4.0 0.43799999356269836 +-4.25 0.65625 0.43799999356269836 +4.25 0.65625 0.43799999356269836 +-4.0 0.65625 0.43799999356269836 +4.0 0.65625 0.43799999356269836 +-4.0 0.65625 1.187999963760376 +4.0 0.65625 1.187999963760376 +-4.25 0.65625 1.687999963760376 +4.25 0.65625 1.687999963760376 +-4.25 1.34375 1.687999963760376 +4.25 1.34375 1.687999963760376 +-4.0 1.34375 1.187999963760376 +4.0 1.34375 1.187999963760376 +-3.5 0.65625 1.687999963760376 +3.5 0.65625 1.687999963760376 +-3.5 1.34375 1.687999963760376 +3.5 1.34375 1.687999963760376 +-4.0 0.0 0.43799999356269836 +-4.25 0.0 0.43799999356269836 +-4.25 0.0 0.43799999356269836 +-4.0 0.0 0.43799999356269836 +-4.0 0.0 0.43799999356269836 +-4.0 0.0 0.00050000101327896118 +-4.0 0.0 0.00050000101327896118 +-4.0 4.0 0.00050000101327896118 +-4.0 -4.0 0.00050000101327896118 +-4.0 -4.0 0.43799999356269836 +-4.25 -4.0 0.43799999356269836 +-4.25 0.65625 0.43799999356269836 +-4.25 0.65625 0.43799999356269836 +-4.0 0.65625 0.43799999356269836 +-4.0 0.65625 0.43799999356269836 +-4.0 0.0 0.43799999356269836 +-4.0 0.65625 1.187999963760376 +-4.0 0.65625 1.187999963760376 +-3.5 0.65625 1.687999963760376 +-3.5 0.65625 1.687999963760376 +-4.25 0.65625 1.687999963760376 +-3.5 1.34375 1.687999963760376 +-3.5 1.34375 1.687999963760376 +-4.25 1.34375 1.687999963760376 +-4.25 1.34375 1.687999963760376 +-4.25 0.65625 1.687999963760376 +-4.25 1.34375 0.43799999356269836 +-4.25 1.34375 0.43799999356269836 +-4.0 1.34375 0.43799999356269836 +-4.0 1.34375 0.43799999356269836 +-4.0 1.34375 1.187999963760376 +-4.0 4.0 0.43799999356269836 +-4.25 4.0 0.43799999356269836 +-4.0 1.34375 1.187999963760376 +4.25 0.0 0.43799999356269836 +4.25 0.0 0.43799999356269836 +4.0 0.0 0.43799999356269836 +4.0 0.0 0.43799999356269836 +4.0 0.0 0.43799999356269836 +4.0 0.65625 0.43799999356269836 +4.0 0.65625 0.43799999356269836 +4.25 0.65625 0.43799999356269836 +4.25 0.65625 0.43799999356269836 +4.25 0.65625 1.687999963760376 +4.25 0.65625 1.687999963760376 +3.5 0.65625 1.687999963760376 +3.5 0.65625 1.687999963760376 +4.0 0.65625 1.187999963760376 +4.0 0.65625 1.187999963760376 +4.0 1.34375 1.187999963760376 +4.0 1.34375 1.187999963760376 +4.0 1.34375 0.43799999356269836 +4.0 1.34375 0.43799999356269836 +4.25 1.34375 0.43799999356269836 +4.25 1.34375 0.43799999356269836 +4.25 1.34375 1.687999963760376 +4.25 4.0 0.43799999356269836 +4.0 4.0 0.43799999356269836 +3.5 1.34375 1.687999963760376 +4.25 1.34375 1.687999963760376 +3.5 1.34375 1.687999963760376 +4.0 0.0 0.0 +4.0 0.0 0.0 +4.0 4.0 0.00050000101327896118 +4.0 -4.0 0.00050000101327896118 +4.0 -4.0 0.43799999356269836 +4.25 -4.0 0.43799999356269836 +4.25 -4.0 -0.12449999898672104 +4.25 0.0 -0.12449999898672104 +4.25 4.0 -0.12449999898672104 +-4.25 -4.0 -0.12449999898672104 +-4.25 0.0 -0.12449999898672104 +-4.25 4.0 -0.12449999898672104 +0.0 -4.0 -0.87449997663497925 +0.0 0.0 -0.87449997663497925 +0.0 4.0 -0.87449997663497925 +76 +1.0 0.0 -0.0 0.080230705440044403 0.85649567842483521 +75 1.0 0.0 -0.0 0.077403604984283447 0.80629646778106689 +59 1.0 0.0 -0.0 0.0478515625 0.98046875 +53 -1.0 -0.0 -0.0 0.685546875 0.98046875 +109 -1.0 -0.0 -0.0 0.65589576959609985 0.80618490278720856 +85 -1.0 -0.0 -0.0 0.65311181545257568 0.85625869035720825 +98 -0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.98046875 +0 -0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.763671875 +16 -0.17378534376621246 0.0 -0.9847835898399353 0.1337890625 0.763671875 +17 -0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.98046875 +0 -0.17378534376621246 0.0 -0.9847835898399353 0.1337890625 0.763671875 +17 -0.17378534376621246 0.0 -0.9847835898399353 0.1337890625 0.98046875 +4 0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.98046875 +121 0.17378534376621246 0.0 -0.9847835898399353 0.599609375 0.98046875 +5 0.17378534376621246 0.0 -0.9847835898399353 0.599609375 0.763671875 +18 0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.98046875 +121 0.17378534376621246 0.0 -0.9847835898399353 0.599609375 0.763671875 +18 0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.763671875 +120 -0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.763671875 +16 -0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.5458984375 +1 -0.17378534376621246 0.0 -0.9847835898399353 0.1337890625 0.5458984375 +2 -0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.763671875 +16 -0.17378534376621246 0.0 -0.9847835898399353 0.1337890625 0.5458984375 +2 -0.17378534376621246 0.0 -0.9847835898399353 0.1337890625 0.763671875 +17 0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.763671875 +120 0.17378534376621246 0.0 -0.9847835898399353 0.599609375 0.763671875 +18 0.17378534376621246 0.0 -0.9847835898399353 0.599609375 0.5458984375 +3 0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.763671875 +120 0.17378534376621246 0.0 -0.9847835898399353 0.599609375 0.5458984375 +3 0.17378534376621246 0.0 -0.9847835898399353 0.3671875 0.5458984375 +119 3.1250063329935074e-05 -6.2500126659870148e-05 1.0 0.72070330381393433 0.51074230670928955 +20 3.1250063329935074e-05 -6.2500126659870148e-05 1.0 0.9658203125 0.5107421875 +9 3.1250063329935074e-05 -6.2500126659870148e-05 1.0 0.9658203125 0.0205078125 +6 3.1250063329935074e-05 -6.2500126659870148e-05 1.0 0.72070330381393433 0.51074230670928955 +20 3.1250063329935074e-05 -6.2500126659870148e-05 1.0 0.9658203125 0.0205078125 +6 3.1250063329935074e-05 -6.2500126659870148e-05 1.0 0.720703125 0.020508110523223877 +19 3.1250063329935074e-05 6.2500126659870148e-05 1.0 0.47558623552322388 0.5107424259185791 +8 3.1250063329935074e-05 6.2500126659870148e-05 1.0 0.72070330381393433 0.51074230670928955 +20 3.1250063329935074e-05 6.2500126659870148e-05 1.0 0.720703125 0.020508110523223877 +19 3.1250063329935074e-05 6.2500126659870148e-05 1.0 0.47558623552322388 0.5107424259185791 +8 3.1250063329935074e-05 6.2500126659870148e-05 1.0 0.720703125 0.020508110523223877 +19 3.1250063329935074e-05 6.2500126659870148e-05 1.0 0.47558599710464478 0.020508229732513428 +7 -1.0 0.0 -0.0 0.1337890625 0.98046875 +118 -1.0 0.0 -0.0 0.1337890625 0.763671875 +117 -1.0 0.0 -0.0 0.098991371691226959 0.8552698940038681 +73 -1.0 0.0 -0.0 0.1337890625 0.98046875 +118 -1.0 0.0 -0.0 0.098991371691226959 0.8552698940038681 +73 -1.0 0.0 -0.0 0.099657684564590454 0.9827565997838974 +12 1.0 -0.0 0.0 0.599609375 0.98046875 +115 1.0 -0.0 0.0 0.63376092910766602 0.98274828866124153 +13 1.0 -0.0 0.0 0.63432306051254272 0.85512977838516235 +100 1.0 -0.0 0.0 0.599609375 0.98046875 +115 1.0 -0.0 0.0 0.63432306051254272 0.85512977838516235 +100 1.0 -0.0 0.0 0.599609375 0.763671875 +114 -1.0 0.0 -0.0 0.1337890625 0.763671875 +117 -1.0 0.0 -0.0 0.1337890625 0.5458984375 +116 -1.0 0.0 -0.0 0.10361310839653015 0.54566940665245056 +56 -1.0 0.0 -0.0 0.1337890625 0.763671875 +117 -1.0 0.0 -0.0 0.10361310839653015 0.54566940665245056 +56 -1.0 0.0 -0.0 0.09914524108171463 0.76159645617008209 +47 1.0 -0.0 0.0 0.599609375 0.763671875 +114 1.0 -0.0 0.0 0.63414770364761353 0.761819988489151 +80 1.0 -0.0 0.0 0.63004958629608154 0.54605787992477417 +112 1.0 -0.0 0.0 0.599609375 0.763671875 +114 1.0 -0.0 0.0 0.63004958629608154 0.54605787992477417 +112 1.0 -0.0 0.0 0.599609375 0.5458984375 +113 0.0 -0.0 1.0 0.077403604984283447 0.80629646778106689 +32 0.0 -0.0 1.0 0.09646306186914444 0.80640128254890442 +30 0.0 -0.0 1.0 0.09914524108171463 0.76159645617008209 +21 0.0 -0.0 1.0 0.077403604984283447 0.80629646778106689 +32 0.0 -0.0 1.0 0.09914524108171463 0.76159645617008209 +21 0.0 -0.0 1.0 0.077493488788604736 0.76215919852256775 +46 0.0 0.0 1.0 0.65589576959609985 0.80618490278720856 +33 0.0 0.0 1.0 0.65572410821914673 0.76172807812690735 +27 0.0 0.0 1.0 0.63414770364761353 0.761819988489151 +22 0.0 0.0 1.0 0.65589576959609985 0.80618490278720856 +33 0.0 0.0 1.0 0.63414770364761353 0.761819988489151 +22 0.0 0.0 1.0 0.63690477609634399 0.80633352696895599 +31 0.0 -1.0 0.0 0.16155180335044861 0.14899587631225586 +34 0.0 -1.0 0.0 0.19056937098503113 0.2070319652557373 +66 0.0 -1.0 0.0 0.19056986272335052 0.061942338943481445 +58 0.0 -1.0 0.0 0.16155180335044861 0.14899587631225586 +34 0.0 -1.0 0.0 0.19056986272335052 0.061942338943481445 +58 0.0 -1.0 0.0 0.16155198216438293 0.06194227933883667 +60 -0.0 -1.0 0.0 0.91537535190582275 0.67025670409202576 +35 -0.0 -1.0 0.0 0.91537648439407349 0.58286798000335693 +86 -0.0 -1.0 0.0 0.88624680042266846 0.58286756277084351 +88 -0.0 -1.0 0.0 0.91537535190582275 0.67025670409202576 +35 -0.0 -1.0 0.0 0.88624680042266846 0.58286756277084351 +88 -0.0 -1.0 0.0 0.88624566793441772 0.72851502895355225 +37 0.0 1.0 0.0 0.27036893367767334 0.061942577362060547 +23 0.0 1.0 0.0 0.27036875486373901 0.20703202486038208 +70 0.0 1.0 0.0 0.29938632249832153 0.14899647235870361 +40 0.0 1.0 0.0 0.27036893367767334 0.061942577362060547 +23 0.0 1.0 0.0 0.29938632249832153 0.14899647235870361 +40 0.0 1.0 0.0 0.29938685894012451 0.061942756175994873 +25 0.0 1.0 0.0 0.80614036321640015 0.58286678791046143 +24 0.0 1.0 0.0 0.77701073884963989 0.58286640048027039 +26 0.0 1.0 0.0 0.77700972557067871 0.67025542259216309 +41 0.0 1.0 0.0 0.80614036321640015 0.58286678791046143 +24 0.0 1.0 0.0 0.77700972557067871 0.67025542259216309 +41 0.0 1.0 0.0 0.80613946914672852 0.72851482033729553 +105 1.0 -0.0 0.0 0.27036920189857483 0.46321463584899902 +75 1.0 -0.0 0.0 0.27036893367767334 0.37616086006164551 +76 1.0 -0.0 0.0 0.19056960940361023 0.37616115808486938 +63 1.0 -0.0 0.0 0.27036920189857483 0.46321463584899902 +75 1.0 -0.0 0.0 0.19056960940361023 0.37616115808486938 +63 1.0 -0.0 0.0 0.19056987762451172 0.46321499347686768 +59 -1.0 0.0 -0.0 0.80613899230957031 0.9856833815574646 +98 -1.0 0.0 -0.0 0.88624542951583862 0.98568323999643326 +85 -1.0 0.0 -0.0 0.8862452507019043 0.89829438179731369 +94 -1.0 0.0 -0.0 0.80613899230957031 0.9856833815574646 +98 -1.0 0.0 -0.0 0.8862452507019043 0.89829438179731369 +94 -1.0 0.0 -0.0 0.80613881349563599 0.89829446375370026 +96 -1.0 -0.0 0.0 0.19056986272335052 0.061942338943481445 +57 -1.0 -0.0 0.0 0.19056937098503113 0.2070319652557373 +71 -1.0 -0.0 0.0 0.27036875486373901 0.20703202486038208 +69 -1.0 -0.0 0.0 0.19056986272335052 0.061942338943481445 +57 -1.0 -0.0 0.0 0.27036875486373901 0.20703202486038208 +69 -1.0 -0.0 0.0 0.27036893367767334 0.061942577362060547 +73 1.0 0.0 0.0 0.88624680042266846 0.58286756277084351 +87 1.0 0.0 0.0 0.80614036321640015 0.58286678791046143 +100 1.0 0.0 0.0 0.80613946914672852 0.72851482033729553 +101 1.0 0.0 0.0 0.88624680042266846 0.58286756277084351 +87 1.0 0.0 0.0 0.80613946914672852 0.72851482033729553 +101 1.0 0.0 0.0 0.88624566793441772 0.72851502895355225 +90 0.0 0.0 1.0 0.27036875486373901 0.20703202486038208 +38 0.0 0.0 1.0 0.19056937098503113 0.2070319652557373 +36 0.0 0.0 1.0 0.1905694305896759 0.29408591985702515 +42 0.0 0.0 1.0 0.27036875486373901 0.20703202486038208 +38 0.0 0.0 1.0 0.1905694305896759 0.29408591985702515 +42 0.0 0.0 1.0 0.2703687846660614 0.2940858006477356 +44 0.0 -0.0 1.0 0.80613946914672852 0.72851482033729553 +39 0.0 -0.0 1.0 0.80613899230957031 0.81590354442596436 +45 0.0 -0.0 1.0 0.88624531030654907 0.81590361893177032 +92 0.0 -0.0 1.0 0.80613946914672852 0.72851482033729553 +39 0.0 -0.0 1.0 0.88624531030654907 0.81590361893177032 +92 0.0 -0.0 1.0 0.88624566793441772 0.72851502895355225 +89 0.0 1.0 -0.0 0.27036875486373901 0.20703202486038208 +70 0.0 1.0 -0.0 0.357421875 0.20703125 +68 0.0 1.0 -0.0 0.29938632249832153 0.14899647235870361 +40 -0.0 1.0 0.0 0.80613946914672852 0.72851482033729553 +105 -0.0 1.0 0.0 0.77700972557067871 0.67025542259216309 +41 -0.0 1.0 0.0 0.71875 0.728515625 +106 -0.0 -1.0 0.0 0.19056937098503113 0.2070319652557373 +66 -0.0 -1.0 0.0 0.16155180335044861 0.14899587631225586 +34 -0.0 -1.0 0.0 0.103515625 0.20703125 +65 0.0 -1.0 0.0 0.88624566793441772 0.72851502895355225 +37 0.0 -1.0 0.0 0.9736328125 0.728515625 +43 ... [truncated message content] |
From: <jl...@us...> - 2010-09-25 02:29:10
|
Revision: 347 http://racer.svn.sourceforge.net/racer/?rev=347&view=rev Author: jlegg Date: 2010-09-25 02:29:03 +0000 (Sat, 25 Sep 2010) Log Message: ----------- Use GLSL shaders for the track and car body if possible. Modified Paths: -------------- trunk/libtrack/Lighting.cpp trunk/libtrack/Lighting.h trunk/racer/Engine/GameObjects/Car.cpp trunk/racer/Engine/GameScene.cpp Modified: trunk/libtrack/Lighting.cpp =================================================================== --- trunk/libtrack/Lighting.cpp 2010-09-25 02:28:09 UTC (rev 346) +++ trunk/libtrack/Lighting.cpp 2010-09-25 02:29:03 UTC (rev 347) @@ -11,11 +11,18 @@ #include "Lighting.h" +#include <GL/glew.h> #include <GL/gl.h> #include "stream_loader.h" #include "FormErrors.h" +#include <sstream> +#include <cmath> +#include <cstring> + +#include <LinearMath/btTransform.h> + namespace Track { @@ -101,6 +108,9 @@ } Lighting::Lighting() + : m_shader_program_handle(0) + , m_vertex_shader_handle(0) + , m_fragment_shader_handle(0) { m_lights.resize(2); const float l0p[4] = {0.0, 0.0, 1.0, 0.0}; @@ -116,10 +126,12 @@ m_lights[1].colour[i] = l1c[i]; m_ambient_light[i] = amb[i]; } - } Lighting::Lighting(std::istream & in) + : m_shader_program_handle(0) + , m_vertex_shader_handle(0) + , m_fragment_shader_handle(0) { unsigned int file_version; in >> file_version; @@ -138,6 +150,58 @@ } } +Lighting::~Lighting() +{ + free_shaders(); +} + +void Lighting::free_shaders() const +{ + glUseProgram(0); + if (m_shader_program_handle) + { + glDeleteProgram(m_shader_program_handle); + } + glDeleteShader(m_vertex_shader_handle); + glDeleteShader(m_fragment_shader_handle); +} + +/** Print the shader or program's compile log to standard output. + * @param obj The handle for the object who's log will be displayed. + * @param program True for a program, false for a vertex or fragment shader. + */ +void printShaderInfoLog(GLuint obj, bool program = false) +{ + int info_log_length = 0; + if (program) + { + glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &info_log_length); + } else { + glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &info_log_length); + } + + if (info_log_length > 0) + { + char * info_log = (char *)malloc(info_log_length); + if (info_log) + { + int charsWritten; + if (program) + { + glGetProgramInfoLog(obj, info_log_length, &charsWritten, + info_log); + } else { + glGetShaderInfoLog(obj, info_log_length, &charsWritten, + info_log); + } + std::cout << info_log << std::endl; + free(info_log); + } else { + std::cerr << "Error allocating buffer for shader's info log.\n"; + } + } +} + void Lighting::initalise() const { // lights @@ -164,6 +228,142 @@ glFogfv(GL_FOG_COLOR, m_fog.colour); if (m_fog.enabled) glEnable(GL_FOG); else glDisable(GL_FOG); + + // Make an OpenGL shader if it is supported. + if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader) + { + // Free shaders created last time, if any. + free_shaders(); + // Vertex shader + std::stringstream vertex_source; + vertex_source << std::fixed + << "varying vec4 light;" << std::endl; + if (m_fog.enabled) + { + vertex_source << "varying float fog_density;" << std::endl; + } + vertex_source + << "varying vec2 tex_coords;" << std::endl + + << "void main() {" << std::endl + << " gl_Position = ftransform();" << std::endl + << " vec3 N = (gl_NormalMatrix * gl_Normal).xyz;" << std::endl + << " tex_coords = gl_MultiTexCoord0.xy;" << std::endl; + if (m_fog.enabled) + { + vertex_source + // The lens is not wide angle enough to justify the + // square root required for calculating the exact + // distance, which is length(gl_Position). + << " float eye_distance = gl_Position.z;" << std::endl + // The fog should be calcualted like we do in fixed + // function pipeline, with GL_FOG_MODE set to GL_EXP2. + << " fog_density = " + << "exp(eye_distance * eye_distance * " + << -(m_fog.density * m_fog.density) + << ");" << std::endl; + } + vertex_source + << " vec4 light_total = vec4(" << + m_ambient_light[0] << ", " << + m_ambient_light[1] << ", " << + m_ambient_light[2] << ", " << + m_ambient_light[3] << ");" << std::endl + << " vec3 L;" << std::endl + << " float diffuse, specular;" << std::endl; + // add contribution for each light + for (unsigned int light_index = 0; light_index < m_lights.size(); light_index++) + { + vertex_source + << " L= gl_LightSource[" << light_index << "].position.xyz;" << std::endl + << " diffuse = max(dot(N, L), 0.0);" << std::endl + << " specular = pow(max(dot(N, gl_LightSource[" << light_index << "].halfVector.xyz), 0.0), 20.0) * 0.5;" << std::endl + << " light_total += (diffuse + specular) * " << + // light colour + "vec4(" << m_lights[light_index].colour[0] << ", " + << m_lights[light_index].colour[1] << ", " + << m_lights[light_index].colour[2] << ", " + << m_lights[light_index].colour[3] << ");" << std::endl; + } + vertex_source + // the multipler brightens the visible edges in diffuse shader. + << " float multiplier = 1.25 - (N.z * N.z) * 0.5;" << std::endl +// I think this way looks better, but isn't worth the processing cost. +// << " vec3 V = normalize((gl_ModelViewMatrix * gl_Vertex).xyz);" << std::endl +// << " float multiplier = 1 + dot(N, V) * 0.7;" << std::endl + // gamma adjustment to reduce underexposure and overexposure. + << " light = pow(light_total * multiplier, vec4(0.45, 0.45, 0.45, 1));" << std::endl + << "}" << std::endl; + char * vertex_shader_cstr = new char[vertex_source.str().size() + 1]; + std::strcpy(vertex_shader_cstr, vertex_source.str().c_str()); + m_vertex_shader_handle = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(m_vertex_shader_handle, 1, const_cast<const char**>(&vertex_shader_cstr), 0); + glCompileShader(m_vertex_shader_handle); +#ifndef NDEBUG + std::cout << "Vertex shader source:\n"; + std::cout << vertex_shader_cstr; + std::cout << "Vertex shader compile log:\n"; + printShaderInfoLog(m_vertex_shader_handle); +#endif + delete[] vertex_shader_cstr; + + // Fragment shader + std::stringstream fragment_source; + fragment_source + << "varying vec4 light;" << std::endl + << "varying vec2 tex_coords;" << std::endl; + if (m_fog.enabled) + { + fragment_source << "varying float fog_density;" << std::endl; + } + fragment_source << std::fixed + << "uniform sampler2D base_sampler;" << std::endl + + << "void main() {" << std::endl + << " vec4 base_tex = texture2D(base_sampler, tex_coords);" << std::endl + << " gl_FragColor = light * base_tex;" << std::endl; + if (m_fog.enabled) + { + // mix in the fog colour + fragment_source + << " gl_FragColor = mix(" << + //fog colour + "vec4(" << m_fog.colour[0] << ", " + << m_fog.colour[1] << ", " + << m_fog.colour[2] << ", " + << m_fog.colour[3] << ")" + << ", gl_FragColor, fog_density);" << std::endl; + } + // close main function + fragment_source << "}" << std::endl; + char * fragment_shader_cstr = new char[fragment_source.str().size() + 1]; + std::strcpy(fragment_shader_cstr, fragment_source.str().c_str()); + m_fragment_shader_handle = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(m_fragment_shader_handle, 1, const_cast<const char**>(&fragment_shader_cstr), 0); + + glCompileShader(m_fragment_shader_handle); +#ifndef NDEBUG + std::cout << "Fragment shader source:\n"; + std::cout << fragment_shader_cstr; + std::cout << "Fragment shader compile log:\n"; + printShaderInfoLog(m_fragment_shader_handle); +#endif + delete[] fragment_shader_cstr; + + // link the shader program. + m_shader_program_handle = glCreateProgram(); + glAttachShader(m_shader_program_handle, m_fragment_shader_handle); + glAttachShader(m_shader_program_handle, m_vertex_shader_handle); + glLinkProgram(m_shader_program_handle); +#ifndef NDEBUG + std::cout << "Shader program link log:\n"; + printShaderInfoLog(m_shader_program_handle, true); +#endif + + glUseProgram(m_shader_program_handle); + /** @todo stop leaking Shader objects. + */ + } } void Lighting::prepare_render() const @@ -173,7 +373,21 @@ glDisable(GL_FOG); for (unsigned int i = 0; i < m_lights.size(); i++) { - glLightfv(GL_LIGHT0 + i, GL_POSITION, m_lights[i].position); + // normalize light direction + float scale = 0; + for (int c = 0; c < 3; c++) + { + scale += m_lights[i].position[c] * m_lights[i].position[c]; + } + scale = std::sqrt(scale); + float l[4]; + for (int c = 0; c < 3; c++) + { + l[c] = m_lights[i].position[c] / scale; + } + l[3] = 0.0; + // tell OpenGL. + glLightfv(GL_LIGHT0 + i, GL_POSITION, l); } } @@ -205,11 +419,28 @@ m_fog = fog; } +void Lighting::turn_off() const +{ + glUseProgram(0); + glDisable(GL_LIGHTING); + glDisable(GL_FOG); +} + +void Lighting::turn_on() const +{ + glUseProgram(m_shader_program_handle); + // If shader is broken or not compatible with hardware/driver, + // we need the fixed function fallback enabled. + glEnable(GL_LIGHTING); + glEnable(GL_FOG); +} + const FogSettings & Lighting::get_fog() const { return m_fog; } + bool Lighting::operator!=(const Lighting & other) const { if (m_lights != other.m_lights) return true; Modified: trunk/libtrack/Lighting.h =================================================================== --- trunk/libtrack/Lighting.h 2010-09-25 02:28:09 UTC (rev 346) +++ trunk/libtrack/Lighting.h 2010-09-25 02:29:03 UTC (rev 347) @@ -49,7 +49,7 @@ float colour[4]; /** Fog thickness. * Must be > 0. - * The fog is blended with a factor of e^(-density*distance^2). + * The fog is blended with a factor of e^(-(density*distance)^2). */ float density; /// true iff the fog is used at all. @@ -61,6 +61,8 @@ std::ostream & operator<<(std::ostream & destination, const FogSettings & fog_settings); /** Defines lighting and atmospheric effects for a track. + * @todo Support positional lighting. Currently only directional + * lighting will work (where the light's position[3] is 0). */ class Lighting { @@ -71,6 +73,8 @@ /// Create from a serialised record. Lighting(std::istream & in); + virtual ~Lighting(); + /** Set up lighting and fog for a render. * Should be called for each render, between setting the matrices and * drawing geometry. @@ -104,10 +108,35 @@ const FogSettings & get_fog() const; bool operator!=(const Lighting & other) const; + + /** Turn off lighting and fog. + * For drawing things like the sky and HUD. + */ + void turn_off() const; + /** Turn on the lighting and fog. + * Use prepare_render as well if the camera moved. + */ + void turn_on() const; private: std::vector<LightSettings> m_lights; FogSettings m_fog; float m_ambient_light[4]; + /** OpenGL shader for drawing track with this lighting. + * @todo this is mutable because it doesn't affect the lighting, + * but the shader needs to be cached. However, it is required + * for turn_on() and turn_off() to work as expected, so maybe + * turn_on() should create the shader if needed instead of + * initialise()? + */ + mutable unsigned int m_shader_program_handle; + mutable unsigned int m_vertex_shader_handle; + mutable unsigned int m_fragment_shader_handle; + + /** Free shaders and the shader program (if previously created). + * If they were not created (i.e. remain at the inital value 0), + * no action is taken. + */ + void free_shaders() const; }; std::ostream & operator<<(std::ostream & destination, const Lighting & lighting); Modified: trunk/racer/Engine/GameObjects/Car.cpp =================================================================== --- trunk/racer/Engine/GameObjects/Car.cpp 2010-09-25 02:28:09 UTC (rev 346) +++ trunk/racer/Engine/GameObjects/Car.cpp 2010-09-25 02:29:03 UTC (rev 347) @@ -281,7 +281,7 @@ if (used_energy_boost > 0) { // show released energy by making the car glow. - glDisable(GL_LIGHTING); + world.get_track().get_lighting().turn_off(); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_BLEND); glDepthFunc(GL_LEQUAL); @@ -292,13 +292,12 @@ glDisable(GL_BLEND); glDepthFunc(GL_LESS); glColor3ub(255, 255, 255); - glEnable(GL_LIGHTING); } // engine flames // The flame is not affected by lighting, double sided, doesn't // change the depth buffer, doesn't darken the scene behind // when blended over, and reflects the textures alpha value. - glDisable(GL_LIGHTING); + world.get_track().get_lighting().turn_off(); glEnable(GL_BLEND); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE); @@ -347,7 +346,7 @@ glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_MODELVIEW); - glEnable(GL_LIGHTING); + world.get_track().get_lighting().turn_on(); glEnable(GL_CULL_FACE); glDepthMask(GL_TRUE); glDisable(GL_BLEND); Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-09-25 02:28:09 UTC (rev 346) +++ trunk/racer/Engine/GameScene.cpp 2010-09-25 02:29:03 UTC (rev 347) @@ -626,7 +626,7 @@ // set up light and fog track.get_lighting().prepare_render(); - + track.get_lighting().turn_on(); if (!show_debug) { const Track::Path & path = track.get_path(); @@ -635,15 +635,13 @@ // draw sky box using only the camera rotation // (no translation to fake infinite distance) - glDisable(GL_FOG); - glDisable(GL_LIGHTING); + track.get_lighting().turn_off(); glPushMatrix(); glLoadIdentity(); car_cameras[player]->rotation_transform(); sky.draw(); glPopMatrix(); - if (track.get_lighting().get_fog().enabled) glEnable(GL_FOG); - glEnable(GL_LIGHTING); + track.get_lighting().turn_on(); // Cars // Need to be drawn over track & sky because of the blended engine flame. @@ -655,8 +653,7 @@ { m_sky_particles.draw(*(car_cameras[player])); } - glDisable(GL_LIGHTING); - glDisable(GL_FOG); + track.get_lighting().turn_off(); // (Debug) draw the ai navigation graph. #ifndef NDEBUG if (show_debug) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-25 02:28:15
|
Revision: 346 http://racer.svn.sourceforge.net/racer/?rev=346&view=rev Author: jlegg Date: 2010-09-25 02:28:09 +0000 (Sat, 25 Sep 2010) Log Message: ----------- Smooth shade track edge pieces. Some themes need adjusting. Modified Paths: -------------- trunk/libtrack/Mesh/MeshFaces.cpp Modified: trunk/libtrack/Mesh/MeshFaces.cpp =================================================================== --- trunk/libtrack/Mesh/MeshFaces.cpp 2010-09-19 14:48:19 UTC (rev 345) +++ trunk/libtrack/Mesh/MeshFaces.cpp 2010-09-25 02:28:09 UTC (rev 346) @@ -361,21 +361,62 @@ void MeshFaces::copy_faces(const MeshFaces & other) { faces = other.faces; - // Set normals for each face vertex. - for (std::vector<Face>::iterator it = faces.begin(); it != faces.end(); it++) + // Smooth shading: calculate vertex normals for curved objects. + // For each vertex, find the average face normal of all faces using it, and renormalize. + for (unsigned int i = 0; i < vertices_position.size(); i++) { - // solid shading: face normals are consistant across a face - assert(it->fv1.vertex_index < vertices_position.size()); - assert(it->fv2.vertex_index < vertices_position.size()); - assert(it->fv3.vertex_index < vertices_position.size()); - btVector3 & v1 = vertices_position[it->fv1.vertex_index]; - btVector3 & v2 = vertices_position[it->fv2.vertex_index]; - btVector3 & v3 = vertices_position[it->fv3.vertex_index]; - btVector3 n = (v2-v1).cross(v3-v1).normalize(); - it->fv1.normal = n; - it->fv2.normal = n; - it->fv3.normal = n; - /// @todo smooth shading: calculate and use vertex normals for curved objects. + // find where the normal is used + std::vector<btVector3 *> normal_uses; + std::vector<btVector3> face_normals; + for (std::vector<Face>::iterator it = faces.begin(); it != faces.end(); it++) + { + bool use_this_face = false; + if (it->fv1.vertex_index == i) + { + normal_uses.push_back(&(it->fv1.normal)); + use_this_face = true; + } + if (it->fv2.vertex_index == i) + { + normal_uses.push_back(&(it->fv2.normal)); + use_this_face = true; + } + if (it->fv3.vertex_index == i) + { + normal_uses.push_back(&(it->fv3.normal)); + use_this_face = true; + } + if (use_this_face) + { + // calculate face normal. + btVector3 & v1 = vertices_position[it->fv1.vertex_index]; + btVector3 & v2 = vertices_position[it->fv2.vertex_index]; + btVector3 & v3 = vertices_position[it->fv3.vertex_index]; + // not normalised, so we get a weighted average by face area. + btVector3 n = (v2-v1).cross(v3-v1); + face_normals.push_back(n); + } + } + // find average face normal + btVector3 smooth_normal(0, 0, 0); + for (std::vector<btVector3>::iterator it = face_normals.begin(); + it != face_normals.end(); + it++) + { + smooth_normal += *it; + } + smooth_normal.normalize(); + // eh hum + /*btScalar ox = smooth_normal.x(); + smooth_normal.setX(smooth_normal.z()); + smooth_normal.setZ(ox);*/ + // now update all the faces that use this. + for (std::vector<btVector3 *>::iterator it = normal_uses.begin(); + it != normal_uses.end(); + it++) + { + **it = smooth_normal; + } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-19 14:48:25
|
Revision: 345 http://racer.svn.sourceforge.net/racer/?rev=345&view=rev Author: jlegg Date: 2010-09-19 14:48:19 +0000 (Sun, 19 Sep 2010) Log Message: ----------- Safer handeling of meshes. Modified Paths: -------------- trunk/libtrack/Mesh/DrawableMesh.cpp trunk/libtrack/Mesh/MeshFaces.cpp Modified: trunk/libtrack/Mesh/DrawableMesh.cpp =================================================================== --- trunk/libtrack/Mesh/DrawableMesh.cpp 2010-09-18 16:25:13 UTC (rev 344) +++ trunk/libtrack/Mesh/DrawableMesh.cpp 2010-09-19 14:48:19 UTC (rev 345) @@ -99,6 +99,7 @@ do_vert(it->fv3) #undef do_vert } + assert(data.size() == faces.size() * 3 * elements_per_vertex); float * pointer_offset = 0; #ifdef USE_VERTEX_BUFFER_OBJECTS if (GLEW_ARB_vertex_buffer_object) @@ -119,13 +120,13 @@ } #endif glEnableClientState(GL_NORMAL_ARRAY); - glNormalPointer(GL_FLOAT, 8 * sizeof(GLfloat), pointer_offset); + glNormalPointer(GL_FLOAT, elements_per_vertex * sizeof(GLfloat), pointer_offset); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), pointer_offset + 3); + glTexCoordPointer(2, GL_FLOAT, elements_per_vertex * sizeof(GLfloat), pointer_offset + 3); glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, 8 * sizeof(GLfloat), pointer_offset + 5); + glVertexPointer(3, GL_FLOAT, elements_per_vertex * sizeof(GLfloat), pointer_offset + 5); display_list = glGenLists(1); glNewList(display_list, GL_COMPILE); Modified: trunk/libtrack/Mesh/MeshFaces.cpp =================================================================== --- trunk/libtrack/Mesh/MeshFaces.cpp 2010-09-18 16:25:13 UTC (rev 344) +++ trunk/libtrack/Mesh/MeshFaces.cpp 2010-09-19 14:48:19 UTC (rev 345) @@ -365,6 +365,9 @@ for (std::vector<Face>::iterator it = faces.begin(); it != faces.end(); it++) { // solid shading: face normals are consistant across a face + assert(it->fv1.vertex_index < vertices_position.size()); + assert(it->fv2.vertex_index < vertices_position.size()); + assert(it->fv3.vertex_index < vertices_position.size()); btVector3 & v1 = vertices_position[it->fv1.vertex_index]; btVector3 & v2 = vertices_position[it->fv2.vertex_index]; btVector3 & v3 = vertices_position[it->fv3.vertex_index]; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-18 16:25:23
|
Revision: 344 http://racer.svn.sourceforge.net/racer/?rev=344&view=rev Author: jlegg Date: 2010-09-18 16:25:13 +0000 (Sat, 18 Sep 2010) Log Message: ----------- Improve AI navigation meshes. Modified Paths: -------------- trunk/racer/data/theme1/theme1 trunk/racer/data/theme1/theme1.blend trunk/racer/data/theme2/theme2 trunk/racer/data/theme2/theme2.blend trunk/racer/data/theme4/theme4 trunk/racer/data/theme4/theme4.blend Modified: trunk/racer/data/theme1/theme1 =================================================================== --- trunk/racer/data/theme1/theme1 2010-09-13 19:34:41 UTC (rev 343) +++ trunk/racer/data/theme1/theme1 2010-09-18 16:25:13 UTC (rev 344) @@ -834,27 +834,31 @@ 199 0.17378534376621246 0.0 -0.9847835898399353 0.33365923166275024 0.78922702372074127 17 0.17378534376621246 0.0 -0.9847835898399353 0.56996077299118042 0.98599416017532349 199 0.17378534376621246 0.0 -0.9847835898399353 0.57006806135177612 0.79026897251605988 -20 6 -3.25 3.2500019073486328 0.0 +20 7 -6.0 3.2499990463256836 0.0 -6.0 -3.25 0.0 -3.2500009536743164 -3.25 0.0 -3.2500019073486328 -5.9999980926513672 0.0 3.2500019073486328 -5.9999980926513672 0.0 -4 +-4.0 3.0 0.0 +3.0 -4.0 0.0 +5 0.0 0.0 1.0 0 0 +2 0.0 0.0 1.0 0 0 +3 0.0 0.0 1.0 0 0 +4 0.0 0.0 1.0 0 0 0 0.0 0.0 1.0 0 0 1 0.0 0.0 1.0 0 0 2 0.0 0.0 1.0 0 0 -0 0.0 0.0 1.0 0 0 +4 0.0 0.0 1.0 0 0 +6 0.0 0.0 1.0 0 0 2 0.0 0.0 1.0 0 0 -3 0.0 0.0 1.0 0 0 +6 0.0 0.0 1.0 0 0 +5 0.0 0.0 1.0 0 0 +2 0.0 0.0 1.0 0 0 +5 0.0 0.0 1.0 0 0 0 0.0 0.0 1.0 0 0 -3 0.0 0.0 1.0 0 0 -4 0.0 0.0 1.0 0 0 -0 0.0 0.0 1.0 0 0 -4 0.0 0.0 1.0 0 0 -5 104 +2 104 4.0 2.0 0.0 4.0 4.0 0.0 4.0 2.0 0.4375 @@ -3737,27 +3741,31 @@ 93 0.0 -0.0 1.0 0.49371212720870972 0.0030316710472106934 138 0.0 -0.0 1.0 0.39551752805709839 0.0027312636375427246 140 0.0 -0.0 1.0 0.39521628618240356 0.10064077377319336 -93 6 --3.2500009536743164 3.2500019073486328 0.0 +93 7 6.0000014305114746 3.2499995231628418 0.0 6.0000014305114746 -3.2500004768371582 0.0 3.2500014305114746 -3.2500009536743164 0.0 3.2500019073486328 -5.9999990463256836 0.0 -3.2500019073486328 -5.9999990463256836 0.0 -4 +4.0 3.0 0.0 +-3.0 -4.0 0.0 +5 0.0 0.0 1.0 0 0 +1 0.0 0.0 1.0 0 0 0 0.0 0.0 1.0 0 0 -5 0.0 0.0 1.0 0 0 +2 0.0 0.0 1.0 0 0 4 0.0 0.0 1.0 0 0 -0 0.0 0.0 1.0 0 0 -4 0.0 0.0 1.0 0 0 3 0.0 0.0 1.0 0 0 -0 0.0 0.0 1.0 0 0 -3 0.0 0.0 1.0 0 0 2 0.0 0.0 1.0 0 0 0 0.0 0.0 1.0 0 0 +5 0.0 0.0 1.0 0 0 2 0.0 0.0 1.0 0 0 -1 100 +5 0.0 0.0 1.0 0 0 +6 0.0 0.0 1.0 0 0 +2 0.0 0.0 1.0 0 0 +6 0.0 0.0 1.0 0 0 +4 0.0 0.0 1.0 0 0 +2 100 -4.0 2.0 0.0 -2.0 4.0 0.4375 -2.0 4.0 0.0 @@ -4425,10 +4433,10 @@ 3.5762786865234375e-07 1.0000003576278687 0.0 0 4 -3.2000000476837158 1.0 0.0 -3.2000000476837158 -1.0 0.0 --3.2000007629394531 -1.0000001192092896 0.0 --3.1999995708465576 1.0000001192092896 0.0 +2.2000000476837158 1.0 0.0 +2.2000000476837158 -1.0 0.0 +-2.2000007629394531 -1.0000001192092896 0.0 +-2.1999995708465576 1.0000001192092896 0.0 2 0.0 -0.0 1.0 0 0 0 0.0 -0.0 1.0 0 0 @@ -6007,8 +6015,8 @@ 11 4 -3.2000012397766113 -0.2499995231628418 0.0 3.2000012397766113 -0.2500004768371582 0.0 --3.2000012397766113 0.2500004768371582 0.0 -3.2000012397766113 0.2499995231628418 0.0 +-2.2000012397766113 0.2500004768371582 0.0 +2.2000012397766113 0.2499995231628418 0.0 2 0.0 0.0 1.0 0 0 2 0.0 0.0 1.0 0 0 @@ -6244,8 +6252,8 @@ 0 0.17378534376621246 0.0 -0.9847835898399353 0.99222832918167114 0.50342261791229248 1 0.17378534376621246 0.0 -0.9847835898399353 0.86872613430023193 0.49663102626800537 11 4 --3.2000012397766113 -0.2499995231628418 0.0 -3.2000012397766113 -0.2500004768371582 0.0 +-2.2000012397766113 -0.2499995231628418 0.0 +2.2000012397766113 -0.2500004768371582 0.0 -3.2000012397766113 0.2500004768371582 0.0 3.2000012397766113 0.2499995231628418 0.0 2 Modified: trunk/racer/data/theme1/theme1.blend =================================================================== (Binary files differ) Modified: trunk/racer/data/theme2/theme2 =================================================================== --- trunk/racer/data/theme2/theme2 2010-09-13 19:34:41 UTC (rev 343) +++ trunk/racer/data/theme2/theme2 2010-09-18 16:25:13 UTC (rev 344) @@ -12396,10 +12396,10 @@ -4.0 8.0 4.7699522554012219e-08 -2.8284261226654053 8.0 2.8284280300140381 2.6067309590871446e-06 8.0 4.0 -3.875 -3.0 -0.125 -3.8749990463256836 -3.000002384185791 0.125 --3.875 -2.9999990463256836 -0.125 --3.8750004768371582 -2.9999985694885254 0.125 +2.875 -3.0 -0.125 +2.8749990463256836 -3.000002384185791 0.125 +-2.875 -2.9999990463256836 -0.125 +-2.8750004768371582 -2.9999985694885254 0.125 -9.5367431640625e-07 -3.0000004768371582 0.125 0.0 -2.9999995231628418 -0.125 2.6587216854095459 8.0 2.6587216854095459 @@ -12420,170 +12420,170 @@ 2.9426820447042701e-07 -1.7229894399642944 0.21401198208332062 -2.1277980804443359 -1.7360317707061768 0.11872832477092743 2.1277980804443359 -1.7360321283340454 0.11872825026512146 -4.0426545143127441 -1.7457951307296753 -0.15399216115474701 --4.0426545143127441 -1.7457941770553589 -0.1539921760559082 +3.0426545143127441 -1.7457951307296753 -0.15399216115474701 +-3.0426545143127441 -1.7457941770553589 -0.1539921760559082 -5.1221286412328482e-07 -1.7232565879821777 0.46324253082275391 -2.1489498615264893 -1.7367864847183228 0.36188679933547974 2.1489489078521729 -1.7367881536483765 0.36188676953315735 -4.094454288482666 -1.7468115091323853 0.087187841534614563 --4.0944557189941406 -1.7468081712722778 0.087187923491001129 +3.094454288482666 -1.7468115091323853 0.087187841534614563 +-3.0944557189941406 -1.7468081712722778 0.087187923491001129 52 0.92385631799697876 0.0 0.38267159461975098 0 0 7 0.70708334445953369 0.0 0.70708334445953369 0 0 -6 0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +6 0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 22 0.92385631799697876 0.0 0.38267159461975098 0 0 -7 0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 -22 0.82961517572402954 -0.060884427279233932 0.55497908592224121 0 0 +7 0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 +22 0.87728506326675415 -0.075533308088779449 0.47392192482948303 0 0 23 -0.70708334445953369 0.0 0.70708334445953369 0 0 9 -0.92385631799697876 0.0 0.38267159461975098 0 0 -8 -0.82961517572402954 -0.060884427279233932 0.55497908592224121 0 0 +8 -0.87728506326675415 -0.075533308088779449 0.47392192482948303 0 0 24 -0.70708334445953369 0.0 0.70708334445953369 0 0 -9 -0.82961517572402954 -0.060884427279233932 0.55497908592224121 0 0 -24 -0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +9 -0.87728506326675415 -0.075533308088779449 0.47392192482948303 0 0 +24 -0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 25 0.0 0.0 1.0 0 0 10 -0.70708334445953369 0.0 0.70708334445953369 0 0 -9 -0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +9 -0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 25 0.0 0.0 1.0 0 0 -10 -0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +10 -0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 25 0.0 -0.17349772155284882 0.9848322868347168 0 0 26 0.0 0.0 1.0 0 0 10 0.0 -0.17349772155284882 0.9848322868347168 0 0 -26 0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +26 0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 22 0.0 0.0 1.0 0 0 -10 0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +10 0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 22 0.70708334445953369 0.0 0.70708334445953369 0 0 6 -0.92385631799697876 0.0 -0.38267159461975098 0 0 -18 -0.82625812292098999 0.061128575354814529 -0.55992311239242554 0 0 -28 -0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +18 -0.87655264139175415 0.07290872186422348 -0.47569200396537781 0 0 +28 -0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 27 -0.92385631799697876 0.0 -0.38267159461975098 0 0 -18 -0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +18 -0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 27 -0.70708334445953369 0.0 -0.70708334445953369 0 0 17 0.70708334445953369 0.0 -0.70708334445953369 0 0 -20 0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 -30 0.82625812292098999 0.061128575354814529 -0.55992311239242554 0 0 +20 0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 +30 0.87655264139175415 0.07290872186422348 -0.47569200396537781 0 0 29 0.70708334445953369 0.0 -0.70708334445953369 0 0 -20 0.82625812292098999 0.061128575354814529 -0.55992311239242554 0 0 +20 0.87655264139175415 0.07290872186422348 -0.47569200396537781 0 0 29 0.92385631799697876 0.0 -0.38267159461975098 0 0 19 0.0 0.0 -1.0 0 0 21 0.0 0.1746879518032074 -0.9846186637878418 0 0 -31 0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +31 0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 30 0.0 0.0 -1.0 0 0 -21 0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +21 0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 30 0.70708334445953369 0.0 -0.70708334445953369 0 0 20 0.0 0.0 -1.0 0 0 21 -0.70708334445953369 0.0 -0.70708334445953369 0 0 -17 -0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +17 -0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 27 0.0 0.0 -1.0 0 0 -21 -0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +21 -0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 27 0.0 0.1746879518032074 -0.9846186637878418 0 0 31 0.0 0.0 1.0 0 0 4 0.0 0.0 1.0 0 0 -2 0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 +2 0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 12 0.0 0.0 1.0 0 0 -4 0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 +4 0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 12 0.0 -0.14761802554130554 0.98901331424713135 0 0 15 0.0 0.0 1.0 0 0 4 0.0 -0.14761802554130554 0.98901331424713135 0 0 -15 -0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 +15 -0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 14 0.0 0.0 1.0 0 0 -4 -0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 +4 -0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 14 0.0 0.0 1.0 0 0 3 0.0 0.0 -1.0 0 0 0 0.0 0.0 -1.0 0 0 5 0.0 0.14947965741157532 -0.98873865604400635 0 0 16 0.0 0.0 -1.0 0 0 0 0.0 0.14947965741157532 -0.98873865604400635 0 0 -16 -0.047303691506385803 0.062135685235261917 -0.99691760540008545 0 0 +16 -0.096407972276210785 0.069338053464889526 -0.9929196834564209 0 0 11 0.0 0.0 -1.0 0 0 -1 0.047303691506385803 0.062135685235261917 -0.99691760540008545 0 0 +1 0.096407972276210785 0.069338053464889526 -0.9929196834564209 0 0 13 0.0 0.14947965741157532 -0.98873865604400635 0 0 16 0.0 0.0 -1.0 0 0 1 0.0 0.14947965741157532 -0.98873865604400635 0 0 16 0.0 0.0 -1.0 0 0 -5 0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -33 0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +5 0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +33 0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 30 0.0 0.1746879518032074 -0.9846186637878418 0 0 -31 0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 +31 0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 33 0.0 0.1746879518032074 -0.9846186637878418 0 0 31 0.0 0.29578539729118347 -0.95522934198379517 0 0 32 0.0 0.29578539729118347 -0.95522934198379517 0 0 32 0.0 0.1746879518032074 -0.9846186637878418 0 0 -31 -0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 +31 -0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 27 0.0 0.29578539729118347 -0.95522934198379517 0 0 -32 -0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 -27 -0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -34 -0.047303691506385803 0.062135685235261917 -0.99691760540008545 0 0 -11 -0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -34 -0.43864253163337708 0.060853909701108932 -0.8965727686882019 0 0 -35 -0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -34 -0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 -27 -0.82625812292098999 0.061128575354814529 -0.55992311239242554 0 0 -28 -0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -34 -0.82625812292098999 0.061128575354814529 -0.55992311239242554 0 0 -28 -0.43864253163337708 0.060853909701108932 -0.8965727686882019 0 0 -35 0.047303691506385803 0.062135685235261917 -0.99691760540008545 0 0 -13 0.43864253163337708 0.060853909701108932 -0.8965727686882019 0 0 -36 0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -33 0.43864253163337708 0.060853909701108932 -0.8965727686882019 0 0 -36 0.82625812292098999 0.061128575354814529 -0.55992311239242554 0 0 -29 0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 -30 0.43864253163337708 0.060853909701108932 -0.8965727686882019 0 0 -36 0.59990233182907104 0.12021240592002869 -0.79094821214675903 0 0 -30 0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 +32 -0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 +27 -0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +34 -0.096407972276210785 0.069338053464889526 -0.9929196834564209 0 0 +11 -0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +34 -0.58449047803878784 0.085329756140708923 -0.80687886476516724 0 0 +35 -0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +34 -0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 +27 -0.87655264139175415 0.07290872186422348 -0.47569200396537781 0 0 +28 -0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +34 -0.87655264139175415 0.07290872186422348 -0.47569200396537781 0 0 +28 -0.58449047803878784 0.085329756140708923 -0.80687886476516724 0 0 +35 0.096407972276210785 0.069338053464889526 -0.9929196834564209 0 0 +13 0.58449047803878784 0.085329756140708923 -0.80687886476516724 0 0 +36 0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +33 0.58449047803878784 0.085329756140708923 -0.80687886476516724 0 0 +36 0.87655264139175415 0.07290872186422348 -0.47569200396537781 0 0 +29 0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 +30 0.58449047803878784 0.085329756140708923 -0.80687886476516724 0 0 +36 0.63957637548446655 0.12826929986476898 -0.75792717933654785 0 0 +30 0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 33 0.0 -0.14761802554130554 0.98901331424713135 0 0 15 0.0 -0.29450362920761108 0.95562607049942017 0 0 -37 -0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 +37 -0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 38 0.0 -0.29450362920761108 0.95562607049942017 0 0 37 0.0 -0.17349772155284882 0.9848322868347168 0 0 -26 -0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +26 -0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 25 0.0 -0.29450362920761108 0.95562607049942017 0 0 -37 -0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 -25 -0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 +37 -0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 +25 -0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 38 0.0 -0.14761802554130554 0.98901331424713135 0 0 -15 0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 +15 0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 39 0.0 -0.29450362920761108 0.95562607049942017 0 0 -37 0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 -39 0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 +37 0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 +39 0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 22 0.0 -0.17349772155284882 0.9848322868347168 0 0 -26 0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 +26 0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 39 0.0 -0.17349772155284882 0.9848322868347168 0 0 26 0.0 -0.29450362920761108 0.95562607049942017 0 0 -37 0.44349497556686401 -0.060060426592826843 0.89425337314605713 0 0 -40 0.82961517572402954 -0.060884427279233932 0.55497908592224121 0 0 -23 0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 -22 0.44349497556686401 -0.060060426592826843 0.89425337314605713 0 0 -40 0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 -22 0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 -39 -0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 -38 -0.6034424901008606 -0.1194494441151619 0.78838467597961426 0 0 -25 -0.82961517572402954 -0.060884427279233932 0.55497908592224121 0 0 -24 -0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 -38 -0.82961517572402954 -0.060884427279233932 0.55497908592224121 0 0 -24 -0.44349497556686401 -0.060060426592826843 0.89425337314605713 0 0 -41 0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 -12 0.44349497556686401 -0.060060426592826843 0.89425337314605713 0 0 -40 0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 -39 0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 -12 0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 +37 0.58278143405914307 -0.090304270386695862 0.80758076906204224 0 0 +40 0.87728506326675415 -0.075533308088779449 0.47392192482948303 0 0 +23 0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 +22 0.58278143405914307 -0.090304270386695862 0.80758076906204224 0 0 +40 0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 +22 0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 +39 -0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 +38 -0.64110231399536133 -0.12897121906280518 0.75649279356002808 0 0 +25 -0.87728506326675415 -0.075533308088779449 0.47392192482948303 0 0 +24 -0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 +38 -0.87728506326675415 -0.075533308088779449 0.47392192482948303 0 0 +24 -0.58278143405914307 -0.090304270386695862 0.80758076906204224 0 0 +41 0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 +12 0.58278143405914307 -0.090304270386695862 0.80758076906204224 0 0 +40 0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 +39 0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 +12 0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 39 0.0 -0.14761802554130554 0.98901331424713135 0 0 -15 0.047303691506385803 0.062135685235261917 -0.99691760540008545 0 0 -13 0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 +15 0.096407972276210785 0.069338053464889526 -0.9929196834564209 0 0 +13 0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 33 0.0 0.14947965741157532 -0.98873865604400635 0 0 -16 0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 +16 0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 33 0.0 0.29578539729118347 -0.95522934198379517 0 0 32 0.0 0.14947965741157532 -0.98873865604400635 0 0 16 0.0 0.14947965741157532 -0.98873865604400635 0 0 16 0.0 0.29578539729118347 -0.95522934198379517 0 0 -32 -0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -34 -0.047303691506385803 0.062135685235261917 -0.99691760540008545 0 0 +32 -0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +34 -0.096407972276210785 0.069338053464889526 -0.9929196834564209 0 0 11 0.0 0.14947965741157532 -0.98873865604400635 0 0 -16 -0.2306283712387085 0.18567460775375366 -0.95513778924942017 0 0 -34 -0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 +16 -0.29239782691001892 0.19904172420501709 -0.93533128499984741 0 0 +34 -0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 14 0.0 -0.14761802554130554 0.98901331424713135 0 0 -15 -0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 -38 -0.046876430511474609 -0.059999391436576843 0.99707019329071045 0 0 -14 -0.23401592671871185 -0.18387402594089508 0.95464950799942017 0 0 -38 -0.44349497556686401 -0.060060426592826843 0.89425337314605713 0 0 +15 -0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 +38 -0.094058044254779816 -0.068880274891853333 0.9931638240814209 0 0 +14 -0.29288613796234131 -0.19928586483001709 0.93511766195297241 0 0 +38 -0.58278143405914307 -0.090304270386695862 0.80758076906204224 0 0 41 156 -3.875 -3.9999990463256836 -0.125 3.8749990463256836 -4.000002384185791 0.125 @@ -28474,10 +28474,10 @@ -4.0 -8.0 4.7699522554012219e-08 -2.8284261226654053 -8.0 2.8284280300140381 2.6067309590871446e-06 -8.0 4.0 -3.875 3.0 -0.125 -3.8749990463256836 3.000002384185791 0.125 --3.875 2.9999990463256836 -0.125 --3.8750004768371582 2.9999985694885254 0.125 +2.875 3.0 -0.125 +2.8749990463256836 3.000002384185791 0.125 +-2.875 2.9999990463256836 -0.125 +-2.8750004768371582 2.9999985694885254 0.125 -9.5367431640625e-07 3.0000004768371582 0.125 0.0 2.9999995231628418 -0.125 2.6587216854095459 -8.0 2.6587216854095459 @@ -28498,170 +28498,170 @@ 2.9426820447042701e-07 1.7229894399642944 0.21401198208332062 -2.1277980804443359 1.7360317707061768 0.11872832477092743 2.1277980804443359 1.7360321283340454 0.11872825026512146 -4.0426545143127441 1.7457951307296753 -0.15399216115474701 --4.0426545143127441 1.7457941770553589 -0.1539921760559082 +3.0426545143127441 1.7457951307296753 -0.15399216115474701 +-3.0426545143127441 1.7457941770553589 -0.1539921760559082 -5.1221286412328482e-07 1.7232565879821777 0.46324253082275391 -2.1489498615264893 1.7367864847183228 0.36188679933547974 2.1489489078521729 1.7367881536483765 0.36188676953315735 -4.094454288482666 1.7468115091323853 0.087187841534614563 --4.0944557189941406 1.7468081712722778 0.087187923491001129 +3.094454288482666 1.7468115091323853 0.087187841534614563 +-3.0944557189941406 1.7468081712722778 0.087187923491001129 52 0.92385631799697876 0.0 0.38267159461975098 0 0 -7 0.82961517572402954 0.060884427279233932 0.55497908592224121 0 0 -23 0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +7 0.87728506326675415 0.075533308088779449 0.47392192482948303 0 0 +23 0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 22 0.92385631799697876 0.0 0.38267159461975098 0 0 -7 0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +7 0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 22 0.70708334445953369 0.0 0.70708334445953369 0 0 6 -0.70708334445953369 0.0 0.70708334445953369 0 0 -9 -0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 -25 -0.82961517572402954 0.060884427279233932 0.55497908592224121 0 0 +9 -0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 +25 -0.87728506326675415 0.075533308088779449 0.47392192482948303 0 0 24 -0.70708334445953369 0.0 0.70708334445953369 0 0 -9 -0.82961517572402954 0.060884427279233932 0.55497908592224121 0 0 +9 -0.87728506326675415 0.075533308088779449 0.47392192482948303 0 0 24 -0.92385631799697876 0.0 0.38267159461975098 0 0 8 0.0 0.0 1.0 0 0 10 0.0 0.17349772155284882 0.9848322868347168 0 0 -26 -0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +26 -0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 25 0.0 0.0 1.0 0 0 -10 -0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +10 -0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 25 -0.70708334445953369 0.0 0.70708334445953369 0 0 9 0.0 0.0 1.0 0 0 10 0.70708334445953369 0.0 0.70708334445953369 0 0 -6 0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +6 0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 22 0.0 0.0 1.0 0 0 -10 0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +10 0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 22 0.0 0.17349772155284882 0.9848322868347168 0 0 26 0.92385631799697876 0.0 0.38267159461975098 0 0 -18 0.82625812292098999 0.061128575354814529 0.55992311239242554 0 0 -28 0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +18 0.87655264139175415 0.07290872186422348 0.47569200396537781 0 0 +28 0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 27 0.92385631799697876 0.0 0.38267159461975098 0 0 -18 0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +18 0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 27 0.70708334445953369 0.0 0.70708334445953369 0 0 17 -0.70708334445953369 0.0 0.70708334445953369 0 0 -20 -0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 -30 -0.82625812292098999 0.061128575354814529 0.55992311239242554 0 0 +20 -0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 +30 -0.87655264139175415 0.07290872186422348 0.47569200396537781 0 0 29 -0.70708334445953369 0.0 0.70708334445953369 0 0 -20 -0.82625812292098999 0.061128575354814529 0.55992311239242554 0 0 +20 -0.87655264139175415 0.07290872186422348 0.47569200396537781 0 0 29 -0.92385631799697876 0.0 0.38267159461975098 0 0 19 0.0 0.0 1.0 0 0 21 0.0 0.1746879518032074 0.9846186637878418 0 0 -31 -0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +31 -0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 30 0.0 0.0 1.0 0 0 -21 -0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +21 -0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 30 -0.70708334445953369 0.0 0.70708334445953369 0 0 20 0.0 0.0 1.0 0 0 21 0.70708334445953369 0.0 0.70708334445953369 0 0 -17 0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +17 0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 27 0.0 0.0 1.0 0 0 -21 0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +21 0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 27 0.0 0.1746879518032074 0.9846186637878418 0 0 31 0.0 0.0 1.0 0 0 4 0.0 0.14761802554130554 0.98901331424713135 0 0 -15 0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 +15 0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 12 0.0 0.0 1.0 0 0 -4 0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 +4 0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 12 0.0 0.0 1.0 0 0 2 0.0 0.0 1.0 0 0 4 0.0 0.0 1.0 0 0 -3 -0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 +3 -0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 14 0.0 0.0 1.0 0 0 -4 -0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 +4 -0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 14 0.0 0.14761802554130554 0.98901331424713135 0 0 15 0.0 0.0 1.0 0 0 0 0.0 0.0 1.0 0 0 5 0.0 0.14947965741157532 0.98873865604400635 0 0 16 0.0 0.0 1.0 0 0 0 0.0 0.14947965741157532 0.98873865604400635 0 0 -16 0.047303691506385803 0.062135685235261917 0.99691760540008545 0 0 +16 0.096407972276210785 0.069338053464889526 0.9929196834564209 0 0 11 0.0 0.0 1.0 0 0 -1 -0.047303691506385803 0.062135685235261917 0.99691760540008545 0 0 +1 -0.096407972276210785 0.069338053464889526 0.9929196834564209 0 0 13 0.0 0.14947965741157532 0.98873865604400635 0 0 16 0.0 0.0 1.0 0 0 1 0.0 0.14947965741157532 0.98873865604400635 0 0 16 0.0 0.0 1.0 0 0 -5 -0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 -33 -0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +5 -0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 +33 -0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 30 0.0 0.1746879518032074 0.9846186637878418 0 0 -31 -0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 +31 -0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 33 0.0 0.1746879518032074 0.9846186637878418 0 0 31 0.0 0.29578539729118347 0.95522934198379517 0 0 32 0.0 0.29578539729118347 0.95522934198379517 0 0 32 0.0 0.1746879518032074 0.9846186637878418 0 0 -31 0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 +31 0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 27 0.0 0.29578539729118347 0.95522934198379517 0 0 -32 0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 -27 0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 -34 0.047303691506385803 0.062135685235261917 0.99691760540008545 0 0 -11 0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 -34 0.43864253163337708 0.060853909701108932 0.8965727686882019 0 0 -35 0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 -34 0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 -27 0.82625812292098999 0.061128575354814529 0.55992311239242554 0 0 -28 0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 -34 0.82625812292098999 0.061128575354814529 0.55992311239242554 0 0 -28 0.43864253163337708 0.060853909701108932 0.8965727686882019 0 0 -35 -0.047303691506385803 0.062135685235261917 0.99691760540008545 0 0 -13 -0.43864253163337708 0.060853909701108932 0.8965727686882019 0 0 -36 -0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 -33 -0.43864253163337708 0.060853909701108932 0.8965727686882019 0 0 -36 -0.82625812292098999 0.061128575354814529 0.55992311239242554 0 0 -29 -0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 -30 -0.43864253163337708 0.060853909701108932 0.8965727686882019 0 0 -36 -0.59990233182907104 0.12021240592002869 0.79094821214675903 0 0 -30 -0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 +32 0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 +27 0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 +34 0.096407972276210785 0.069338053464889526 0.9929196834564209 0 0 +11 0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 +34 0.58449047803878784 0.085329756140708923 0.80687886476516724 0 0 +35 0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 +34 0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 +27 0.87655264139175415 0.07290872186422348 0.47569200396537781 0 0 +28 0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 +34 0.87655264139175415 0.07290872186422348 0.47569200396537781 0 0 +28 0.58449047803878784 0.085329756140708923 0.80687886476516724 0 0 +35 -0.096407972276210785 0.069338053464889526 0.9929196834564209 0 0 +13 -0.58449047803878784 0.085329756140708923 0.80687886476516724 0 0 +36 -0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 +33 -0.58449047803878784 0.085329756140708923 0.80687886476516724 0 0 +36 -0.87655264139175415 0.07290872186422348 0.47569200396537781 0 0 +29 -0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 +30 -0.58449047803878784 0.085329756140708923 0.80687886476516724 0 0 +36 -0.63957637548446655 0.12826929986476898 0.75792717933654785 0 0 +30 -0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 33 0.0 0.14761802554130554 0.98901331424713135 0 0 -15 -0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 +15 -0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 38 0.0 0.29450362920761108 0.95562607049942017 0 0 37 0.0 0.29450362920761108 0.95562607049942017 0 0 -37 -0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 -38 -0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +37 -0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 +38 -0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 25 0.0 0.29450362920761108 0.95562607049942017 0 0 -37 -0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 +37 -0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 25 0.0 0.17349772155284882 0.9848322868347168 0 0 26 0.0 0.14761802554130554 0.98901331424713135 0 0 15 0.0 0.29450362920761108 0.95562607049942017 0 0 -37 0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 -39 0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 +37 0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 +39 0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 39 0.0 0.29450362920761108 0.95562607049942017 0 0 37 0.0 0.17349772155284882 0.9848322868347168 0 0 -26 0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 +26 0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 39 0.0 0.17349772155284882 0.9848322868347168 0 0 -26 0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 -22 0.44349497556686401 0.060060426592826843 0.89425337314605713 0 0 -40 0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 -39 0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 -22 0.44349497556686401 0.060060426592826843 0.89425337314605713 0 0 -40 0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 -22 0.82961517572402954 0.060884427279233932 0.55497908592224121 0 0 -23 -0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 -38 -0.44349497556686401 0.060060426592826843 0.89425337314605713 0 0 -41 -0.82961517572402954 0.060884427279233932 0.55497908592224121 0 0 -24 -0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 -38 -0.82961517572402954 0.060884427279233932 0.55497908592224121 0 0 -24 -0.6034424901008606 0.1194494441151619 0.78838467597961426 0 0 -25 0.047303691506385803 0.062135685235261917 0.99691760540008545 0 0 +26 0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 +22 0.58278143405914307 0.090304270386695862 0.80758076906204224 0 0 +40 0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 +39 0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 +22 0.58278143405914307 0.090304270386695862 0.80758076906204224 0 0 +40 0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 +22 0.87728506326675415 0.075533308088779449 0.47392192482948303 0 0 +23 -0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 +38 -0.58278143405914307 0.090304270386695862 0.80758076906204224 0 0 +41 -0.87728506326675415 0.075533308088779449 0.47392192482948303 0 0 +24 -0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 +38 -0.87728506326675415 0.075533308088779449 0.47392192482948303 0 0 +24 -0.64110231399536133 0.12897121906280518 0.75649279356002808 0 0 +25 0.096407972276210785 0.069338053464889526 0.9929196834564209 0 0 11 0.0 0.14947965741157532 0.98873865604400635 0 0 -16 0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 +16 0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 34 0.0 0.14947965741157532 0.98873865604400635 0 0 16 0.0 0.29578539729118347 0.95522934198379517 0 0 -32 0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 -34 -0.047303691506385803 0.062135685235261917 0.99691760540008545 0 0 -13 -0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 +32 0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 +34 -0.096407972276210785 0.069338053464889526 0.9929196834564209 0 0 +13 -0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 33 0.0 0.14947965741157532 0.98873865604400635 0 0 16 0.0 0.14947965741157532 0.98873865604400635 0 0 -16 -0.2306283712387085 0.18567460775375366 0.95513778924942017 0 0 +16 -0.29239782691001892 0.19904172420501709 0.93533128499984741 0 0 33 0.0 0.29578539729118347 0.95522934198379517 0 0 -32 0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 +32 0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 12 0.0 0.14761802554130554 0.98901331424713135 0 0 -15 0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 -39 0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 -12 0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 -39 0.44349497556686401 0.060060426592826843 0.89425337314605713 0 0 -40 -0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 -14 -0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 +15 0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 +39 0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 +12 0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 +39 0.58278143405914307 0.090304270386695862 0.80758076906204224 0 0 +40 -0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 +14 -0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 38 0.0 0.14761802554130554 0.98901331424713135 0 0 -15 -0.046876430511474609 0.059999391436576843 0.99707019329071045 0 0 -14 -0.44349497556686401 0.060060426592826843 0.89425337314605713 0 0 -41 -0.23401592671871185 0.18387402594089508 0.95464950799942017 0 0 +15 -0.094058044254779816 0.068880274891853333 0.9931638240814209 0 0 +14 -0.58278143405914307 0.090304270386695862 0.80758076906204224 0 0 +41 -0.29288613796234131 0.19928586483001709 0.93511766195297241 0 0 38 156 -3.875 3.9999990463256836 -0.125 3.8749990463256836 4.000002384185791 0.125 @@ -43752,7 +43752,7 @@ 923 -0.8733176589012146 0.17371135950088501 0.45506149530410767 0.58914214372634888 0.14507198333740234 1525 -0.70601516962051392 0.0 -0.70815151929855347 0.57846128940582275 0.14369601011276245 923 -0.89043855667114258 0.0 0.45506149530410767 0.57849299907684326 0.14510172605514526 -922 156 +922 144 2.8284270763397217 -2.8284268379211426 1.0000001192092896 4.0 5.2054855359529029e-07 1.0 2.8284270763397217 2.8284273147583008 0.99999988079071045 @@ -43827,11 +43827,11 @@ 0.0 -18.0 -0.125 0.0 -20.0 -0.125 0.0 -22.0 -0.125 --3.0635690689086914 22.0 -0.125 --3.7356247901916504 19.999998092651367 -0.125 --4.9030799865722656 17.999998092651367 -0.125 --7.3623876571655273 15.999999046325684 -0.125 --9.9839563369750977 13.999998092651367 -0.125 +-2.8772580623626709 22.0 -0.125 +-3.2170512676239014 19.999998092651367 -0.125 +-4.0120396614074707 17.999998092651367 -0.125 +-5.0678873062133789 15.999999046325684 -0.125 +-6.5764126777648926 13.999998092651367 -0.125 -4.5421552658081055 11.999998092651367 -0.125 -2.7000000476837158 23.999998092651367 -0.125 0.0 22.0 -0.125 @@ -43841,7 +43841,7 @@ 0.0 14.0 -0.125 4.5286250114440918 11.999999046325684 -0.125 0.0 24.0 -0.125 -0.0 11.999998092651367 -0.125 +0.0 9.9999980926513672 -0.125 -9.7993086001224583e-07 22.0 0.125 -5.6765304634609492e-07 20.0 0.125 0.0 18.0 0.125 @@ -43849,30 +43849,30 @@ 0.0 14.0 0.125 -4.5286240577697754 11.999999046325684 0.125 -9.5367431640625e-07 24.0 0.125 -2.813568115234375 22.000001907348633 0.125 -3.4856243133544922 20.000003814697266 0.125 -4.6530795097351074 18.000003814697266 0.125 -7.1123876571655273 16.000003814697266 0.125 -9.7339563369750977 14.000002861022949 0.125 +2.642460823059082 22.000001907348633 0.125 +3.0017554759979248 20.000003814697266 0.125 +3.8074719905853271 18.000003814697266 0.125 +4.8958001136779785 16.000003814697266 0.125 +6.411738395690918 14.000002861022949 0.125 4.5421576499938965 12.000001907348633 0.125 2.6999998092651367 24.000003814697266 0.125 -9.5367431640625e-07 12.000000953674316 0.125 --9.9786806106567383 13.999999046325684 0.125 --7.3577299118041992 15.999998092651367 0.125 --4.8991093635559082 17.999998092651367 0.125 --3.7356266975402832 19.999998092651367 0.125 --3.0635700225830078 22.0 0.125 -2.8135690689086914 22.0 -0.125 -3.4856252670288086 20.0 -0.125 -4.6491084098815918 18.0 -0.125 -7.1077289581298828 16.0 -0.125 -9.7286815643310547 14.000000953674316 -0.125 --10.949999809265137 11.999999046325684 0.125 +9.5367431640625e-07 10.000000953674316 0.125 +-6.5729374885559082 13.999999046325684 0.125 +-5.0646810531616211 15.999998092651367 0.125 +-4.0087904930114746 17.999998092651367 0.125 +-3.217052698135376 19.999998092651367 0.125 +-2.8772590160369873 22.0 0.125 +2.6424617767333984 22.0 -0.125 +3.0017561912536621 20.0 -0.125 +3.804222583770752 18.0 -0.125 +4.8925933837890625 16.0 -0.125 +6.4082636833190918 14.000000953674316 -0.125 +-8.9724140167236328 11.999999046325684 0.125 -2.7000000476837158 23.999996185302734 0.125 -10.95000171661377 12.000001907348633 0.125 --10.949997901916504 11.999998092651367 -0.125 +8.9724149703979492 12.000001907348633 0.125 +-8.972412109375 11.999998092651367 -0.125 2.6999998092651367 24.0 -0.125 -10.949999809265137 12.0 -0.125 +8.9724140167236328 12.0 -0.125 -4.5421552658081055 -12.000001907348633 -0.125 0.0 -12.000001907348633 -0.125 -10.949997901916504 -12.000002861022949 -0.125 @@ -43882,34 +43882,22 @@ 7.4880013465881348 -2.1194711052885395e-07 -0.125 6.9180102348327637 -2.8655345439910889 -0.125 5.2948160171508789 -5.2948160171508789 -0.125 -2.8655345439910889 -6.9180111885070801 -0.125 -1.1306566420898889e-06 -7.4880013465881348 -0.125 --2.865532398223877 -6.9180116653442383 -0.125 -5.2948145866394043 -5.2948174476623535 -0.125 -6.9180102348327637 -2.865534782409668 -0.125 -7.4880013465881348 2.0465733996388735e-07 -0.125 -6.9180102348327637 2.8655352592468262 -0.125 -5.2948141098022461 5.2948174476623535 -0.125 --2.865530252456665 6.9180121421813965 -0.125 -4.8798005991557147e-06 7.4880013465881348 -0.125 -2.86553955078125 6.9180088043212891 -0.125 5.2948217391967773 5.2948107719421387 0.125 6.9180140495300293 2.865525484085083 0.125 7.4880013465881348 -1.0324036338715814e-05 0.125 6.918006420135498 -2.8655447959899902 0.125 5.2948079109191895 -5.294825553894043 0.125 -2.8655216693878174 -6.9180159568786621 0.125 --1.4460815691563766e-05 -7.488001823425293 0.125 --2.8655486106872559 -6.9180049896240234 0.125 -5.294827938079834 -5.2948040962219238 0.125 -6.9180173873901367 -2.8655178546905518 0.125 -7.4880013465881348 1.8597593225422315e-05 0.125 -6.9180030822753906 2.8655519485473633 0.125 -5.2948007583618164 5.2948307991027832 0.125 --2.8655128479003906 6.9180192947387695 0.125 -2.4041828510235064e-05 7.4880008697509766 0.125 -2.8655571937561035 6.9180006980895996 0.125 -188 +172 0.70708334445953369 -0.70708334445953369 0.0 0 0 0 0.70708334445953369 -0.70708334445953369 0.0 0 0 8 1.0 0.0 0.0 0 0 @@ -44318,142 +44306,70 @@ 101 0.0 0.0 1.0 0 0 93 0.0 0.0 1.0 0 0 101 0.0 0.0 1.0 0 0 -100 -0.0 0.0 1.0 0 0 -60 -0.0 0.0 1.0 0 0 -146 -0.0 0.0 1.0 0 0 -147 -0.0 0.0 1.0 0 0 -60 -0.0 0.0 1.0 0 0 -147 -0.0 0.0 1.0 0 0 -67 -0.0 0.0 -1.0 0 0 -120 -0.0 0.0 -1.0 0 0 -131 -0.0 0.0 -1.0 0 0 -130 -0.0 0.0 -1.0 0 0 -120 -0.0 0.0 -1.0 0 0 -130 -0.0 0.0 -1.0 0 0 -121 0.0 0.0 1.0 0 0 -94 0.0 0.0 1.0 0 0 -153 0.0 0.0 1.0 0 0 -154 0.0 0.0 1.0 0 0 -94 0.0 0.0 1.0 0 0 -154 0.0 0.0 1.0 0 0 -103 0.0 0.0 -1.0 0 0 -79 0.0 0.0 -1.0 0 0 -88 0.0 0.0 -1.0 0 0 -138 0.0 0.0 -1.0 0 0 -79 0.0 0.0 -1.0 0 0 -138 0.0 0.0 -1.0 0 0 -137 0.0 -0.0 1.0 0 0 -101 0.0 -0.0 1.0 0 0 -103 0.0 -0.0 1.0 0 0 -154 0.0 -0.0 1.0 0 0 -101 0.0 -0.0 1.0 0 0 -154 0.0 -0.0 1.0 0 0 -155 0.0 0.0 -1.0 0 0 -86 0.0 0.0 -1.0 0 0 -139 0.0 0.0 -1.0 0 0 -138 0.0 0.0 -1.0 0 0 -86 0.0 0.0 -1.0 0 0 -138 0.0 0.0 -1.0 0 0 -88 -0.0 0.0 -1.0 0 0 -121 -0.0 0.0 -1.0 0 0 -130 -0.0 0.0 -1.0 0 0 -129 -0.0 0.0 -1.0 0 0 -121 -0.0 0.0 -1.0 0 0 -129 -0.0 0.0 -1.0 0 0 -123 0.0 0.0 1.0 0 0 -60 0.0 0.0 1.0 0 0 -66 0.0 0.0 1.0 0 0 -145 0.0 0.0 1.0 0 0 -60 0.0 0.0 1.0 0 0 -145 0.0 0.0 1.0 0 0 -146 0.0 -0.0 1.0 0 0 +100 0.0 -0.0 1.0 0 0 94 0.0 -0.0 1.0 0 0 -152 0.0 -0.0 1.0 0 0 -153 0.0 -0.0 1.0 0 0 -94 0.0 -0.0 1.0 0 0 -151 0.0 -0.0 1.0 0 0 -152 0.0 0.0 1.0 0 0 +142 0.0 -0.0 1.0 0 0 +143 0.0 0.0 1.0 0 0 67 0.0 0.0 1.0 0 0 -147 0.0 0.0 1.0 0 0 -148 0.0 0.0 1.0 0 0 -67 0.0 0.0 1.0 0 0 -148 0.0 0.0 1.0 0 0 -149 -0.0 -0.0 -1.0 0 0 +139 0.0 0.0 1.0 0 0 +140 -0.0 -0.0 -1.0 0 0 120 -0.0 -0.0 -1.0 0 0 -132 -0.0 -0.0 -1.0 0 0 -131 -0.0 -0.0 -1.0 0 0 -120 -0.0 -0.0 -1.0 0 0 -133 -0.0 -0.0 -1.0 0 0 -132 0.0 0.0 -1.0 0 0 +130 -0.0 -0.0 -1.0 0 0 +129 0.0 0.0 -1.0 0 0 79 0.0 0.0 -1.0 0 0 -137 0.0 0.0 -1.0 0 0 -136 0.0 0.0 -1.0 0 0 -79 0.0 0.0 -1.0 0 0 -136 0.0 0.0 -1.0 0 0 -135 0.0 0.0 -1.0 0 0 +133 0.0 0.0 -1.0 0 0 +132 0.0 0.0 -1.0 0 0 86 0.0 0.0 -1.0 0 0 -124 0.0 0.0 -1.0 0 0 -139 0.0 -0.0 1.0 0 0 -101 0.0 -0.0 1.0 0 0 -155 0.0 -0.0 1.0 0 0 -140 0.0 0.0 -1.0 0 0 -86 0.0 0.0 -1.0 0 0 125 0.0 0.0 -1.0 0 0 -124 -0.0 0.0 1.0 0 0 -66 -0.0 0.0 1.0 0 0 -144 -0.0 0.0 1.0 0 0 -145 0.0 -0.0 -1.0 0 0 -123 0.0 -0.0 -1.0 0 0 -129 0.0 -0.0 -1.0 0 0 -128 0.0 0.0 -1.0 0 0 +124 0.0 0.0 -1.0 0 0 123 0.0 0.0 -1.0 0 0 128 0.0 0.0 -1.0 0 0 127 -0.0 0.0 1.0 0 0 66 -0.0 0.0 1.0 0 0 -143 -0.0 0.0 1.0 0 0 -144 0.0 0.0 1.0 0 0 +137 -0.0 0.0 1.0 0 0 +138 0.0 0.0 1.0 0 0 38 0.0 0.0 1.0 0 0 67 0.0 0.0 1.0 0 0 -149 0.0 0.0 1.0 0 0 +140 0.0 0.0 1.0 0 0 38 0.0 0.0 1.0 0 0 -149 0.0 0.0 1.0 0 0 +140 0.0 0.0 1.0 0 0 114 0.0 0.0 -1.0 0 0 117 0.0 0.0 -1.0 0 0 -133 0.0 0.0 -1.0 0 0 +130 0.0 0.0 -1.0 0 0 120 0.0 0.0 -1.0 0 0 117 0.0 0.0 -1.0 0 0 120 0.0 0.0 -1.0 0 0 122 0.0 -0.0 1.0 0 0 94 0.0 -0.0 1.0 0 0 114 0.0 -0.0 1.0 0 0 -151 0.0 0.0 -1.0 0 0 +142 0.0 0.0 -1.0 0 0 79 0.0 0.0 -1.0 0 0 -135 0.0 0.0 -1.0 0 0 +132 0.0 0.0 -1.0 0 0 +117 0.0 0.0 -1.0 0 0 +114 0.0 0.0 -1.0 0 0 +140 0.0 0.0 -1.0 0 0 +141 0.0 0.0 1.0 0 0 117 0.0 0.0 1.0 0 0 +131 0.0 0.0 1.0 0 0 +130 0.0 0.0 1.0 0 0 114 0.0 0.0 1.0 0 0 -149 0.0 0.0 1.0 0 0 -150 0.0 0.0 -1.0 0 0 +141 0.0 0.0 1.0 0 0 +142 0.0 0.0 -1.0 0 0 117 0.0 0.0 -1.0 0 0 -134 0.0 0.0 -1.0 0 0 -133 0.0 0.0 1.0 0 0 -114 0.0 0.0 1.0 0 0 -150 0.0 0.0 1.0 0 0 -151 0.0 0.0 -1.0 0 0 -117 0.0 0.0 -1.0 0 0 -135 0.0 0.0 -1.0 0 0 -134 -0.0 0.0 -1.0 0 0 +132 0.0 0.0 -1.0 0 0 +131 -0.0 0.0 -1.0 0 0 86 -0.0 0.0 -1.0 0 0 119 -0.0 0.0 -1.0 0 0 125 0.0 0.0 1.0 0 0 101 0.0 0.0 1.0 0 0 -140 0.0 0.0 1.0 0 0 -141 0.0 0.0 1.0 0 0 +134 0.0 0.0 1.0 0 0 +135 0.0 0.0 1.0 0 0 101 0.0 0.0 1.0 0 0 -141 0.0 0.0 1.0 0 0 +135 0.0 0.0 1.0 0 0 116 0.0 -0.0 1.0 0 0 116 0.0 -0.0 1.0 0 0 -141 0.0 -0.0 1.0 0 0 -142 0.0 0.0 -1.0 0 0 +135 0.0 -0.0 1.0 0 0 +136 0.0 0.0 -1.0 0 0 119 0.0 0.0 -1.0 0 0 126 0.0 0.0 -1.0 0 0 125 0.0 -0.0 -1.0 0 0 @@ -44468,13 +44384,37 @@ 127 -0.0 0.0 1.0 0 0 35 -0.0 0.0 1.0 0 0 116 -0.0 0.0 1.0 0 0 -142 -0.0 0.0 1.0 0 0 +136 -0.0 0.0 1.0 0 0 35 -0.0 0.0 1.0 0 0 -142 -0.0 0.0 1.0 0 0 -143 -0.0 0.0 1.0 0 0 +136 -0.0 0.0 1.0 0 0 +137 -0.0 0.0 1.0 0 0 35 -0.0 0.0 1.0 0 0 -143 -0.0 0.0 1.0 0 0 -66 264 +137 -0.0 0.0 1.0 0 0 +66 0.0 -0.0 1.0 0 0 +94 0.0 -0.0 1.0 0 0 +143 0.0 -0.0 1.0 0 0 +103 0.0 0.0 -1.0 0 0 +79 0.0 0.0 -1.0 0 0 +88 0.0 0.0 -1.0 0 0 +133 0.0 -0.0 1.0 0 0 +101 0.0 -0.0 1.0 0 0 +103 0.0 -0.0 1.0 0 0 +134 0.0 0.0 -1.0 0 0 +86 0.0 0.0 -1.0 0 0 +124 0.0 0.0 -1.0 0 0 +88 -0.0 -0.0 -1.0 0 0 +120 -0.0 -0.0 -1.0 0 0 +129 -0.0 -0.0 -1.0 0 0 +121 -0.0 0.0 -1.0 0 0 +121 -0.0 0.0 -1.0 0 0 +128 -0.0 0.0 -1.0 0 0 +123 0.0 0.0 1.0 0 0 +60 0.0 0.0 1.0 0 0 +66 0.0 0.0 1.0 0 0 +138 -0.0 -0.0 1.0 0 0 +60 -0.0 -0.0 1.0 0 0 +139 -0.0 -0.0 1.0 0 0 +67 264 11.518750190734863 -12.0 -0.125 3.875 -24.0 -0.125 -3.875 -23.999998092651367 -0.125 Modified: trunk/racer/data/theme2/theme2.blend =================================================================== (Binary files differ) Modified: trunk/racer/data/theme4/theme4 =================================================================== --- trunk/racer/data/theme4/theme4 2010-09-13 19:34:41 UTC (rev 343) +++ trunk/racer/data/theme4/theme4 2010-09-18 16:25:13 UTC (rev 344) @@ -3,10 +3,10 @@ 3.5762786865234375e-07 1.0000003576278687 0.0 0 4 -2.5 1.0 0.0 --2.5 1.0 0.0 -2.5 -1.0 0.0 --2.5 -1.0 0.0 +1.5 1.0 0.0 +-1.5 1.0 0.0 +1.5 -1.0 0.0 +-1.5 -1.0 0.0 2 -0.0 0.0 1.0 0.89610475301742554 0.60156464576721191 2 -0.0 0.0 1.0 0.90266120433807373 0.48445338010787964 @@ -297,10 +297,10 @@ 61 -0.38267159461975098 0.0 0.92385631799697876 0.0046763010323047638 0.99999995440913025 29 0.0 0.0 1.0 0.50233817100524902 0.99999997720456513 60 4 --2.625 -0.25 0.0 -2.625 -0.25 0.0 --2.625 0.25 0.0 -2.625 0.25 0.0 +-1.5 -0.25 0.0 +1.5 -0.25 0.0 +-2.0 0.25 0.0 +2.0 0.25 0.0 2 0.0 0.0 1.0 0.0046763010323047638 0.99999995440913025 0 0.0 0.0 1.0 1.0 1.0 @@ -1051,17 +1051,17 @@ 64 0.10675374418497086 -0.53672903776168823 0.83693963289260864 0.050798878073692322 0.52145811915397644 71 0.62187564373016357 -0.62187564373016357 0.47593614459037781 0.012862203642725945 0.54120489954948425 73 11 --2.5 -3.0 0.0 -2.5 -3.0 0.0 --2.5 -2.75 0.0 -2.5 -2.75 0.0 -2.75 2.5 0.0 -2.75 -2.5 0.0 -3.0 2.5 0.0 -3.0 -2.4999997615814209 0.0 -2.573223352432251 -2.573223352432251 0.0 --2.5 2.0 0.0 --2.0 2.5 0.0 +-2.0 -3.0 0.0 +2.0 -3.0 0.0 +-2.0 -2.75 0.0 +2.0 -2.25 0.0 +2.75 2.0 0.0 +2.25 -2.0 0.0 +3.0 2.0 0.0 +3.0 -1.9999997615814209 0.0 +2.073223352432251 -2.073223352432251 0.0 +-2.0 0.0 0.0 +0.0 2.0 0.0 9 0.0 0.0 1.0 0.46370020508766174 0.49057817459106445 0 0.0 0.0 1.0 0.94648909568786621 0.49057817459106445 @@ -2092,22 +2092,22 @@ 118 0.0 0.0 1.0 0.51282012462615967 0.20512884855270386 19 0.0 0.0 1.0 0.25641006231307983 0.20512884855270386 119 16 -2.5 -1.0 3.0 --2.5 -1.0 3.0 -2.5 0.0 3.0 --2.5 0.0 3.0 -3.0000002384185791 1.7114274442064925e-08 2.4999995231628418 -2.9999997615814209 1.8825701886271418e-07 -2.5000004768371582 -3.0000004768371582 -1.0 2.4999995231628418 -2.9999997615814209 -0.99999982118606567 -2.5000004768371582 --2.5000009536743164 -0.9999997615814209 -2.9999995231628418 -2.4999990463256836 -0.99999982118606567 -3.0000009536743164 --2.5000009536743164 2.0537127909392439e-07 -2.9999995231628418 -2.4999990463256836 2.0537127909392439e-07 -3.0000004768371582 --3.0000007152557373 1.882569478084406e-07 -2.4999985694885254 --2.9999992847442627 1.7114231809500779e-08 2.5000014305114746 --3.0000011920928955 -0.99999988079071045 -2.4999985694885254 --2.9999990463256836 -0.99999994039535522 2.5000014305114746 +2.0 -1.0 3.0 +-2.0 -1.0 3.0 +2.0 0.0 3.0 +-2.0 0.0 3.0 +3.0000002384185791 1.7114274442064925e-08 1.9999995231628418 +2.9999997615814209 1.8825701886271418e-07 -2.0000004768371582 +3.0000004768371582 -1.0 1.9999995231628418 +2.9999997615814209 -0.99999982118606567 -2.0000004768371582 +-2.0000009536743164 -0.9999997615814209 -2.9999995231628418 +1.9999990463256836 -0.99999982118606567 -3.0000009536743164 +-2.0000009536743164 2.0537127909392439e-07 -2.9999995231628418 +1.9999990463256836 2.0537127909392439e-07 -3.0000004768371582 +-3.0000007152557373 1.882569478084406e-07 -1.9999985694885254 +-2.9999992847442627 1.7114231809500779e-08 2.0000014305114746 +-3.0000011920928955 -0.99999988079071045 -1.9999985694885254 +-2.9999990463256836 -0.99999994039535522 2.0000014305114746 8 0.0 0.0 1.0 2.4453170155425141e-08 0.30769282579421997 1 0.0 0.0 1.0 0.51282012462615967 0.30769282579421997 @@ -2115,24 +2115,24 @@ 2 0.0 0.0 1.0 2.4453170155425141e-08 0.30769282579421997 1 0.0 0.0 1.0 0.51282012462615967 0.20512884855270386 2 0.0 0.0 1.0 0.0 0.20512884855270386 -3 1.0 1.1920928955078125e-07 -1.1920928955078125e-07 0.58974331617355347 0.48717987537384033 -6 1.0 1.1920928955078125e-07 -1.1920928955078125e-07 0.58974331617355347 0.99999997554682984 -7 1.0 1.1920928955078125e-07 -1.1920928955078125e-07 0.69230735301971436 1.0 -5 1.0 1.1920928955078125e-07 -1.1920928955078125e-07 0.58974331617355347 0.48717987537384033 -6 1.0 1.1920928955078125e-07 -1.1920928955078125e-07 0.69230735301971436 1.0 -5 1.0 1.1920928955078125e-07 -1.1920928955078125e-07 0.69230735301971436 0.48717987537384033 -4 -2.384185791015625e-07 2.384185791015625e-07 -1.0 0.51282012462615967 0.30769288539886475 -9 -2.384185791015625e-07 2.384185791015625e-07 -1.0 0.0 0.30769282579421997 -8 -2.384185791015625e-07 2.384185791015625e-07 -1.0 0.0 0.41025680303573608 -10 -2.384185791015625e-07 2.384185791015625e-07 -1.0 0.51282012462615967 0.30769288539886475 -9 -2.384185791015625e-07 2.384185791015625e-07 -1.0 0.0 0.41025680303573608 -10 -2.384185791015625e-07 2.384185791015625e-07 -1.0 0.51282012462615967 0.41025692224502563 -11 -1.0 1.1920928955078125e-07 3.5762786865234375e-07 0.79487133026123047 1.0 -14 -1.0 1.1920928955078125e-07 3.5762786865234375e-07 0.79487133026123047 0.48717987537384033 -15 -1.0 1.1920928955078125e-07 3.5762786865234375e-07 0.69230735301971436 0.48717987537384033 -13 -1.0 1.1920928955078125e-07 3.5762786865234375e-07 0.79487133026123047 1.0 -14 -1.0 1.1920928955078125e-07 3.5762786865234375e-07 0.69230735301971436 0.48717987537384033 -13 -1.0 1.1920928955078125e-07 3.5762786865234375e-07 0.69230735301971436 1.0 +3 1.0 1.1920928955078125e-07 -1.4901161193847656e-07 0.58974331617355347 0.48717987537384033 +6 1.0 1.1920928955078125e-07 -1.4901161193847656e-07 0.58974331617355347 0.99999997554682984 +7 1.0 1.1920928955078125e-07 -1.4901161193847656e-07 0.69230735301971436 1.0 +5 1.0 1.1920928955078125e-07 -1.4901161193847656e-07 0.58974331617355347 0.48717987537384033 +6 1.0 1.1920928955078125e-07 -1.4901161193847656e-07 0.69230735301971436 1.0 +5 1.0 1.1920928955078125e-07 -1.4901161193847656e-07 0.69230735301971436 0.48717987537384033 +4 -2.9802322387695312e-07 2.384185791015625e-07 -1.0 0.51282012462615967 0.30769288539886475 +9 -2.9802322387695312e-07 2.384185791015625e-07 -1.0 0.0 0.30769282579421997 +8 -2.9802322387695312e-07 2.384185791015625e-07 -1.0 0.0 0.41025680303573608 +10 -2.9802322387695312e-07 2.384185791015625e-07 -1.0 0.51282012462615967 0.30769288539886475 +9 -2.9802322387695312e-07 2.384185791015625e-07 -1.0 0.0 0.41025680303573608 +10 -2.9802322387695312e-07 2.384185791015625e-07 -1.0 0.51282012462615967 0.41025692224502563 +11 -1.0 1.1920928955078125e-07 4.4703483581542969e-07 0.79487133026123047 1.0 +14 -1.0 1.1920928955078125e-07 4.4703483581542969e-07 0.79487133026123047 0.48717987537384033 +15 -1.0 1.1920928955078125e-07 4.4703483581542969e-07 0.69230735301971436 0.48717987537384033 +13 -1.0 1.1920928955078125e-07 4.4703483581542969e-07 0.79487133026123047 1.0 +14 -1.0 1.1920928955078125e-07 4.4703483581542969e-07 0.69230735301971436 0.48717987537384033 +13 -1.0 1.1920928955078125e-07 4.4703483581542969e-07 0.69230735301971436 1.0 12 112 2.5 -1.0 2.625 2.75 -1.0 2.875 @@ -3221,16 +3221,16 @@ 91 0.70708334445953369 0.0 0.70708334445953369 0.0080924127250909805 0.70226675271987915 48 0.0 0.0 1.0 0.38637292385101318 0.70213758945465088 94 10 -2.5 -1.0 0.0 --2.5 -1.0 0.0 -2.5 1.0 0.0 --2.5 1.0 0.0 --2.5 -0.5 0.0 --2.5 0.0 0.0 --2.5 0.5 0.0 -2.5 -0.5 0.0 -2.5 0.0 0.0 -2.5 0.5 0.0 +2.0 -1.0 0.0 +-2.0 -1.0 0.0 +2.0 1.0 0.0 +-2.0 1.0 0.0 +-2.0 -0.5 0.0 +-2.0 0.0 0.0 +-2.0 0.5 0.0 +2.0 -0.5 0.0 +2.0 0.0 0.0 +2.0 0.5 0.0 8 0.0 0.0 1.0 0.75656074285507202 0.99974202617886476 0 0.0 0.0 1.0 0.75925827026367188 0.90049749612808228 @@ -4102,10 +4102,10 @@ 45 -0.25968199968338013 0.73448896408081055 0.6269417405128479 0.0 1.0 3 0.0 0.0 1.0 0.4979737401008606 0.99999962386328889 44 4 -2.625 -0.25 0.0 --2.625 -0.25 0.0 -2.625 0.25 0.0 --2.625 0.25 0.0 +2.0 -0.25 0.0 +-2.0 -0.25 0.0 +1.5 0.25 0.0 +-1.5 0.25 0.0 2 0.0 0.0 1.0 0.99594748020172119 0.99999924772657778 0 0.0 0.0 1.0 1.0 0.73882830142974854 @@ -4488,12 +4488,12 @@ 1 0.0 -0.0 -1.0 0.010854505002498627 0.99999997290697884 31 0.0 -0.0 -1.0 0.9928937554359436 1.0 30 6 -2.5 0.5 0.0 --2.5 0.5 0.0 -2.5 0.0 0.0 --2.5 0.0 0.0 -2.5 0.75 0.0 --2.5 0.75 0.0 +1.5 0.5 0.0 +-1.5 0.5 0.0 +2.0 0.0 0.0 +-2.0 0.0 0.0 +1.5 0.75 0.0 +-1.5 0.75 0.0 4 -0.0 0.0 1.0 0.90266072750091553 0.74798184633255005 2 -0.0 0.0 1.0 0.90591955184936523 0.62957334518432617 @@ -4868,12 +4868,12 @@ 1 0.0 0.0 -1.0 0.0074207568541169167 0.72070172429084778 31 0.0 0.0 -1.0 0.0 0.84944179654121399 9 6 -2.5 -0.5 0.0 --2.5 -0.5 0.0 -2.5 0.0 0.0 --2.5 0.0 0.0 -2.5 -0.75 0.0 --2.5 -0.75 0.0 +1.5 -0.5 0.0 +-1.5 -0.5 0.0 +2.0 0.0 0.0 +-2.0 0.0 0.0 +1.5 -0.75 0.0 +-1.5 -0.75 0.0 4 0.0 0.0 1.0 0.0037143449299037457 0.72070169448852539 1 0.0 0.0 1.0 0.91280525922775269 0.72070169448852539 @@ -6820,779 +6820,381 @@ 278 0.14001892507076263 0.98019349575042725 0.14001892507076263 0.74584758281707764 0.10099053382873535 265 0.0 0.0 1.0 0.59331041574478149 0.26739472150802612 278 0.70708334445953369 0.0 0.70708334445953369 0.74584758281707764 0.26739472150802612 -276 125 -2.4519631862640381 0.0 -0.48772549629211426 -2.3096988201141357 0.0 -0.95670819282531738 +276 72 +2.0 4.0 -1.0 +-2.0 4.0 -1.0 +2.4519631862640381 -1.5 -0.48772549629211426 2.0786740779876709 0.0 -1.3889254331588745 -1.7677669525146484 0.0 -1.7677669525146484 1.3889254331588745 0.0 -2.0786740779876709 -0.95670819282531738 0.0 -2.3096990585327148 0.48772519826889038 0.0 -2.4519631862640381 --8.1460342471473268e-07 0.0 -2.5 -0.48772680759429932 0.0 -2.451962947845459 --0.95670968294143677 0.0 -2.3096983432769775 -1.3889267444610596 0.0 -2.0786733627319336 --1.7677681446075439 0.0 -1.7677658796310425 -2.0786750316619873 0.0 -1.3889241218566895 --2.309699535369873 0.0 -0.95670676231384277 --2.4519636631011963 0.0 -0.48772358894348145 +-2.4519636631011963 -1.5 -0.48772358894348145 +2.0786740779876709 -3.0 1.3889255523681641 +2.4519631862640381 -3.0 0.48772597312927246 2.4519631862640381 -3.0 -0.48772549629211426 -2.3096988201141357 -3.0 -0.95670819282531738 2.0786740779876709 -3.0 -1.3889254331588745 -1.7677669525146484 -3.0 -1.7677669525146484 1.3889254331588745 -3.0 -2.0786740779876709 -0.95670819282531738 -3.0 -2.3096990585327148 0.48772519826889038 -3.0 -2.4519631862640381 --8.1460342471473268e-07 -3.0 -2.5 -0.48772680759429932 -3.0 -2.451962947845459 --0.95670968294143677 -3.0 -2.3096983432769775 -1.3889267444610596 -3.0 -2.0786733627319336 --1.7677681446075439 -3.0 -1.7677658796310425 -2.0786750316619873 -3.0 -1.3889241218566895 --2.309699535369873 -3.0 -0.95670676231384277 -2.4519636631011963 -3.0 -0.48772358894348145 -2.5 4.0 -1.0 --2.5 4.0 -1.0 --4.2870613015111303e-07 2.0189623832702637 -1.8581943511962891 +-2.4519627094268799 -3.0 0.48772835731506348 +-2.0786724090576172 -3.0 1.3889281749725342 +-1.3889228105545044 -3.0 2.0786762237548828 +0.48772978782653809 -3.0 2.4519619941711426 +1.3889294862747192 -3.0 2.0786714553833008 +0.41690480709075928 2.0180008411407471 -1.829851508140564 -0.41690567135810852 2.0180008411407471 -1.8298513889312744 --0.82128447294235229 2.0153064727783203 -1.7464876174926758 -1.2016302347183228 2.0114200115203857 -1.6129150390625 --1.5483328104019165 2.0071380138397217 -1.4365801811218262 -1.8543063402175903 2.0033485889434814 -1.2268087863922119 --2.1153144836425781 2.0007991790771484 -0.99391728639602661 --2.3299846649169922 1.9998283386230469 -0.7482830286026001 -0.41690480709075928 2.0180008411407471 -1.829851508140564 -0.82128375768661499 2.0153064727783203 -1.746488094329834 1.201629638671875 2.0114200115203857 -1.6129155158996582 -1.5483320951461792 2.0071380138397217 -1.4365806579589844 1.8543057441711426 2.0033485889434814 -1.2268095016479492 -2.1153140068054199 2.0007991790771484 -0.99391800165176392 -2.3299846649169922 1.9998283386230469 -0.7482839822769165 --6.338108846648538e-07 1.0217286348342896 -2.2398452758789062 --2.1643040781782474e-07 3.0147953033447266 -1.4437016248703003 +0.46231561899185181 1.0206084251403809 -2.1991028785705566 +0.36654376983642578 3.014052152633667 -1.4290573596954346 -0.46231687068939209 1.0206083059310913 -2.1991028785705566 -0.36654418706893921 3.014052152633667 -1.429057240486145 --0.90704786777496338 1.0174781084060669 -2.0790896415710449 --0.72652333974838257 3.0119678974151611 -1.386012077331543 -1.3179038763046265 1.0129902362823486 -1.8862282037734985 -1.0739283561706543 3.0089559555053711 -1.3171346187591553 --1.6810215711593628 1.0080935955047607 -1.6305339336395264 --1.4037894010543823 3.0056281089782715 -1.2264070510864258 -1.9858005046844482 1.0038199424743652 -1.3246920108795166 -1.7125296592712402 3.0026695728302002 -1.11882483959198 --2.2253601551055908 1.0009917020797729 -0.98294496536254883 --1.9981570243835449 3.0006611347198486 -0.99991387128829956 --2.3969962596893311 0.99989533424377441 -0.62005162239074707 --2.2606925964355469 2.9997811317443848 -0.87612521648406982 -0.46231561899185181 1.0206084251403809 -2.1991028785705566 -0.36654376983642578 3.014052152633667 -1.4290573596954346 -0.90704679489135742 1.0174781084060669 -2.0790901184082031 -0.72652298212051392 3.0119678974151611 -1.3860123157501221 1.3179029226303101 1.0129902362823486 -1.8862287998199463 1.0739281177520752 3.0089561939239502 -1.3171348571777344 -1.6810206174850464 1.0080935955047607 -1.6305347681045532 -1.4037891626358032 3.0056281089782715 -1.2264072895050049 1.9857995510101318 1.0038200616836548 -1.3246930837631226 1.7125294208526611 3.0026695728302002 -1.1188250780105591 -2.2253594398498535 1.0009917020797729 -0.98294603824615479 -1.9981567859649658 3.0006611347198486 -0.99991422891616821 -2.3969962596893311 0.99989533424377441 -0.62005305290222168 -2.2606925964355469 2.9997811317443848 -0.87612569332122803 --5.3303580216379487e-07 1.5237787961959839 -2.0595698356628418 --7.2987683097380796e-07 0.51578664779663086 -2.397162914276123 --3.230452136904205e-07... [truncated message content] |
From: <jl...@us...> - 2010-09-13 19:34:47
|
Revision: 343 http://racer.svn.sourceforge.net/racer/?rev=343&view=rev Author: jlegg Date: 2010-09-13 19:34:41 +0000 (Mon, 13 Sep 2010) Log Message: ----------- When quiting GameScene from the pause menu, restore the sound. Also fix type error in car ranking. Modified Paths: -------------- trunk/racer/Engine/GameScene.cpp Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-09-12 23:28:30 UTC (rev 342) +++ trunk/racer/Engine/GameScene.cpp 2010-09-13 19:34:41 UTC (rev 343) @@ -340,6 +340,10 @@ break; case UI::PauseMenu::R_QUIT: // quit selected, then confirm pressed (space or enter). + // resume the paused audio so it functions correctly + // if something else wants to use it. + Audio::get_instance().resume(); + // abort this game. main_loop->pop_scene(); break; } @@ -1053,7 +1057,7 @@ // rank cars in reverse order of car_positions, as it is sorted from the // lowest distance around to course to the highest: highest is the leader. int rank = cars.size() - 1; - for (std::map<btScalar, std::size_t>::iterator it = car_positions.begin(); + for (std::multimap<btScalar, std::size_t>::iterator it = car_positions.begin(); it != car_positions.end(); it++) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-12 23:28:36
|
Revision: 342 http://racer.svn.sourceforge.net/racer/?rev=342&view=rev Author: jlegg Date: 2010-09-12 23:28:30 +0000 (Sun, 12 Sep 2010) Log Message: ----------- Add sound files to install target. Modified Paths: -------------- trunk/racer/data/Makefile.am Modified: trunk/racer/data/Makefile.am =================================================================== --- trunk/racer/data/Makefile.am 2010-09-12 11:40:20 UTC (rev 341) +++ trunk/racer/data/Makefile.am 2010-09-12 23:28:30 UTC (rev 342) @@ -16,6 +16,13 @@ generic/particle_fire.png\ generic/start-marker\ generic/start-marker.png\ +sound/boost.wav\ +sound/car_break.wav\ +sound/car_drag.wav\ +sound/car_release_energy.wav\ +sound/collision.wav\ +sound/engine.wav\ +sound/slide.wav\ theme1/theme1\ theme1/cap_end.png\ theme1/cap_start.png\ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-12 11:40:26
|
Revision: 341 http://racer.svn.sourceforge.net/racer/?rev=341&view=rev Author: jlegg Date: 2010-09-12 11:40:20 +0000 (Sun, 12 Sep 2010) Log Message: ----------- Fix bug where sounds were cut short. Modified Paths: -------------- trunk/racer/Engine/Audio.cpp trunk/racer/Engine/Audio.h Modified: trunk/racer/Engine/Audio.cpp =================================================================== --- trunk/racer/Engine/Audio.cpp 2010-09-12 00:30:52 UTC (rev 340) +++ trunk/racer/Engine/Audio.cpp 2010-09-12 11:40:20 UTC (rev 341) @@ -125,7 +125,7 @@ std::multiset<SoundInstance *, SoundInstancePtrGainCompare> audiable_sounds; // Anything at this gain or below is not mixed. - ALfloat min_gain = 0.000976562; + ALfloat min_gain = 0.0; for (source_it = m_sources.begin(); source_it != m_sources.end(); source_it++) { SoundSource & source = **source_it; @@ -178,7 +178,10 @@ // has been started for all listeners, it shouldn't be restarted next // time as that would cause it to play from the beginning again if it // wasn't supposed to loop. - if (source.m_playing) source.m_started = true; + if (source.m_playing) + { + source.m_started = true; + } } // queue up the changes and submit them all at once @@ -227,12 +230,45 @@ // unassign any remaining sources. while (alindex < m_al_sources.size()) { - m_al_sources[alindex]->unassign(); + if (available[alindex]) + { + m_al_sources[alindex]->unassign(); + } alindex++; } alcProcessContext(alcGetCurrentContext()); + // If a non looping SoundSource has no playing instances, stop it. + std::size_t source_index = 0; + std::size_t diff = m_sources.size(); + for (source_it = m_sources.begin(); source_it != m_sources.end(); source_it++, source_index++) + { + SoundSource & source = **source_it; + if (source.m_playing && !source.m_looping) + { + // set playing to true if there is an instance that is playing it. + bool playing = false; + for (std::size_t i = source_index; + i < m_instances.size(); + i += diff) + { + const SoundInstance & instance = *(m_instances[i]); + if (instance.get_assigned()) + { + if (instance.get_playing()) + { + playing = true; + break; + } + } + } + // no playing instance. + // stop trying to play the sound. + if (!playing) source.m_playing = false; + } + } + // We could do this after every call, but it costs a lot in performance. ALenum error = alGetError(); if (error != AL_NO_ERROR) @@ -424,7 +460,7 @@ m_looping = loop; } -bool SoundInstance::get_looping() +bool SoundInstance::get_looping() const { return m_looping; } @@ -477,12 +513,21 @@ { } -bool SoundInstance::get_assigned() +SoundInstance::~SoundInstance() { + // unassign any assigned sound source. + if (m_source) + { + m_source->unassign(); + } +} + +bool SoundInstance::get_assigned() const +{ return bool(m_source); } -ALuint SoundInstance::get_buffer() +ALuint SoundInstance::get_buffer() const { return m_buffer; } @@ -501,6 +546,11 @@ void SoundInstance::unassign() { m_source = 0; + if (!m_looping) + { + // if unassigned, a non looping sound cannot be continued. + m_playing = false; + } } void SoundInstance::set_gain(ALfloat gain) @@ -508,7 +558,7 @@ m_gain = gain; } -ALfloat SoundInstance::get_gain() +ALfloat SoundInstance::get_gain() const { return m_gain; } @@ -531,6 +581,14 @@ void SoundInstance::play() { + if (m_playing) + { + // already playing, restart the sound. + if (m_source) + { + alSourcePlay(m_source->get_name()); + } + } m_playing = true; } @@ -539,7 +597,7 @@ m_playing = false; } -bool SoundInstance::get_playing() +bool SoundInstance::get_playing() const { return m_playing; } @@ -577,7 +635,7 @@ } } -unsigned int SoundInstance::get_index() +unsigned int SoundInstance::get_index() const { return m_source_index; } Modified: trunk/racer/Engine/Audio.h =================================================================== --- trunk/racer/Engine/Audio.h 2010-09-12 00:30:52 UTC (rev 340) +++ trunk/racer/Engine/Audio.h 2010-09-12 11:40:20 UTC (rev 341) @@ -221,12 +221,13 @@ /** Create associating with an OpenAL buffer object's name. */ SoundInstance(ALuint buffer); + ~SoundInstance(); /// Return the buffer name given on creation. - ALuint get_buffer(); + ALuint get_buffer() const; /// return true if an ALSoundSource is being used. - bool get_assigned(); + bool get_assigned() const; /// Get the index value passed when assigned - unsigned int get_index(); + unsigned int get_index() const; /** Use an ALSoundSource. * After this, the ALSoundSource is not assigned to any SoundInstance * it was assigned to before. @@ -241,7 +242,7 @@ */ void set_gain(ALfloat gain); /// return the gain set by set_gain() - ALfloat get_gain(); + ALfloat get_gain() const; /// Set the scale for the pitch of the sound (1 is normal) void set_pitch(ALfloat pitch); /// Set the position of the sound relative to the viewer. @@ -256,14 +257,14 @@ */ void set_looping(bool looping = true); /// Return true iff the sound should repeat continuosly. - bool get_looping(); + bool get_looping() const; /// Play the sound. void play(); /// Stop the sound from playing. void stop(); /// Return true iff the sound is playing. - bool get_playing(); + bool get_playing() const; /// Update the assigned ALSoundSource void update_al(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-12 00:30:58
|
Revision: 340 http://racer.svn.sourceforge.net/racer/?rev=340&view=rev Author: jlegg Date: 2010-09-12 00:30:52 +0000 (Sun, 12 Sep 2010) Log Message: ----------- Better car ranking in AITrainer. Modified Paths: -------------- trunk/racer/Engine/AITrainer.cpp Modified: trunk/racer/Engine/AITrainer.cpp =================================================================== --- trunk/racer/Engine/AITrainer.cpp 2010-09-12 00:25:57 UTC (rev 339) +++ trunk/racer/Engine/AITrainer.cpp 2010-09-12 00:30:52 UTC (rev 340) @@ -131,22 +131,17 @@ void AITrainer::rank_cars() { - std::map<btScalar, unsigned int> scores; + std::multimap<btScalar, unsigned int> scores; for (unsigned int i = 0; i < number_of_cars; i++) { btScalar score = car_score(i) - starting_scores[i]; - // don't let two vechicles with the same score mess ordering up. - while (scores.find(score) != scores.end()) - { - if (score == 0) score = -1; else score *= (31.0/32.0); - } // DEBUG_MESSAGE("Car " << i << " scored " << score); scores.insert(std::pair<btScalar, unsigned int>(score, i)); } // was insertion sorted, but we want highest score first. car_ranks.resize(number_of_cars - 1); unsigned int rank = number_of_cars - 1; - for (std::map<btScalar, unsigned int>::iterator it = scores.begin(); + for (std::multimap<btScalar, unsigned int>::iterator it = scores.begin(); it != scores.end(); it++) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-12 00:26:03
|
Revision: 339 http://racer.svn.sourceforge.net/racer/?rev=339&view=rev Author: jlegg Date: 2010-09-12 00:25:57 +0000 (Sun, 12 Sep 2010) Log Message: ----------- Make NearTrack::scan_direction() return correct results between construnction or jump_to() and move_towards_recursive() calls. Modified Paths: -------------- trunk/libtrack/NearTrack.cpp Modified: trunk/libtrack/NearTrack.cpp =================================================================== --- trunk/libtrack/NearTrack.cpp 2010-09-12 00:08:58 UTC (rev 338) +++ trunk/libtrack/NearTrack.cpp 2010-09-12 00:25:57 UTC (rev 339) @@ -132,6 +132,7 @@ max_distance2 = distance2; // remember information about this face. m_world_coord = nearest_point; + best_world_coords = m_world_coord; m_face = v_descriptor; } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-12 00:09:04
|
Revision: 338 http://racer.svn.sourceforge.net/racer/?rev=338&view=rev Author: jlegg Date: 2010-09-12 00:08:58 +0000 (Sun, 12 Sep 2010) Log Message: ----------- Don't try to check if source or sink nodes (for vertex graph used to establish lap distances) is on the line. Modified Paths: -------------- trunk/libtrack/Track.cpp Modified: trunk/libtrack/Track.cpp =================================================================== --- trunk/libtrack/Track.cpp 2010-09-11 13:12:07 UTC (rev 337) +++ trunk/libtrack/Track.cpp 2010-09-12 00:08:58 UTC (rev 338) @@ -229,6 +229,14 @@ for (its = boost::vertices(graph); its.first != its.second; its.first++) { MeshFaces::VertexGraph::vertex_descriptor vertex = *(its.first); + if (vertex == source_descriptor) + { + continue; + } + if (vertex == sink_descriptor) + { + continue; + } btVector3 position = graph[vertex]; // find distance from plane. btScalar to_plane = start_plane_normal.dot(position) + start_plane_distance; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-11 13:12:13
|
Revision: 337 http://racer.svn.sourceforge.net/racer/?rev=337&view=rev Author: jlegg Date: 2010-09-11 13:12:07 +0000 (Sat, 11 Sep 2010) Log Message: ----------- Initialise frame rate for TitleScene so on the first frame the counter does not use an unintialised value. Modified Paths: -------------- trunk/racer/UI/TitleScene.cpp Modified: trunk/racer/UI/TitleScene.cpp =================================================================== --- trunk/racer/UI/TitleScene.cpp 2010-09-11 12:56:20 UTC (rev 336) +++ trunk/racer/UI/TitleScene.cpp 2010-09-11 13:12:07 UTC (rev 337) @@ -27,6 +27,7 @@ TitleScene::TitleScene() : t(0), logo_texture("data/icons/r512.png"), + fps(0), main_menu(new Menu()), // main menu items mi_single_player(L"Single player"), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-11 12:56:26
|
Revision: 336 http://racer.svn.sourceforge.net/racer/?rev=336&view=rev Author: jlegg Date: 2010-09-11 12:56:20 +0000 (Sat, 11 Sep 2010) Log Message: ----------- Don't remove cars from tick observers during posttick callback, as it could break the iterator used to notify the tick observers. Modified Paths: -------------- trunk/racer/Engine/GameObjects/Car.cpp Modified: trunk/racer/Engine/GameObjects/Car.cpp =================================================================== --- trunk/racer/Engine/GameObjects/Car.cpp 2010-09-03 19:55:01 UTC (rev 335) +++ trunk/racer/Engine/GameObjects/Car.cpp 2010-09-11 12:56:20 UTC (rev 336) @@ -244,6 +244,7 @@ Car::~Car() { if (!m_removed) remove(); + world.remove_tick_observer(this); // Stop the car's InputDevice from sending reports to this car. input_device->set_car(0); } @@ -432,6 +433,7 @@ void Car::posttick() { + if (m_removed) return; #ifdef TRAIN_AI static std::ofstream data_file("ANN_training_data"); static bool train_data_init = false; @@ -792,10 +794,6 @@ btScalar Car::get_lap_distance() const { - if (m_removed) - { - return 0; - } const Track::MeshFaces::FaceGraph face = m_graph[get_face_descriptor()]; return face_u_interpolation(face, get_position()); } @@ -805,7 +803,6 @@ m_last_transform = rigid_body->getCenterOfMassTransform(); m_removed = true; world.get_dynamics_world().removeRigidBody(rigid_body); - world.remove_tick_observer(this); delete rigid_body; m_course_complete_time = world.get_tick_number(); m_engine_sound_source->stop(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-03 19:55:07
|
Revision: 335 http://racer.svn.sourceforge.net/racer/?rev=335&view=rev Author: jlegg Date: 2010-09-03 19:55:01 +0000 (Fri, 03 Sep 2010) Log Message: ----------- Start human players behind the computer player. Also, instead of writing 11st, 12nd, and 13rd, write 11th, 12th, and 13th. Modified Paths: -------------- trunk/racer/Engine/GameScene.cpp Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-09-01 00:30:41 UTC (rev 334) +++ trunk/racer/Engine/GameScene.cpp 2010-09-03 19:55:01 UTC (rev 335) @@ -111,7 +111,8 @@ const Track::StartingPosition * pos = dynamic_cast<const Track::StartingPosition *>(&(**it)); if (pos) { - if (pos->get_rank() == i) + // cars go in reverse order, so human players go last. + if (pos->get_rank() == number_of_cars - i - 1) { initial_transform = pos->get_global_transform(); } @@ -455,6 +456,11 @@ column_size, row_size); glScissor(column * column_size, row * row_size, column_size, row_size); + /** @todo 'player' is only going to work if the human players (with + * cameras and viewports) are before all the computer players in the + * car list. + * Otherwise the HUD will refer to the wrong car. + */ draw_for_player(player, best_aspect); column++; player++; @@ -840,9 +846,14 @@ std::stringstream rank_text; int rank = car_ranks[player] + 1; rank_text << rank; - if (rank % 10 == 1) rank_text << "st"; - else if (rank % 10 == 2) rank_text << "nd"; - else if (rank % 10 == 3) rank_text << "rd"; + // work out what suffix to use (st, nd, rd, or th) + int rank_mod100 = rank % 100; + int rank_mod10 = rank % 10; + // catch 11th, 12th, and 13th, so they don't become 11st, 12nd, and 13rd. + if (rank_mod100 >= 4 && rank_mod100 <= 20) rank_text << "th"; + else if (rank_mod10 == 1) rank_text << "st"; + else if (rank_mod10 == 2) rank_text << "nd"; + else if (rank_mod10 == 3) rank_text << "rd"; else rank_text << "th"; glTranslatef(-0.1, 0.8, 0.0); glScalef(0.004, 0.004, 1); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-09-01 00:30:47
|
Revision: 334 http://racer.svn.sourceforge.net/racer/?rev=334&view=rev Author: jlegg Date: 2010-09-01 00:30:41 +0000 (Wed, 01 Sep 2010) Log Message: ----------- Pause sound effects when the game is paused. + some audio improvements. Modified Paths: -------------- trunk/racer/Engine/Audio.cpp trunk/racer/Engine/Audio.h trunk/racer/Engine/GameScene.cpp Modified: trunk/racer/Engine/Audio.cpp =================================================================== --- trunk/racer/Engine/Audio.cpp 2010-08-31 20:52:07 UTC (rev 333) +++ trunk/racer/Engine/Audio.cpp 2010-09-01 00:30:41 UTC (rev 334) @@ -25,6 +25,7 @@ // so speed of sound in air ~ 330 m/s / 6 = 55 units per second. // in space the speed of sound should be much lower, but it sounds weird. : m_speed_of_sound (300.0) + , m_paused(false) { // Open the default audio device alutInit(0, 0); @@ -60,14 +61,49 @@ void Audio::pause() { - //alcSuspendContext(alcGetCurrentContext()); + m_paused = true; + // pause all playing sources + alcSuspendContext(alcGetCurrentContext()); + for (std::vector<boost::shared_ptr<ALSoundSource> >::iterator it = m_al_sources.begin(); + it != m_al_sources.end(); + it++) + { + ALuint name = (**it).get_name(); + ALint state; + alGetSourcei(name, AL_SOURCE_STATE, &state); + if (state == AL_PLAYING) + { + alSourcePause(name); + } + } + alcProcessContext(alcGetCurrentContext()); } void Audio::resume() { - //alcProcessContext(alcGetCurrentContext()); + m_paused = false; + // resume all paused sources. + alcSuspendContext(alcGetCurrentContext()); + for (std::vector<boost::shared_ptr<ALSoundSource> >::iterator it = m_al_sources.begin(); + it != m_al_sources.end(); + it++) + { + ALuint name = (**it).get_name(); + ALint state; + alGetSourcei(name, AL_SOURCE_STATE, &state); + if (state == AL_PAUSED) + { + alSourcePlay(name); + } + } + alcProcessContext(alcGetCurrentContext()); } +bool Audio::get_paused() +{ + return m_paused; +} + class SoundInstancePtrGainCompare { public: @@ -126,7 +162,7 @@ { instance.play(); } - else if (!(source.m_playing && source.m_looping)) + else if (!(source.m_playing) || !(source.m_looping || source.m_started)) { instance.stop(); } @@ -144,6 +180,11 @@ // wasn't supposed to loop. if (source.m_playing) source.m_started = true; } + + // queue up the changes and submit them all at once + // (if supported, otherwise this is a no-op.) + alcSuspendContext(alcGetCurrentContext()); + // We want to play the loudest m_al_sources.size() sounds. unsigned int count = 0; std::vector<bool> available(m_al_sources.size(), true); @@ -190,6 +231,8 @@ alindex++; } + alcProcessContext(alcGetCurrentContext()); + // We could do this after every call, but it costs a lot in performance. ALenum error = alGetError(); if (error != AL_NO_ERROR) @@ -638,7 +681,14 @@ DEBUG_MESSAGE("here."); throw AudioError(); } - alSourcePlay(m_name); + if (Audio::get_instance().get_paused()) + { + alSourcePause(m_name); + } + else + { + alSourcePlay(m_name); + } error = alGetError(); if (error != AL_NO_ERROR) { Modified: trunk/racer/Engine/Audio.h =================================================================== --- trunk/racer/Engine/Audio.h 2010-08-31 20:52:07 UTC (rev 333) +++ trunk/racer/Engine/Audio.h 2010-09-01 00:30:41 UTC (rev 334) @@ -39,6 +39,11 @@ void pause(); /// Resume all paused sound effects. void resume(); + /** Return the paused state. + * @return true if resume() was called after the last call to pause(), + * or if pause() has not been called. Returns false otherwise. + */ + bool get_paused(); /** Commit all changes to the listeners and sound sources. * Sounds will begin to play at their set gains and pitches. * Should be called once per tick after sounds have been set using @@ -83,7 +88,14 @@ */ std::vector<boost::shared_ptr<ALSoundSource> > m_al_sources; + /** The speed of sound for doppler calculations. + * It isn't used to delay distant sounds, only change the pitch when + * something moves. + */ ALfloat m_speed_of_sound; + + /// True if all sounds have been paused. + bool m_paused; }; /** A sound loaded from a file. Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-08-31 20:52:07 UTC (rev 333) +++ trunk/racer/Engine/GameScene.cpp 2010-09-01 00:30:41 UTC (rev 334) @@ -291,10 +291,12 @@ car_skip[car] = true; } else { paused = true; + Audio::get_instance().pause(); } } else { // unused controller. paused = true; + Audio::get_instance().pause(); } } break; @@ -1055,6 +1057,8 @@ // menu is already giving a response. Reset it so we can show it again. m_pause_menu.reset(); paused = false; + // start the sound + Audio::get_instance().resume(); } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-31 20:52:13
|
Revision: 333 http://racer.svn.sourceforge.net/racer/?rev=333&view=rev Author: jlegg Date: 2010-08-31 20:52:07 +0000 (Tue, 31 Aug 2010) Log Message: ----------- Fix bug where looping sounds don't stop on request. Modified Paths: -------------- trunk/racer/Engine/Audio.cpp Modified: trunk/racer/Engine/Audio.cpp =================================================================== --- trunk/racer/Engine/Audio.cpp 2010-08-31 20:46:49 UTC (rev 332) +++ trunk/racer/Engine/Audio.cpp 2010-08-31 20:52:07 UTC (rev 333) @@ -126,7 +126,7 @@ { instance.play(); } - else if (!(source.m_playing || source.m_looping)) + else if (!(source.m_playing && source.m_looping)) { instance.stop(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-31 20:46:55
|
Revision: 332 http://racer.svn.sourceforge.net/racer/?rev=332&view=rev Author: jlegg Date: 2010-08-31 20:46:49 +0000 (Tue, 31 Aug 2010) Log Message: ----------- Fix bug in ranking when multiple cars are disqualified. Modified Paths: -------------- trunk/racer/Engine/GameScene.cpp Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-08-31 06:18:17 UTC (rev 331) +++ trunk/racer/Engine/GameScene.cpp 2010-08-31 20:46:49 UTC (rev 332) @@ -1017,7 +1017,7 @@ { car_ranks.resize(cars.size()); // sort cars by lap position + lap length * number of laps completed. - std::map<btScalar, std::size_t> car_positions; + std::multimap<btScalar, std::size_t> car_positions; // Add a bit on to the lap length. The lap length is the shortest route // around, so a car may be further around the lap than the lap is long, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-31 06:18:24
|
Revision: 331 http://racer.svn.sourceforge.net/racer/?rev=331&view=rev Author: jlegg Date: 2010-08-31 06:18:17 +0000 (Tue, 31 Aug 2010) Log Message: ----------- Improve sound performance with many players / cars. Modified Paths: -------------- trunk/racer/Engine/Audio.cpp trunk/racer/Engine/Audio.h trunk/racer/Engine/GameObjects/Car.cpp Modified: trunk/racer/Engine/Audio.cpp =================================================================== --- trunk/racer/Engine/Audio.cpp 2010-08-31 02:14:56 UTC (rev 330) +++ trunk/racer/Engine/Audio.cpp 2010-08-31 06:18:17 UTC (rev 331) @@ -12,7 +12,9 @@ #include "Audio.h" #include <iostream> +#include <set> #include <algorithm> + #include <Debug.h> namespace Engine @@ -27,6 +29,22 @@ // Open the default audio device alutInit(0, 0); alDistanceModel(AL_NONE); + // Allocate some ALSoundSources. + for (unsigned int i = 0; i < 30; i++) + { + try + { + m_al_sources.push_back(boost::shared_ptr<ALSoundSource>(new ALSoundSource)); + } + catch (AudioError t) + { + // Probably hit some hardware limit. + // Nothing to worry about, we will just have less + // simultaneously playing sounds than normal. + return; + } + } + } Audio::~Audio() @@ -42,36 +60,52 @@ void Audio::pause() { - alcSuspendContext(alcGetCurrentContext()); + //alcSuspendContext(alcGetCurrentContext()); } void Audio::resume() { - alcProcessContext(alcGetCurrentContext()); + //alcProcessContext(alcGetCurrentContext()); } +class SoundInstancePtrGainCompare +{ + public: + bool operator () (SoundInstance * sound_instance_1, + SoundInstance * sound_instance_2) + { + return sound_instance_1->get_gain() < + sound_instance_2->get_gain(); + } +}; + void Audio::commit() { // Update each ALSoundSource's values to match listener and source - std::vector<boost::shared_ptr<ALSoundSource> >::iterator al_it = m_alsources.begin(); + std::vector<boost::shared_ptr<SoundInstance> >::iterator instance_it = m_instances.begin(); std::vector<SoundListener *>::iterator listener_it; std::vector<SoundSource *>::iterator source_it; + // This will store the sounds we want to be heard. + std::multiset<SoundInstance *, SoundInstancePtrGainCompare> audiable_sounds; + + // Anything at this gain or below is not mixed. + ALfloat min_gain = 0.000976562; for (source_it = m_sources.begin(); source_it != m_sources.end(); source_it++) { SoundSource & source = **source_it; for (listener_it = m_listeners.begin(); listener_it != m_listeners.end(); listener_it++) { SoundListener & listener = **listener_it; - ALuint al_source_name = (**al_it).get_name(); - ALfloat distance2 = listener.m_position.distance2(source.m_position); - // gain has inverse square attenuation. - ALfloat gain = source.m_gain/distance2; - alSourcef(al_source_name, AL_GAIN, gain); + SoundInstance & instance = (**instance_it); + ALfloat distance = listener.m_position.distance(source.m_position); + // gain has inverse attenuation. + // The gain behaviour in openAL makes the inverse square rule wrong. + ALfloat gain = source.m_gain/distance; + instance.set_gain(gain); // find the position local to the viewer. btVector3 position = listener.m_inverse_transform(source.m_position); - alSource3f(al_source_name, AL_POSITION, - position.x(), position.y(), position.z()); + instance.set_position(position); // find the pitch from the relative velocity btVector3 relative_position = source.m_position - listener.m_position; @@ -79,37 +113,82 @@ ALfloat s_source = relative_position.dot(listener.m_velocity); ALfloat pitch = (m_speed_of_sound + s_listener) / (m_speed_of_sound + s_source) * source.m_pitch; if (pitch <= 0) pitch = 0.001; - alSourcef(al_source_name, AL_PITCH, pitch); + instance.set_pitch(pitch); - alSourcei(al_source_name, AL_LOOPING, source.m_looping ? AL_TRUE : AL_FALSE); + instance.set_looping(source.m_looping); - // Is it playing? - ALint state; - alGetSourcei(al_source_name, AL_SOURCE_STATE, &state); - bool playing = state == AL_PLAYING; - // should it be playing? - if (playing != source.m_playing) + // playing state. + // Non looping sounds that have to low a gain at the start are + // skipped. + if ( source.m_playing + && !source.m_started + && (source.m_looping || gain > min_gain)) { - if (source.m_playing) - { - if (!source.m_started) - { - alSourcePlay(al_source_name); - } - } - else - { - alSourceStop(al_source_name); - } + instance.play(); } + else if (!(source.m_playing || source.m_looping)) + { + instance.stop(); + } - al_it++; + if (instance.get_playing() && gain > min_gain) + { + // should be audiable. + audiable_sounds.insert(&instance); + } + + instance_it++; } // has been started for all listeners, it shouldn't be restarted next // time as that would cause it to play from the beginning again if it // wasn't supposed to loop. if (source.m_playing) source.m_started = true; } + // We want to play the loudest m_al_sources.size() sounds. + unsigned int count = 0; + std::vector<bool> available(m_al_sources.size(), true); + std::vector<SoundInstance *> remaining; + // update the loudest m_al_sources.size() sounds that have already been + // assigned an ALSoundSource. + std::multiset<SoundInstance *, SoundInstancePtrGainCompare>::reverse_iterator it = audiable_sounds.rbegin(); + for (; + it != audiable_sounds.rend() && count < m_al_sources.size(); + it++, count++) + { + if ((**it).get_assigned()) + { + (**it).update_al(); + available[(**it).get_index()] = false; + } else { + // We need to find a less audiable sound to replace. + remaining.push_back(*it); + } + } + // The remaining non looping sound instances should be stopped, so that + // they don't play out of time when a ALSoundSource becomes available. + for (; it != audiable_sounds.rend(); it++) + { + if (!(**it).get_looping()) + { + (**it).stop(); + } + } + // Reassign ALSoundSources for the remaining SoundInstances. + unsigned int alindex = 0; + for (std::vector<SoundInstance *>::iterator it = remaining.begin(); + it != remaining.end(); it++) + { + while (!available[alindex]) {alindex++;} + assert(alindex < m_al_sources.size()); + (**it).assign(*(m_al_sources[alindex]), alindex); + available[alindex] = false; + } + // unassign any remaining sources. + while (alindex < m_al_sources.size()) + { + m_al_sources[alindex]->unassign(); + alindex++; + } // We could do this after every call, but it costs a lot in performance. ALenum error = alGetError(); @@ -127,13 +206,13 @@ // add a OpenAL source for each SoundSource. // There should be one after each listener group of the existing ones. std::size_t frequency = m_listeners.size(); - m_alsources.reserve((frequency+1)*m_sources.size()); - std::vector<boost::shared_ptr<ALSoundSource> >::iterator it = m_alsources.begin(); + m_instances.reserve((frequency+1)*m_instances.size()); + std::vector<boost::shared_ptr<SoundInstance> >::iterator it = m_instances.begin(); for (std::size_t i = 0; i < m_sources.size(); i++) { it += frequency; - it = m_alsources.insert(it, - boost::shared_ptr<ALSoundSource>(new ALSoundSource(m_sources[i]->get_buffer().get_al_buffer()))); + it = m_instances.insert(it, + boost::shared_ptr<SoundInstance>(new SoundInstance(m_sources[i]->get_buffer().get_al_buffer()))); it++; } m_listeners.push_back(listener); @@ -156,9 +235,9 @@ ALint al_buffer = source->get_buffer().get_al_buffer(); for (std::size_t i = 0; i < amount; i++) { - m_alsources.push_back(boost::shared_ptr<ALSoundSource>(new ALSoundSource(al_buffer))); + m_instances.push_back(boost::shared_ptr<SoundInstance>(new SoundInstance(al_buffer))); } - assert(m_sources.size() * m_listeners.size() == m_alsources.size()); + assert(m_sources.size() * m_listeners.size() == m_instances.size()); } void Audio::forget_listener(SoundListener * listener) @@ -177,16 +256,19 @@ index++; } - std::vector<boost::shared_ptr<ALSoundSource> >::iterator it; for (std::size_t i = m_sources.size() - 1; i != std::size_t(-1) ; i--) { - std::vector<boost::shared_ptr<ALSoundSource> >::iterator it = - m_alsources.begin() + (index + frequency * i); - m_alsources.erase(it); + std::vector<boost::shared_ptr<SoundInstance> >::iterator it = + m_instances.begin() + (index + frequency * i); + m_instances.erase(it); } m_listeners.erase(l_it); - assert(m_sources.size() * m_listeners.size() == m_alsources.size()); + assert(m_sources.size() * m_listeners.size() == m_instances.size()); + + // commit to this, it will stop ALSoundSources using buffers that could + // be deleted. + commit(); } void Audio::forget_source(SoundSource * source) @@ -199,10 +281,10 @@ index++; } std::size_t scale = m_listeners.size(); - m_alsources.erase(m_alsources.begin()+scale*index, - m_alsources.begin()+scale*(index+1)); + m_instances.erase(m_instances.begin()+scale*index, + m_instances.begin()+scale*(index+1)); m_sources.erase(source_it); - assert(m_sources.size() * m_listeners.size() == m_alsources.size()); + assert(m_sources.size() * m_listeners.size() == m_instances.size()); } SoundBuffer::SoundBuffer(const char * filename) @@ -299,6 +381,11 @@ m_looping = loop; } +bool SoundInstance::get_looping() +{ + return m_looping; +} + void SoundSource::set_position(btVector3 position) { m_position = position; @@ -335,10 +422,127 @@ return m_buffer; } -ALSoundSource::ALSoundSource(ALint buffer) +SoundInstance::SoundInstance(ALuint buffer) + : m_source(0) + , m_buffer(buffer) + , m_gain(1) + , m_pitch(1) + , m_position(btVector3(0, 0, 0)) + , m_looping(false) + , m_playing(false) + , m_assigning(false) { +} + +bool SoundInstance::get_assigned() +{ + return bool(m_source); +} + +ALuint SoundInstance::get_buffer() +{ + return m_buffer; +} + +void SoundInstance::assign(ALSoundSource & source, unsigned int index) +{ + m_source = &source; + m_source_index = index; + + // this will call update_al. + m_assigning = true; + m_source->assign(*this); + m_assigning = false; +} + +void SoundInstance::unassign() +{ + m_source = 0; +} + +void SoundInstance::set_gain(ALfloat gain) +{ + m_gain = gain; +} + +ALfloat SoundInstance::get_gain() +{ + return m_gain; +} + +void SoundInstance::set_pitch(ALfloat pitch) +{ + m_pitch = pitch; +} + +void SoundInstance::set_position(btVector3 position) +{ + m_position = position; +} + +void SoundInstance::set_looping(bool looping) +{ + m_looping = looping; +} + + +void SoundInstance::play() +{ + m_playing = true; +} + +void SoundInstance::stop() +{ + m_playing = false; +} + +bool SoundInstance::get_playing() +{ + return m_playing; +} + +void SoundInstance::update_al() +{ + if (!m_source) return; + + ALuint al_source_name = m_source->get_name(); + alSourcef(al_source_name, AL_GAIN, m_gain); + alSource3f(al_source_name, AL_POSITION, + m_position.x(), m_position.y(), m_position.z()); + alSourcef(al_source_name, AL_PITCH, m_pitch); + ALenum error = alGetError(); + if (error != AL_NO_ERROR) + { + std::cerr << "OpenAL error: " + << alutGetErrorString(error) << "\n"; + DEBUG_MESSAGE("here."); + throw AudioError(); + } + // Looping shouldn't change while playing. + // It is actually handled by ALSoundSource when first attached. + + if (!m_looping && !m_assigning) + { + // if the (not looped) sound finishes, give up the slot. + ALint state; + alGetSourcei(al_source_name, AL_SOURCE_STATE, &state); + if (state == AL_STOPPED) + { + m_playing = false; + unassign(); + } + } +} + +unsigned int SoundInstance::get_index() +{ + return m_source_index; +} + +ALSoundSource::ALSoundSource() + : m_instance(0) +{ alGenSources (1, &m_name); - alSourcei (m_name, AL_BUFFER, buffer); ALenum error = alGetError(); if (error != AL_NO_ERROR) { @@ -364,8 +568,86 @@ ALuint ALSoundSource::get_name() { - return m_name; + return m_name; } +SoundInstance * ALSoundSource::get_instance() +{ + return m_instance; +} + +void ALSoundSource::unassign() +{ + if (!m_instance) + { + return; + } + else + { + alSourceStop(m_name); + ALenum error = alGetError(); + if (error != AL_NO_ERROR) + { + std::cerr << "OpenAL error: " + << alutGetErrorString(error) << "\n"; + DEBUG_MESSAGE("here."); + throw AudioError(); + } + m_instance->unassign(); + m_instance = 0; + // Incase the buffer gets deleted, stop using it. + alSourcei (m_name, AL_BUFFER, 0); + } +} + +void ALSoundSource::assign(SoundInstance & si) +{ + if (m_instance) + { + // stop using the previous assignment. + unassign(); + } + m_instance = &si; + ALenum error = alGetError(); + if (error != AL_NO_ERROR) + { + std::cerr << "OpenAL error: " + << alutGetErrorString(error) << "\n"; + DEBUG_MESSAGE("here."); + throw AudioError(); + } + alSourcei(m_name, AL_BUFFER, m_instance->get_buffer()); + //DEBUG_MESSAGE("Assigned buffer " << m_instance->get_buffer() << " to source " << m_name); + error = alGetError(); + if (error != AL_NO_ERROR) + { + std::cerr << "OpenAL error: " + << alutGetErrorString(error) << "\n"; + DEBUG_MESSAGE("here."); + throw AudioError(); + } + // get the instance to set the gain, pitch, and location. + m_instance->update_al(); + // it won't set the looping by itself though: + alSourcei(m_name, AL_LOOPING, m_instance->get_looping() ? AL_TRUE : AL_FALSE); + error = alGetError(); + if (error != AL_NO_ERROR) + { + std::cerr << "OpenAL error: " + << alutGetErrorString(error) << "\n"; + DEBUG_MESSAGE("here."); + throw AudioError(); + } + alSourcePlay(m_name); + error = alGetError(); + if (error != AL_NO_ERROR) + { + std::cerr << "OpenAL error: " + << alutGetErrorString(error) << "\n"; + DEBUG_MESSAGE("here."); + throw AudioError(); + } +} + }// namespace Engine Modified: trunk/racer/Engine/Audio.h =================================================================== --- trunk/racer/Engine/Audio.h 2010-08-31 02:14:56 UTC (rev 330) +++ trunk/racer/Engine/Audio.h 2010-08-31 06:18:17 UTC (rev 331) @@ -23,6 +23,7 @@ class SoundSource; class SoundListener; +class SoundInstance; class ALSoundSource; /** Singleton for managing sound effects and music. @@ -60,20 +61,27 @@ */ void forget_source(SoundSource * source); private: - /** The OpenAL sources for positional audio. + /** The playing sound instances for positional audio. * Sorted by listner then SoundSource, since the listners are created * and destroyed less often, and they can cause the most moves when * created or destroyed. */ - std::vector<boost::shared_ptr<ALSoundSource> > m_alsources; + std::vector<boost::shared_ptr<SoundInstance> > m_instances; /** Listeners - * Ordered as in m_alsources. + * Ordered as in m_instances. */ std::vector<SoundListener *> m_listeners; /** SoundSources - * Ordered as in m_alsources. + * Ordered as in m_instances. */ std::vector<SoundSource *> m_sources; + /** OpenAL sources. + * There is a hardware / sound driver limit on the number available, + * and also performance problems with large numbers of sounds. + * Therefore we have a fixed limited number of OpenAL sources. Only + * the most audiable sounds are played. + */ + std::vector<boost::shared_ptr<ALSoundSource> > m_al_sources; ALfloat m_speed_of_sound; }; @@ -190,16 +198,96 @@ SoundSource & operator=(const SoundSource & source); }; +/** A sound heard by a SoundListener because of a SoundSource. + * May or may not be actually played. + * Only the most audioable sounds are fed into OpenAL. + * The less audiable ones are ignored for performance reasons. + */ +class SoundInstance +{ + public: + /** Create associating with an OpenAL buffer object's name. + */ + SoundInstance(ALuint buffer); + /// Return the buffer name given on creation. + ALuint get_buffer(); + /// return true if an ALSoundSource is being used. + bool get_assigned(); + /// Get the index value passed when assigned + unsigned int get_index(); + /** Use an ALSoundSource. + * After this, the ALSoundSource is not assigned to any SoundInstance + * it was assigned to before. + * This will make the sound actually audiable. + */ + void assign(ALSoundSource & source, unsigned int index); + /// Stop using an ALSoundSource. + void unassign(); + /** Set how loud the sound should be played. + * 1 is normal volume, 0.5 is -6dB, 0.25 is -25dB, etc. + * 0 is silent. + */ + void set_gain(ALfloat gain); + /// return the gain set by set_gain() + ALfloat get_gain(); + /// Set the scale for the pitch of the sound (1 is normal) + void set_pitch(ALfloat pitch); + /// Set the position of the sound relative to the viewer. + void set_position(btVector3 position); + /** Set whether the sound should repeat continuosly. + * A looping sound source can be assigned an ALSoundSource at any + * time. A sound source which doesn't loop that could not be assigned + * an ALSoundSource will never be heard. + * The looping value shouldn't be changed while playing and attached + * to a ALSoundSource. You can call this function with the previous + * state again though, it will just do nothing. + */ + void set_looping(bool looping = true); + /// Return true iff the sound should repeat continuosly. + bool get_looping(); + + /// Play the sound. + void play(); + /// Stop the sound from playing. + void stop(); + /// Return true iff the sound is playing. + bool get_playing(); + + /// Update the assigned ALSoundSource + void update_al(); + private: + ALSoundSource * m_source; + unsigned int m_source_index; + ALuint m_buffer; + ALfloat m_gain; + ALfloat m_pitch; + btVector3 m_position; + bool m_looping; + bool m_playing; + bool m_assigning; +}; + /** Wrapper for an OpenAL source. */ class ALSoundSource { public: - ALSoundSource(ALint buffer); + ALSoundSource(); ~ALSoundSource(); ALuint get_name(); + /// Get the SoundInstance that was last assigned, if any. + SoundInstance * get_instance(); + /// Assign a SoundInstance. + void assign(SoundInstance & si); + /** Stop playing any sound, and forget the last assigment. + */ + void unassign(); private: ALuint m_name; + SoundInstance * m_instance; + // no copying or assignment + ALSoundSource (const ALSoundSource & source); + ALSoundSource & operator=(const ALSoundSource & source); }; class AudioError Modified: trunk/racer/Engine/GameObjects/Car.cpp =================================================================== --- trunk/racer/Engine/GameObjects/Car.cpp 2010-08-31 02:14:56 UTC (rev 330) +++ trunk/racer/Engine/GameObjects/Car.cpp 2010-08-31 06:18:17 UTC (rev 331) @@ -1056,7 +1056,7 @@ { m_release_energy_sound_source->set_position(rigid_body->getCenterOfMassTransform().getOrigin()); m_release_energy_sound_source->set_velocity(rigid_body->getLinearVelocity()); - m_release_energy_sound_source->set_gain(5.0 * used_energy_boost / float(2<<21)); + m_release_energy_sound_source->set_gain(5.0 * used_energy_boost / float(2<<23)); m_release_energy_sound_source->set_pitch(1.0 + used_energy_boost / float(2<<21)); } else { m_release_energy_sound_source->set_gain(0); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-31 02:15:03
|
Revision: 330 http://racer.svn.sourceforge.net/racer/?rev=330&view=rev Author: jlegg Date: 2010-08-31 02:14:56 +0000 (Tue, 31 Aug 2010) Log Message: ----------- Don't bother setting optimiser flags. ./configure sets its own unless you set CXXFLAGS first, and these overide the options in configure.ac. Modified Paths: -------------- trunk/configure.ac Modified: trunk/configure.ac =================================================================== --- trunk/configure.ac 2010-08-31 01:13:28 UTC (rev 329) +++ trunk/configure.ac 2010-08-31 02:14:56 UTC (rev 330) @@ -26,9 +26,9 @@ AM_CONDITIONAL([DEBUG], [test x$debug = xtrue]) #set options specific to debug / release builds if test x$debug = xtrue; then -AC_SUBST([debug_specific_CFLAGS], ["-g3 -O0"]) +AC_SUBST([debug_specific_CFLAGS], ["-g3"]) else -AC_SUBST([debug_specific_CFLAGS], ["-DNDEBUG -O3"]) +AC_SUBST([debug_specific_CFLAGS], ["-DNDEBUG"]) fi #boost libraries build type This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-31 01:13:34
|
Revision: 329 http://racer.svn.sourceforge.net/racer/?rev=329&view=rev Author: jlegg Date: 2010-08-31 01:13:28 +0000 (Tue, 31 Aug 2010) Log Message: ----------- Install the ai network data file. Modified Paths: -------------- trunk/racer/data/Makefile.am Modified: trunk/racer/data/Makefile.am =================================================================== --- trunk/racer/data/Makefile.am 2010-08-30 01:28:17 UTC (rev 328) +++ trunk/racer/data/Makefile.am 2010-08-31 01:13:28 UTC (rev 329) @@ -1,4 +1,5 @@ racer_data_files = \ +ai.net\ cars/1/craft_full.png\ cars/1/mesh\ cars/1/preview.png\ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-30 01:28:24
|
Revision: 328 http://racer.svn.sourceforge.net/racer/?rev=328&view=rev Author: jlegg Date: 2010-08-30 01:28:17 +0000 (Mon, 30 Aug 2010) Log Message: ----------- Don't show view of computer player, and don't wait for computer players to finish, during a race. Modified Paths: -------------- trunk/racer/Engine/GameScene.cpp trunk/racer/Engine/GameScene.h Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-08-30 00:21:47 UTC (rev 327) +++ trunk/racer/Engine/GameScene.cpp 2010-08-30 01:28:17 UTC (rev 328) @@ -13,6 +13,7 @@ #include "../Graphics/Window.h" #include <Debug.h> #include "InputHandler.h" +#include "InputDeviceAI.h" #include "ResourceHandler.h" #include "../UI/BasicFonts.h" #include "../MainLoop.h" @@ -125,14 +126,22 @@ start_plane_normal, start_plane_distance, start_point)); - car_cameras.push_back(new CarCamera(*cars[i], world)); + if (!dynamic_cast<InputDeviceAI*>(*(input_devices[i].first))) + { + // Not a computer player (though it could be a replay of one). + /** @todo In a replay, give one view of a car, and allow switching + * the car that is visible. + */ + car_cameras.push_back(new CarCamera(*cars[i], world)); + car_skip.push_back(false); + m_humans.push_back(i); + } // The pointer to the input device will be used to identify input // reports during the game. Store it as a long int instead of hex. m_replay_header << (unsigned long int)&*(input_devices[i].first) << " " // Record which car model was used. << input_devices[i].second << " "; - car_skip.push_back(false); } btDefaultMotionState motion_state(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 0, 0))); @@ -346,16 +355,18 @@ } world.update(milliseconds_elapsed); - // if we are done with all cars, quit the scene. + // if we are done with non computer controlled cars, quit the scene. bool quit = true; - for (unsigned int i = 0; i < cars.size(); i++) + for (unsigned int i = 0; i < m_humans.size(); i++) { - bool this_car = cars[i]->get_disqualified() || cars[i]->get_finished(); + int car_index = m_humans[i]; + bool this_car = cars[car_index]->get_disqualified() || + cars[car_index]->get_finished(); if (this_car) { // done after a few seconds or if manually skipped. - if ( (world.get_tick_number() - cars[i]->get_finish_time() < 300) - && !car_skip[i]) + if ( (world.get_tick_number() - cars[car_index]->get_finish_time() < 300) + && !car_skip[car_index]) { quit = false; } Modified: trunk/racer/Engine/GameScene.h =================================================================== --- trunk/racer/Engine/GameScene.h 2010-08-30 00:21:47 UTC (rev 327) +++ trunk/racer/Engine/GameScene.h 2010-08-30 01:28:17 UTC (rev 328) @@ -80,8 +80,11 @@ Graphics::SkyParticles m_sky_particles; Track::OcclusionTester occlusion_tester; std::vector<GameObjects::Car *> cars; - // For each car, true if they tried to skip the pause before quitting. + /// Indices of the human players in cars and m_input_devices. + std::vector<unsigned int> m_humans; + /// For each human player, true if they tried to skip the pause before quitting. std::vector<bool> car_skip; + /// Cameras for each human player's car. std::vector<CarCamera *> car_cameras; btRigidBody * track_body; btRigidBody * floor_body; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-30 00:21:56
|
Revision: 327 http://racer.svn.sourceforge.net/racer/?rev=327&view=rev Author: jlegg Date: 2010-08-30 00:21:47 +0000 (Mon, 30 Aug 2010) Log Message: ----------- Better AI. Introduces dependency on FANN. Modified Paths: -------------- trunk/Makefile.am trunk/Readme trunk/configure.ac trunk/libtrack/NearTrack.cpp trunk/libtrack/NearTrack.h trunk/racer/Engine/GameObjects/Car.cpp trunk/racer/Engine/GameObjects/Car.h trunk/racer/Engine/InputDeviceAI.cpp trunk/racer/Engine/InputDeviceAI.h trunk/racer/Engine/LoadScene.h trunk/racer/Engine/Makefile.am trunk/racer/Engine/Physics/World.cpp trunk/racer/RacerApp.cpp Added Paths: ----------- trunk/ai_train.cpp trunk/racer/Engine/AITrainer.cpp trunk/racer/Engine/AITrainer.h trunk/racer/data/ai.net Modified: trunk/Makefile.am =================================================================== --- trunk/Makefile.am 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/Makefile.am 2010-08-30 00:21:47 UTC (rev 327) @@ -7,3 +7,8 @@ Debug.h #TODO: Get 'doxygen Doxyfile' to be run in @top_srcdir@ when source file #changes for some documentation target. + +bin_PROGRAMS = racer_ai_train +racer_ai_train_SOURCES = ai_train.cpp +racer_ai_train_CPPFLAGS = $(ai_train_CFLAGS) +racer_ai_train_LDADD = $(ai_train_LIBS) Modified: trunk/Readme =================================================================== --- trunk/Readme 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/Readme 2010-08-30 00:21:47 UTC (rev 327) @@ -42,6 +42,8 @@ The OpenGL Extension Wrangler library (GLEW): http://glew.sourceforge.net/ glibmm-2.4: http://www.gtkmm.org/ freealut: http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx?RootFolder=%2Fopenal%2FDownloads%2FALUT + FANN: http://sourceforge.net/projects/fann/ + You need version 2.1.0 beta or later. For the editor: gtkmm-2.4: http://gtkmm.org/ gtkglextmm: http://gtkglext.sourceforge.net/ @@ -60,7 +62,8 @@ On Ubuntu you can do most of this with the following command: sudo apt-get install automake g++ svn libsdl1.2-dev libsdl-image1.2-dev libftgl-dev libgtkmm-2.4-dev libgtkglextmm-x11-dev libboost-dev libboost-graph-dev libglew1.5-dev libalut-dev but Bullet isn't in the repositories, so you'll have to install it from - source. :-( + source. Also, libfann in the repository is too outdated except in + Maveric, so build version this from source too. 2) cd to the directory this Readme file is in. If you are browsing this file on the web, use this command to get racer from svn: svn co https://racer.svn.sourceforge.net/svnroot/racer/trunk racer Added: trunk/ai_train.cpp =================================================================== --- trunk/ai_train.cpp (rev 0) +++ trunk/ai_train.cpp 2010-08-30 00:21:47 UTC (rev 327) @@ -0,0 +1,58 @@ +/** @file racer/ai_train.cpp + * Train the neural network for the computer controlled (AI) players. + * @author James Legg + */ +/* Copyright © 2010 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but without any warranty; without even the implied warranty of + merchantability or fitness for a particular purpose. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "floatfann.h" +#include "racer/Engine/GameObjects/Car.h" + +/** Main function for racer_ai_train program. + * Trains AI based on training data. The training data can be produced by + * recording how a human player (or a replay of one) reacts to different + * combinations of sensor values. You must edit Engine/GameObjects/Car.cpp to + * produce the training data. + * A neural network is produced trained to fit this data. Training is + * necessary when the AI sensors or car behaviour changes. + * + * After running racer_ai_Train, you can further tweak the AI produced with a + * genetic algortihm, by running "racer t track" where track is some track + * filename. + */ +int main() +{ + const unsigned int num_input = Engine::GameObjects::Car::NUM_SENSORS; + const unsigned int num_output = 3; + const unsigned int num_layers = 4; + struct fann *ann = fann_create_standard(num_layers, + num_input, 8, 6, num_output); + + fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE); + fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE); + + // This level of accuracy is not really going to happen, but we can hope. + const float desired_error = 0.0001; + const unsigned int max_iterations = 1000; + const unsigned int iterations_between_reports = 100; + fann_train_on_file(ann, "racer/ANN_training_data", max_iterations, + iterations_between_reports, desired_error); + + fann_save(ann, "racer/data/ai.net"); + fann_print_connections(ann); + fann_destroy(ann); + return 0; +} + Modified: trunk/configure.ac =================================================================== --- trunk/configure.ac 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/configure.ac 2010-08-30 00:21:47 UTC (rev 327) @@ -48,7 +48,7 @@ #We can't get all of the dependencies in pkg-config, so we add the ones that are #avalible to others we find manually. #line to ./configure too, since it doesn't provide a script or a pkg config file. -PKG_CHECK_MODULES(racer_partial, [freetype2 >= 0 ftgl >= 2.0.0 gl >= 0.0.0, bullet, glibmm-2.4, freealut]) +PKG_CHECK_MODULES(racer_partial, [freetype2 >= 0 ftgl >= 2.0.0 gl >= 0.0.0, bullet, glibmm-2.4, freealut, fann >= 2.1.0]) AC_SUBST(racer_partial_LIBS) AC_SUBST(racer_partial_CFLAGS) AC_SUBST(racer_CFLAGS, ["$EXTRA_CFLAGS $SDL_CFLAGS $racer_partial_CFLAGS"]) @@ -63,5 +63,10 @@ AC_SUBST(racer_editor_CFLAGS, ["$racer_editor_CFLAGS $SDL_CFLAGS"]) AC_SUBST(racer_editor_LIBS, ["-lGLEW $racer_editor_LIBS $SDL_LIBS -lSDL_image -lboost_program_options$BOOST_LIB_SUFFIX"]) +#Find dependices for the ai training program +PKG_CHECK_MODULES(ai_train, [fann]) +AC_SUBST(ai_train_CFLAGS) +AC_SUBST(ai_train_LIBS) + #Generate lots of yummy makefiles. AC_OUTPUT([Makefile libtrack/Makefile libtrack/document/Makefile libtrack/edit_base/Makefile libtrack/Mesh/Makefile libtrack/path/Makefile racer_editor/Makefile racer/Makefile racer/data/Makefile racer/UI/Makefile racer/Graphics/Makefile racer/Engine/Makefile racer/Engine/GameObjects/Makefile racer/Engine/Physics/Makefile]) Modified: trunk/libtrack/NearTrack.cpp =================================================================== --- trunk/libtrack/NearTrack.cpp 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/libtrack/NearTrack.cpp 2010-08-30 00:21:47 UTC (rev 327) @@ -10,6 +10,7 @@ */ #include "NearTrack.h" +#include <Debug.h> #include <limits> @@ -159,6 +160,205 @@ return m_object_name; } +void NearTrack::scan_direction(btVector3 glob, btScalar * results) const +{ + btScalar & total_distance = results[SENSOR_NAVIGABLE_DISTANCE]; + btScalar & total_unsigned_angle = results[SENSOR_UNSIGNED_ANGLE]; + btScalar & total_lap_distance = results[SENSOR_LAP_DISTANCE]; + btScalar & differential_lap_distance = results[SENSOR_LAP_DIFFERENTIAL]; + + + // Recursive projection + // glob onto face, then find the intersecting edge in that direction + // if the edge is shared, project the projection onto the adjacent face. + btVector3 direction = glob; + btVector3 start = best_world_coords; + MeshFaces::FaceGraph face = m_graph[m_face]; + MeshFaces::Graph::vertex_descriptor face_descriptor = m_face; + bool edge_shared; + total_distance = 0; + total_unsigned_angle = 0; + total_lap_distance = 0; + bool first = true; + btVector3 end; + unsigned int num_iterations = 0; + do + { + num_iterations++; + // Work out where the point on the face in the given direction is + btVector3 end_face_coords = face.to_face_coords(start+direction); + end_face_coords.setZ(0.0); // project it onto the plane + btVector3 start_face_coords = face.to_face_coords(start); + // start should already be on the face. + // clamp the line start_face_coords to end_face_coords to the face. + // Face coordinates use the triangle (0,0,0), (0,1,0), (1,0,0). + // check where the line segment intersects the sides of the triangle. + // start_face_coords is on the triangle, end_face_coords might not be. + btVector3 dir = end_face_coords - start_face_coords; + if (end_face_coords.x() < 0.0) + { + end_face_coords.setX(0); + if (dir.x() < 0.0001 && dir.x() > -0.0001) + { + } else { + end_face_coords.setY(start_face_coords.y() + dir.y() * (-start_face_coords.x()/dir.x())); + } + } + if (end_face_coords.y() < 0.0) + { + if(dir.y() < 0.0001 && dir.y() > -0.0001) + { + end_face_coords.setY(0); + } + end_face_coords = start_face_coords + dir * (-start_face_coords.y()/dir.y()); + } + if (end_face_coords.x() + end_face_coords.y() > 1.0) + { + //assert(dir.x()+dir.y()); + btScalar n = (1.0 - start_face_coords.x() - start_face_coords.y()) / + (dir.x() + dir.y()); + end_face_coords = start_face_coords + n * dir; + } + end = face.to_world_coords(end_face_coords); + + // add change in lap possition across this face + // We do it seperatly for each face incase it is not consistant across + // the shared edge (the case along the start line). + btScalar start_lap_pos = face_u_interpolation(face, start); + btScalar end_lap_pos = face_u_interpolation(face, end); + total_lap_distance += end_lap_pos - start_lap_pos; + + if (first) + { + // what was the differential of the lap distance under the start? + differential_lap_distance = (face_u_interpolation(face, end) - start_lap_pos) + / (end-start).length(); + first = false; + } + + // If this is on an edge, we might be able to continue looking. + // Otherwise, we've scanned the maximum distance. + btVector3 end_face_coord = face.to_face_coords(end); + if ( end_face_coord.x() < 0.001 + || end_face_coord.y() < 0.001 + || end_face_coord.x() + end_face_coord.y() > 0.999) + { + + // It is at an edge. If the edge is shared, we can continue on the + // adjacent face. + typedef MeshFaces::Graph::adjacency_iterator AdjacencyIterator; + std::pair<AdjacencyIterator, AdjacencyIterator> its; + btScalar max_error = std::numeric_limits<btScalar>::max(); + int adjacent_count = 0; + for (its = boost::adjacent_vertices(face_descriptor, m_graph); + its.first != its.second; + its.first++) + { + // There could be an adjacent face on the other edges to. + // If the edge is shared, the point on the edge (end) is on + // both faces. So find the face containing the closest point + // to it, as it must share that edge. + MeshFaces::Graph::vertex_descriptor this_face_descriptor = *(its.first); + MeshFaces::FaceGraph this_face = m_graph[this_face_descriptor]; + btScalar this_error = (end - this_face.find_nearest_point(end)).length2(); + if (this_error < max_error) + { + max_error = this_error; + face = this_face; + face_descriptor = this_face_descriptor; + } + adjacent_count++; + } + // could be broken if adjacent_count < 4, but we should check for + // well made stuff in the editor and give warnings. + //assert (adjacent_count < 4); + + // if the edge is not shared, we have gone looking in the wrong + // direction. Check if max_error is low enough. + edge_shared = max_error < 0.001; + if (edge_shared) + { + // actually, tracing the shared edge might not work if it hits + // a corner. + ///@todo handle crossing very close to a vertex properly + if ( (end_face_coord.x() < 0.001 && end_face_coord.y() < 0.001) + ||(end_face_coord.x() < 0.001 && end_face_coord.y() > 0.999) + ||(end_face_coord.y() < 0.001 && end_face_coord.x() > 0.999)) + { + // Too close to a vertex of the face. + // The corner may be shared by many faces. + // The one we switched to may be the wrong one. + // Give up looking to avoid an infinite loop. + edge_shared = false; + //DEBUG_MESSAGE("Hit face corner: " << max_error << " close, but facecoords are (" << end_face_coord.x() << ", " << end_face_coord.y() << ")."); + } + else if ((start-end).length2() < 0.001) + { + // Something has likely gone wrong with switching between + // faces. To avoid an infinite loop, stop looking. + // maybe found a face with 0 area. + edge_shared = false; + /** @bug This happens way to often. + */ + } + } + //else DEBUG_MESSAGE("Stopped at the edge"); + } else { + // the we aren't at a shared edge, as we aren't at an edge at all. + edge_shared = false; + // We must have scanned something near the maximum distance, + // unless the drivable faces of the course has sharp edges. + total_distance = glob.length(); + //DEBUG_MESSAGE("Clamping to maximum distance"); + } + + // Find the direction to continue searching in. + btVector3 diff = end-start; + // The length should be the distance yet to cover. + btScalar diff_length = diff.length(); + btVector3 diff_normalised = diff.normalized(); + total_distance += diff_length; + btVector3 new_direction = diff_normalised * (direction.length() - diff_length); + + // update statistics: + // what new global distance did we cover? + total_distance += diff_length; + // what is the change in angle? + btScalar angle_diff = acos(diff_normalised.cross(direction.normalized()).length()); + // unsigned version. + if (angle_diff >= 0) + { + total_unsigned_angle += angle_diff; + } + else + { + total_unsigned_angle -=angle_diff; + } + + if (num_iterations > 48) + { + // DEBUG_MESSAGE("Stopping scan due to complex mesh"); + edge_shared = false; + } + + direction = new_direction; + start = end; + start_lap_pos = end_lap_pos; + } while (edge_shared); + // scale the relative things by the navigable distance. + total_lap_distance /= total_distance; + total_unsigned_angle /= total_distance; + total_distance = 1.0 / total_distance; +} + +btScalar NearTrack::face_u_interpolation(MeshFaces::FaceGraph face, btVector3 point) +{ + btVector3 face_coords = face.to_face_coords(point); + return face.fv1.texture_coord_u + + face_coords.x() * (face.fv2.texture_coord_u - face.fv1.texture_coord_u) + + face_coords.y() * (face.fv3.texture_coord_u - face.fv1.texture_coord_u); +} + void NearTrack::set_info() { MeshFaces::FaceGraph face = m_graph[m_face]; Modified: trunk/libtrack/NearTrack.h =================================================================== --- trunk/libtrack/NearTrack.h 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/libtrack/NearTrack.h 2010-08-30 00:21:47 UTC (rev 327) @@ -19,6 +19,17 @@ namespace Track { +/** The index of the properties returned by NearTrack::scan_direction(). + */ +enum SensorIndex +{ + SENSOR_NAVIGABLE_DISTANCE = 0, ///< distance available despite curves + SENSOR_UNSIGNED_ANGLE = 1, ///< sum of angles divided by navigable distance + SENSOR_LAP_DISTANCE = 2, ///< the advance in lap position divided by navigable distance. + SENSOR_LAP_DIFFERENTIAL = 3, ///< the gradient of the lap position + SENSOR_COUNT = 4 ///< the number of sensor values calculated by NearTrack::scan_direction(). +}; + class Track; /** An object which has an attachment point to an AI mesh. @@ -87,6 +98,30 @@ */ unsigned long int get_object_name() const; + /** Calculate statistics about potential movement in a given direction. + * Projections the direction of the given vector onto the current + * face, then finds the furthest edge in that direction. If the edge + * is shared, project the face vector onto the next face recursively. + * When the edge is not shared, the total navigable distance in that + * direction is returned. + * @param glob global vector giving the direction to scan. The length + * of the vector is the maximum distance to scan. + * @param results address of the first of 4 btScalars to store the + * calculated statistics: + * \li The inverse of the navigable distance (1.0 / distance covered + * by recursive projections starting in the requested direction up to + * the requested length), + * \li unsigned total angle (the total change in angle, but each + * change in angle is positive rather than depending on direction) + * \li the total scaled lap distance (ranging from 1 when the full + * distance is available and it is the shortest path around the lap, + * to -1 for the direct opposite), and + * \li the immediate differential lap distance (the rate of change in + * lap distance on the current face in the requested direction). + * + * Each result is indexed by the SensorIndex enum. + */ + void scan_direction(btVector3 glob, btScalar * results) const; protected: /** Relative coordinates for edge or vertex attached to. * For an edge, this is distorted to lie along the edge. @@ -136,6 +171,15 @@ /// The world coordianates of the solution with the lowest distance reached by move_towards(). btVector3 best_world_coords; + /** Find a u texture coordinate at a point on a face. + * The u texture coordinate is used for lap distance. + * @param face The face to inspect. + * @param point The point in global coordinates to inspect. + * @return Value of u texture coordinate at point, linearly + * interpolated across the face. + */ + static btScalar face_u_interpolation(MeshFaces::FaceGraph face, btVector3 point); + /** Set m_object_coord m_object_name m_object_type, m_face_s/t from m_face. * Finds relavent information from the graph face. */ Added: trunk/racer/Engine/AITrainer.cpp =================================================================== --- trunk/racer/Engine/AITrainer.cpp (rev 0) +++ trunk/racer/Engine/AITrainer.cpp 2010-08-30 00:21:47 UTC (rev 327) @@ -0,0 +1,222 @@ +/** @file AITrainer.cpp + * @brief Implement the Engine::AITrainer class. + * @author James Legg + */ +/* Copyright © 2010 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#include "AITrainer.h" + +#include <libtrack/StartingPosition.h> +#include <iostream> +#include <Debug.h> + +/** Read a line from a c++ standard library input stream. + * @param input stream to read a line from. + * @return The read line as a c++ standard library string object. + */ +std::string read_line(std::istream & input) +{ + std::string line; + std::getline(input, line); + return line; +} + +namespace Engine +{ + +AITrainer::AITrainer(std::string track_filename) + : number_of_cars(30) + , ticks_simulated(2000) + , max_iterations(150) + , track_file(track_filename.c_str()) + , theme_filename(read_line(track_file)) + , theme_file(theme_filename.c_str()) + , theme(theme_file) + , track (track_file, theme) +{ + // make the AI graph for the track. + track.update_ai_mesh(); + btDefaultMotionState motion_state(btTransform(btQuaternion(0, 0, 0, 1), + btVector3(0, 0, 0))); + track_shape = track.get_collision_shape(); + btRigidBody::btRigidBodyConstructionInfo + rigid_body_CI(btScalar(0), + &motion_state, + &(*track_shape), + btVector3(0, 0, 0)); + rigid_body_CI.m_restitution = 1.0; + track_body = boost::shared_ptr<btRigidBody>(new btRigidBody(rigid_body_CI)); + + // now do the floor shape in the floor world. + floor_shape = track.get_floor_shape(); + rigid_body_CI.m_collisionShape = &(*floor_shape); + floor_body = boost::shared_ptr<btRigidBody>(new btRigidBody(rigid_body_CI)); + + starting_scores.resize(number_of_cars); + cars.reserve(number_of_cars); + m_ai_devices.reserve(number_of_cars); + + // Add some AI cars + for (unsigned int ai = 0; ai < number_of_cars; ai++) + { + m_ai_devices.push_back(boost::shared_ptr<InputDeviceAI>(new InputDeviceAI())); + } + + // Do the simulation + for (iteration = 0; iteration < max_iterations; iteration++) + { + world = new Physics::World(track); + world->get_dynamics_world().addRigidBody(&(*track_body)); + world->get_floor_world().addCollisionObject(&(*floor_body)); + reset_cars(); + ///@todo special mutate for the first iteration? + simulate(); + rank_cars(); + mutate(); + delete world; + } + // save the ai net for the best car. + m_ai_devices[car_ranks[0]]->save(); +} + +void AITrainer::reset_cars() +{ + cars.clear(); + const Track::Path & path = track.get_path(); + const Track::PathEdge & edge = path.get_edge(path.get_starting_edge()); + start_point = path.find_start_position(); + path.find_start_plane(start_plane_normal, start_plane_distance); + for (unsigned int ai = 0; ai < number_of_cars; ai++) + { + // Find the starting position for this car. + btTransform initial_transform = + // guess transformation when starting position cannot be found. + edge.get_transform(float(ai) / float(number_of_cars)); + // find the starting position track attachment for this car's rank. + for (std::vector<boost::shared_ptr<Track::TrackAttachment> >::const_iterator it = edge.get_attachments().begin(); + it != edge.get_attachments().end(); + it++) + { + const Track::StartingPosition * pos = dynamic_cast<const Track::StartingPosition *>(&(**it)); + if (pos) + { + if (pos->get_rank() == ai) + { + initial_transform = pos->get_global_transform(); + } + } + } + // raise the car above the track slightly. + initial_transform.setOrigin(initial_transform(btVector3(0.0, 0.0, 0.2))); + cars.push_back(new GameObjects::Car(*world, + initial_transform, + &(*m_ai_devices[ai]), + ai%2,// ai alterenatly uses both cars + start_plane_normal, + start_plane_distance, + start_point)); + starting_scores[ai] = car_score(ai); + } +} + +void AITrainer::simulate() +{ + // simulate some game time + world->update(ticks_simulated * 10); +} + +void AITrainer::rank_cars() +{ + std::map<btScalar, unsigned int> scores; + for (unsigned int i = 0; i < number_of_cars; i++) + { + btScalar score = car_score(i) - starting_scores[i]; + // don't let two vechicles with the same score mess ordering up. + while (scores.find(score) != scores.end()) + { + if (score == 0) score = -1; else score *= (31.0/32.0); + } + // DEBUG_MESSAGE("Car " << i << " scored " << score); + scores.insert(std::pair<btScalar, unsigned int>(score, i)); + } + // was insertion sorted, but we want highest score first. + car_ranks.resize(number_of_cars - 1); + unsigned int rank = number_of_cars - 1; + for (std::map<btScalar, unsigned int>::iterator it = scores.begin(); + it != scores.end(); + it++) + { + car_ranks[rank] = it->second; + rank--; + } + assert(rank == (unsigned int) -1); + if (!(iteration % 5)) + { + std::cout << "Iteration " << iteration << " score " << scores.rbegin()->first << '\n'; + } +} + +btScalar AITrainer::car_score(unsigned int i) +{ + // score first based on distance traveled around the course. + btScalar score = cars[i]->get_lap_distance() + + (cars[i]->get_lap() * track.get_lap_length() + 100.0); + if (cars[i]->get_finished()) + { + // bonus meter per tick for finishing all 3 laps before the end of + // the simulation. + score += ticks_simulated - cars[i]->get_finish_time(); + DEBUG_MESSAGE("Car " << i << " finished 3 laps in " << cars[i]->get_finish_time() << " ticks."); + } else if (cars[i]->get_disqualified()) + { + // Penalty: deduct a meter per tick for falling off before the end + // of the simulation. + score -= ticks_simulated - cars[i]->get_finish_time(); + DEBUG_MESSAGE("Car " << i << " was disqualified in " << cars[i]->get_finish_time() << " ticks."); + } + return score; + /**@todo For good competitive AIs, rank is more important than speed. + * If it knocks all rivals off, it can take as long as it likes to + * finish. + */ +} + +void AITrainer::mutate() +{ + // Leave cars ranked 0-4 alone. + // cars ranked 5-9 get a little variation. + for (unsigned int r = 5; r < 10; r++) + { + m_ai_devices[car_ranks[r]]->mutate(); + } + // the rest gets reconstructed from two of the top 5 + for (unsigned int r = 10; r < number_of_cars; r++) + { + // pick two different classes to cross + /*int p1 = rand() / (RAND_MAX / 5); + int p2 = p1; + while (p2 == p1) + { + p2 = rand() / (RAND_MAX / 5); + }*/ + + // cross a unique pair from the top 5 + int p1 = (r-10)%5; + int p2 = (r-10)/4; + if (p2 >= p1) p2++; + assert(number_of_cars == 30); + m_ai_devices[car_ranks[r]]->cross(*m_ai_devices[car_ranks[p1]], + *m_ai_devices[car_ranks[p2]]); + } +} + +AITrainer::~AITrainer() +{ +} + +} + Added: trunk/racer/Engine/AITrainer.h =================================================================== --- trunk/racer/Engine/AITrainer.h (rev 0) +++ trunk/racer/Engine/AITrainer.h 2010-08-30 00:21:47 UTC (rev 327) @@ -0,0 +1,100 @@ +/** @file AITrainer.h + * @brief Declare the Engine::AITrainer class. + * @author James Legg + */ +/* Copyright © 2010 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#ifndef AI_TRAINER_H +#define AI_TRAINER_H + +#include "Scene.h" +#include "Physics/World.h" +#include "GameObjects/Car.h" +#include "InputHandler.h" +#include "InputDeviceAI.h" + +#include <libtrack/Track.h> +#include <libtrack/Mesh/BulletMesh.h> + +#include <fstream> + +namespace Engine +{ + +class AITrainer +{ +public: + /** Train AI by racing variants against each other. + * @param track_filename The filename of the track to use relative to + where the data directory is. + */ + AITrainer(std::string track_filename); + virtual ~AITrainer(); + void hello(); +private: + /// Create cars in their initial positions. + void reset_cars(); + /// Run the simulation for a while. + void simulate(); + /// Score the cars and store the order in car_ranks. + void rank_cars(); + /** Edit the car's neural networks. + * The top 5 cars are left alone, the next 5 cars have some random + * variations added, and the rest are reconstructed from parts of the top + * 5. + */ + void mutate(); + + /// Get the score for the requested car. Higher is better. + btScalar car_score(unsigned int car); + + /// number of cars to use. + unsigned int number_of_cars; + /// number of game ticks in a match + unsigned int ticks_simulated; + /// number of matches to try + unsigned int max_iterations; + /// how many matches have been run + unsigned int iteration; + + /// The InputDevices for the computer controlled players. + std::vector<boost::shared_ptr<Engine::InputDeviceAI> > m_ai_devices; + + std::vector<GameObjects::Car *> cars; + + std::ifstream track_file; + std::string theme_filename; + std::ifstream theme_file; + Track::Theme theme; + /// The course to test on + Track::Track track; + + + Physics::World * world; + boost::shared_ptr<btRigidBody> track_body; + boost::shared_ptr<btRigidBody> floor_body; + boost::shared_ptr<btCollisionShape> track_shape; + boost::shared_ptr<btCollisionShape> floor_shape; + + /** The ranking of the cars. + * car_ranks[0] is the index of the best performing car. + */ + std::vector<int> car_ranks; + /// The scores the cars have in there initial positions. + std::vector<btScalar> starting_scores; + + /// The centre of the starting plane + btVector3 start_point; + /// A normalized vector in the direction perpendicular to the starting plane. + btVector3 start_plane_normal; + /// The signed distance between the origin and and the starting plane. + btScalar start_plane_distance; +}; + +} + +#endif // AI_TRAINER_H Modified: trunk/racer/Engine/GameObjects/Car.cpp =================================================================== --- trunk/racer/Engine/GameObjects/Car.cpp 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/racer/Engine/GameObjects/Car.cpp 2010-08-30 00:21:47 UTC (rev 327) @@ -20,6 +20,27 @@ #include <limits> #include <libtrack/TrackBooster.h> +// uncomment and play (or watch replay) with a single human player +// to make artificial neural network training data file. +// Train the neural network to this file with racer_train_ai. +// After that, you can disable it again and run racer t track to improve on +// the neural network by racing mutated AI players against each other. +//#define TRAIN_AI + +#ifdef TRAIN_AI +#include <fstream> +#endif + +const btVector3 feeler_vectors[Engine::GameObjects::Car::NUM_SENSORS_FEELERS] = { + btVector3( 0, 1, 0), + btVector3( 1, 0, 0), + btVector3(-1, 0, 0), + btVector3( 1, 1, 0).normalized(), + btVector3(-1, 1, 0).normalized(), + btVector3(-0.2, 1, 0).normalized(), + btVector3( 0.2, 1, 0).normalized(), + btVector3( 0, -1, 0)}; + const btScalar _force_scale = 1.5; /// stearing torque scale per car model. High values imply car turns easily. @@ -215,6 +236,9 @@ m_drag_sound_source->play(); m_break_sound_source->play(); m_slide_sound_source->play(); + + // we should be in a reasonable starting position + assert(get_position().distance2(rigid_body->getCenterOfMassPosition()) < 225.0); } Car::~Car() @@ -346,8 +370,32 @@ glVertex3f(v[2].x(), v[2].y(), v[2].z()); glEnd(); glColor3f(1,1,1); + + // AI sensors- disabled because it is slightly slower and distracting +#if 0 + btScalar r[NUM_SENSORS]; + get_sensors(r); + // feeler distances + glDisable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); -#endif + glBegin(GL_LINES); + + for (unsigned int i = 0; i < NUM_SENSORS_FEELERS; i++) + { + glTexCoord2f(0, 0); + glVertex3f(transform.getOrigin().x(), + transform.getOrigin().y(), + transform.getOrigin().z()); + btScalar distance = 1.0 / r[i * NUM_FEELER_PROPERTIES]; + btVector3 d =transform(feeler_vectors[i] * distance); + glTexCoord2f(distance, distance); + glVertex3f(d.x(), d.y(), d.z()); + } + glEnd(); + glEnable(GL_DEPTH_TEST); +#endif // 0 + glEnable(GL_TEXTURE_2D); +#endif // debug } void Car::take_input(InputReport & report) @@ -384,6 +432,49 @@ void Car::posttick() { +#ifdef TRAIN_AI + static std::ofstream data_file("ANN_training_data"); + static bool train_data_init = false; + if (!train_data_init) + { + // artifical neural network training data file should begin with the + // number of tests, the number of inputs, and the number of outputs. + // We don't know the number of tests yet, but the rest we can provide: + // The tests is the number of lines subtract 1 (this one), halved (in&out) + data_file << "#tests "<< NUM_SENSORS << " 3\n"; + train_data_init = true; + } + // Save sensor data and response pairs for AI neural network training data. + // The inputs to the neural network are the sensor data. + // every tick is a bit excessive, so provide less data: + if (world.get_tick_number() % 4 == 3) + { + float results[NUM_SENSORS]; + get_sensors(results); + // infinite or NaN values might upset training. + // Also, these are mostly produced when the edge avoision blending + // gives no weight to the neural network anyway, so it doesn't make + // sense to use a neural network on it. + bool good = true; + for (unsigned int i = 0; i < NUM_SENSORS; i++) + { + good &= std::isfinite(results[i]); + } + good &= results[4] > 0.0; + if (good) + { + for (unsigned int i = 0; i < NUM_SENSORS; i++) + { + data_file << results[i] << ' '; + } + data_file << '\n'; + // The outputs are acceleration, slide, and steering, + // scaled to the range -1 to 1: + data_file << force.y()/32767.0 << ' ' << force.x()/32767.0 << ' ' << torque.z()/-32767.0 << '\n'; + } + } +#endif + btTransform transform = rigid_body->getCenterOfMassTransform(); btTransform rotation_transform(transform); rotation_transform.setOrigin(btVector3(0, 0, 0)); @@ -432,7 +523,7 @@ } } infront_of_start = new_infront_of_start; - DEBUG_MESSAGE("Car " << this << " is on lap " << lap); + //DEBUG_MESSAGE("Car " << this << " is on lap " << lap); } // near booster? @@ -463,7 +554,7 @@ // boost boost_timer = 0; m_boost_sound_source->play(); - DEBUG_MESSAGE("Boosted"); + //DEBUG_MESSAGE("Boosted"); } } } @@ -499,7 +590,7 @@ { if (!floor_stick) { - DEBUG_MESSAGE("Sticking car to floor."); + //DEBUG_MESSAGE("Sticking car to floor."); } floor_stick = true; rigid_body->setGravity(btVector3(0.0, 0.0, 0.0)); @@ -567,7 +658,7 @@ { rigid_body->setGravity(btVector3(0.0, 0.0, -9.8)); floor_stick = false; - DEBUG_MESSAGE("Car left floor."); + //DEBUG_MESSAGE("Car left floor."); } } // prevent sliding when not pressing slide key, and break when not accelerating. @@ -699,14 +790,6 @@ return rigid_body->getLinearVelocity().length(); } -btScalar face_u_interpolation(Track::MeshFaces::FaceGraph face, btVector3 point) -{ - btVector3 face_coords = face.to_face_coords(face.find_nearest_point(point)); - return face.fv1.texture_coord_u + - face_coords.x() * (face.fv2.texture_coord_u - face.fv1.texture_coord_u) + - face_coords.y() * (face.fv3.texture_coord_u - face.fv1.texture_coord_u); -} - btScalar Car::get_lap_distance() const { if (m_removed) @@ -714,7 +797,7 @@ return 0; } const Track::MeshFaces::FaceGraph face = m_graph[get_face_descriptor()]; - return face_u_interpolation(face, face.find_nearest_point(get_position())); + return face_u_interpolation(face, get_position()); } void Car::remove() @@ -815,46 +898,133 @@ return 0; } -void Car::get_sensors(float results[6]) const +void Car::get_sensors(float results[NUM_SENSORS]) const { if (m_removed) return; - const Track::MeshFaces::FaceGraph & face = m_graph[get_face_descriptor()]; - btVector3 current_coords = get_position(); - btScalar current_lap_position = face_u_interpolation(face, current_coords); + + // Feeler sensors + + // These return the available distance and relative position along the lap in + // several directions. btTransform transform = rigid_body->getCenterOfMassTransform(); - const int check_vectors_size = 6; - const btVector3 check_vectors[check_vectors_size] = { - btVector3( 0, 1, 0), - btVector3( 0, -1, 0), - btVector3( 1, 0, 0), - btVector3(-1, 0, 0), - btVector3( 1, 1, 0), - btVector3(-1, 1, 0)}; - for (int i = 0; i < check_vectors_size; i++) + // transformation between a global vector and the vector relative to the + // car's rotation (y forwards along the car). + btTransform rotate = transform; + rotate.setOrigin(btVector3(0, 0, 0)); + for (unsigned int i = 0; i < NUM_SENSORS_FEELERS; i++) { - btVector3 facing_coords = transform(check_vectors[i]); - Track::NearTrack front(*this); - front.move_towards(facing_coords); - facing_coords = front.get_position(); - const Track::MeshFaces::FaceGraph & face2 = m_graph[front.get_face_descriptor()]; - btScalar facing_lap_position = face_u_interpolation(face2, facing_coords); - if ( facing_coords.dot(plane_normal) + plane_distance > 0.0 - && current_coords.dot(plane_normal) + plane_distance <= 0.0 - && facing_coords.distance2(start_point) < 289.0) + scan_direction(rotate(feeler_vectors[i]*64.0), &(results[i*NUM_FEELER_PROPERTIES])); + } + + // Non feeler sensors. + btTransform unrotate = rotate.inverse(); + btVector3 current_coords = get_position(); + // sensors for motion: linear velocity and angular velocity. + // remove rotation of the car to make it meaningful. + const unsigned int nonfeeler_start = NUM_SENSORS_FEELERS*NUM_FEELER_PROPERTIES; + const btVector3 velocity = unrotate(rigid_body->getLinearVelocity()); + results[nonfeeler_start] = velocity.x(); + results[nonfeeler_start + 1] = velocity.y(); + results[nonfeeler_start + 2] = velocity.z(); + const btVector3 ang_vel = unrotate(rigid_body->getAngularVelocity()); + results[nonfeeler_start + 3] = ang_vel.x() / 10.0; + results[nonfeeler_start + 4] = ang_vel.y() / 10.0; + results[nonfeeler_start + 5] = ang_vel.z(); + // stored energy + results[nonfeeler_start + 6] = elastic_potential_energy / float(2 << 27); + assert(results[nonfeeler_start + 6] > -1); + // difference between position on the road and car position. + const btVector3 position_diff = unrotate(rigid_body->getCenterOfMassPosition() - current_coords); + results[nonfeeler_start + 7] = position_diff.x(); + results[nonfeeler_start + 8] = position_diff.y(); + results[nonfeeler_start + 9] = position_diff.z(); + // boost strength- 1 just after boost, 0 when no effect (after a second). + results[nonfeeler_start + 10] = (100.0 - boost_timer) * 0.01; + if (results[nonfeeler_start + 10] < 0) results[nonfeeler_start + 10] = 0; + if (results[nonfeeler_start + 10] > 1) results[nonfeeler_start + 10] = 1; + // gravity in z direction - so it knows when it is flying. + results[nonfeeler_start + 11] = rigid_body->getGravity().z() / 9.8; + + // work out a possible direction for steering from the longest feeler distance. + // since distances are inverted, we find the lowest number. + unsigned int best_direction = 0; + btScalar min = std::numeric_limits<btScalar>::max(); + // ignore the backwards one. + for (unsigned int i = 0; i < NUM_SENSORS_FEELERS-1; i++) + { + if (min > results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_NAVIGABLE_DISTANCE]) { - // The point is infont of the finish line, unlike the car's centre. - // this is the minimum it could be. - results[i] = facing_lap_position; - DEBUG_MESSAGE("Crossing line"); + min = results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_NAVIGABLE_DISTANCE]; + best_direction = i; } - else + } + // which direction to steer this way? + if (feeler_vectors[best_direction].x() > 0.0) + { + results[nonfeeler_start + 12] = 1; + } + else if (feeler_vectors[best_direction].x() < 0.0) + { + results[nonfeeler_start + 12] = -1; + } + else // go straight ahead. + { + results[nonfeeler_start + 12] = 0; + } + // if it points backwards, do the opposite. + if (results[best_direction * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL] < 0) + { + results[nonfeeler_start + 12] *= -1; + } + + // work out a possible steering direction from the gradient of lap + // distance of all the feelers. + // this moves the car towards the shortest path, but not necessarily + // the fastest line to take. + // find the most positive gradient's direction. + btScalar max = std::numeric_limits<btScalar>::min(); + for (unsigned int i = 0; i < NUM_SENSORS_FEELERS; i++) + { + if (max < results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL]) { - results[i] = (current_lap_position - facing_lap_position) * - // extra penalty if over the edge of the mesh. - // the nearest point on the mesh will be nearer the car. - facing_coords.distance2(current_coords); + max = results[i * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL]; + best_direction = i; } } + // which direction to steer this way? + if (feeler_vectors[best_direction].x() > 0.0) + { + results[nonfeeler_start + 13] = 1; + } + else if (feeler_vectors[best_direction].x() < 0.0) + { + results[nonfeeler_start + 13] = -1; + } + else + { + if (best_direction == 7) + { + // driving the wrong way. + if (results[1 * NUM_FEELER_PROPERTIES + 4] > results[2 * NUM_FEELER_PROPERTIES + Track::SENSOR_LAP_DIFFERENTIAL]) + { + // positive x better than negative x. Turn that way to face + // the right direction. + results[nonfeeler_start + 13] = 1; + } + else + { + results[nonfeeler_start + 13] = -1; + } + } else { + // keep going straight ahead. + results[nonfeeler_start + 13] = 0; + } + } + + ///@todo direction and distance to nearest booster(s). + ///@todo direction and distance to nearest other car(s). + ///@todo See if the scales can be removed once the AI is good enough. + ///@todo See if any sensor can be removed once the AI is good enough. } void Car::update_sounds() Modified: trunk/racer/Engine/GameObjects/Car.h =================================================================== --- trunk/racer/Engine/GameObjects/Car.h 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/racer/Engine/GameObjects/Car.h 2010-08-30 00:21:47 UTC (rev 327) @@ -31,7 +31,7 @@ namespace GameObjects { -/** A vehical that can be controled by a player using an input device. +/** A vehicle that can be controled by a player using an input device. */ class Car : public Track::AABBDrawable @@ -41,6 +41,13 @@ Car(Car & car); Car & operator=(Car & car); public: + /// The number of directions the cpu car directional sensors read + const static unsigned int NUM_SENSORS_FEELERS = 8; + /// The number of values of data each directional feeler has. + const static unsigned int NUM_FEELER_PROPERTIES = Track::SENSOR_COUNT; + /// The total number of sensor values. + const static unsigned int NUM_SENSORS = NUM_SENSORS_FEELERS * NUM_FEELER_PROPERTIES + 14; + /** Construct the car and place it into the game. * @param world The Physics world of the game. * @param start The transformation for the starting position. @@ -112,7 +119,7 @@ * 1m behind the car, 1m to the left of the car, 1m to the right of the car * @param results array where calculated values will be written. */ - void get_sensors(float results[6]) const; + void get_sensors(float results[NUM_SENSORS]) const; private: // graphical representation Track::Texture *texture; Modified: trunk/racer/Engine/InputDeviceAI.cpp =================================================================== --- trunk/racer/Engine/InputDeviceAI.cpp 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/racer/Engine/InputDeviceAI.cpp 2010-08-30 00:21:47 UTC (rev 327) @@ -23,12 +23,15 @@ : acceleration(0) , slide(0) , steering(0) - + , ann(fann_create_from_file("data/ai.net")) { + fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE); + fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE); } InputDeviceAI::~InputDeviceAI() { + fann_destroy(ann); } int InputDeviceAI::get_acceleration() @@ -48,56 +51,236 @@ void InputDeviceAI::poll() { - int new_acceleration = 0; - int new_slide = 0; - int new_steering = 0; - /// @todo make good AI. Neural network? - float sensors[6] ; + fann_type sensors[Engine::GameObjects::Car::NUM_SENSORS] ; assert(car); car->get_sensors(sensors); - if (sensors[0] > 0.0) + /** @todo Blend with some more manual behaviour as the car approaches the + * boundary of the nav mesh. + * This behaviour should check all directions, not just forwards. + * It should help most with narrow paths. + * Possible solution: + * Pick slide based on left and right sensors to move towards the middle. + * Weighted blend for slide between neural network using minimum sideways + * distance. + * Pick stearing based on front left and front right sensors towards middle. + * Weighted blend for stearing based on minimum distance on forward diagonals. + * Pick acceleration based on forwards and backwards sensors towards biggest space. + * Weighted blend for acceleration with neural netowrk using minimum y + * distance. + * If the car is facing backwards, replace steering. + */ + const btScalar min_edge_distance = 0.05; // 5cm or less is full avoision + const btScalar max_edge_distance = 0.8; // 80cm or more is full ANN. + const btScalar edge_distance_range = max_edge_distance - min_edge_distance; + // sensors have the inverse of the distance. + const btScalar min_edge_sensor = 1.0 / min_edge_distance; + const btScalar max_edge_sensor = 1.0 / max_edge_distance; + // slide to avoid edges + btScalar left_sensor = sensors[Track::SENSOR_COUNT * 1 + Track::SENSOR_NAVIGABLE_DISTANCE]; + btScalar right_sensor = sensors[Track::SENSOR_COUNT * 2 + Track::SENSOR_NAVIGABLE_DISTANCE]; + btScalar edge_avoid_slide = 0; + btScalar slide_avoid_weight = 0; + if (left_sensor > right_sensor) { - new_acceleration = -32767; + edge_avoid_slide = -32767; + slide_avoid_weight = left_sensor; } - else if (sensors[1] > 0.0) + else if (right_sensor > left_sensor) { - new_acceleration = 32767; + edge_avoid_slide = 32767; + slide_avoid_weight = right_sensor; } + // change the weights to give sensible values between 0 and 1. + if (slide_avoid_weight > min_edge_sensor || !std::isfinite(slide_avoid_weight)) + { + slide_avoid_weight = 1.0; + } + else if (slide_avoid_weight < max_edge_sensor) + { + slide_avoid_weight = 0.0; + } + else + { + slide_avoid_weight = 1.0 - ((1.0 / slide_avoid_weight) - min_edge_distance) / edge_distance_range; + } - if (sensors[2] > 0.0 && sensors[3] <= 0.0) + // steer to avoid edges + btScalar front_left_sensor = sensors[Track::SENSOR_COUNT * 3 + Track::SENSOR_NAVIGABLE_DISTANCE]; + btScalar front_right_sensor = sensors[Track::SENSOR_COUNT * 4 + Track::SENSOR_NAVIGABLE_DISTANCE]; + btScalar edge_avoid_steer = 0; + btScalar steer_avoid_weight = 0; + if (front_left_sensor > front_right_sensor) { - new_slide = -32767; + edge_avoid_steer = -32767; + steer_avoid_weight = front_left_sensor; } - else if (sensors[3] > 0.0) + else if (front_left_sensor < front_right_sensor) { - new_slide = 32767; + edge_avoid_steer = 32767; + steer_avoid_weight = front_right_sensor; } + // adjust weight to sensible value between 0 and 1. + if (steer_avoid_weight > min_edge_sensor) + { + steer_avoid_weight = 1.0; + } + else if (steer_avoid_weight < max_edge_sensor) + { + steer_avoid_weight = 0.0; + } + else + { + steer_avoid_weight = 1.0 - ((1.0 / steer_avoid_weight) - min_edge_distance) / edge_distance_range; + } - if (sensors[4] > 0.0 && sensors[5] <= 0.0) + // accelerate/reverse to avoid going straight forwards or backwards over the edge. + btScalar front_sensor = sensors[Track::SENSOR_NAVIGABLE_DISTANCE]; + btScalar back_sensor = sensors[Track::SENSOR_NAVIGABLE_DISTANCE + (GameObjects::Car::NUM_SENSORS_FEELERS -1)* Track::SENSOR_COUNT]; + btScalar edge_avoid_accel = 0; + btScalar accel_avoid_weight = 0; + if (front_sensor > back_sensor) { - new_steering = -32767; + edge_avoid_accel = -32767; + accel_avoid_weight = front_sensor; } - else if (sensors[5] > 0.0) + else if (front_sensor < back_sensor) { - new_steering = 32767; + edge_avoid_accel = 32867; + accel_avoid_weight = back_sensor; } + // adjust weight to sensible value between 0 and 1. + if (accel_avoid_weight > min_edge_sensor) + { + accel_avoid_weight = 1.0; + } + else if (accel_avoid_weight < max_edge_sensor) + { + accel_avoid_weight = 0.0; + } + else + { + accel_avoid_weight = 1.0 - ((1.0 / accel_avoid_weight) - min_edge_distance) / edge_distance_range; + } - new_steering = new_steering / 4 * 3 + new_slide / 4; - - if (new_acceleration != acceleration) + #if 0 + if (sensors[Track::SENSOR_LAP_DIFFERENTIAL] > 0.0) { - acceleration = new_acceleration; - report(InputReport::RT_CHANGE_ACCEL, new_acceleration); + #endif + fann_type *output; + output = fann_run(ann, sensors); + + for (int i = 0; i < 4; i++) + { + output[i] *= 32767.0; + if (output[i] > 32767) output[i] = 32767; + if (output[i] < -32767) output[i] = -32767; + } + //weighted blend outputs for edge avoision + output[0] = output[0] * (1.0 - accel_avoid_weight) + edge_avoid_accel * accel_avoid_weight; + output[1] = output[1] * (1.0 - slide_avoid_weight) + edge_avoid_slide * slide_avoid_weight; + output[2] = output[2] * (1.0 - steer_avoid_weight) + edge_avoid_steer * steer_avoid_weight; + if (int(output[0]) != acceleration) + { + acceleration = int(output[0]); + report(InputReport::RT_CHANGE_ACCEL, output[0]); + } + + if (int(output[1]) != slide) + { + slide = int(output[1]); + report(InputReport::RT_CHANGE_SLIDE, output[1]); + } + + if (int(output[2]) != steering) + { + steering = int(output[2]); + report(InputReport::RT_CHANGE_STEERING, output[2]); + } + #if 0 + } else if (std::isfinite(sensors[Track::SENSOR_LAP_DIFFERENTIAL])) + { + // facing backwards. + // try to slow down if going forwards + if (sensors[Engine::GameObjects::Car::NUM_SENSORS_FEELERS * Engine::GameObjects::Car::NUM_FEELER_PROPERTIES + 1] > 0.0) + { + acceleration = -32767; + } else { + acceleration = 0; + } + report(InputReport::RT_CHANGE_ACCEL, acceleration); + report(InputReport::RT_CHANGE_SLIDE, 0); + // turn around. + int ts = sensors[Engine::GameObjects::Car::NUM_SENSORS - 1]*32767; + if (ts != steering) + { + steering = ts; + report(InputReport::RT_CHANGE_STEERING, ts); + } + //DEBUG_MESSAGE("Oh no! Wrong way!"); + } else { + // driven over the boundary + if (std::isfinite(sensors[Engine::GameObjects::Car::NUM_FEELER_PROPERTIES * (Engine::GameObjects::Car::NUM_SENSORS_FEELERS - 1) + Track::SENSOR_LAP_DIFFERENTIAL])) + { + // backwards is safe + // reverse hard to reduce change of falling off. + report(InputReport::RT_CHANGE_ACCEL, -32767); + } + if (std::isfinite(sensors[Engine::GameObjects::Car::NUM_FEELER_PROPERTIES * 1 + Track::SENSOR_LAP_DIFFERENTIAL])) + { + // +x is available + report(InputReport::RT_CHANGE_SLIDE, 32767); + report(InputReport::RT_CHANGE_STEERING, 32767); + } + else if (std::isfinite(sensors[Engine::GameObjects::Car::NUM_FEELER_PROPERTIES * 2 + Track::SENSOR_LAP_DIFFERENTIAL])) + { + // -x is available + report(InputReport::RT_CHANGE_SLIDE, -32767); + report(InputReport::RT_CHANGE_STEERING, -32767); + } + //DEBUG_MESSAGE("Argh, I've driven over the edge!"); } - if (new_slide != slide) + #endif +} + +void InputDeviceAI::save() +{ + fann_save(ann, "data/ai.net"); +} + +void InputDeviceAI::mutate() +{ + // set a few connection weights to random values. + int num_connections = fann_get_total_connections(ann); + fann_connection cs[num_connections]; + fann_get_connection_array(ann, cs); + + // decide how many variables to mutate + float v = float(rand())/float(RAND_MAX); + // raise it to the power of three so we get more lower numbers. + v *= v*v; + unsigned int variables_mutated = 1 + int(v * (num_connections - 1)); + + // mutate the variables + for (unsigned int i = 0; i < variables_mutated; i++) { - slide = new_slide; - report(InputReport::RT_CHANGE_SLIDE, new_slide); + int w = rand()/(RAND_MAX/num_connections); + btScalar scale = std::pow(2, rand() / (RAND_MAX / 15) - 5); + fann_set_weight(ann, cs[w].from_neuron, cs[w].to_neuron, + float(rand())/float(RAND_MAX)*scale - scale/2.0); } - if (new_steering != steering) +} + +void InputDeviceAI::cross(const InputDeviceAI & p1, const InputDeviceAI & p2) +{ + unsigned int num_connections = fann_get_total_connections(ann); + fann_connection c2[num_connections]; + fann_get_connection_array(p2.ann, c2); + for (unsigned int i = 0; i < num_connections; i++) { - steering = new_steering; - report(InputReport::RT_CHANGE_STEERING, new_steering); + if (rand()/(RAND_MAX/2)) + { + fann_set_weight(ann, c2[i].from_neuron, c2[i].to_neuron, c2[i].weight); + } } } Modified: trunk/racer/Engine/InputDeviceAI.h =================================================================== --- trunk/racer/Engine/InputDeviceAI.h 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/racer/Engine/InputDeviceAI.h 2010-08-30 00:21:47 UTC (rev 327) @@ -12,6 +12,7 @@ #define INPUT_DEVICE_AI_H #include "InputDevice.h" +#include "floatfann.h" namespace Engine { @@ -28,8 +29,15 @@ virtual int get_slide(); virtual int get_steering(); virtual void poll(); + /// Write the neural network to a file. + void save(); + /// Add some random variance to the neural network + void mutate(); + /// Set neural network weights that combine those of another two InputDeviceAIs. + void cross(const InputDeviceAI & p1, const InputDeviceAI & p2); private: int acceleration, slide, steering; + fann * ann; }; } // namespace Engine Modified: trunk/racer/Engine/LoadScene.h =================================================================== --- trunk/racer/Engine/LoadScene.h 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/racer/Engine/LoadScene.h 2010-08-30 00:21:47 UTC (rev 327) @@ -63,6 +63,7 @@ , function_object(0) , done(false) , error(false) + , m_number_of_ais(number_of_ais) { main_loop = 0; } @@ -153,7 +154,7 @@ // Add some AI players /// @todo Let a player pick the number of AI players? - for (unsigned int ai = 0; ai < 1; ai++) + for (unsigned int ai = 0; ai < m_number_of_ais; ai++) { m_ai_devices.push_back(boost::shared_ptr<InputDeviceAI>(new InputDeviceAI())); input_devices.push_back(std::pair<InputHandler::iterator, unsigned int>( @@ -268,6 +269,9 @@ /// True if and only if track cannot be loaded. bool error; + + /// The number of computer controlled cars requested. + unsigned int m_number_of_ais; }; } // namespace Engine Modified: trunk/racer/Engine/Makefile.am =================================================================== --- trunk/racer/Engine/Makefile.am 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/racer/Engine/Makefile.am 2010-08-30 00:21:47 UTC (rev 327) @@ -1,6 +1,7 @@ SUBDIRS = GameObjects Physics noinst_LTLIBRARIES = libengine.la -libengine_la_SOURCES = Audio.cpp Audio.h\ +libengine_la_SOURCES = AITrainer.cpp AITrainer.h\ + Audio.cpp Audio.h\ CarCamera.cpp CarCamera.h\ GameScene.cpp GameScene.h\ InputDevice.cpp InputDevice.h\ Modified: trunk/racer/Engine/Physics/World.cpp =================================================================== --- trunk/racer/Engine/Physics/World.cpp 2010-08-22 02:36:30 UTC (rev 326) +++ trunk/racer/Engine/Physics/World.cpp 2010-08-30 00:21:47 UTC (rev 327) @@ -120,6 +120,12 @@ } } } + // check for input (some of the time so AI doesn't take so long). + // input is additionaly checked by scenes once per rendered frame. + if (!(tick_number % 4)) + { + Engine::InputHandler::get_instance().poll(); + } // forces set in the tick callback don't seem ... [truncated message content] |
From: <jl...@us...> - 2010-08-22 02:36:38
|
Revision: 326 http://racer.svn.sourceforge.net/racer/?rev=326&view=rev Author: jlegg Date: 2010-08-22 02:36:30 +0000 (Sun, 22 Aug 2010) Log Message: ----------- Beginning of AI. Modified Paths: -------------- trunk/racer/Engine/GameObjects/Car.cpp trunk/racer/Engine/GameObjects/Car.h trunk/racer/Engine/GameScene.cpp trunk/racer/Engine/InputDevice.cpp trunk/racer/Engine/InputDevice.h trunk/racer/Engine/InputDeviceJoystick.cpp trunk/racer/Engine/InputDeviceJoystick.h trunk/racer/Engine/InputDeviceKeyboard.cpp trunk/racer/Engine/LoadScene.h trunk/racer/Engine/Makefile.am trunk/racer/UI/GameStartMenuItem.cpp Added Paths: ----------- trunk/racer/Engine/InputDeviceAI.cpp trunk/racer/Engine/InputDeviceAI.h Modified: trunk/racer/Engine/GameObjects/Car.cpp =================================================================== --- trunk/racer/Engine/GameObjects/Car.cpp 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/GameObjects/Car.cpp 2010-08-22 02:36:30 UTC (rev 326) @@ -815,6 +815,48 @@ return 0; } +void Car::get_sensors(float results[6]) const +{ + if (m_removed) return; + const Track::MeshFaces::FaceGraph & face = m_graph[get_face_descriptor()]; + btVector3 current_coords = get_position(); + btScalar current_lap_position = face_u_interpolation(face, current_coords); + btTransform transform = rigid_body->getCenterOfMassTransform(); + const int check_vectors_size = 6; + const btVector3 check_vectors[check_vectors_size] = { + btVector3( 0, 1, 0), + btVector3( 0, -1, 0), + btVector3( 1, 0, 0), + btVector3(-1, 0, 0), + btVector3( 1, 1, 0), + btVector3(-1, 1, 0)}; + for (int i = 0; i < check_vectors_size; i++) + { + btVector3 facing_coords = transform(check_vectors[i]); + Track::NearTrack front(*this); + front.move_towards(facing_coords); + facing_coords = front.get_position(); + const Track::MeshFaces::FaceGraph & face2 = m_graph[front.get_face_descriptor()]; + btScalar facing_lap_position = face_u_interpolation(face2, facing_coords); + if ( facing_coords.dot(plane_normal) + plane_distance > 0.0 + && current_coords.dot(plane_normal) + plane_distance <= 0.0 + && facing_coords.distance2(start_point) < 289.0) + { + // The point is infont of the finish line, unlike the car's centre. + // this is the minimum it could be. + results[i] = facing_lap_position; + DEBUG_MESSAGE("Crossing line"); + } + else + { + results[i] = (current_lap_position - facing_lap_position) * + // extra penalty if over the edge of the mesh. + // the nearest point on the mesh will be nearer the car. + facing_coords.distance2(current_coords); + } + } +} + void Car::update_sounds() { // The sounds are stopped when the car goes. Modified: trunk/racer/Engine/GameObjects/Car.h =================================================================== --- trunk/racer/Engine/GameObjects/Car.h 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/GameObjects/Car.h 2010-08-22 02:36:30 UTC (rev 326) @@ -105,6 +105,14 @@ * Takes into account the slope of the track infront of the car. */ btScalar get_camera_height() const; + + /** Return the relative distance to the goal in various directions. + * Gives the difference between the distance to the goal of the car and + * the difference to the goal of other points: 1m infront of the car, + * 1m behind the car, 1m to the left of the car, 1m to the right of the car + * @param results array where calculated values will be written. + */ + void get_sensors(float results[6]) const; private: // graphical representation Track::Texture *texture; Modified: trunk/racer/Engine/GameScene.cpp =================================================================== --- trunk/racer/Engine/GameScene.cpp 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/GameScene.cpp 2010-08-22 02:36:30 UTC (rev 326) @@ -94,6 +94,7 @@ const std::size_t number_of_cars = input_devices.size(); cars.reserve(number_of_cars); + /** @todo Don't make cameras for AI cars. */ car_cameras.reserve(number_of_cars); for(unsigned int i = 0; i< number_of_cars; i++) { Modified: trunk/racer/Engine/InputDevice.cpp =================================================================== --- trunk/racer/Engine/InputDevice.cpp 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/InputDevice.cpp 2010-08-22 02:36:30 UTC (rev 326) @@ -42,5 +42,17 @@ return handler_reference; } +void InputDevice::report(InputReport::ReportType type, int value) +{ + InputReport report(this, type, value); + if(scene) + { + scene->take_input(report); + } + if (car) + { + car->take_input(report); + } +} } Modified: trunk/racer/Engine/InputDevice.h =================================================================== --- trunk/racer/Engine/InputDevice.h 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/InputDevice.h 2010-08-22 02:36:30 UTC (rev 326) @@ -46,7 +46,7 @@ * right). 0 indicates no sliding. */ virtual int get_slide() = 0; - /* Stearing angle + /** Stearing angle * @return a number between -32767 (maximum left) and 32767 (maximum * right). 0 is straight ahead. */ @@ -65,6 +65,11 @@ Scene * scene; GameObjects::Car * car; std::set<InputDevice *>::iterator handler_reference; + /** Make a report, then submit it the car and scene as required. + * @param type The classification of the event that occured. + * @param value The new value for steering, slide, and acceleration changed events. + */ + void report(InputReport::ReportType type, int value = 0); }; } Added: trunk/racer/Engine/InputDeviceAI.cpp =================================================================== --- trunk/racer/Engine/InputDeviceAI.cpp (rev 0) +++ trunk/racer/Engine/InputDeviceAI.cpp 2010-08-22 02:36:30 UTC (rev 326) @@ -0,0 +1,104 @@ +/** @file InputDeviceAI.cpp + * @brief Implement the Engine::InputDeviceAI class. + * @author James Legg + */ +/* Copyright © 2010 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ + +#include "InputDeviceAI.h" +#include <Debug.h> +#include "GameObjects/Car.h" + +namespace Engine +{ + +/** A input device for a computer player. + * Simulated events occur when an AI decides. + */ +InputDeviceAI::InputDeviceAI() + : acceleration(0) + , slide(0) + , steering(0) + +{ +} + +InputDeviceAI::~InputDeviceAI() +{ +} + +int InputDeviceAI::get_acceleration() +{ + return acceleration; +} + +int InputDeviceAI::get_slide() +{ + return slide; +} + +int InputDeviceAI::get_steering() +{ + return steering; +} + +void InputDeviceAI::poll() +{ + int new_acceleration = 0; + int new_slide = 0; + int new_steering = 0; + /// @todo make good AI. Neural network? + float sensors[6] ; + assert(car); + car->get_sensors(sensors); + if (sensors[0] > 0.0) + { + new_acceleration = -32767; + } + else if (sensors[1] > 0.0) + { + new_acceleration = 32767; + } + + if (sensors[2] > 0.0 && sensors[3] <= 0.0) + { + new_slide = -32767; + } + else if (sensors[3] > 0.0) + { + new_slide = 32767; + } + + if (sensors[4] > 0.0 && sensors[5] <= 0.0) + { + new_steering = -32767; + } + else if (sensors[5] > 0.0) + { + new_steering = 32767; + } + + new_steering = new_steering / 4 * 3 + new_slide / 4; + + if (new_acceleration != acceleration) + { + acceleration = new_acceleration; + report(InputReport::RT_CHANGE_ACCEL, new_acceleration); + } + if (new_slide != slide) + { + slide = new_slide; + report(InputReport::RT_CHANGE_SLIDE, new_slide); + } + if (new_steering != steering) + { + steering = new_steering; + report(InputReport::RT_CHANGE_STEERING, new_steering); + } +} + +} // namespace Engine Added: trunk/racer/Engine/InputDeviceAI.h =================================================================== --- trunk/racer/Engine/InputDeviceAI.h (rev 0) +++ trunk/racer/Engine/InputDeviceAI.h 2010-08-22 02:36:30 UTC (rev 326) @@ -0,0 +1,37 @@ +/** @file InputDeviceAI.h + * @brief Declare the Engine::InputDeviceAI class. + * @author James Legg + */ +/* Copyright © 2010 James Legg. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. +*/ +#ifndef INPUT_DEVICE_AI_H +#define INPUT_DEVICE_AI_H + +#include "InputDevice.h" + +namespace Engine +{ + +/** A input device for a computer player. + * Simulated events occur when an AI decides. + */ +class InputDeviceAI : public Engine::InputDevice +{ +public: + InputDeviceAI(); + virtual ~InputDeviceAI(); + virtual int get_acceleration(); + virtual int get_slide(); + virtual int get_steering(); + virtual void poll(); +private: + int acceleration, slide, steering; +}; + +} // namespace Engine + +#endif // INPUT_DEVICE_AI_H Modified: trunk/racer/Engine/InputDeviceJoystick.cpp =================================================================== --- trunk/racer/Engine/InputDeviceJoystick.cpp 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/InputDeviceJoystick.cpp 2010-08-22 02:36:30 UTC (rev 326) @@ -176,17 +176,4 @@ } } -void InputDeviceJoystick::report(InputReport::ReportType type, int value) -{ - InputReport report(this, type, value); - if(scene) - { - scene->take_input(report); - } - if (car) - { - car->take_input(report); - } } - -} Modified: trunk/racer/Engine/InputDeviceJoystick.h =================================================================== --- trunk/racer/Engine/InputDeviceJoystick.h 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/InputDeviceJoystick.h 2010-08-22 02:36:30 UTC (rev 326) @@ -40,8 +40,6 @@ SDL_Joystick *sdl_joystick_handle; int steering, acceleration, slide; bool slide_left_down, slide_right_down; - /// Report an event to linked scene and car. - void report(InputReport::ReportType type, int value = 0); }; } Modified: trunk/racer/Engine/InputDeviceKeyboard.cpp =================================================================== --- trunk/racer/Engine/InputDeviceKeyboard.cpp 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/InputDeviceKeyboard.cpp 2010-08-22 02:36:30 UTC (rev 326) @@ -131,15 +131,7 @@ break; } // Update the scene with a report for the keypress. - InputReport report(this, report_type, value); - if(scene) - { - scene->take_input(report); - } - if (car) - { - car->take_input(report); - } + report(report_type, value); } } Modified: trunk/racer/Engine/LoadScene.h =================================================================== --- trunk/racer/Engine/LoadScene.h 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/LoadScene.h 2010-08-22 02:36:30 UTC (rev 326) @@ -13,6 +13,7 @@ #include "GameScene.h" #include "InputHandler.h" +#include "InputDeviceAI.h" #include "../MainLoop.h" #include "../UI/BasicFonts.h" #include "../Graphics/Window.h" @@ -51,9 +52,11 @@ * @param input_devices InputHandler iterators for devices to use, paired * with the car each device's user wants. The order is important. * @param filename The filename of the track. + * @param number_of_ais The number of AI players wanted. */ LoadScene(std::vector<std::pair<InputHandler::iterator, unsigned int> > input_devices, - std::string filename) + std::string filename, + unsigned int number_of_ais = 0) : blanked(false) , track_filename(filename) , input_devices(input_devices) @@ -148,6 +151,16 @@ // make the AI graph for the track. track->update_ai_mesh(); + // Add some AI players + /// @todo Let a player pick the number of AI players? + for (unsigned int ai = 0; ai < 1; ai++) + { + m_ai_devices.push_back(boost::shared_ptr<InputDeviceAI>(new InputDeviceAI())); + input_devices.push_back(std::pair<InputHandler::iterator, unsigned int>( + m_ai_devices.back()->get_handle(), + ai%2)); // ai alterenatly uses both cars + } + // create the game scene game_scene = boost::shared_ptr<T>(new T(input_devices, *track)); game_scene->attach_main_loop(*main_loop); @@ -240,9 +253,13 @@ /// The course to play on boost::shared_ptr<Track::Track> track; - boost::shared_ptr<T> game_scene; + /// The InputDevices for the computer controlled players. + std::vector<boost::shared_ptr<Engine::InputDeviceAI> > m_ai_devices; + /// Devices to use for inputs paired with chosen car. std::vector<std::pair<InputHandler::iterator, unsigned int> > input_devices; + + boost::shared_ptr<T> game_scene; /// object to send new scene to. Q * function_object; Modified: trunk/racer/Engine/Makefile.am =================================================================== --- trunk/racer/Engine/Makefile.am 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/Engine/Makefile.am 2010-08-22 02:36:30 UTC (rev 326) @@ -4,6 +4,7 @@ CarCamera.cpp CarCamera.h\ GameScene.cpp GameScene.h\ InputDevice.cpp InputDevice.h\ + InputDeviceAI.cpp InputDeviceAI.h\ InputDeviceJoystick.cpp InputDeviceJoystick.h\ InputDeviceKeyboard.cpp InputDeviceKeyboard.h\ InputDeviceReplay.cpp InputDeviceReplay.h\ Modified: trunk/racer/UI/GameStartMenuItem.cpp =================================================================== --- trunk/racer/UI/GameStartMenuItem.cpp 2010-08-20 02:47:57 UTC (rev 325) +++ trunk/racer/UI/GameStartMenuItem.cpp 2010-08-22 02:36:30 UTC (rev 326) @@ -38,17 +38,6 @@ void GameStartMenuItem::activate() { assert(main_loop); - DEBUG_MESSAGE("Using one player per avaliable controller."); - std::vector<Engine::InputHandler::iterator> input_devices; - Engine::InputHandler & input_handler = Engine::InputHandler::get_instance(); - input_devices.reserve(input_handler.get_number_of_devices()); - - for (Engine::InputHandler::iterator it = input_handler.begin(); - it != input_handler.end(); - it++) - { - input_devices.push_back(it); - } glPushAttrib(GL_ALL_ATTRIB_BITS); // select cars CarSelectScene car_select_scene; @@ -57,7 +46,8 @@ if (!car_select_scene.get_canceled()) { Engine::LoadScene<> load_scene(car_select_scene.get_choice(), - filename); + filename, + 1); main_loop->push_scene(load_scene); } glPopAttrib(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jl...@us...> - 2010-08-20 02:48:03
|
Revision: 325 http://racer.svn.sourceforge.net/racer/?rev=325&view=rev Author: jlegg Date: 2010-08-20 02:47:57 +0000 (Fri, 20 Aug 2010) Log Message: ----------- When starting the editor, set keyboard focus to the list of themes, not the quit button on the toolbar. Modified Paths: -------------- trunk/racer_editor/EditorWindow.cpp trunk/racer_editor/NewForm.cpp trunk/racer_editor/NewForm.h Modified: trunk/racer_editor/EditorWindow.cpp =================================================================== --- trunk/racer_editor/EditorWindow.cpp 2010-08-08 01:35:46 UTC (rev 324) +++ trunk/racer_editor/EditorWindow.cpp 2010-08-20 02:47:57 UTC (rev 325) @@ -52,6 +52,9 @@ if (filename != "") { open_file(filename); + } else { + // give the theme list focus. + m_new_form.grab_focus(); } } @@ -825,4 +828,7 @@ m_viewport_top.set_document(*m_document); m_viewport_top.show(); m_guide.set_visible(m_guide_preference); + + // Move keyboard focus to the view controls on the toolbar. + m_ref_ui_manager->get_widget("/ToolBar/ShowTop")->grab_focus(); } Modified: trunk/racer_editor/NewForm.cpp =================================================================== --- trunk/racer_editor/NewForm.cpp 2010-08-08 01:35:46 UTC (rev 324) +++ trunk/racer_editor/NewForm.cpp 2010-08-20 02:47:57 UTC (rev 325) @@ -67,6 +67,11 @@ } } +void NewForm::grab_focus() +{ + theme_icon_view.grab_focus(); +} + void NewForm::set_themes() { add_themes_from("data"); Modified: trunk/racer_editor/NewForm.h =================================================================== --- trunk/racer_editor/NewForm.h 2010-08-08 01:35:46 UTC (rev 324) +++ trunk/racer_editor/NewForm.h 2010-08-20 02:47:57 UTC (rev 325) @@ -47,6 +47,10 @@ * disk doesn't delay the time until the main window appears. */ void find_themes(); + + /** Give the theme list keyboard focus for the window it is inside. + */ + void grab_focus(); protected: /// parent window Gtk::Window & window; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |