计算机图形学 实验二 三维模型读取与控制

目录

一、实验内容

二、具体内容

(在实验2.3的基础上进行修改)

1、OFF格式三维模型文件的读取

2、三维模型的旋转动画

3、键盘鼠标的交互

4、模型的修改

三、代码


一、实验内容

  1. 读取实验提供的off格式三维模型,并对其赋色。利用鼠标和键盘的交互,控制动画效果,模型的颜色自己可以自行设置,好看就行。

二、具体内容

(在实验2.3的基础上进行修改)

1、OFF格式三维模型文件的读取

参考上机实验2.2的内容,完成对OFF格式三维模型文件的读取与显示,可改变物体的显示颜色,尽量特别,但不要太难看。

1)修改init()方法,读取OFF格式三维模型文件cow.off。

2)修改颜色:

在readoff()方法中:将坐标值(-1,1)映射到颜色值(0,1)

方法1:坐标值加1,除2(结果和实验给的类似)

方法2:坐标值取绝对值

(应该还不算难看)

2、三维模型的旋转动画

参考实验2.1中动画的生成方式,并结合实验2.3中对模型进行旋转变换的过程,生成旋转动画。

默认绕X轴旋转,每1000毫秒转一下rotateDelta角度。

定义相关变量:

在mian函数中定义相关操作:

3、键盘鼠标的交互

参考实验2.1中鼠标与键盘的交互,通过键盘设定选择绕x、y、z轴进行旋转,通过鼠标左右键控制动画的开始与暂停。

1)通过键盘设定选择绕x、y、z轴进行旋转:

在key_callback函数内增加增加如下代码,通过按"X"、"Y"、"Z"控制。

2)通过鼠标左右键控制动画的开始与暂停:

增加mouse_button_callback函数:

然后在main函数中绑定。

3)修改提示语:

4、模型的修改

参考以下代码,通过键盘设定可以在cow.off和cube.off之间切换。

清除顶点数组缓存:

glBindVertexArray(0);

glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

二维向量:

std::vector<std::vector<?>>

其他STL相关代码可以查阅C++ STL 教程 | 菜鸟教程 (runoob.com)

1)添加变量表示绘制的是牛还是方块:

2)修改init()函数:通过判断currentModel选择读取的文件:

3)在key_callback函数中添加如下代码:

通过按下"N"绘制牛,按下"M"绘制方块。

4)并修改相关提示语。

效果如下:

三、代码

1、main.cpp

复制代码
#include "Angel.h"
#include "TriMesh.h"
#include <vector>
#include <string>
//#include "main.h"
using namespace std;

const int X_AXIS = 0;
const int Y_AXIS = 1;
const int Z_AXIS = 2;

const int TRANSFORM_SCALE = 0;
const int TRANSFORM_ROTATE = 1;
const int TRANSFORM_TRANSLATE = 2;

const double DELTA_DELTA = 0.3;		// Delta的变化率
const double DEFAULT_DELTA = 0.5;	// 默认的Delta值

double scaleDelta = DEFAULT_DELTA;
double rotateDelta = DEFAULT_DELTA;
double translateDelta = DEFAULT_DELTA;

glm::vec3 scaleTheta(1.0, 1.0, 1.0);		// 缩放控制变量
glm::vec3 rotateTheta(0.0, 0.0, 0.0);    // 旋转控制变量
glm::vec3 translateTheta(0.0, 0.0, 0.0);	// 平移控制变量

int currentTransform = TRANSFORM_ROTATE;	// 设置当前变换
int mainWindow;

//------------------------------------------------------------------------
bool isplaying = true;	// 动画状态
int rotationAxis = X_AXIS;	// 当前旋转轴,默认为 X 轴
int rotationTime = 1000;		// 每1000帧旋转一次

string currentModel ="cow";


struct openGLObject
{
	// 顶点数组对象
	GLuint vao;
	// 顶点缓存对象
	GLuint vbo;

	// 着色器程序
	GLuint program;
	// 着色器文件
	std::string vshader;
	std::string fshader;
	// 着色器变量
	GLuint pLocation;
	GLuint cLocation;
	GLuint matrixLocation;
	GLuint darkLocation;
};

openGLObject cube_object;

TriMesh* cube = new TriMesh();

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

void bindObjectAndData(TriMesh* mesh, openGLObject& object, const std::string& vshader, const std::string& fshader) {

	// 创建顶点数组对象
	glGenVertexArrays(1, &object.vao);  	// 分配1个顶点数组对象
	glBindVertexArray(object.vao);  	// 绑定顶点数组对象

	// 创建并初始化顶点缓存对象
	glGenBuffers(1, &object.vbo);
	glBindBuffer(GL_ARRAY_BUFFER, object.vbo);
	glBufferData(GL_ARRAY_BUFFER,
		mesh->getPoints().size() * sizeof(glm::vec3) + mesh->getColors().size() * sizeof(glm::vec3),
		NULL,
		GL_STATIC_DRAW);

	// @TODO: Task3-修改完TriMesh.cpp的代码成后再打开下面注释,否则程序会报错
	glBufferSubData(GL_ARRAY_BUFFER, 0, mesh->getPoints().size() * sizeof(glm::vec3), &mesh->getPoints()[0]);
	glBufferSubData(GL_ARRAY_BUFFER, mesh->getPoints().size() * sizeof(glm::vec3), mesh->getColors().size() * sizeof(glm::vec3), &mesh->getColors()[0]);

	object.vshader = vshader;
	object.fshader = fshader;
	object.program = InitShader(object.vshader.c_str(), object.fshader.c_str());

	// 从顶点着色器中初始化顶点的位置
	object.pLocation = glGetAttribLocation(object.program, "vPosition");
	glEnableVertexAttribArray(object.pLocation);
	glVertexAttribPointer(object.pLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

	// 从顶点着色器中初始化顶点的颜色
	object.cLocation = glGetAttribLocation(object.program, "vColor");
	glEnableVertexAttribArray(object.cLocation);
	glVertexAttribPointer(object.cLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(mesh->getPoints().size() * sizeof(glm::vec3)));

	// 获得矩阵存储位置
	object.matrixLocation = glGetUniformLocation(object.program, "matrix");

}

void init()
{
	std::string vshader, fshader;
	// 读取着色器文件路径
	vshader = "shaders/vshader.glsl";
	fshader = "shaders/fshader.glsl";

	//cube->generateCube();
	//cube->readOff("./Models/cow.off");
	if (currentModel == "cow") {
		cube->readOff("./Models/cow.off");
	}
	else {
		cube->generateCube();
	}
	bindObjectAndData(cube, cube_object, vshader, fshader);

	// 设置背景色为黑色
	glClearColor(0.0, 0.0, 0.0, 1.0);
}

// 渲染函数
void display()
{
	// 清空颜色缓冲和深度缓冲
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glUseProgram(cube_object.program);

	glBindVertexArray(cube_object.vao);

	// 初始化变换矩阵  glm::mat4表示 4x4 矩阵
	glm::mat4 m(1.0, 0.0, 0.0, 0.0,
		0.0, 1.0, 0.0, 0.0,
		0.0, 0.0, 1.0, 0.0,
		0.0, 0.0, 0.0, 1.0);

	// @TODO: Task4-在此处修改函数,计算最终的变换矩阵
	// 调用函数传入三种变化的变化量,累加得到变化矩阵
	// 注意三种变化累加的顺序
	// 构建旋转矩阵
	glm::mat4 rotationMatrix = glm::rotate(glm::mat4(1.0), rotateTheta.x, glm::vec3(1.0, 0.0, 0.0))
		* glm::rotate(glm::mat4(1.0), rotateTheta.y, glm::vec3(0.0, 1.0, 0.0))
		* glm::rotate(glm::mat4(1.0), rotateTheta.z, glm::vec3(0.0, 0.0, 1.0));

	// 构建缩放矩阵
	glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0), glm::vec3(scaleTheta.x, scaleTheta.y, scaleTheta.z));

	// 构建平移矩阵
	glm::mat4 translateMatrix = glm::translate(glm::mat4(1.0), glm::vec3(translateTheta.x, translateTheta.y, translateTheta.z));

	// 按照平移、旋转、缩放的顺序相乘得到最终的变换矩阵
	m = translateMatrix * rotationMatrix * scaleMatrix;

	// 传递变换矩阵到着色器
	glUniformMatrix4fv(cube_object.matrixLocation, 1, GL_FALSE, glm::value_ptr(m));

	// 绘制立方体中的各个三角形
	glDrawArrays(GL_TRIANGLES, 0, cube->getPoints().size());
}

// 通过Delta值更新Theta
// axis 表示坐标轴,sign 表示增加或减少
// currentTransform 表示当前变换类型
void updateTheta(int axis, int sign) {
	switch (currentTransform) {
		// 根据变换类型,增加或减少某种变换的变化量
	case TRANSFORM_SCALE:
		//增加或减少缩放的 Theta 值
		scaleTheta[axis] += sign * scaleDelta;
		break;
	case TRANSFORM_ROTATE:
		//增加或减少旋转的 Theta 值
		rotateTheta[axis] += sign * rotateDelta;
		break;
	case TRANSFORM_TRANSLATE:
		//增加或减少平移的 Theta 值
		translateTheta[axis] += sign * translateDelta;
		break;
	}
}

// 复原Theta和Delta
void resetTheta()
{
	scaleTheta = glm::vec3(1.0, 1.0, 1.0);	//scaleTheta 表示缩放变换的角度
	rotateTheta = glm::vec3(0.0, 0.0, 0.0);
	translateTheta = glm::vec3(0.0, 0.0, 0.0);
	scaleDelta = DEFAULT_DELTA;				//缩放变换的单位变化量
	rotateDelta = DEFAULT_DELTA;
	translateDelta = DEFAULT_DELTA;
}

// 更新变化Delta值
void updateDelta(int sign)
{
	switch (currentTransform) {
		// 根据变化类型增加或减少每一次变化的单位变化量
	case TRANSFORM_SCALE:
		scaleDelta += sign * DELTA_DELTA;
		break;
	case TRANSFORM_ROTATE:
		rotateDelta += sign * DELTA_DELTA;
		break;
	case TRANSFORM_TRANSLATE:
		translateDelta += sign * DELTA_DELTA;
		break;
	}
}

void cleanData();

// 处理键盘输入的回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	switch (key)
	{
		// 退出。
	case GLFW_KEY_ESCAPE:
		if (action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE);
		break;
		// 1:缩放模式
	case GLFW_KEY_1:
		if (action == GLFW_PRESS) currentTransform = TRANSFORM_SCALE;
		break;
		// 2: 旋转模式
	case GLFW_KEY_2:
		if (action == GLFW_PRESS) currentTransform = TRANSFORM_ROTATE;
		break;
		// 3: 移动模式
	case GLFW_KEY_3:
		if (action == GLFW_PRESS) currentTransform = TRANSFORM_TRANSLATE;
		break;
		// 4: 绘制线。
	case GLFW_KEY_4:
		if (action == GLFW_PRESS) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		break;
		// 5: 绘制面。
	case GLFW_KEY_5:
		if (action == GLFW_PRESS) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		break;
		// Q: 增加 x。
	case GLFW_KEY_Q:
		if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(X_AXIS, 1);
		break;
		// A: 减少 x。
	case GLFW_KEY_A:
		if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(X_AXIS, -1);
		break;
		// W: 增加 y。
	case GLFW_KEY_W:
		if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Y_AXIS, 1);
		break;
		// S: 减少 y。
	case GLFW_KEY_S:
		if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Y_AXIS, -1);
		break;
		// E: 增加 z。
	case GLFW_KEY_E:
		if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Z_AXIS, 1);
		break;
		// D: 减少 z。
	case GLFW_KEY_D:
		if (action == GLFW_PRESS || action == GLFW_REPEAT) updateTheta(Z_AXIS, -1);
		break;
		// R: 增加变化量。
	case GLFW_KEY_R:
		if (action == GLFW_PRESS) updateDelta(1);
		break;
		// F: 减少变化量。
	case GLFW_KEY_F:
		if (action == GLFW_PRESS) updateDelta(-1);
		break;
		// T: 所有值重置。
	case GLFW_KEY_T:
		if (action == GLFW_PRESS) resetTheta();
		break;
		//-------------------------------------------------------------------
		// 选择绕 X 轴旋转
	case GLFW_KEY_X:
		if (action == GLFW_PRESS) rotationAxis = X_AXIS;
		break;
		// 选择绕 Y 轴旋转
	case GLFW_KEY_Y:
		if (action == GLFW_PRESS) rotationAxis = Y_AXIS;
		break;
		// 选择绕 Z 轴旋转
	case GLFW_KEY_Z:
		if (action == GLFW_PRESS) rotationAxis = Z_AXIS;
		break;
		
    // N: 加载cow.off模型
    case GLFW_KEY_N:
        if (action == GLFW_PRESS) {
			glBindVertexArray(0);//清除顶点数组缓存
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
			currentModel = "cow";
			init();
        }
        break;
    // M: 加载cube模型
    case GLFW_KEY_M:
        if (action == GLFW_PRESS) {
			glBindVertexArray(0);//清除顶点数组缓存
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
			currentModel = "cube";
			init();
        }
        break;
	}

}

//-------------------------------------------------------------------------------
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
	if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
		isplaying = true; // 左键按下,开始动画
	}
	if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
		isplaying = false; // 右键按下,暂停动画
	}
}


// 输出帮助信息
void printHelp() {
	printf("%s\n\n", "3D Transfomations");
	printf("Keyboard options:\n");
	printf("n: Draw cow\n");
	printf("m: Draw block\n");
	printf("left mouse button: start playing\n");
	printf("right mouse button: Pause playback\n");
	printf("x: Rotate around the X-axis\n");
	printf("y: Rotate around the Y-axis\n");
	printf("z: Rotate around the Z-axis\n");
	printf("The following are the operations previously used:\n");
	printf("1: Transform Scale\n");
	printf("2: Transform Rotate\n");
	printf("3: Transform Translate\n");
	printf("q: Increase x\n");
	printf("a: Decrease x\n");
	printf("w: Increase y\n");
	printf("s: Decrease y\n");
	printf("e: Increase z\n");
	printf("d: Decrease z\n");
	printf("r: Increase delta of currently selected transform\n");
	printf("f: Decrease delta of currently selected transform\n");
	printf("t: Reset all transformations and deltas\n");
}

// 清理数据
void cleanData() {
	cube->cleanData();

	// 释放内存
	delete cube;
	cube = NULL;

	// 删除绑定的对象
	glDeleteVertexArrays(1, &cube_object.vao);

	glDeleteBuffers(1, &cube_object.vbo);
	glDeleteProgram(cube_object.program);
}

int main(int argc, char** argv)
{
	// 初始化GLFW库,必须是应用程序调用的第一个GLFW函数
	glfwInit();

	// 配置GLFW
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
	//设置字符格式
    #pragma execution_character_set("GBK");
	GLFWwindow* window = glfwCreateWindow(600, 600, "homework", NULL, NULL);

	if (window == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetKeyCallback(window, key_callback);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	// 调用任何OpenGL的函数之前初始化GLAD
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	init();
	// 输出帮助信息
	printHelp();
	// 启用深度测试
	glEnable(GL_DEPTH_TEST);

	//------------------------------------------------------------------------
	glfwSetMouseButtonCallback(window, mouse_button_callback); // 设置鼠标回调

	int flag = clock();
	while (!glfwWindowShouldClose(window))
	{
		display();

		// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)
		glfwSwapBuffers(window);
		glfwPollEvents();
		
		//--------------------------------------------------------------------------------
		//处理动画
		int now = clock();
		if (isplaying) {
			// 每1000帧旋转一定的角度
			if ((now - flag) >= rotationTime) {
				if (rotationAxis == X_AXIS) {
					rotateTheta.x += rotateDelta; // 沿X轴旋转
				}
				else if (rotationAxis == Y_AXIS) {
					rotateTheta.y += rotateDelta; // 沿Y轴旋转
				}
				else if (rotationAxis == Z_AXIS) {
					rotateTheta.z += rotateDelta; // 沿Z轴旋转
				}

				flag = now;
			}
		}
	}
	cleanData();

	return 0;
}

2、TriMesh.cpp

复制代码
#include "TriMesh.h"


// 一些基础颜色
const glm::vec3 basic_colors[8] = {
    glm::vec3(1.0, 1.0, 1.0),	// White
    glm::vec3(1.0, 1.0, 0.0),	// Yellow
    glm::vec3(0.0, 1.0, 0.0),	// Green
    glm::vec3(0.0, 1.0, 1.0),	// Cyan
    glm::vec3(1.0, 0.0, 1.0),	// Magenta
    glm::vec3(1.0, 0.0, 0.0),	// Red
    glm::vec3(0.0, 0.0, 0.0),	// Black
    glm::vec3(0.0, 0.0, 1.0)		// Blue
};

// 立方体的各个点
const glm::vec3 cube_vertices[8] = {
    glm::vec3(-0.5, -0.5, -0.5),
    glm::vec3(0.5, -0.5, -0.5),
    glm::vec3(-0.5,  0.5, -0.5),
    glm::vec3(0.5,  0.5, -0.5),
    glm::vec3(-0.5, -0.5,  0.5),
    glm::vec3(0.5, -0.5,  0.5),
    glm::vec3(-0.5,  0.5,  0.5),
    glm::vec3(0.5,  0.5,  0.5)
};

TriMesh::TriMesh()
{
}

TriMesh::~TriMesh()
{
}

std::vector<glm::vec3> TriMesh::getVertexPositions()
{
    return vertex_positions;
}

std::vector<glm::vec3> TriMesh::getVertexColors()
{
    return vertex_colors;
}

std::vector<vec3i> TriMesh::getFaces()
{
    return faces;
}


std::vector<glm::vec3> TriMesh::getPoints()
{
    return points;
}

std::vector<glm::vec3> TriMesh::getColors()
{
    return colors;
}

void TriMesh::cleanData() {
    vertex_positions.clear();
    vertex_colors.clear();

    faces.clear();

    points.clear();
    colors.clear();

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

void TriMesh::storeFacesPoints() {

    // @TODO: Task-2修改此函数在points和colors容器中存储每个三角面片的各个点和颜色信息
    // 根据每个三角面片的顶点下标存储要传入GPU的数据
     // 清空points和colors容器  
    points.clear();
    colors.clear();

    // 遍历每个面  
    for (const auto& face : faces) {
        // 根据索引获取顶点的位置和颜色  
        unsigned int x = face.x;
        unsigned int y = face.y;
        unsigned int z = face.z;

        glm::vec3 pos1 = vertex_positions[x];
        glm::vec3 col1 = vertex_colors[x];
        glm::vec3 pos2 = vertex_positions[y];
        glm::vec3 col2 = vertex_colors[y];
        glm::vec3 pos3 = vertex_positions[z];
        glm::vec3 col3 = vertex_colors[z];

        // 将顶点位置和颜色添加到points和colors容器中  
        points.push_back(pos1);
        colors.push_back(col1);
        points.push_back(pos2);
        colors.push_back(col2);
        points.push_back(pos3);
        colors.push_back(col3);
    }

}

// 立方体生成12个三角形的顶点索引
void TriMesh::generateCube() {
    // 创建顶点前要先把那些vector清空
    cleanData();

    // @TODO: Task1-修改此函数,存储立方体的各个面信息
    // vertex_positions和vertex_colors先保存每个顶点的数据
    for (int i = 0; i < 8; ++i) {
        vertex_positions.push_back(cube_vertices[i]);
        // 这里简单使用基本颜色数组中的颜色,每个顶点按顺序分配颜色  
        vertex_colors.push_back(basic_colors[i % 8]);
    }
    // faces再记录每个面片上顶点的下标

    // 立方体12个面的顶点索引  
    // 每个面由两个三角形组成  
    faces.push_back(vec3i(1, 3, 7)); // 前面  
    faces.push_back(vec3i(1, 7, 5));
    faces.push_back(vec3i(0, 2, 6)); // 后面  
    faces.push_back(vec3i(0, 6, 4));
    faces.push_back(vec3i(2, 6, 7)); // 右面  
    faces.push_back(vec3i(2, 7, 3));
    faces.push_back(vec3i(0, 4, 5)); // 左面  
    faces.push_back(vec3i(0, 5, 1));
    faces.push_back(vec3i(4, 5, 7)); // 顶面  
    faces.push_back(vec3i(4, 7, 6));
    faces.push_back(vec3i(0, 1, 3)); // 底面  
    faces.push_back(vec3i(0, 3, 2));
    storeFacesPoints();
}



void TriMesh::readOff(const std::string& filename)
{
    // fin打开文件读取文件信息
    if (filename.empty())
    {
        return;
    }
    std::ifstream fin;
    fin.open(filename);
    if (!fin)
    {
        printf("File on error\n");
        return;
    }
    else
    {
        printf("File open success\n");
        cleanData();
        int nVertices, nFaces, nEdges;

        // 读取OFF字符串
        std::string str;
        fin >> str;
        // 读取文件中顶点数、面片数、边数
        fin >> nVertices >> nFaces >> nEdges;
        // 根据顶点数,循环读取每个顶点坐标
        for (int i = 0; i < nVertices; i++)
        {
            glm::vec3 tmp_node;
            fin >> tmp_node.x >> tmp_node.y >> tmp_node.z;
            vertex_positions.push_back(tmp_node);
            //vertex_colors.push_back(tmp_node);
            // 将坐标值([-1,1])映射到颜色值([0,1])
            /*
            //方法1:加1,除2(结果和实验给的一样)
                glm::vec3 color = (tmp_node + glm::vec3(1.0f)) * 0.5f;
             */
            //方法2:取坐标绝对值
            float a = tmp_node.x>0? tmp_node.x:-tmp_node.x;
            float b = tmp_node.y>0? tmp_node.y:-tmp_node.y;
            float g = tmp_node.z>0? tmp_node.z:-tmp_node.z;
            glm::vec3 color(a,b,g);

            vertex_colors.push_back(color);

        }
        // 根据面片数,循环读取每个面片信息,并用构建的vec3i结构体保存
        for (int i = 0; i < nFaces; i++)
        {
            int num, a, b, c;
            // num记录此面片由几个顶点构成,a、b、c为构成该面片顶点序号
            fin >> num >> a >> b >> c;
            faces.push_back(vec3i(a, b, c));
        }
    }
    fin.close();
    storeFacesPoints();
};
相关推荐
GlobalInfo1 小时前
行业报告解读:可穿戴电子织物市场扩张背后,有哪些新机遇?
人工智能·物联网·microsoft
公子小六1 天前
基于.NET的Windows窗体编程之WinForms图像控件
windows·microsoft·c#·.net·winforms
一只小bit1 天前
LangChain 核型组件与消息管理及提示词模版
windows·microsoft·langchain
Nontee1 天前
Set 这个东西,跟我一开始想的不太一样
java·linux·windows·microsoft
大意的百合1 天前
Electron 应用如何上架微软商店:从 MSIX 打包到商店提交
javascript·microsoft·electron
XUHUOJUN1 天前
Windows Server 2025 Stretch Cluster 架构详解
windows·microsoft·azure local
纸小铭1 天前
Azure MCP 工具现已内置集成至 Visual Studio 2022,无需额外安装扩展
microsoft·azure·visual studio
XUHUOJUN1 天前
Windows Server 2025 RAC vs Stretch Cluster 技术定位
windows·microsoft·azure local
小鹿软件办公2 天前
微软确认 Windows 11 26H2 将调整系统默认备份机制
microsoft·windows 11 26h2
kyle~2 天前
Schema---数据结构的形式化规则描述
数据结构·windows·microsoft