// Ac3d - 3D objects
//
// Copyright (C) 2003 Sam Varner
//
// This file is part of Vamos Automotive Simulator.
//
// Vamos 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.
//
// Vamos 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 Vamos. If not, see <http://www.gnu.org/licenses/>.
#ifndef _AC3D_H_
#define _AC3D_H_
#include "Texture_Image.hpp"
#include "../geometry/Three_Matrix.hpp"
#include "../geometry/Three_Vector.hpp"
#include <GL/gl.h>
#include <memory>
#include <string>
#include <vector>
namespace Vamos_Media
{
struct Ac3d_Exception : std::exception
{
Ac3d_Exception(std::string message) : message(message) {};
virtual const char* what() const noexcept override { return message.c_str(); }
std::string message;
};
struct No_File : public Ac3d_Exception
{
No_File(std::string message) : Ac3d_Exception(message) {};
};
struct Not_An_Ac3d_File : public Ac3d_Exception
{
Not_An_Ac3d_File(std::string message) : Ac3d_Exception(message) {};
};
struct Malformed_Ac3d_File : public Ac3d_Exception
{
Malformed_Ac3d_File(std::string message) : Ac3d_Exception(message) {};
};
/// The material properties for a surface. The names of their members and accessors mean
/// the same things as the OpenGL material parameters.
struct Ac3d_Material
{
std::array<GLfloat, 3> color;
std::array<GLfloat, 3> ambient;
std::array<GLfloat, 3> emission;
std::array<GLfloat, 3> specular;
GLfloat shininess;
GLfloat transparency;
};
// The first three are defined by the AC3D file format. The POLYGON type may be
// changed to one of the triangle or quadrilateral types.
enum Figure_Type
{
POLYGON, LINE, CLOSED_LINE, TRIANGLE, TRIANGLE_STRIP, TRIANGLE_FAN,
QUADRILATERAL, QUADRILATERAL_STRIP
};
struct Vertex
{
Vertex(const Vamos_Geometry::Three_Vector& v) : coordinates(v) {}
Vertex() {}
Vamos_Geometry::Three_Vector coordinates;
Vamos_Geometry::Three_Vector normal;
double texture_x = 0.0;
double texture_y = 0.0;
};
/// A surface is a rendered figure such as a triangle, or a strip of quadrilaterals.
/// Vertices, normals, and material properties are stored in a surface object.
class Ac3d_Surface
{
using V_Vertex = std::vector<Vertex>;
public:
Ac3d_Surface(std::string figure_type_code, double scale,
const Vamos_Geometry::Three_Vector& offset,
const Vamos_Geometry::Three_Matrix& rotation);
void set_material(const Vamos_Media::Ac3d_Material* mat) { mp_material = mat; }
const Ac3d_Material* get_material() const { return mp_material; }
void set_figure_type(Figure_Type type) { m_figure_type = type; }
Figure_Type get_figure_type() const { return m_figure_type; }
// Add a vertex from another surface. Keep its normal.
void add_vertex(const Vertex& vert);
// Add new vertices. Calculate normals if more than 2.
void add_vertices(const V_Vertex& verts);
const V_Vertex& get_vertices() const { return m_vertices; }
/// Change the order of the vertices when converting a polygon to a strip.
void rearrange_vertices(const std::vector<size_t>& index);
bool is_shaded() const { return m_shaded; }
bool is_two_sided() const { return m_two_sided; }
/// Add the OpenGL calls for rendering this surface to the display list.
void build() const;
private:
// Return the appropriate argument for glBegin().
GLenum get_gl_figure_type() const;
void set_material_properties() const;
void draw_figure() const;
/// Materials and vertices are handled by pointer. Vertices on different surfaces may
/// point to the same vertex object. Materials are owned by the Ac3d object.
const Vamos_Media::Ac3d_Material* mp_material = nullptr;
V_Vertex m_vertices;
// See enum Figure_Type above.
Figure_Type m_figure_type;
bool m_shaded;
bool m_two_sided;
// Transformations
double m_scale;
Vamos_Geometry::Three_Vector m_offset;
Vamos_Geometry::Three_Matrix m_rotation;
};
/// An array of surfaces for an object. This class turns individual polygons into strips
/// if it can.
class V_Surface
{
using V_Vertex = std::vector<Vertex>;
public:
void push_back(Ac3d_Surface* surface);
bool join_surface(const Ac3d_Surface* surface);
void build() const;
private:
bool join_quadrilateral_to_edge(size_t index1, size_t index2,
const V_Vertex& old_vertices, const V_Vertex& new_vertices);
bool join_triangle_to_edge(size_t index1, size_t index2, const V_Vertex& old_vertices,
const V_Vertex& new_vertices);
bool join_quadrilateral(const V_Vertex& new_vertices, size_t olddex1,
size_t olddex2, size_t newdex1, size_t newdex2);
bool join_triangle(const V_Vertex& new_vertices, size_t newdex1, size_t newdex2,
Figure_Type new_type);
/// Vertex indices used to see if a quadrilateral candidate for joining would join at
// the same side as the others.
size_t m_last_vertex1;
size_t m_last_vertex2;
std::vector<Vamos_Media::Ac3d_Surface*> m_surfaces;
};
/// An object is a collection of surfaces. Objects store the vertices that the surfaces'
/// vertices point to.
class Ac3d_Object
{
public:
Ac3d_Object(const Vamos_Geometry::Three_Vector& translation,
const Vamos_Geometry::Three_Vector& rotation);
void set_texture_image(std::string file);
void set_rotation(const Vamos_Geometry::Three_Matrix& rot);
const Vamos_Geometry::Three_Matrix& get_rotation() const { return m_rotation; }
void set_location(const Vamos_Geometry::Three_Vector& loc);
const Vamos_Geometry::Three_Vector& get_location() const { return m_location; }
void add_kid(const Ac3d_Object* obj) { m_kids.emplace_back(obj); }
void add_surface(Vamos_Media::Ac3d_Surface* surf);
void build() const;
private:
Vamos_Geometry::Three_Matrix m_rotation;
Vamos_Geometry::Three_Vector m_location;
std::unique_ptr<Texture_Image> mp_texture;
std::vector<std::unique_ptr<const Ac3d_Object>> m_kids;
V_Surface m_surfaces;
};
/// Class Ac3d owns objects. Objects own surfaces, and other objects. Surfaces own
/// materials, and vertices.
class Ac3d
{
public:
using V_Material = std::vector<std::unique_ptr<const Ac3d_Material>>;
using V_Object = std::vector<std::unique_ptr<const Ac3d_Object>>;
Ac3d(std::string file, double scale, const Vamos_Geometry::Three_Vector& rotation,
const Vamos_Geometry::Three_Vector& translation);
GLuint build();
private:
V_Material m_materials;
V_Object m_objects;
};
} // namespace Vamos_Media
#endif // not _AC3D_H_