书接上回,记得昨天留下的问题吗。
1.通过字符串来实现不同的纹理绑定。
2.字符串的转换
突破1
然后今天我发现,其实用不上什么mes、message来调控,我们不是已经有了一个条件了吗:
if ((camera.m_Position.x <= (outlinePos[i - 1].x + 0.2f) && camera.m_Position.x >= (outlinePos[i - 1].x - 0.2f)) && (camera.m_Position.y <= (outlinePos[i - 1].y + 0.2f) && camera.m_Position.y >= (outlinePos[i - 1].y - 0.2f)))
如果判断成功,不就意味着我们轮廓已经绘制,岂不是可以直接绑定纹理?不在需要其他条件控制,不需要获取什么mes。
突破2
昨天一直在考虑怎样将已获取的字符串转换为一个texture类型的变量,然后使用其成员函数Bind(),后来发现其实Bind()中不过两行代码,直接在主函数中硬编码实现即可,这下也不用考虑什么静态函数之类的问题了。
突破3
因为之前绑定过两个纹理,所以在内存中前两个的m_RenderID分别是unsigned int 的0和1,想要绑定之后的12个纹理,需要GLCALL(glBindTexture(GL_TEXTURE_2D, i + 3)),然后,然后又发现一个问题
突破4
for(size_t i = 1; i < outlinePos.size() + 1; i++){
记得之前的i吗,现在我们没必要让i从1开始,可以让他为0开始.
做总结
//绘制outlining
GLCALL(glStencilMask(0x00)); //禁止模板写入
GLCALL(glStencilFunc(GL_NOTEQUAL, 1, 0xff)); //模板值不等于缓冲区中模板值时更新 通过测试
glDisable(GL_DEPTH_TEST); //选中的物体可以透过遮挡,不使用深度测试
outlineShader.Bind();
outlineShader.SetUniformMatrix4fv("u_Projection", projection.GetMatrixData());
outlineShader.SetUniformMatrix4fv("u_View", view.GetMatrixData());
for(size_t i = 0; i < outlinePos.size(); i++){
if ((camera.m_Position.x <= (outlinePos[i].x + 0.2f) && camera.m_Position.x >= (outlinePos[i].x - 0.2f)) && (camera.m_Position.y <= (outlinePos[i].y + 0.2f) && camera.m_Position.y >= (outlinePos[i].y - 0.2f)))
{
Matrix stencilModel("mat4");
stencilModel.Rotate(90.0f, glm::vec3(1.0f, 0.0f, 0.0f));
stencilModel.Translate(labels[i]);
stencilModel.Scale(glm::vec3(1.5f, 1.5f, 1.5f));
outlineShader.SetUniformMatrix4fv("u_Model", stencilModel.GetMatrixData());
renderer.DrawElements(cuboidVa, cuboidIb, outlineShader);
//messages
GLCALL(glDisable(GL_DEPTH_TEST));
GLCALL(glViewport(SCR_WIDTH / 3, SCR_WIDTH / 2.5, SCR_WIDTH / 2.5, SCR_HEIGHT / 2.5));
quadShader.Bind();
GLCALL(glActiveTexture(GL_TEXTURE0));
GLCALL(glBindTexture(GL_TEXTURE_2D, i + 3 ));
renderer.DrawArrays(quadVa, 6, quadShader);
GLCALL(glBindTexture(GL_TEXTURE_2D, 0));
}
}
glEnable(GL_DEPTH_TEST); //恢复深度测试
GLCALL(glStencilMask(0xFF));
开始做条件2吧
等不及了
之前我们实现了imgui的窗口和button,现在我们需要添加一些条件语句和作用域更改。
记得之前的代码吗,我先在文本渲染中打印一下当前时间
#define _CRT_SECURE_NO_WARNINGS //在现在版本c++标准库中使用localtime函数会出现警告,在确保安全的情况系故禁用这个警告
#include<sstream>
#include<ctime>
#include<cmath>
#include<chrono>
#include<iomanip>
std::string GetTime()
{
auto now = std::chrono::system_clock::now(); // 获取当前时间点
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count(); // 将时间点转换为时间戳(以秒为单位)
std::time_t time_from_epoch = std::time_t(timestamp); // 转换为time_t类型,以便能够使用ctime函数进行格式化输出
std::tm local_time = *std::localtime(&time_from_epoch); // 转换为本地时间并格式化输出
std::ostringstream oss; // 使用ostringstream构建字符串
oss << std::put_time(&local_time, "%Y-%m-%d %H:%M:%S"); // 将本地时间格式化为字符串
return oss.str(); // 将字符串存储在std::string变量中
}
设置了一个表示时间的函数,然后在application中
std::string time = GetTime();
if (fontDraw) {
//....
font.RenderText(fontShader, time, 25.0f, 25.0f, 1.0f, glm::vec3(0.5, 0.8f, 0.2f));
}
and we got it ! (左下角)
而且单击exit按钮,我们也可以退出这个打印。
接下来的任务
现在我们需要做的是,通过初赛成绩进行决赛分组,生成一个秩序册,将队伍分配到17个决赛室(秩序册中每个决赛室的进场顺序为初赛成绩降序排列)。然后模拟候赛区大屏上动态展示各参赛队候场、比赛中、比赛结束的状态。
所以为了渲染比赛状态,我需要先对进行初赛的队伍分组,生成秩序册之后从中读取文字,用来显示在大屏上。
---------------------------------------------------------------------------------------------------------------------
在阅读任务书后发现,team.txt文件和有决赛室标识的文件其实很相像,分给这些队伍60~100的成绩就是默认了他们都进入决赛,所以我可以给team.txt分一些初赛成绩,然后以有决赛室标识的这个文件为条件后面再去判断。
我觉得是可以先写一个脚本,读入team.txt文件,然后依次为每一行队伍分配一个60到100的初赛成绩,读出并保存,然后去使用这个更新后的文件(反正也不影响条件一那些增删查的问题)。让我们开始吧。
初赛成绩赋值脚本
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <random>
int main() {
const std::string& inputPath = "YOUR LOCAL FILEPATH"; // 替换为你的输入文件路径
const std::string& outputPath = "WRITE IN THIS FILE"; // 替换为你的输出文件路径
std::ifstream inputStream(inputPath);
std::ofstream outputStream(outputPath);
std::string line;
if (!inputStream.is_open() || !outputStream.is_open()) {
std::cerr << "Failed to open the file." << std::endl;
return 1;
}
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(60, 100); // 60到100之间的随机数
while (getline(inputStream, line)) {
if (!line.empty()) { // 检查行是否为空,因为第一行可能是空的
std::uniform_int_distribution<> disNew(60, 100); // 生成一个新的随机数分布
int randomNum = disNew(gen); // 生成一个新的随机数并存储在变量中
line += "\t" + std::to_string(randomNum); // 在行末添加一个制表符和一个新的随机数
outputStream << line << std::endl; // 将修改后的行写入新的文件
}
}
inputStream.close();
outputStream.close();
return 0;
}
然后我的到了一个有成绩的文件
虽然第一行多了个数字但无伤大雅。
记得我们说过team.txt和“决赛分组-江科大.txt”这个文件差不多吗,反正都是要用,不如直接用这个文件来添加随机数,等会判断决赛室时候也方便。
故对“决赛分组-江科大.txt”重命名,改成英文的,然后使用刚才那个脚本处理一下。
( 偷偷删除第一行的那个数字:) )
那么我们就处理好了,接下来就是写一个代码来按照每一个决赛室中的队伍和初赛成绩来分类。(要不也写一个脚本处理?)
决赛分组
排序方式用归并排序或直接插入排序,因为稳定。然后归并开销较小,但直接插入易编码实现。各有裨益。
所以我又写了一个脚本。
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <random>
#include <vector>
#include <algorithm> //sort函数
int main() {
const std::string& inputPath = "刚才得到的输出的文件";
const std::string& outputPath = "输出目标文件";
std::ifstream inputStream(inputPath);
std::ofstream outputStream(outputPath);
std::string line;
if (!inputStream.is_open() || !outputStream.is_open()) {
std::cerr << "Failed to open the file." << std::endl;
return 1;
}
std::vector<std::pair<std::string, int>> DataWithScores;
while (getline(inputStream, line)) {
if (!line.empty()) { // 检查行是否为空,因为第一行可能是空的
if (line[0] == '1' && line[1] == '\t') { // 从一行数据头部开始到第二个字符,查找'1'
int length = line.length();
std::string score = line.substr(length - 3, 3); // 获取这一行的最后两个元素,假设这是成绩
int num = std::stoi(score);
DataWithScores.push_back(std::make_pair(line, num)); // 将行和成绩存储在容器中
}
else{
std::cout << "fa la la";
}
}
}
inputStream.close();
std::sort(DataWithScores.begin(), DataWithScores.end(), [](const std::pair<std::string, int>& a, const std::pair<std::string, int>& b) {
return a.second > b.second; // 根据成绩降序排序 (到时候改成直接插入排序,排的是pair存储的分数num)
});
for (const auto& pair : DataWithScores) {
outputStream << pair.first << std::endl; // 将排序后的行写入到输出文件
}
outputStream.close();
return 0;
}
现在我们对决赛组为1的队伍们进行了成绩的降序排序。
这是上一次处理的带随机分数的数据。
这是按照成绩排序的分组。(上面的脚本额只写了决赛组为1的降序排序)
处理的关键代码是
while (getline(inputStream, line)) {
if (!line.empty()) { // 检查行是否为空,因为第一行可能是空的
if (line[0] == '1' && line[1] == '\t') { // 从一行数据头部开始到第二个字符,查找'1'
int length = line.length();
std::string score = line.substr(length - 3, 3); // 获取这一行的最后两个元素,假设这是成绩
int num = std::stoi(score);
DataWithScores.push_back(std::make_pair(line, num)); // 将行和成绩存储在容器中
}
}
}
之后呢就是if (line[0] == ' 2或3或4...直到9 ' && line[1] == '\t'),因为line[0]只有一个字符嘛,
后面要改为if (line[0] == '1' && line[2] == ' 1或2或3...直到7 ' && line[3] == '\t')这样就好
最后我们得到了这样的文件。
---------------------------------------------------------------
明天将使用这样的文件,来进行赛场信息打印。同时更改排序方式为简单选择排序。