60fd3191c5
It was replaced by `Handle::GetPointerOrNull()`. Every use of `GetPointerOrNull` should be reviewed. Some of them were already removed.
608 lines
19 KiB
C++
608 lines
19 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 "GLDynamicLightShader.h"
|
|
#include "GLImage.h"
|
|
#include "GLProgram.h"
|
|
#include "GLProgramAttribute.h"
|
|
#include "GLProgramUniform.h"
|
|
#include "GLRenderer.h"
|
|
#include "GLShadowMapShader.h"
|
|
#include "GLShadowShader.h"
|
|
#include "IGLShadowMapRenderer.h"
|
|
#include <Core/Debug.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.png");
|
|
}
|
|
GLVoxelModel::GLVoxelModel(VoxelModel *m, GLRenderer *r) : device(r->GetGLDevice()) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
renderer = r;
|
|
|
|
program = renderer->RegisterProgram("Shaders/VoxelModel.program");
|
|
dlightProgram = renderer->RegisterProgram("Shaders/VoxelModelDynamicLit.program");
|
|
shadowMapProgram = renderer->RegisterProgram("Shaders/VoxelModelShadowMap.program");
|
|
aoImage = renderer->RegisterImage("Gfx/AmbientOcclusion.png").Cast<GLImage>();
|
|
|
|
BuildVertices(m);
|
|
|
|
buffer = device.GenBuffer();
|
|
device.BindBuffer(IGLDevice::ArrayBuffer, buffer);
|
|
device.BufferData(IGLDevice::ArrayBuffer,
|
|
static_cast<IGLDevice::Sizei>(vertices.size() * sizeof(Vertex)),
|
|
vertices.data(), IGLDevice::StaticDraw);
|
|
|
|
idxBuffer = device.GenBuffer();
|
|
device.BindBuffer(IGLDevice::ArrayBuffer, idxBuffer);
|
|
device.BufferData(IGLDevice::ArrayBuffer,
|
|
static_cast<IGLDevice::Sizei>(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::Prerender(std::vector<client::ModelRenderParam> params, bool ghostPass) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
RenderSunlightPass(params, ghostPass);
|
|
}
|
|
|
|
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 ¶m = params[i];
|
|
|
|
if (!param.castShadow || param.ghost) {
|
|
continue;
|
|
}
|
|
|
|
// 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,
|
|
bool ghostPass) {
|
|
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);
|
|
|
|
static GLProgramUniform viewOriginVector("viewOriginVector");
|
|
viewOriginVector(program);
|
|
const auto &viewOrigin = renderer->GetSceneDef().viewOrigin;
|
|
viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.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 ¶m = params[i];
|
|
|
|
if (param.ghost != ghostPass) {
|
|
continue;
|
|
}
|
|
|
|
// 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);
|
|
|
|
static GLProgramUniform modelOpacity("modelOpacity");
|
|
modelOpacity(program);
|
|
modelOpacity.SetValue(param.opacity);
|
|
|
|
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);
|
|
|
|
static GLProgramUniform viewOriginVector("viewOriginVector");
|
|
viewOriginVector(dlightProgram);
|
|
const auto &viewOrigin = renderer->GetSceneDef().viewOrigin;
|
|
viewOriginVector.SetValue(viewOrigin.x, viewOrigin.y, viewOrigin.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 ¶m = params[i];
|
|
|
|
if (param.ghost)
|
|
continue;
|
|
|
|
// 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 (!lights[i].SphereCull(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);
|
|
}
|
|
} // namespace draw
|
|
} // namespace spades
|