Scratch openGL-- Third, the model loads and mouse interaction to achieve

Foreword

 In the last article , we introduce the basic graphics rendering. This blog will introduce load, draw and realize mouse interaction model.

Load Model

Model storage

To achieve model reading, drawing, we first need to know how the model is stored in a file.

Usually by a grid model is composed of, and generally triangular mesh. The reason is:

  1. Other polygonal mesh can be easily split triangular
  2. Three coplanar: to ensure planarity
  3. You can easily define the inner and outer direction, the interpolation operation

The data structure may be employed comprises:

  1. Face list
    • Storing surface vertex triples (v1, v2, v3)
    • Advantages: easy and compact, can express unpopular grid
    • Disadvantages: can not effectively support point, the relationship between the adjacent surfaces inquiry
  2. Adjacency matrix
    • Pros: Support adjacency information (VV) between the vertices efficient queries, popular support for non-grid
    • Disadvantages: does not show the expression side, does not support the VF (vertex to face), VE (vertex to edge), EV (edge ​​to vertex), FE (face to edge), EF (edge ​​to face) Quick Query
  3. Half of the structure, etc.
    • Discipline all faces, edges and vertices, including geometry, topology information, subsidiary attributes, the set of most popular modeling application
    • Advantages: All queries are time complexity of the operation o (1), all the editing operations are time complexity o (1)
    • Disadvantages: can only express popular grid
    • Common structure to achieve half: CGAL ( http://www.cgal.org/ ), the Open the Mesh ( http://www.openmesh.org/ )

Here, I use the face list.

Define the header file

#ifndef OBJ_CLASS
#define OBJ_CLASS

#include <vector>
#include <cmath>

struct Vector3;
Vector3 operator + (const Vector3& one, const Vector3& two);
Vector3 operator - (const Vector3& one, const Vector3& two);
Vector3 operator * (const Vector3& one, double scale);
Vector3 operator / (const Vector3& one, double scale);
Vector3 Cross(Vector3& one, Vector3& two);

struct Vector3
{
    double fX;
    double fY;
    double fZ;
    Vector3(double x = 0.0, double y = 0.0, double z = 0.0) : fX(x), fY(y), fZ(z) {}
    Vector3 operator +=(const Vector3& v) { return *this = *this + v; }
    double Length() { return sqrt(fX * fX + fY * fY + fZ * fZ); }
    void Normalize()//归一化
    {
        double fLen = Length();
        if (fLen == 0.0f)
            fLen = 1.0f;
        if (fabs(fLen) > 1e-6)
        {
            fX /= fLen;
            fY /= fLen;
            fZ /= fLen;
        }
    }
};

struct Point
{
    Vector3 pos;
    Vector3 normal;
};

struct Face
{
    intPTS [ . 3 ]; 
    Vector3 Normal; 
}; 

class The CObj 
{ 
public : 
    The CObj ( void );
     ~ The CObj ( void ); 

    STD :: Vector <Point> m_pts; // vertex 
    STD :: Vector <Face> m_faces; // surface 

public :
     BOOL ReadObjFile ( const  char * pcszFileName); // read into the model file 

Private :
     void UnifyModel (); // unit model 
    void ComputeFaceNormal (face & F); // calculate surface normals 
};

#endif

Then some simple vector calculation, and operator overloading

#include "Obj.h"
#include <iostream>
#include <sstream>
#include <algorithm>

using std::min;
using std::max;

Vector3 operator + (const Vector3& one, const Vector3& two) //两个向量相加
{
    return Vector3(one.fX + two.fX, one.fY + two.fY, one.fZ + two.fZ);
}

Vector3 operator - (const Vector3& one, const Vector3& two) //两个向量相减
{
    returnVector3 (one.fX - two.fX, one.fY - two.fY, one.fZ - two.fZ); 
} 

Vector3 operator * ( const Vector3 & One, Double Scale) // multiply operations and the number of vectors 
{
     return Vector3 (one.fX * Scale, one.fY * Scale, one.fZ * Scale); 
} 

Vector3 operator / ( const Vector3 & One, Double Scale) // vector and the other operand 
{
     return One * ( 1.0 / Scale); 
} 

Vector3 Cross (Vector3 & One, Vector3 & TWO) 
{ //Calculating the cross product of two vectors 
    Vector3 Vcross;                                 

    vCross.fX = ((one.fY two.fZ *) - (* one.fZ two.fY)); 
    vCross.fY = ((one.fZ two.fX *) - (one.fX * two.fZ)); 
    vCross.fZ = ((one.fX two.fY *) - (* one.fY two.fX)); 

    return Vcross;         
} 

The CObj :: The CObj ( void ) 
{ 
} 


The CObj :: ~ The CObj ( void ) 
{ 
}

Read operations and the like below Laijiangjiang Model

Model reading

Generally there are so few identifiers stored in the model file:

  • v represents a vertex position
  • vt texture coordinates of vertices
  • vn represents the vertex normals
  • f denotes a surface

Opened it, something like this

 

 Well, you can start thinking about how to read and store data to the list inside, or simply reading a file, fopen (), fgets (), feof (), the rest is the key to the string into digital, c ++ in there are ready-made function can be called, sstream header file istringstream.

BOOL The CObj :: ReadObjFile ( const  char * pcszFileName) 
{ // read the model file 

    the FILE * = fpFile the fopen (pcszFileName, " R & lt " ); // Read-only file 
    IF (fpFile == NULL) 
    { 
        return  to false ; 
    } 

    m_pts.clear (); 
    m_faces.clear (); 

    // the TODO: the surface point and the data model are stored in files and m_faces m_pts the 
    char of strLine [ 1024 ]; 
    point point; 
    face face; 
    STD :: String S1 ;
     the while (!feof (fpFile)) 
    { 
        fgets (of strLine, 1024 , fpFile);
         IF (of strLine [ 0 ] == ' V ' ) 
        { 
            IF (of strLine [ . 1 ] == ' n- ' ) 
            { // VN file is not used in the I vn data not achieved 
                
            } 
            the else 
            { // V point 
                STD :: an istringstream SiN (of strLine); 
                SiN >> >> point.pos.fY point.pos.fX S1 >> >> point.pos.fZ; 
                m_pts .push_back (Point); 
            } 
        } 
        the else if (strLine[0] == 'f')
        {//
            std::istringstream sin(strLine);
            sin >> s1 >> face.pts[0] >> face.pts[1] >> face.pts[2];
            ComputeFaceNormal(face);
            m_faces.push_back(face);
        }
        printf("%s\n", strLine);
    }

    fclose(fpFile);

    UnifyModel(); //将模型归一化

    return true;
}

Drawing rings and cylinder through the article to know the normal vector is very important, so calculated for each surface normal vector is essential

The principle is very simple, it can take the fork

void The CObj :: ComputeFaceNormal (Face & f) 
{ // the TODO: calculating the normal vector of the face f and save 
    f.normal = Cross (m_pts [f.pts [ . 1 ] - . 1 ] .POS - m_pts [f.pts [ 0 ] - . 1 ] .POS, m_pts [f.pts [ 2 ] - . 1 ] .POS - m_pts [f.pts [ . 1 ] - . 1 ] .POS); 
    f.normal.Normalize (); 
}

For the model normalized, normalized why it? Imagine, you get a cell phone camera, if the camera away from the object close to the camera, that show up in the phone image look like? But how to do that in the case if they can not move the distance between the camera and the object? The target geometric compression!

void CObj :: UnifyModel () 
{ // unified display of models of different sizes, the model normalization, the scale model size to between 0.0 and 1.0
 // Principle: Find the maximum and minimum boundary model, and then look for the center of the model
 // center point as a reference model for the vertices of the model is scaled
 @ the TODO: Add a normalized model code 

    Vector3 vec_max, vec_min (1E5, 1E5, 1E5), VEC; 
    for ( int I = 0 ; I <m_pts.size (); I ++ ) 
    { 
        vec_max.fX = STD :: max (vec_max.fX, m_pts [I] .pos.fX); 
        vec_max.fY = STD :: max (vec_max.fY, m_pts [I] .pos.fY); 
        vec_max.fZ = STD :: max (vec_max.fZ, m_pts [I] .pos.fZ); 

        vec_min.fX =STD :: min (vec_min.fX, m_pts [i] .pos.fX); 
        vec_min.fY = STD :: min (vec_min.fY, m_pts [i] .pos.fY); 
        vec_min.fZ = STD :: min (vec_min.fZ, m_pts [i] .pos.fZ); 
    } 

    Vec.fX = vec_max.fX - vec_min.fX; 
    vec.fY = vec_max.fY - vec_min.fY; 
    vec.fZ = vec_max.fZ - vec_min.fZ; 

    for ( int i = 0 ; i <m_pts.size (); i ++ ) 
    { 
        m_pts [i] .normal = m_pts [i] .pos; 
        m_pts [i] .normal.fX = (m_pts [i] .normal.fX - vec_min.fX) / vec.fX - 0.5f ;
        m_pts [i] .normal.fY = (m_pts [i] .normal.fY - vec_min.fY) / vec.fY - 0.5f ; 
        m_pts [i] .normal.fZ = (m_pts [i] .normal.fZ - vec_min.fZ) / vec.fZ - 0.5f ; 
    } 

    // m_pts.push_back (vec); 
}

Drawing model

For rendering model, very easy to implement, because the information of each patch.

void DrawModel(CObj &model)
{//TODO: 绘制模型
    for (int i = 0; i < model.m_faces.size(); i++)
    {
        glBegin(GL_TRIANGLES);
        glNormal3f(model.m_faces[i].normal.fX, model.m_faces[i].normal.fY, model.m_faces[i].normal.fZ);
        glVertex3f(model.m_pts[model.m_faces[i].pts[0] - 1].normal.fX, model.m_pts[model.m_faces[i].pts[0] - 1].normal.fY, model.m_pts[model.m_faces[i].pts[0] - 1].normal.fZ);
        glVertex3f(model.m_pts[model.m_faces[i].pts[1] - 1].normal.fX, model.m_pts[model.m_faces[i].pts[1] - 1].normal.fY, model.m_pts[model.m_faces[i].pts[1] - 1].normal.fZ);
        glVertex3f(model.m_pts[model.m_faces[i].pts[2] - 1].normal.fX, model.m_pts[model.m_faces[i].pts[2] - 1].normal.fY, model.m_pts[model.m_faces[i].pts[2] - 1].normal.fZ);
        glEnd();
    }

}


if (g_draw_content == SHAPE_MODEL)
    {//绘制模型
        glTranslatef(g_x_offset, g_y_offset, g_z_offset);
        glRotatef(g_rquad_x, 0.0f, 1.0f, 0.0f);
        glRotatef(g_rquad_y, 1.0f, 0.0f, 0.0f);
        glScalef(g_scale_size, g_scale_size, g_scale_size);
        DrawModel(g_obj);
        
    }

Running, load the model!

Ah, yes, it successfully out.

and many more! Why is the head in front of me, how I adjust the angle? Looks a bit small, I can not take it larger?

The following will be introduced to achieve mouse interaction.

Mouse interaction

Opengl mouse interaction is still pretty good, the first need is initialized when the mouse output registered achieve callback and callback functions mouse motion events. The framework code to the last article in have achieved. That is how the rest of the rotation, zoom and drag the

Rotation

First, we should note that, in the given code framework, lookat camera is this

gluLookAt (0.0, 0.0, 8.0, 0, 0, 0, 0, 1.0, 0);

This function defines a view matrix, and multiplied by the current matrix
locations of the first group eyex, eyey, eyez camera in the world coordinates; position of the second group centerx, centery, centerz camera at an object in the world coordinates; a first direction of the three groups upx, upy, upz camera up in world coordinates.

所以,这里摄像机是从z轴看下去的,那么初始看到的二维平面分为为x轴和y轴。理解了这个,旋转就很简单了。水平拖动的时候让模型绕y轴转,竖直拖动的时候让模型绕x轴转。按下左键旋转。

if (g_xform_mode == TRANSFORM_ROTATE) //旋转
    {//TODO:添加鼠标移动控制模型旋转参数的代码
        g_rquad_x += (x - g_press_x) * 0.5f;
        g_rquad_y += (y - g_press_y) * 0.5f;
        g_press_x = x;
        g_press_y = y;
    }

 平移

平移的实现十分简单,计算鼠标移动的距离即可,按下右键拖动

    else if(g_xform_mode == TRANSFORM_TRANSLATE) //平移
    {//TODO:添加鼠标移动控制模型平移参数的代码
        g_x_offset += (x - g_press_x) * 0.002f;
        g_y_offset += -(y - g_press_y) * 0.002f;
        g_press_x = x;
        g_press_y = y;
    }

缩放

缩放与平移相似,按下滚轮键滑动鼠标

    else if(g_xform_mode == TRANSFORM_SCALE) //缩放
    {//TODO:添加鼠标移动控制模型缩放参数的代码
        g_scale_size += (x - g_press_x) * 0.01f;
    }

至此,我们的鼠标交互也实现完了,下面就来试试效果

小节

 这样,模型的加载及鼠标交互也就介绍完了,但是是不是还缺些什么?好像这个模型跟想象当中的还是有很大区别的,表面的图案呢??下一篇将介绍纹理贴图和曲线绘制。

Guess you like

Origin www.cnblogs.com/csu-lmw/p/12006050.html