celestia/src/celengine/mapmanager.cpp

190 lines
5.1 KiB
C++

//
// mapmanager.cpp
//
// Copyright © 2020 Celestia Development Team. All rights reserved.
//
// 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 2
// of the License, or (at your option) any later version.
#include <array>
#include <cmath>
#include <cstring>
#include <fstream>
#include <celutil/logger.h>
#include <celutil/fsutils.h>
#include "mapmanager.h"
using namespace std;
using namespace celestia;
using celestia::util::GetLogger;
static std::array<const char*, 1> extensions = {"map"};
WarpMesh::WarpMesh(int nx, int ny, float *data) :
nx(nx),
ny(ny),
data(data)
{
}
WarpMesh::~WarpMesh()
{
delete data;
}
void WarpMesh::scopedDataForRendering(const function<void (float*, int)>& f) const
{
int step = 5 * 6;
int size = (nx - 1) * (ny - 1) * step * sizeof(float);
float *renderingData = new float[size];
for (int y = 0; y < ny - 1; y += 1)
{
for (int x = 0; x < nx - 1; x += 1)
{
float *destination = &renderingData[(y * (nx - 1) + x) * step];
float *source = &data[(y * nx + x) * 5];
// Top left triangle
memcpy(destination, source + 5 * nx, sizeof(float) * 5);
memcpy(destination + 5, source, sizeof(float) * 5);
memcpy(destination + 10, source + 5, sizeof(float) * 5);
// Bottom right triangle
memcpy(destination + 15, source + 5 * nx, sizeof(float) * 5);
memcpy(destination + 20, source + 5, sizeof(float) * 5);
memcpy(destination + 25, source + 5 * nx + 5, sizeof(float) * 5);
}
}
f(renderingData, size);
delete[] renderingData;
}
int WarpMesh::count() const
{
return 6 * (nx - 1) * (ny - 1);
}
bool WarpMesh::mapVertex(float x, float y, float* u, float* v) const
{
float minX = data[0];
float minY = data[1];
float maxX = data[(nx * ny - 1) * 5];
float maxY = data[(nx * ny - 1) * 5 + 1];
float stepX = (maxX - minX) / (nx - 1);
float stepY = (maxY - minY) / (ny - 1);
float locX = (x - minX) / stepX;
float locY = (y - minY) / stepY;
int floX = floorf(locX);
int floY = floorf(locY);
locX -= floX;
locY -= floY;
if (floX < 0 || floX >= nx - 1 || floY < 0 || floY >= ny - 1)
return false;
float p1x = data[(floY * nx + floX) * 5 + 2];
float p1y = data[(floY * nx + floX) * 5 + 3];
float p2x = data[(floY * nx + floX + 1) * 5 + 2];
float p2y = data[(floY * nx + floX + 1) * 5 + 3];
float p3x = data[(floY * nx + floX + nx) * 5 + 2];
float p3y = data[(floY * nx + floX + nx) * 5 + 3];
float p4x = data[(floY * nx + floX + nx + 1) * 5 + 2];
float p4y = data[(floY * nx + floX + nx + 1) * 5 + 3];
if (locX + locY <= 1)
{
// the top left part triangle
*u = p1x + locX * (p2x - p1x) + locY * (p3x - p1x);
*v = p1y + locX * (p2y - p1y) + locY * (p3y - p1y);
}
else
{
// the bottom right triangle
locX -= 1;
locY -= 1;
*u = p4x + locX * (p4x - p3x) + locY * (p4x - p2x);
*v = p4y + locX * (p4y - p3y) + locY * (p4y - p2y);
}
// Texture coordinate is [0, 1], normalize to [-1, 1]
*u = (*u) * 2 - 1;
*v = (*v) * 2 - 1;
return true;
}
WarpMeshManager* GetWarpMeshManager()
{
static WarpMeshManager* warpMeshManager = nullptr;
if (warpMeshManager == nullptr)
warpMeshManager = new WarpMeshManager("warp");
return warpMeshManager;
}
fs::path WarpMeshInfo::resolve(const fs::path& baseDir)
{
bool wildcard = source.extension() == ".*";
fs::path filename = baseDir / source;
if (wildcard)
{
fs::path matched = util::ResolveWildcard(filename, extensions);
if (!matched.empty())
return matched;
}
return filename;
}
WarpMesh* WarpMeshInfo::load(const fs::path& name)
{
#define MESHTYPE_RECT 2
ifstream f(name.string());
if (!f.good())
return nullptr;
int type, nx, ny;
if (!(f >> type))
{
GetLogger()->error("Failed to read mesh header\n");
return nullptr;
}
if (type != MESHTYPE_RECT)
{
GetLogger()->error("Unsupported mesh type found: {}\n", type);
return nullptr;
}
if (!(f >> nx >> ny))
{
GetLogger()->error("Failed to read mesh header\n");
return nullptr;
}
if (nx < 2 || ny < 2)
{
GetLogger()->error("Row and column numbers should be larger than 2\n");
return nullptr;
}
float *data = new float[nx * ny * 5];
for (int y = 0; y < ny; y += 1)
{
for (int x = 0; x < nx; x += 1)
{
float *base = &data[(y * nx + x) * 5];
if (!(f >> base[0] >> base[1] >> base[2] >> base[3] >> base[4]))
{
GetLogger()->error("Failed to read mesh data\n");
delete[] data;
return nullptr;
}
}
}
GetLogger()->info("Read a mesh of {} x {}\n", nx, ny);
return new WarpMesh(nx, ny, data);
}