TRX/lib/ati3dcif/Renderer.cpp

376 lines
10 KiB
C++
Raw Normal View History

2021-11-12 20:03:04 +01:00
#include "Renderer.hpp"
#include "Error.hpp"
#include "Utils.hpp"
#include <glrage_gl/Utils.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/mat4x4.hpp>
namespace glrage {
namespace cif {
using std::placeholders::_1;
Renderer::Renderer()
{
2021-11-14 23:59:19 +01:00
// The vertex stream passes primitives to the delayer to test if they have
// translucency and in that case delay them. When the delayer needs to
// display the delayed primitives it calls back to the vertex stream to do
// so.
m_vertexStream.setDelayer(
[this](C3D_VTCF* verts) { return m_transDelay.delayTriangle(verts); });
2021-11-12 20:03:04 +01:00
// bind sampler
m_sampler.bind(0);
// improve texture filtering quality
float filterAniso = m_config.getFloat("ati3dcif.filter_anisotropy", 16.0f);
if (filterAniso > 0) {
m_sampler.parameterf(GL_TEXTURE_MAX_ANISOTROPY_EXT, filterAniso);
}
// compile and link shaders and configure program
2021-11-14 23:42:18 +01:00
std::string basePath = m_context.getBasePath();
2021-11-12 20:03:04 +01:00
m_program.attach(gl::Shader(GL_VERTEX_SHADER)
2021-11-14 23:42:18 +01:00
.fromFile(basePath + "\\shaders\\ati3dcif.vsh"));
2021-11-12 20:03:04 +01:00
m_program.attach(gl::Shader(GL_FRAGMENT_SHADER)
2021-11-14 23:42:18 +01:00
.fromFile(basePath + "\\shaders\\ati3dcif.fsh"));
2021-11-12 20:03:04 +01:00
m_program.link();
m_program.fragmentData("fragColor");
m_program.bind();
// negate Z axis so the model is rendered behind the viewport, which is
2021-11-14 23:44:58 +01:00
// better than having a negative zNear in the ortho matrix, which seems
// to mess up depth testing
2021-11-12 20:03:04 +01:00
auto modelView = glm::scale(glm::mat4(), glm::vec3(1, 1, -1));
m_program.uniformMatrix4fv(
"matModelView", 1, GL_FALSE, glm::value_ptr(modelView));
// cache frequently used config values
m_wireframe = m_config.getBool("ati3dcif.wireframe", false);
gl::Utils::checkError(__FUNCTION__);
}
void Renderer::renderBegin(C3D_HRC hRC)
{
glEnable(GL_BLEND);
// set wireframe mode if set
if (m_wireframe) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
// bind objects
m_program.bind();
m_vertexStream.bind();
m_sampler.bind(0);
// restore texture binding
tmapRestore();
// CIF always uses an orthographic view, the application deals with the
// perspective when required
auto width = static_cast<float>(m_context.getDisplayWidth());
auto height = static_cast<float>(m_context.getDisplayHeight());
auto projection = glm::ortho<float>(0, width, height, 0, -1e6, 1e6);
m_program.uniformMatrix4fv(
"matProjection", 1, GL_FALSE, glm::value_ptr(projection));
gl::Utils::checkError(__FUNCTION__);
}
void Renderer::renderEnd()
{
// make sure everything has been rendered
m_vertexStream.renderPending();
// including the delayed translucent primitives
m_program.uniform1i("keyOnAlpha", true);
2021-11-14 23:59:19 +01:00
m_transDelay.render([this](std::vector<C3D_VTCF> verts) {
m_vertexStream.renderPrims(verts);
});
2021-11-12 20:03:04 +01:00
// restore polygon mode
if (m_wireframe) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
gl::Utils::checkError(__FUNCTION__);
}
void Renderer::textureReg(C3D_PTMAP ptmapToReg, C3D_PHTX phtmap)
{
// LOG_TRACE("fmt=%d, xlg2=%d, ylg2=%d, mip=%d",
// ptmapToReg->eTexFormat, ptmapToReg->u32MaxMapXSizeLg2,
// ptmapToReg->u32MaxMapYSizeLg2, ptmapToReg->bMipMap);
auto texture = std::make_shared<Texture>();
texture->bind();
texture->load(ptmapToReg, m_palettes[ptmapToReg->htxpalTexPalette]);
// use id as texture handle
*phtmap = reinterpret_cast<C3D_HTX>(texture->id());
// store in texture map
m_textures[*phtmap] = texture;
// restore previously bound texture
tmapRestore();
gl::Utils::checkError(__FUNCTION__);
}
void Renderer::textureUnreg(C3D_HTX htxToUnreg)
{
// LOG_TRACE("id=%d", id);
auto it = m_textures.find(htxToUnreg);
if (it == m_textures.end()) {
throw Error("Invalid texture handle", C3D_EC_BADPARAM);
}
// unbind texture if currently bound
2021-11-14 23:44:58 +01:00
if (htxToUnreg == m_tmapSelect) {
tmapSelect(0);
2021-11-12 20:03:04 +01:00
}
std::shared_ptr<Texture> texture = it->second;
m_textures.erase(htxToUnreg);
}
2021-11-14 23:59:19 +01:00
void Renderer::texturePaletteCreate(C3D_ECI_TMAP_TYPE epalette,
void* pPalette,
C3D_PHTXPAL phtpalCreated)
2021-11-12 20:03:04 +01:00
{
if (epalette != C3D_ECI_TMAP_8BIT) {
throw Error("Unsupported palette type: " +
std::string(C3D_ECI_TMAP_TYPE_NAMES[epalette]),
C3D_EC_NOTIMPYET);
}
// copy palette entries to vector
auto palettePtr = static_cast<C3D_PPALETTENTRY>(pPalette);
std::vector<C3D_PALETTENTRY> palette(palettePtr, palettePtr + 256);
// create new palette handle
auto handle = reinterpret_cast<C3D_HTXPAL>(m_paletteID++);
// store palette
m_palettes[handle] = palette;
*phtpalCreated = handle;
}
void Renderer::texturePaletteDestroy(C3D_HTXPAL htxpalToDestroy)
{
m_palettes.erase(htxpalToDestroy);
}
void Renderer::renderPrimStrip(C3D_VSTRIP vStrip, C3D_UINT32 u32NumVert)
{
m_context.setRendered();
m_vertexStream.addPrimStrip(vStrip, u32NumVert);
}
void Renderer::renderPrimList(C3D_VLIST vList, C3D_UINT32 u32NumVert)
{
m_context.setRendered();
m_vertexStream.addPrimList(vList, u32NumVert);
}
void Renderer::setState(C3D_ERSID eRStateID, C3D_PRSDATA pRStateData)
{
2021-11-14 23:44:58 +01:00
switch (eRStateID) {
2021-11-14 23:59:19 +01:00
case C3D_ERS_VERTEX_TYPE:
vertexType(*reinterpret_cast<C3D_EVERTEX*>(pRStateData));
break;
case C3D_ERS_PRIM_TYPE:
primType(*reinterpret_cast<C3D_EPRIM*>(pRStateData));
break;
case C3D_ERS_SOLID_CLR:
solidColor(*reinterpret_cast<C3D_COLOR*>(pRStateData));
break;
case C3D_ERS_SHADE_MODE:
shadeMode(*reinterpret_cast<C3D_ESHADE*>(pRStateData));
break;
case C3D_ERS_TMAP_EN:
tmapEnable(*reinterpret_cast<C3D_BOOL*>(pRStateData));
break;
case C3D_ERS_TMAP_SELECT:
tmapSelect(*reinterpret_cast<C3D_HTX*>(pRStateData));
break;
case C3D_ERS_TMAP_LIGHT:
tmapLight(*reinterpret_cast<C3D_ETLIGHT*>(pRStateData));
break;
case C3D_ERS_TMAP_FILTER:
tmapFilter(*reinterpret_cast<C3D_ETEXFILTER*>(pRStateData));
break;
case C3D_ERS_TMAP_TEXOP:
tmapTexOp(*reinterpret_cast<C3D_ETEXOP*>(pRStateData));
break;
case C3D_ERS_ALPHA_SRC:
alphaSrc(*reinterpret_cast<C3D_EASRC*>(pRStateData));
break;
case C3D_ERS_ALPHA_DST:
alphaDst(*reinterpret_cast<C3D_EADST*>(pRStateData));
break;
case C3D_ERS_Z_CMP_FNC:
zCmpFunc(*reinterpret_cast<C3D_EZCMP*>(pRStateData));
break;
case C3D_ERS_Z_MODE:
zMode(*reinterpret_cast<C3D_EZMODE*>(pRStateData));
break;
default:
throw Error("Unsupported state: " + std::to_string(eRStateID),
C3D_EC_NOTIMPYET);
2021-11-14 23:44:58 +01:00
}
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::vertexType(C3D_EVERTEX value)
2021-11-12 20:03:04 +01:00
{
m_vertexStream.renderPending();
2021-11-14 23:44:58 +01:00
m_vertexStream.vertexType(value);
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::primType(C3D_EPRIM value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
m_vertexStream.primType(value);
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::solidColor(C3D_COLOR value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
C3D_COLOR color = value;
2021-11-14 23:59:19 +01:00
m_program.uniform4f("solidColor",
color.r / 255.0f,
color.g / 255.0f,
color.b / 255.0f,
color.a / 255.0f);
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::shadeMode(C3D_ESHADE value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
m_program.uniform1i("shadeMode", value);
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::tmapEnable(C3D_BOOL value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
C3D_BOOL enable = value;
2021-11-12 20:03:04 +01:00
m_program.uniform1i("tmapEn", enable);
m_transDelay.setTexturingEnabled(enable != C3D_FALSE);
if (enable) {
glEnable(GL_TEXTURE_2D);
} else {
glDisable(GL_TEXTURE_2D);
}
}
2021-11-14 23:44:58 +01:00
void Renderer::tmapSelect(C3D_HTX value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
m_tmapSelect = value;
tmapSelectImpl(value);
2021-11-12 20:03:04 +01:00
}
void Renderer::tmapSelectImpl(C3D_HTX handle)
{
// unselect texture if handle is zero
if (handle == 0) {
glBindTexture(GL_TEXTURE_2D, 0);
return;
}
// check if handle is correct
auto it = m_textures.find(handle);
if (it == m_textures.end()) {
throw Error("Invalid texture handle", C3D_EC_BADPARAM);
}
// get texture object and bind it
auto texture = it->second;
texture->bind();
// Tell the transparent primitive delayer what texture is currently in use
m_transDelay.setTexture(texture);
// send chroma key color to shader
auto ck = texture->chromaKey();
m_program.uniform3f(
"chromaKey", ck.r / 255.0f, ck.g / 255.0f, ck.b / 255.0f);
m_program.uniform1i("keyOnAlpha", texture->keyOnAlpha());
}
2021-11-14 23:59:19 +01:00
void Renderer::tmapRestore()
{
2021-11-14 23:44:58 +01:00
tmapSelectImpl(m_tmapSelect);
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::tmapLight(C3D_ETLIGHT value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
m_program.uniform1i("tmapLight", value);
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::tmapFilter(C3D_ETEXFILTER value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
auto filter = value;
2021-11-12 20:03:04 +01:00
m_sampler.parameteri(
GL_TEXTURE_MAG_FILTER, GLCIF_TEXTURE_MAG_FILTER[filter]);
m_sampler.parameteri(
GL_TEXTURE_MIN_FILTER, GLCIF_TEXTURE_MIN_FILTER[filter]);
}
2021-11-14 23:44:58 +01:00
void Renderer::tmapTexOp(C3D_ETEXOP value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
m_program.uniform1i("texOp", value);
2021-11-12 20:03:04 +01:00
}
2021-11-14 23:44:58 +01:00
void Renderer::alphaSrc(C3D_EASRC value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
m_easrc = value;
C3D_EASRC alphaSrc = value;
C3D_EADST alphaDst = m_eadst;
2021-11-12 20:03:04 +01:00
glBlendFunc(GLCIF_BLEND_FUNC[alphaSrc], GLCIF_BLEND_FUNC[alphaDst]);
}
2021-11-14 23:44:58 +01:00
void Renderer::alphaDst(C3D_EADST value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
m_eadst = value;
2021-11-14 23:59:19 +01:00
C3D_EASRC alphaSrc = m_easrc;
2021-11-14 23:44:58 +01:00
C3D_EADST alphaDst = value;
2021-11-12 20:03:04 +01:00
glBlendFunc(GLCIF_BLEND_FUNC[alphaSrc], GLCIF_BLEND_FUNC[alphaDst]);
}
2021-11-14 23:44:58 +01:00
void Renderer::zCmpFunc(C3D_EZCMP value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
C3D_EZCMP func = value;
2021-11-12 20:03:04 +01:00
if (func < C3D_EZCMP_MAX) {
glDepthFunc(GLCIF_DEPTH_FUNC[func]);
}
}
2021-11-14 23:44:58 +01:00
void Renderer::zMode(C3D_EZMODE value)
2021-11-12 20:03:04 +01:00
{
2021-11-14 23:44:58 +01:00
m_vertexStream.renderPending();
auto mode = value;
2021-11-12 20:03:04 +01:00
glDepthMask(GLCIF_DEPTH_MASK[mode]);
if (mode > C3D_EZMODE_TESTON) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
}
} // namespace cif
2021-11-14 23:42:18 +01:00
} // namespace glrage