openspades/Sources/Draw/GLVoxelModel.cpp
2013-09-11 13:50:18 +09:00

625 lines
18 KiB
C++

/*
Copyright (c) 2013 yvt
This file is part of OpenSpades.
OpenSpades 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 3 of the License, or
(at your option) any later version.
OpenSpades is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GLVoxelModel.h"
#include "GLRenderer.h"
#include "GLImage.h"
#include "GLProgram.h"
#include "GLProgramAttribute.h"
#include "GLProgramUniform.h"
#include "../Core/Debug.h"
#include "GLShadowShader.h"
#include "GLDynamicLightShader.h"
#include "IGLShadowMapRenderer.h"
#include "GLShadowMapShader.h"
namespace spades {
namespace draw {
void GLVoxelModel::PreloadShaders(spades::draw::GLRenderer *renderer) {
renderer->RegisterProgram("Shaders/VoxelModel.program");
renderer->RegisterProgram("Shaders/VoxelModelDynamicLit.program");
renderer->RegisterProgram("Shaders/VoxelModelShadowMap.program");
renderer->RegisterImage("Gfx/AmbientOcclusion.tga");
}
GLVoxelModel::GLVoxelModel(VoxelModel *m,
GLRenderer *r){
SPADES_MARK_FUNCTION();
renderer = r;
device = r->GetGLDevice();
program = renderer->RegisterProgram("Shaders/VoxelModel.program");
dlightProgram = renderer->RegisterProgram("Shaders/VoxelModelDynamicLit.program");
shadowMapProgram = renderer->RegisterProgram("Shaders/VoxelModelShadowMap.program");
aoImage = (GLImage *)renderer->RegisterImage("Gfx/AmbientOcclusion.tga");
BuildVertices(m);
buffer = device->GenBuffer();
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->BufferData(IGLDevice::ArrayBuffer,
vertices.size() * sizeof(Vertex),
vertices.data(), IGLDevice::StaticDraw);
idxBuffer = device->GenBuffer();
device->BindBuffer(IGLDevice::ArrayBuffer, idxBuffer);
device->BufferData(IGLDevice::ArrayBuffer,
indices.size() * sizeof(uint32_t),
indices.data(), IGLDevice::StaticDraw);
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
origin = m->GetOrigin();
origin -= .5f; // (0,0,0) is center of voxel (0,0,0)
Vector3 minPos = {0, 0, 0};
Vector3 maxPos = {
(float)m->GetWidth(), (float)m->GetHeight(), (float)m->GetDepth()
};
minPos += origin; maxPos += origin;
Vector3 maxDiff = {
std::max(fabsf(minPos.x), fabsf(maxPos.x)),
std::max(fabsf(minPos.y), fabsf(maxPos.y)),
std::max(fabsf(minPos.z), fabsf(maxPos.z))
};
radius = maxDiff.GetLength();
boundingBox.min = minPos;
boundingBox.max = maxPos;
// clean up
numIndices = (unsigned int)indices.size();
std::vector<Vertex>().swap(vertices);
std::vector<uint32_t>().swap(indices);
}
GLVoxelModel::~GLVoxelModel() {
SPADES_MARK_FUNCTION();
device->DeleteBuffer(idxBuffer);
device->DeleteBuffer(buffer);
}
uint8_t GLVoxelModel::calcAOID(VoxelModel *m,
int x, int y, int z,
int ux, int uy, int uz,
int vx, int vy, int vz){
int v = 0;
if(m->IsSolid(x - ux, y - uy, z - uz))
v |= 1;
if(m->IsSolid(x + ux, y + uy, z + uz))
v |= 1 << 1;
if(m->IsSolid(x - vx, y - vy, z - vz))
v |= 1 << 2;
if(m->IsSolid(x + vx, y + vy, z + vz))
v |= 1 << 3;
if(m->IsSolid(x - ux + vx, y - uy + vy, z - uz + vz))
v |= 1 << 4;
if(m->IsSolid(x - ux - vx, y - uy - vy, z - uz - vz))
v |= 1 << 5;
if(m->IsSolid(x + ux + vx, y + uy + vy, z + uz + vz))
v |= 1 << 6;
if(m->IsSolid(x + ux - vx, y + uy - vy, z + uz - vz))
v |= 1 << 7;
return (uint8_t)v;
}
void GLVoxelModel::EmitFace(spades::VoxelModel *model,
int x, int y, int z,
int nx, int ny, int nz,
uint32_t color) {
SPADES_MARK_FUNCTION_DEBUG();
// decide face tangent
int ux = ny, uy = nz, uz = nx;
int vx = nz, vy = nx, vz = ny;
if(nx < 0 || ny < 0 || nz < 0){
vx = -vx; vy = -vy; vz = -vz;
}
// now cross(u, v) = n (somehow)
SPAssert(ux*vx+uy*vy+uz*vz == 0);
SPAssert(ux*nx+uy*ny+uz*nz == 0);
SPAssert(nx*vx+ny*vy+nz*vz == 0);
SPAssert(uy*vz-uz*vy == -nx);
SPAssert(uz*vx-ux*vz == -ny);
SPAssert(ux*vy-uy*vx == -nz);
// decide face origin
int fx = 0, fy = 0, fz = 0;
if(ux == -1 || vx == -1){
fx = 1;
SPAssert(ux + vx == -1);
}
if(uy == -1 || vy == -1){
fy = 1;
SPAssert(uy + vy == -1);
}
if(uz == -1 || vz == -1){
fz = 1;
SPAssert(uz + vz == -1);
}
SPAssert(nx*fx+ny*fy+nz*fz == 0);
uint8_t aoID = calcAOID(model,
x+nx, y+ny, z+nz,
ux, uy, uz,
vx, vy, vz);
Vertex v;
uint32_t idx = (uint32_t)vertices.size();
v.aoID = aoID;
v.red = (uint8_t)(color);
v.green = (uint8_t)(color >> 8);
v.blue = (uint8_t)(color >> 16);
v.diffuse = 255;
v.nx = nx; v.ny = ny; v.nz = nz;
x += fx; y += fy; z += fz;
if(nx > 0) x++;
if(ny > 0) y++;
if(nz > 0) z++;
SPAssert(x >= 0);
SPAssert(y >= 0);
SPAssert(y >= 0);
SPAssert(x + ux >= 0);
SPAssert(y + uy >= 0);
SPAssert(z + uz >= 0);
SPAssert(x + vx >= 0);
SPAssert(y + vy >= 0);
SPAssert(z + vz >= 0);
SPAssert(x + ux + vx >= 0);
SPAssert(y + uy + vy >= 0);
SPAssert(z + uz + vz >= 0);
v.u = 0; v.v = 0;
v.x = x; v.y = y; v.z = z;
vertices.push_back(v);
v.u = 1; v.v = 0;
v.x = x + ux; v.y = y + uy; v.z = z + uz;
vertices.push_back(v);
v.u = 0; v.v = 1;
v.x = x + vx; v.y = y + vy; v.z = z + vz;
vertices.push_back(v);
v.u = 1; v.v = 1;
v.x = x + ux + vx; v.y = y + uy + vy; v.z = z + uz + vz;
vertices.push_back(v);
indices.push_back(idx);
indices.push_back(idx + 1);
indices.push_back(idx + 2);
indices.push_back(idx + 1);
indices.push_back(idx + 3);
indices.push_back(idx + 2);
}
void GLVoxelModel::BuildVertices(spades::VoxelModel *model) {
SPADES_MARK_FUNCTION();
SPAssert(vertices.empty());
SPAssert(indices.empty());
int w = model->GetWidth();
int h = model->GetHeight();
int d = model->GetDepth();
for(int x = 0; x < w; x++){
for(int y = 0; y < h; y++){
for(int z = 0; z < d; z++){
if(!model->IsSolid(x, y, z))
continue;
uint32_t color = model->GetColor(x,y,z);
color |= 0xff000000UL;
if(!model->IsSolid(x-1,y,z))
EmitFace(model,
x, y, z,
-1, 0, 0,
color);
if(!model->IsSolid(x+1,y,z))
EmitFace(model,
x, y, z,
1, 0, 0,
color);
if(!model->IsSolid(x,y-1,z))
EmitFace(model,
x, y, z,
0, -1, 0,
color);
if(!model->IsSolid(x,y+1,z))
EmitFace(model,
x, y, z,
0, 1, 0,
color);
if(!model->IsSolid(x,y,z-1))
EmitFace(model,
x, y, z,
0, 0, -1,
color);
if(!model->IsSolid(x,y,z+1))
EmitFace(model,
x, y, z,
0, 0, 1,
color);
}
}
}
}
void GLVoxelModel::RenderShadowMapPass(std::vector<client::ModelRenderParam> params) {
SPADES_MARK_FUNCTION();
device->Enable(IGLDevice::CullFace, true);
device->Enable(IGLDevice::DepthTest, true);
shadowMapProgram->Use();
static GLShadowMapShader shadowMapShader;
shadowMapShader(renderer, shadowMapProgram, 0);
static GLProgramUniform modelOrigin("modelOrigin");
modelOrigin(shadowMapProgram);
modelOrigin.SetValue(origin.x, origin.y, origin.z);
// setup attributes
static GLProgramAttribute positionAttribute("positionAttribute");
static GLProgramAttribute normalAttribute("normalAttribute");
positionAttribute(shadowMapProgram);
normalAttribute(shadowMapProgram);
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->VertexAttribPointer(positionAttribute(),
4, IGLDevice::UnsignedByte,
false, sizeof(Vertex),
(void *)0);
if(normalAttribute() != -1){
device->VertexAttribPointer(normalAttribute(),
3, IGLDevice::Byte,
false, sizeof(Vertex),
(void *)12);
}
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
device->EnableVertexAttribArray(positionAttribute(), true);
if(normalAttribute() != -1)
device->EnableVertexAttribArray(normalAttribute(), true);
device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer);
for(size_t i = 0; i < params.size(); i++){
const client::ModelRenderParam& param = params[i];
// frustrum cull
float rad = radius;
rad *= param.matrix.GetAxis(0).GetLength();
if(param.depthHack)
continue;
if(!renderer->GetShadowMapRenderer()->SphereCull(param.matrix.GetOrigin(),
rad)){
continue;
}
Matrix4 modelMatrix = param.matrix;
static GLProgramUniform modelMatrixU("modelMatrix");
modelMatrixU(shadowMapProgram);
modelMatrixU.SetValue(modelMatrix);
modelMatrix.m[12] = 0.f;
modelMatrix.m[13] = 0.f;
modelMatrix.m[14] = 0.f;
static GLProgramUniform modelNormalMatrix("modelNormalMatrix");
modelNormalMatrix(shadowMapProgram);
modelNormalMatrix.SetValue(modelMatrix);
device->DrawElements(IGLDevice::Triangles,
numIndices,
IGLDevice::UnsignedInt,
(void *)0);
}
device->BindBuffer(IGLDevice::ElementArrayBuffer, 0);
device->EnableVertexAttribArray(positionAttribute(), false);
if(normalAttribute() != -1)
device->EnableVertexAttribArray(normalAttribute(), false);
device->ActiveTexture(0);
device->BindTexture(IGLDevice::Texture2D, 0);
}
void GLVoxelModel::RenderSunlightPass(std::vector<client::ModelRenderParam> params) {
SPADES_MARK_FUNCTION();
device->ActiveTexture(0);
aoImage->Bind(IGLDevice::Texture2D);
device->TexParamater(IGLDevice::Texture2D,
IGLDevice::TextureMinFilter,
IGLDevice::Linear);
device->Enable(IGLDevice::CullFace, true);
device->Enable(IGLDevice::DepthTest, true);
program->Use();
static GLShadowShader shadowShader;
shadowShader(renderer, program, 1);
static GLProgramUniform fogDistance("fogDistance");
fogDistance(program);
fogDistance.SetValue(renderer->GetFogDistance());
static GLProgramUniform fogColor("fogColor");
fogColor(program);
Vector3 fogCol = renderer->GetFogColorForSolidPass();
fogCol *= fogCol;
fogColor.SetValue(fogCol.x, fogCol.y, fogCol.z);
static GLProgramUniform aoUniform("ambientOcclusionTexture");
aoUniform(program);
aoUniform.SetValue(0);
static GLProgramUniform modelOrigin("modelOrigin");
modelOrigin(program);
modelOrigin.SetValue(origin.x, origin.y, origin.z);
static GLProgramUniform sunLightDirection("sunLightDirection");
sunLightDirection(program);
Vector3 sunPos = MakeVector3(0, -1, -1);
sunPos = sunPos.Normalize();
sunLightDirection.SetValue(sunPos.x, sunPos.y, sunPos.z);
// setup attributes
static GLProgramAttribute positionAttribute("positionAttribute");
static GLProgramAttribute textureCoordAttribute("textureCoordAttribute");
static GLProgramAttribute colorAttribute("colorAttribute");
static GLProgramAttribute normalAttribute("normalAttribute");
positionAttribute(program);
textureCoordAttribute(program);
colorAttribute(program);
normalAttribute(program);
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->VertexAttribPointer(positionAttribute(),
4, IGLDevice::UnsignedByte,
false, sizeof(Vertex),
(void *)0);
device->VertexAttribPointer(textureCoordAttribute(),
2, IGLDevice::UnsignedShort,
false, sizeof(Vertex),
(void *)4);
device->VertexAttribPointer(colorAttribute(),
4, IGLDevice::UnsignedByte,
true, sizeof(Vertex),
(void *)8);
device->VertexAttribPointer(normalAttribute(),
3, IGLDevice::Byte,
false, sizeof(Vertex),
(void *)12);
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
device->EnableVertexAttribArray(positionAttribute(), true);
device->EnableVertexAttribArray(textureCoordAttribute(), true);
device->EnableVertexAttribArray(colorAttribute(), true);
device->EnableVertexAttribArray(normalAttribute(), true);
device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer);
for(size_t i = 0; i < params.size(); i++){
const client::ModelRenderParam& param = params[i];
// frustrum cull
float rad = radius;
rad *= param.matrix.GetAxis(0).GetLength();
if(!renderer->SphereFrustrumCull(param.matrix.GetOrigin(),
rad)){
continue;
}
static GLProgramUniform customColor("customColor");
customColor(program);
customColor.SetValue(param.customColor.x, param.customColor.y, param.customColor.z);
Matrix4 modelMatrix = param.matrix;
static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix");
projectionViewModelMatrix(program);
projectionViewModelMatrix.SetValue(renderer->GetProjectionViewMatrix() * modelMatrix);
static GLProgramUniform viewModelMatrix("viewModelMatrix");
viewModelMatrix(program);
viewModelMatrix.SetValue(renderer->GetViewMatrix() * modelMatrix);
static GLProgramUniform modelMatrixU("modelMatrix");
modelMatrixU(program);
modelMatrixU.SetValue(modelMatrix);
modelMatrix.m[12] = 0.f;
modelMatrix.m[13] = 0.f;
modelMatrix.m[14] = 0.f;
static GLProgramUniform modelNormalMatrix("modelNormalMatrix");
modelNormalMatrix(program);
modelNormalMatrix.SetValue(modelMatrix);
if(param.depthHack){
device->DepthRange(0.f, 0.1f);
}
device->DrawElements(IGLDevice::Triangles,
numIndices,
IGLDevice::UnsignedInt,
(void *)0);
if(param.depthHack){
device->DepthRange(0.f, 1.f);
}
}
device->BindBuffer(IGLDevice::ElementArrayBuffer, 0);
device->EnableVertexAttribArray(positionAttribute(), false);
device->EnableVertexAttribArray(textureCoordAttribute(), false);
device->EnableVertexAttribArray(colorAttribute(), false);
device->EnableVertexAttribArray(normalAttribute(), false);
device->ActiveTexture(0);
device->BindTexture(IGLDevice::Texture2D, 0);
}
void GLVoxelModel::RenderDynamicLightPass(std::vector<client::ModelRenderParam> params, std::vector<GLDynamicLight> lights) {
SPADES_MARK_FUNCTION();
device->ActiveTexture(0);
device->Enable(IGLDevice::CullFace, true);
device->Enable(IGLDevice::DepthTest, true);
dlightProgram->Use();
static GLDynamicLightShader dlightShader;
static GLProgramUniform fogDistance("fogDistance");
fogDistance(dlightProgram);
fogDistance.SetValue(renderer->GetFogDistance());
static GLProgramUniform modelOrigin("modelOrigin");
modelOrigin(dlightProgram);
modelOrigin.SetValue(origin.x, origin.y, origin.z);
static GLProgramUniform sunLightDirection("sunLightDirection");
sunLightDirection(dlightProgram);
Vector3 sunPos = MakeVector3(0, -1, -1);
sunPos = sunPos.Normalize();
sunLightDirection.SetValue(sunPos.x, sunPos.y, sunPos.z);
// setup attributes
static GLProgramAttribute positionAttribute("positionAttribute");
static GLProgramAttribute colorAttribute("colorAttribute");
static GLProgramAttribute normalAttribute("normalAttribute");
positionAttribute(dlightProgram);
colorAttribute(dlightProgram);
normalAttribute(dlightProgram);
device->BindBuffer(IGLDevice::ArrayBuffer, buffer);
device->VertexAttribPointer(positionAttribute(),
4, IGLDevice::UnsignedByte,
false, sizeof(Vertex),
(void *)0);
device->VertexAttribPointer(colorAttribute(),
4, IGLDevice::UnsignedByte,
true, sizeof(Vertex),
(void *)8);
device->VertexAttribPointer(normalAttribute(),
3, IGLDevice::Byte,
false, sizeof(Vertex),
(void *)12);
device->BindBuffer(IGLDevice::ArrayBuffer, 0);
device->EnableVertexAttribArray(positionAttribute(), true);
device->EnableVertexAttribArray(colorAttribute(), true);
device->EnableVertexAttribArray(normalAttribute(), true);
device->BindBuffer(IGLDevice::ElementArrayBuffer, idxBuffer);
for(size_t i = 0; i < params.size(); i++){
const client::ModelRenderParam& param = params[i];
// frustrum cull
float rad = radius;
rad *= param.matrix.GetAxis(0).GetLength();
if(!renderer->SphereFrustrumCull(param.matrix.GetOrigin(),
rad)){
continue;
}
static GLProgramUniform customColor("customColor");
customColor(dlightProgram);
customColor.SetValue(param.customColor.x, param.customColor.y, param.customColor.z);
Matrix4 modelMatrix = param.matrix;
static GLProgramUniform projectionViewModelMatrix("projectionViewModelMatrix");
projectionViewModelMatrix(dlightProgram);
projectionViewModelMatrix.SetValue(renderer->GetProjectionViewMatrix() * modelMatrix);
static GLProgramUniform viewModelMatrix("viewModelMatrix");
viewModelMatrix(dlightProgram);
viewModelMatrix.SetValue(renderer->GetViewMatrix() * modelMatrix);
static GLProgramUniform modelMatrixU("modelMatrix");
modelMatrixU(dlightProgram);
modelMatrixU.SetValue(modelMatrix);
modelMatrix.m[12] = 0.f;
modelMatrix.m[13] = 0.f;
modelMatrix.m[14] = 0.f;
static GLProgramUniform modelNormalMatrix("modelNormalMatrix");
modelNormalMatrix(dlightProgram);
modelNormalMatrix.SetValue(modelMatrix);
if(param.depthHack){
device->DepthRange(0.f, 0.1f);
}
for(size_t i = 0; i < lights.size(); i++){
if(!GLDynamicLightShader::SphereCull(lights[i],
param.matrix.GetOrigin(), rad))
continue;
dlightShader(renderer, dlightProgram, lights[i], 0);
device->DrawElements(IGLDevice::Triangles,
numIndices,
IGLDevice::UnsignedInt,
(void *)0);
}
if(param.depthHack){
device->DepthRange(0.f, 1.f);
}
}
device->BindBuffer(IGLDevice::ElementArrayBuffer, 0);
device->EnableVertexAttribArray(positionAttribute(), false);
device->EnableVertexAttribArray(colorAttribute(), false);
device->EnableVertexAttribArray(normalAttribute(), false);
device->ActiveTexture(0);
}
}
}