Add view port effect for post processing
parent
1f8cc6bb52
commit
17cd02b3cc
|
@ -0,0 +1,8 @@
|
|||
varying vec2 texCoord;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_FragColor = texture2D(tex, texCoord);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
attribute vec2 in_Position;
|
||||
attribute vec2 in_TexCoord0;
|
||||
|
||||
varying vec2 texCoord;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = vec4(in_Position.xy, 0.0, 1.0);
|
||||
texCoord = in_TexCoord0.st;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
varying vec2 texCoord;
|
||||
varying float intensity;
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_FragColor = vec4(texture2D(tex, texCoord).rgb * intensity, 1.0);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
attribute vec2 in_Position;
|
||||
attribute vec2 in_TexCoord0;
|
||||
attribute float in_Intensity;
|
||||
|
||||
varying vec2 texCoord;
|
||||
varying float intensity;
|
||||
|
||||
uniform float screenRatio;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
float offset = 0.5 - screenRatio * 0.5;
|
||||
gl_Position = vec4(in_Position.x * screenRatio, in_Position.y, 0.0, 1.0);
|
||||
texCoord = vec2(in_TexCoord0.x * screenRatio + offset, in_TexCoord0.y);
|
||||
intensity = in_Intensity;
|
||||
}
|
|
@ -69,6 +69,8 @@ set(CELENGINE_SOURCES
|
|||
location.h
|
||||
lodspheremesh.cpp
|
||||
lodspheremesh.h
|
||||
mapmanager.cpp
|
||||
mapmanager.h
|
||||
marker.cpp
|
||||
marker.h
|
||||
meshmanager.cpp
|
||||
|
@ -168,6 +170,8 @@ set(CELENGINE_SOURCES
|
|||
vecgl.h
|
||||
vertexobject.cpp
|
||||
vertexobject.h
|
||||
viewporteffect.h
|
||||
viewporteffect.cpp
|
||||
virtualtex.cpp
|
||||
virtualtex.h
|
||||
visibleregion.cpp
|
||||
|
|
|
@ -138,6 +138,8 @@ FramebufferObject::generateFbo(unsigned int attachments)
|
|||
{
|
||||
// Create the FBO
|
||||
glGenFramebuffers(1, &m_fboId);
|
||||
GLint oldFboId;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFboId);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_fboId);
|
||||
|
||||
#ifndef GL_ES
|
||||
|
@ -151,7 +153,7 @@ FramebufferObject::generateFbo(unsigned int attachments)
|
|||
m_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (m_status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFboId);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
@ -171,7 +173,7 @@ FramebufferObject::generateFbo(unsigned int attachments)
|
|||
m_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (m_status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFboId);
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
@ -182,7 +184,7 @@ FramebufferObject::generateFbo(unsigned int attachments)
|
|||
}
|
||||
|
||||
// Restore default frame buffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldFboId);
|
||||
}
|
||||
|
||||
// Delete all GL objects associated with this framebuffer object
|
||||
|
@ -218,9 +220,8 @@ FramebufferObject::bind()
|
|||
}
|
||||
|
||||
bool
|
||||
FramebufferObject::unbind()
|
||||
FramebufferObject::unbind(GLint oldfboId)
|
||||
{
|
||||
// Restore default frame buffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, oldfboId);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class FramebufferObject
|
|||
GLuint depthTexture() const;
|
||||
|
||||
bool bind();
|
||||
bool unbind();
|
||||
bool unbind(GLint oldfboId);
|
||||
|
||||
private:
|
||||
void generateColorTexture();
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
//
|
||||
// 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 <celutil/debug.h>
|
||||
#include <fstream>
|
||||
#include "mapmanager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static string resolveWildcard(const string& filename)
|
||||
{
|
||||
string base(filename, 0, filename.length() - 1);
|
||||
|
||||
string mapfile = base + "map";
|
||||
ifstream in(mapfile);
|
||||
if (in.good())
|
||||
return mapfile;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
fs::path WarpMeshInfo::resolve(const fs::path& baseDir)
|
||||
{
|
||||
bool wildcard = false;
|
||||
if (!source.empty() && source.at(source.length() - 1) == '*')
|
||||
wildcard = true;
|
||||
|
||||
fs::path filename = baseDir / source;
|
||||
if (wildcard)
|
||||
{
|
||||
string matched = resolveWildcard(filename.string());
|
||||
if (matched.empty())
|
||||
return filename; // . . . for lack of any better way to handle it.
|
||||
else
|
||||
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))
|
||||
{
|
||||
DPRINTF(LOG_LEVEL_ERROR, "Failed to read mesh header\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type != MESHTYPE_RECT)
|
||||
{
|
||||
DPRINTF(LOG_LEVEL_ERROR, "Unsupported mesh type found: %d\n", type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!(f >> nx >> ny))
|
||||
{
|
||||
DPRINTF(LOG_LEVEL_ERROR, "Failed to read mesh header\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (nx < 2 || ny < 2)
|
||||
{
|
||||
DPRINTF(LOG_LEVEL_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]))
|
||||
{
|
||||
DPRINTF(LOG_LEVEL_ERROR, "Failed to read mesh data\n");
|
||||
delete[] data;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
DPRINTF(LOG_LEVEL_INFO, "Read a mesh of %d * %d\n", nx, ny);
|
||||
return new WarpMesh(nx, ny, data);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// mapmanager.h
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <celutil/resmanager.h>
|
||||
|
||||
// File format for data used to warp an image, for
|
||||
// detail, see http://paulbourke.net/dataformats/meshwarp/
|
||||
class WarpMesh
|
||||
{
|
||||
public:
|
||||
WarpMesh(int nx, int ny, float* data);
|
||||
~WarpMesh();
|
||||
|
||||
// Map data to triangle vertices used for drawing
|
||||
void scopedDataForRendering(const std::function<void(float*, int)>&) const;
|
||||
int count() const; // Number of vertices
|
||||
|
||||
// Convert a vertex coordinate to texture coordinate
|
||||
bool mapVertex(float x, float y, float* u, float* v) const;
|
||||
|
||||
private:
|
||||
int nx;
|
||||
int ny;
|
||||
float* data;
|
||||
};
|
||||
|
||||
class WarpMeshInfo : public ResourceInfo<WarpMesh>
|
||||
{
|
||||
public:
|
||||
std::string source;
|
||||
|
||||
WarpMeshInfo(const std::string& source) : source(source) {};
|
||||
|
||||
fs::path resolve(const fs::path&) override;
|
||||
WarpMesh* load(const fs::path&) override;
|
||||
};
|
||||
|
||||
inline bool operator<(const WarpMeshInfo& wi0, const WarpMeshInfo& wi1)
|
||||
{
|
||||
return wi0.source < wi1.source;
|
||||
}
|
||||
|
||||
typedef ResourceManager<WarpMeshInfo> WarpMeshManager;
|
||||
|
||||
WarpMeshManager* GetWarpMeshManager();
|
|
@ -910,6 +910,8 @@ void renderGeometryShadow_GLSL(Geometry* geometry,
|
|||
if (prog == nullptr)
|
||||
return;
|
||||
|
||||
GLint oldFboId;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFboId);
|
||||
shadowFbo->bind();
|
||||
glViewport(0, 0, shadowFbo->width(), shadowFbo->height());
|
||||
|
||||
|
@ -939,5 +941,5 @@ void renderGeometryShadow_GLSL(Geometry* geometry,
|
|||
// Re-enable the color buffer
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glCullFace(GL_BACK);
|
||||
shadowFbo->unbind();
|
||||
shadowFbo->unbind(oldFboId);
|
||||
}
|
||||
|
|
|
@ -3213,6 +3213,10 @@ ShaderManager::buildProgram(const ShaderProperties& props)
|
|||
CelestiaGLProgram::ColorAttributeIndex,
|
||||
"in_Color");
|
||||
|
||||
glBindAttribLocation(prog->getID(),
|
||||
CelestiaGLProgram::IntensityAttributeIndex,
|
||||
"in_Intensity");
|
||||
|
||||
if (props.texUsage & ShaderProperties::NormalTexture)
|
||||
{
|
||||
glBindAttribLocation(prog->getID(),
|
||||
|
@ -3313,6 +3317,10 @@ ShaderManager::buildProgram(const std::string& vs, const std::string& fs)
|
|||
CelestiaGLProgram::PointSizeAttributeIndex,
|
||||
"in_PointSize");
|
||||
|
||||
glBindAttribLocation(prog->getID(),
|
||||
CelestiaGLProgram::IntensityAttributeIndex,
|
||||
"in_Intensity");
|
||||
|
||||
status = prog->link();
|
||||
}
|
||||
|
||||
|
|
|
@ -183,6 +183,7 @@ class CelestiaGLProgram
|
|||
TangentAttributeIndex = 6,
|
||||
PointSizeAttributeIndex = 7,
|
||||
ColorAttributeIndex = 8,
|
||||
IntensityAttributeIndex = 9,
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
//
|
||||
// viewporteffect.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 "viewporteffect.h"
|
||||
#include "framebuffer.h"
|
||||
#include "render.h"
|
||||
#include "shadermanager.h"
|
||||
#include "mapmanager.h"
|
||||
|
||||
bool ViewportEffect::preprocess(Renderer* renderer, FramebufferObject* fbo)
|
||||
{
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFboId);
|
||||
return fbo->bind();
|
||||
}
|
||||
|
||||
bool ViewportEffect::prerender(Renderer* renderer, FramebufferObject* fbo)
|
||||
{
|
||||
if (!fbo->unbind(oldFboId))
|
||||
return false;
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ViewportEffect::distortXY(float &x, float &y)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
PassthroughViewportEffect::PassthroughViewportEffect() :
|
||||
ViewportEffect(),
|
||||
vo(GL_ARRAY_BUFFER, 0, GL_STATIC_DRAW)
|
||||
{
|
||||
}
|
||||
|
||||
bool PassthroughViewportEffect::prerender(Renderer* renderer, FramebufferObject* fbo)
|
||||
{
|
||||
if (!ViewportEffect::prerender(renderer, fbo))
|
||||
return false;
|
||||
|
||||
renderer->disableDepthTest();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PassthroughViewportEffect::render(Renderer* renderer, FramebufferObject* fbo, int width, int height)
|
||||
{
|
||||
CelestiaGLProgram *prog = renderer->getShaderManager().getShader("passthrough");
|
||||
if (prog == nullptr)
|
||||
return false;
|
||||
|
||||
vo.bind();
|
||||
if (!vo.initialized())
|
||||
initializeVO(vo);
|
||||
|
||||
prog->use();
|
||||
prog->samplerParam("tex") = 0;
|
||||
glBindTexture(GL_TEXTURE_2D, fbo->colorTexture());
|
||||
draw(vo);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
vo.unbind();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PassthroughViewportEffect::initializeVO(celgl::VertexObject& vo)
|
||||
{
|
||||
static float quadVertices[] = {
|
||||
// positions // texCoords
|
||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f,
|
||||
|
||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
vo.allocate(sizeof(quadVertices), quadVertices);
|
||||
vo.setVertices(2, GL_FLOAT, false, 4 * sizeof(float), 0);
|
||||
vo.setTextureCoords(2, GL_FLOAT, false, 4 * sizeof(float), 2 * sizeof(float));
|
||||
}
|
||||
|
||||
void PassthroughViewportEffect::draw(celgl::VertexObject& vo)
|
||||
{
|
||||
vo.draw(GL_TRIANGLES, 6);
|
||||
}
|
||||
|
||||
WarpMeshViewportEffect::WarpMeshViewportEffect(WarpMesh *mesh) :
|
||||
ViewportEffect(),
|
||||
vo(GL_ARRAY_BUFFER, 0, GL_STATIC_DRAW),
|
||||
mesh(mesh)
|
||||
{
|
||||
}
|
||||
|
||||
bool WarpMeshViewportEffect::prerender(Renderer* renderer, FramebufferObject* fbo)
|
||||
{
|
||||
if (mesh == nullptr)
|
||||
return false;
|
||||
|
||||
if (!ViewportEffect::prerender(renderer, fbo))
|
||||
return false;
|
||||
|
||||
renderer->disableDepthTest();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WarpMeshViewportEffect::render(Renderer* renderer, FramebufferObject* fbo, int width, int height)
|
||||
{
|
||||
CelestiaGLProgram *prog = renderer->getShaderManager().getShader("warpmesh");
|
||||
if (prog == nullptr)
|
||||
return false;
|
||||
|
||||
vo.bind();
|
||||
if (!vo.initialized())
|
||||
initializeVO(vo);
|
||||
|
||||
prog->use();
|
||||
prog->samplerParam("tex") = 0;
|
||||
prog->floatParam("screenRatio") = (float)height / width;
|
||||
glBindTexture(GL_TEXTURE_2D, fbo->colorTexture());
|
||||
draw(vo);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
vo.unbind();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WarpMeshViewportEffect::initializeVO(celgl::VertexObject& vo)
|
||||
{
|
||||
mesh->scopedDataForRendering([&vo](float *data, int size){
|
||||
vo.allocate(size, data);
|
||||
vo.setVertices(2, GL_FLOAT, false, 5 * sizeof(float), 0);
|
||||
vo.setTextureCoords(2, GL_FLOAT, false, 5 * sizeof(float), 2 * sizeof(float));
|
||||
vo.setVertexAttribArray(CelestiaGLProgram::IntensityAttributeIndex, 1, GL_FLOAT, false, 5 * sizeof(float), 4 * sizeof(float));
|
||||
});
|
||||
}
|
||||
|
||||
void WarpMeshViewportEffect::draw(celgl::VertexObject& vo)
|
||||
{
|
||||
vo.draw(GL_TRIANGLES, mesh->count());
|
||||
}
|
||||
|
||||
bool WarpMeshViewportEffect::distortXY(float &x, float &y)
|
||||
{
|
||||
if (mesh == nullptr)
|
||||
return false;
|
||||
|
||||
float u;
|
||||
float v;
|
||||
if (!mesh->mapVertex(x * 2, y * 2, &u, &v))
|
||||
return false;
|
||||
|
||||
x = u / 2;
|
||||
y = v / 2;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// viewporteffect.h
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <celengine/glsupport.h>
|
||||
#include <celengine/vertexobject.h>
|
||||
|
||||
class FramebufferObject;
|
||||
class Renderer;
|
||||
class CelestiaGLProgram;
|
||||
class WarpMesh;
|
||||
|
||||
class ViewportEffect
|
||||
{
|
||||
public:
|
||||
virtual ~ViewportEffect() = default;
|
||||
|
||||
virtual bool preprocess(Renderer*, FramebufferObject*);
|
||||
virtual bool prerender(Renderer*, FramebufferObject*);
|
||||
virtual bool render(Renderer*, FramebufferObject*, int width, int height) = 0;
|
||||
virtual bool distortXY(float& x, float& y);
|
||||
|
||||
private:
|
||||
GLint oldFboId;
|
||||
};
|
||||
|
||||
class PassthroughViewportEffect : public ViewportEffect
|
||||
{
|
||||
public:
|
||||
PassthroughViewportEffect();
|
||||
~PassthroughViewportEffect() override = default;
|
||||
|
||||
bool prerender(Renderer*, FramebufferObject* fbo) override;
|
||||
bool render(Renderer*, FramebufferObject*, int width, int height) override;
|
||||
|
||||
private:
|
||||
celgl::VertexObject vo;
|
||||
|
||||
void initializeVO(celgl::VertexObject&);
|
||||
void draw(celgl::VertexObject&);
|
||||
};
|
||||
|
||||
class WarpMeshViewportEffect : public ViewportEffect
|
||||
{
|
||||
public:
|
||||
WarpMeshViewportEffect(WarpMesh *mesh);
|
||||
~WarpMeshViewportEffect() override = default;
|
||||
|
||||
bool prerender(Renderer*, FramebufferObject* fbo) override;
|
||||
bool render(Renderer*, FramebufferObject*, int width, int height) override;
|
||||
bool distortXY(float& x, float& y) override;
|
||||
|
||||
private:
|
||||
celgl::VertexObject vo;
|
||||
WarpMesh *mesh;
|
||||
|
||||
void initializeVO(celgl::VertexObject&);
|
||||
void draw(celgl::VertexObject&);
|
||||
};
|
|
@ -30,6 +30,7 @@
|
|||
#include <celengine/axisarrow.h>
|
||||
#include <celengine/planetgrid.h>
|
||||
#include <celengine/visibleregion.h>
|
||||
#include <celengine/framebuffer.h>
|
||||
#include <celmath/geomutil.h>
|
||||
#include <celutil/color.h>
|
||||
#include <celutil/filetype.h>
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include <ctime>
|
||||
#include <set>
|
||||
#include <celengine/rectangle.h>
|
||||
#include <celengine/mapmanager.h>
|
||||
|
||||
#ifdef CELX
|
||||
#include <celephem/scriptobject.h>
|
||||
|
@ -424,8 +426,11 @@ void CelestiaCore::mouseButtonUp(float x, float y, int button)
|
|||
(*activeView)->mapWindowToView((float) x / (float) width,
|
||||
(float) y / (float) height,
|
||||
pickX, pickY);
|
||||
Vector3f pickRay =
|
||||
sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY);
|
||||
pickX *= aspectRatio;
|
||||
if (isViewportEffectUsed)
|
||||
viewportEffect->distortXY(pickX, pickY);
|
||||
|
||||
Vector3f pickRay = sim->getActiveObserver()->getPickRay(pickX, pickY);
|
||||
|
||||
Selection oldSel = sim->getSelection();
|
||||
Selection newSel = sim->pickObject(pickRay, renderer->getRenderFlags(), pickTolerance);
|
||||
|
@ -441,8 +446,11 @@ void CelestiaCore::mouseButtonUp(float x, float y, int button)
|
|||
(*activeView)->mapWindowToView((float) x / (float) width,
|
||||
(float) y / (float) height,
|
||||
pickX, pickY);
|
||||
Vector3f pickRay =
|
||||
sim->getActiveObserver()->getPickRay(pickX * aspectRatio, pickY);
|
||||
pickX *= aspectRatio;
|
||||
if (isViewportEffectUsed)
|
||||
viewportEffect->distortXY(pickX, pickY);
|
||||
|
||||
Vector3f pickRay = sim->getActiveObserver()->getPickRay(pickX, pickY);
|
||||
|
||||
Selection sel = sim->pickObject(pickRay, renderer->getRenderFlags(), pickTolerance);
|
||||
if (!sel.empty())
|
||||
|
@ -2057,30 +2065,13 @@ void CelestiaCore::draw()
|
|||
return;
|
||||
viewChanged = false;
|
||||
|
||||
if (views.size() == 1)
|
||||
{
|
||||
// I'm not certain that a special case for one view is required; but,
|
||||
// it's possible that there exists some broken hardware out there
|
||||
// that has to fall back to software rendering if the scissor test
|
||||
// is enabled. To keep performance on this hypothetical hardware
|
||||
// reasonable in the typical single view case, we'll use this
|
||||
// scissorless special case. I'm only paranoid because I've been
|
||||
// burned by crap hardware so many times. cjl
|
||||
// Render each view
|
||||
for (const auto view : views)
|
||||
draw(view);
|
||||
|
||||
// Reset to render to the main window
|
||||
if (views.size() > 1)
|
||||
renderer->setRenderRegion(0, 0, width, height, false);
|
||||
sim->render(*renderer);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto view : views)
|
||||
{
|
||||
if (view->type == View::ViewWindow)
|
||||
{
|
||||
view->switchTo(width, height);
|
||||
sim->render(*renderer, *view->observer);
|
||||
}
|
||||
}
|
||||
renderer->setRenderRegion(0, 0, width, height, false);
|
||||
}
|
||||
|
||||
bool toggleAA = renderer->isMSAAEnabled();
|
||||
if (toggleAA && (renderer->getRenderFlags() & Renderer::ShowCloudMaps))
|
||||
|
@ -2143,6 +2134,47 @@ void CelestiaCore::resize(GLsizei w, GLsizei h)
|
|||
return;
|
||||
}
|
||||
|
||||
void CelestiaCore::draw(View* view)
|
||||
{
|
||||
if (view->type != View::ViewWindow) return;
|
||||
|
||||
bool viewportEffectUsed = false;
|
||||
|
||||
FramebufferObject *fbo = nullptr;
|
||||
if (viewportEffect != nullptr)
|
||||
{
|
||||
// create/update FBO for viewport effect
|
||||
view->updateFBO(width, height);
|
||||
fbo = view->getFBO();
|
||||
}
|
||||
bool process = fbo != nullptr && viewportEffect->preprocess(renderer, fbo);
|
||||
|
||||
int x = view->x * width;
|
||||
int y = view->y * height;
|
||||
int viewWidth = view->width * width;
|
||||
int viewHeight = view->height * height;
|
||||
// If we need to process, we draw to the FBO which starts at point zero
|
||||
renderer->setRenderRegion(process ? 0 : x, process ? 0 : y, viewWidth, viewHeight, !view->isRootView());
|
||||
|
||||
if (view->isRootView())
|
||||
sim->render(*renderer);
|
||||
else
|
||||
sim->render(*renderer, *view->observer);
|
||||
|
||||
// Viewport need to be reset to start from (x,y) instead of point zero
|
||||
if (process && (x != 0 || y != 0))
|
||||
renderer->setRenderRegion(x, y, viewWidth, viewHeight);
|
||||
|
||||
if (process && viewportEffect->prerender(renderer, fbo))
|
||||
{
|
||||
if (viewportEffect->render(renderer, fbo, viewWidth, viewHeight))
|
||||
viewportEffectUsed = true;
|
||||
else
|
||||
DPRINTF(LOG_LEVEL_ERROR, "Unable to render viewport effect.\n");
|
||||
}
|
||||
isViewportEffectUsed = viewportEffectUsed;
|
||||
}
|
||||
|
||||
|
||||
void CelestiaCore::setSafeAreaInsets(int left, int top, int right, int bottom)
|
||||
{
|
||||
|
@ -3781,6 +3813,30 @@ bool CelestiaCore::initSimulation(const fs::path& configFileName,
|
|||
}
|
||||
}
|
||||
|
||||
if (!config->viewportEffect.empty() && config->viewportEffect != "none")
|
||||
{
|
||||
if (config->viewportEffect == "passthrough")
|
||||
viewportEffect = unique_ptr<ViewportEffect>(new PassthroughViewportEffect);
|
||||
else if (config->viewportEffect == "warpmesh")
|
||||
{
|
||||
if (config->warpMeshFile.empty())
|
||||
{
|
||||
DPRINTF(LOG_LEVEL_WARNING, "No warp mesh file specified for this effect\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
WarpMeshManager *manager = GetWarpMeshManager();
|
||||
WarpMesh *mesh = manager->find(manager->getHandle(WarpMeshInfo(config->warpMeshFile)));
|
||||
if (mesh != nullptr)
|
||||
viewportEffect = unique_ptr<ViewportEffect>(new WarpMeshViewportEffect(mesh));
|
||||
else
|
||||
DPRINTF(LOG_LEVEL_WARNING, "Failed to read warp mesh file %s\n", config->warpMeshFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
DPRINTF(LOG_LEVEL_WARNING, "Unknown viewport effect %s\n", config->viewportEffect);
|
||||
}
|
||||
|
||||
sim = new Simulation(universe);
|
||||
if ((renderer->getRenderFlags() & Renderer::ShowAutoMag) == 0)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <celengine/render.h>
|
||||
#include <celengine/simulation.h>
|
||||
#include <celengine/overlayimage.h>
|
||||
#include <celengine/viewporteffect.h>
|
||||
#include "configfile.h"
|
||||
#include "favorites.h"
|
||||
#include "destination.h"
|
||||
|
@ -210,6 +211,7 @@ class CelestiaCore // : public Watchable<CelestiaCore>
|
|||
void joystickButton(int button, bool down);
|
||||
void resize(GLsizei w, GLsizei h);
|
||||
void draw();
|
||||
void draw(View*);
|
||||
void tick();
|
||||
|
||||
Simulation* getSimulation() const;
|
||||
|
@ -465,6 +467,9 @@ class CelestiaCore // : public Watchable<CelestiaCore>
|
|||
int screenDpi{ 96 };
|
||||
int distanceToScreen{ 400 };
|
||||
|
||||
unique_ptr<ViewportEffect> viewportEffect { nullptr };
|
||||
bool isViewportEffectUsed { false };
|
||||
|
||||
struct EdgeInsets
|
||||
{
|
||||
int left;
|
||||
|
|
|
@ -93,6 +93,8 @@ CelestiaConfig* ReadCelestiaConfig(const fs::path& filename, CelestiaConfig *con
|
|||
configParams->getString("TitleFont", config->titleFont);
|
||||
configParams->getPath("LogoTexture", config->logoTextureFile);
|
||||
configParams->getString("Cursor", config->cursor);
|
||||
configParams->getString("ViewportEffect", config->viewportEffect);
|
||||
configParams->getString("WarpMeshFile", config->warpMeshFile);
|
||||
|
||||
float maxDist = 1.0;
|
||||
configParams->getNumber("SolarSystemMaxDistance", maxDist);
|
||||
|
|
|
@ -78,6 +78,9 @@ public:
|
|||
|
||||
float SolarSystemMaxDistance;
|
||||
unsigned ShadowMapSize;
|
||||
|
||||
std::string viewportEffect;
|
||||
std::string warpMeshFile;
|
||||
};
|
||||
|
||||
CelestiaConfig* ReadCelestiaConfig(const fs::path& filename, CelestiaConfig* config = nullptr);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <celengine/rectangle.h>
|
||||
#include <celengine/render.h>
|
||||
#include <celengine/framebuffer.h>
|
||||
#include <celutil/color.h>
|
||||
#include "view.h"
|
||||
|
||||
|
@ -150,14 +151,6 @@ Observer* View::getObserver() const
|
|||
return observer;
|
||||
}
|
||||
|
||||
void View::switchTo(int gWidth, int gHeight)
|
||||
{
|
||||
renderer->setRenderRegion(int(x * gWidth),
|
||||
int(y * gHeight),
|
||||
int(width * gWidth),
|
||||
int(height * gHeight));
|
||||
}
|
||||
|
||||
bool View::isSplittable(Type type) const
|
||||
{
|
||||
// If active view is too small, don't split it.
|
||||
|
@ -254,6 +247,7 @@ void View::reset()
|
|||
parent = nullptr;
|
||||
child1 = nullptr;
|
||||
child2 = nullptr;
|
||||
fbo = nullptr;
|
||||
}
|
||||
|
||||
void View::drawBorder(int gWidth, int gHeight, const Color &color, float linewidth)
|
||||
|
@ -264,3 +258,25 @@ void View::drawBorder(int gWidth, int gHeight, const Color &color, float linewid
|
|||
r.setLineWidth(linewidth);
|
||||
renderer->drawRectangle(r, renderer->getOrthoProjectionMatrix());
|
||||
}
|
||||
|
||||
void View::updateFBO(int gWidth, int gHeight)
|
||||
{
|
||||
int newWidth = width * gWidth;
|
||||
int newHeight = height * gHeight;
|
||||
if (fbo && fbo.get()->width() == newWidth && fbo.get()->height() == newHeight)
|
||||
return;
|
||||
|
||||
// recreate FBO when FBO not exisits or on size change
|
||||
fbo = unique_ptr<FramebufferObject>(new FramebufferObject(newWidth, newHeight,
|
||||
FramebufferObject::ColorAttachment | FramebufferObject::DepthAttachment));
|
||||
if (!fbo->isValid())
|
||||
{
|
||||
DPRINTF(LOG_LEVEL_ERROR, "Error creating view FBO.\n");
|
||||
fbo = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FramebufferObject *View::getFBO() const
|
||||
{
|
||||
return fbo.get();
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ class View
|
|||
void walkTreeResize(View*, int);
|
||||
bool walkTreeResizeDelta(View*, float, bool);
|
||||
|
||||
void switchTo(int gWidth, int gHeight);
|
||||
Observer* getObserver() const;
|
||||
bool isRootView() const;
|
||||
bool isSplittable(Type type) const;
|
||||
|
@ -46,6 +45,8 @@ class View
|
|||
void reset();
|
||||
static View* remove(View*);
|
||||
void drawBorder(int gWidth, int gHeight, const Color &color, float linewidth = 1.0f);
|
||||
void updateFBO(int gWidth, int gHeight);
|
||||
FramebufferObject *getFBO() const;
|
||||
|
||||
public:
|
||||
Type type;
|
||||
|
@ -63,6 +64,9 @@ class View
|
|||
int labelMode { 0 };
|
||||
float zoom { 1.0f };
|
||||
float alternateZoom { 1.0f };
|
||||
|
||||
private:
|
||||
std::unique_ptr<FramebufferObject> fbo;
|
||||
};
|
||||
|
||||
//}
|
||||
|
|
|
@ -37,6 +37,7 @@ static const char CelestiaModelExt[] = ".cmod";
|
|||
static const char CelestiaParticleSystemExt[] = ".cpart";
|
||||
static const char CelestiaXYZTrajectoryExt[] = ".xyz";
|
||||
static const char CelestiaXYZVTrajectoryExt[] = ".xyzv";
|
||||
static const char Content_WarpMeshExt[] = ".map";
|
||||
|
||||
ContentType DetermineFileType(const fs::path& filename)
|
||||
{
|
||||
|
@ -83,6 +84,8 @@ ContentType DetermineFileType(const fs::path& filename)
|
|||
return Content_CelestiaXYZTrajectory;
|
||||
if (compareIgnoringCase(CelestiaXYZVTrajectoryExt, ext) == 0)
|
||||
return Content_CelestiaXYZVTrajectory;
|
||||
if (compareIgnoringCase(Content_WarpMeshExt, ext) == 0)
|
||||
return Content_WarpMesh;
|
||||
else
|
||||
return Content_Unknown;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ enum ContentType
|
|||
Content_CelestiaXYZTrajectory = 18,
|
||||
Content_CelestiaXYZVTrajectory = 19,
|
||||
Content_CelestiaParticleSystem = 20,
|
||||
Content_WarpMesh = 21,
|
||||
Content_Unknown = -1,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue