mirror of
https://github.com/overte-org/overte.git
synced 2025-04-13 21:08:35 +02:00
Basic splatting.
This commit is contained in:
parent
416d9bac2e
commit
72500630b0
6 changed files with 138 additions and 39 deletions
|
@ -11,10 +11,18 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// the diffuse texture
|
||||
uniform sampler2D diffuseMap;
|
||||
const int SPLAT_COUNT = 4;
|
||||
|
||||
// the splat textures
|
||||
uniform sampler2D diffuseMaps[SPLAT_COUNT];
|
||||
|
||||
// alpha values for the four splat textures
|
||||
varying vec4 alphaValues;
|
||||
|
||||
void main(void) {
|
||||
// compute the base color based on OpenGL lighting model
|
||||
gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
|
||||
// blend the splat textures
|
||||
gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
|
||||
texture2D(diffuseMaps[1], gl_TexCoord[0].st) * alphaValues.y +
|
||||
texture2D(diffuseMaps[2], gl_TexCoord[0].st) * alphaValues.z +
|
||||
texture2D(diffuseMaps[3], gl_TexCoord[0].st) * alphaValues.w);
|
||||
}
|
||||
|
|
|
@ -14,20 +14,37 @@
|
|||
// the height texture
|
||||
uniform sampler2D heightMap;
|
||||
|
||||
// the texture that contains the texture indices
|
||||
uniform sampler2D textureMap;
|
||||
|
||||
// the distance between height points in texture space
|
||||
uniform float heightScale;
|
||||
|
||||
// the scale between height and texture textures
|
||||
uniform float textureScale;
|
||||
|
||||
// the lower bounds of the values corresponding to the splat textures
|
||||
uniform vec4 textureValueMinima;
|
||||
|
||||
// the upper bounds of the values corresponding to the splat textures
|
||||
uniform vec4 textureValueMaxima;
|
||||
|
||||
// alpha values for the four splat textures
|
||||
varying vec4 alphaValues;
|
||||
|
||||
void main(void) {
|
||||
// add the height to the position
|
||||
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
|
||||
|
||||
// the zero height should be invisible
|
||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
|
||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0 - step(height, 0.0));
|
||||
|
||||
// pass along the scaled/offset texture coordinates
|
||||
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * textureScale;
|
||||
|
||||
// compute the alpha values for each texture
|
||||
float value = texture2D(textureMap, gl_TexCoord[0].st).r;
|
||||
vec4 valueVector = vec4(value, value, value, value);
|
||||
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
|
||||
}
|
||||
|
|
|
@ -673,13 +673,17 @@ public:
|
|||
glm::vec3 vertex;
|
||||
};
|
||||
|
||||
const int SPLAT_COUNT = 4;
|
||||
const GLint SPLAT_TEXTURE_UNITS[] = { 3, 4, 5, 6 };
|
||||
|
||||
void HeightfieldBuffer::render(bool cursor) {
|
||||
// initialize textures, etc. on first render
|
||||
if (_heightTextureID == 0) {
|
||||
glGenTextures(1, &_heightTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _heightTextureID);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _heightSize, _heightSize, 0,
|
||||
|
@ -702,7 +706,8 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
if (!_texture.isEmpty()) {
|
||||
glGenTextures(1, &_textureTextureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _textureTextureID);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
int textureSize = glm::sqrt(_texture.size());
|
||||
|
@ -800,11 +805,53 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glDepthMask(false);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_DST_COLOR, GL_ZERO);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glPolygonOffset(-1.0f, -1.0f);
|
||||
|
||||
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().bind();
|
||||
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getSplatHeightScaleLocation(), 1.0f / _heightSize);
|
||||
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getSplatTextureScaleLocation(), (float)_heightSize / innerSize);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _textureTextureID);
|
||||
|
||||
const int TEXTURES_PER_SPLAT = 4;
|
||||
for (int i = 0; i < _textures.size(); i += TEXTURES_PER_SPLAT) {
|
||||
for (int j = 0; j < SPLAT_COUNT; j++) {
|
||||
glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[j]);
|
||||
int index = i + j;
|
||||
if (index < _networkTextures.size()) {
|
||||
const NetworkTexturePointer& texture = _networkTextures.at(index);
|
||||
glBindTexture(GL_TEXTURE_2D, texture ? texture->getID() : 0);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
const float QUARTER_STEP = 0.25f * EIGHT_BIT_MAXIMUM_RECIPROCAL;
|
||||
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getSplatTextureValueMinimaLocation(),
|
||||
(i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP,
|
||||
(i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL - QUARTER_STEP);
|
||||
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().setUniformValue(
|
||||
DefaultMetavoxelRendererImplementation::getSplatTextureValueMaximaLocation(),
|
||||
(i + 1) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 2) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP,
|
||||
(i + 3) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP, (i + 4) * EIGHT_BIT_MAXIMUM_RECIPROCAL + QUARTER_STEP);
|
||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||
}
|
||||
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glBlendFunc(GL_DST_COLOR, GL_ZERO);
|
||||
|
||||
for (int i = 0; i < SPLAT_COUNT; i++) {
|
||||
glActiveTexture(GL_TEXTURE0 + SPLAT_TEXTURE_UNITS[i]);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) {
|
||||
|
@ -831,7 +878,8 @@ void HeightfieldBuffer::render(bool cursor) {
|
|||
glDisable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
glDepthMask(true);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
} else {
|
||||
|
@ -992,9 +1040,12 @@ void DefaultMetavoxelRendererImplementation::init() {
|
|||
|
||||
_splatHeightfieldProgram.bind();
|
||||
_splatHeightfieldProgram.setUniformValue("heightMap", 0);
|
||||
_splatHeightfieldProgram.setUniformValue("diffuseMap", 1);
|
||||
_splatHeightfieldProgram.setUniformValue("textureMap", 1);
|
||||
_splatHeightfieldProgram.setUniformValueArray("diffuseMaps", SPLAT_TEXTURE_UNITS, SPLAT_COUNT);
|
||||
_splatHeightScaleLocation = _splatHeightfieldProgram.uniformLocation("heightScale");
|
||||
_splatTextureScaleLocation = _splatHeightfieldProgram.uniformLocation("textureScale");
|
||||
_splatTextureValueMinimaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMinima");
|
||||
_splatTextureValueMaximaLocation = _splatHeightfieldProgram.uniformLocation("textureValueMaxima");
|
||||
_splatHeightfieldProgram.release();
|
||||
|
||||
_lightHeightfieldProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() +
|
||||
|
@ -1638,6 +1689,8 @@ int DefaultMetavoxelRendererImplementation::_baseColorScaleLocation;
|
|||
ProgramObject DefaultMetavoxelRendererImplementation::_splatHeightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_splatHeightScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_splatTextureScaleLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_splatTextureValueMinimaLocation;
|
||||
int DefaultMetavoxelRendererImplementation::_splatTextureValueMaximaLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_lightHeightfieldProgram;
|
||||
int DefaultMetavoxelRendererImplementation::_lightHeightScaleLocation;
|
||||
ProgramObject DefaultMetavoxelRendererImplementation::_shadowLightHeightfieldProgram;
|
||||
|
|
|
@ -249,6 +249,8 @@ public:
|
|||
static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; }
|
||||
static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; }
|
||||
static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; }
|
||||
static int getSplatTextureValueMinimaLocation() { return _splatTextureValueMinimaLocation; }
|
||||
static int getSplatTextureValueMaximaLocation() { return _splatTextureValueMaximaLocation; }
|
||||
|
||||
static ProgramObject& getLightHeightfieldProgram() { return _lightHeightfieldProgram; }
|
||||
static int getLightHeightScaleLocation() { return _lightHeightScaleLocation; }
|
||||
|
@ -292,6 +294,8 @@ private:
|
|||
static ProgramObject _splatHeightfieldProgram;
|
||||
static int _splatHeightScaleLocation;
|
||||
static int _splatTextureScaleLocation;
|
||||
static int _splatTextureValueMinimaLocation;
|
||||
static int _splatTextureValueMaximaLocation;
|
||||
|
||||
static ProgramObject _lightHeightfieldProgram;
|
||||
static int _lightHeightScaleLocation;
|
||||
|
|
|
@ -932,6 +932,29 @@ void HeightfieldColorData::set(const QImage& image) {
|
|||
memcpy(_contents.data(), image.constBits(), _contents.size());
|
||||
}
|
||||
|
||||
const int TEXTURE_HEADER_SIZE = sizeof(qint32) * 4;
|
||||
|
||||
static QByteArray encodeTexture(int offsetX, int offsetY, int width, int height, const QByteArray& contents) {
|
||||
QByteArray inflated(TEXTURE_HEADER_SIZE, 0);
|
||||
qint32* header = (qint32*)inflated.data();
|
||||
*header++ = offsetX;
|
||||
*header++ = offsetY;
|
||||
*header++ = width;
|
||||
*header++ = height;
|
||||
inflated.append(contents);
|
||||
return qCompress(inflated);
|
||||
}
|
||||
|
||||
static QByteArray decodeTexture(const QByteArray& encoded, int& offsetX, int& offsetY, int& width, int& height) {
|
||||
QByteArray inflated = qUncompress(encoded);
|
||||
const qint32* header = (const qint32*)inflated.constData();
|
||||
offsetX = *header++;
|
||||
offsetY = *header++;
|
||||
width = *header++;
|
||||
height = *header++;
|
||||
return inflated.mid(TEXTURE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
HeightfieldTextureData::HeightfieldTextureData(const QByteArray& contents, const QVector<SharedObjectPointer>& textures) :
|
||||
HeightfieldData(contents),
|
||||
_textures(textures) {
|
||||
|
@ -951,32 +974,31 @@ HeightfieldTextureData::HeightfieldTextureData(Bitstream& in, int bytes, const H
|
|||
in.readDelta(_textures, reference->getTextures());
|
||||
reference->setDeltaData(HeightfieldDataPointer(this));
|
||||
_contents = reference->getContents();
|
||||
QImage image = decodeHeightfieldImage(reference->getEncodedDelta());
|
||||
if (image.isNull()) {
|
||||
|
||||
int offsetX, offsetY, width, height;
|
||||
QByteArray delta = decodeTexture(reference->getEncodedDelta(), offsetX, offsetY, width, height);
|
||||
if (delta.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QPoint offset = image.offset();
|
||||
if (offset.x() == 0) {
|
||||
set(image);
|
||||
if (offsetX == 0) {
|
||||
_contents = delta;
|
||||
return;
|
||||
}
|
||||
int minX = offset.x() - 1;
|
||||
int minY = offset.y() - 1;
|
||||
int minX = offsetX - 1;
|
||||
int minY = offsetY - 1;
|
||||
int size = glm::sqrt((float)_contents.size());
|
||||
const char* src = delta.constData();
|
||||
char* dest = _contents.data() + minY * size + minX;
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
memcpy(dest, image.constScanLine(y), image.width());
|
||||
dest += size;
|
||||
for (int y = 0; y < height; y++, src += width, dest += size) {
|
||||
memcpy(dest, src, width);
|
||||
}
|
||||
}
|
||||
|
||||
void HeightfieldTextureData::write(Bitstream& out) {
|
||||
QMutexLocker locker(&_encodedMutex);
|
||||
if (_encoded.isEmpty()) {
|
||||
QImage image;
|
||||
int size = glm::sqrt((float)_contents.size());
|
||||
image = QImage((uchar*)_contents.data(), size, size, QImage::Format_Indexed8);
|
||||
_encoded = encodeHeightfieldImage(image, true);
|
||||
_encoded = encodeTexture(0, 0, size, size, _contents);
|
||||
}
|
||||
out << _encoded.size();
|
||||
out.writeAligned(_encoded);
|
||||
|
@ -990,7 +1012,6 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture
|
|||
}
|
||||
QMutexLocker locker(&reference->getEncodedDeltaMutex());
|
||||
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
|
||||
QImage image;
|
||||
int size = glm::sqrt((float)_contents.size());
|
||||
int minX = size, minY = size;
|
||||
int maxX = -1, maxY = -1;
|
||||
|
@ -1010,18 +1031,19 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture
|
|||
maxY = qMax(maxY, y);
|
||||
}
|
||||
}
|
||||
QByteArray delta;
|
||||
int width = 0, height = 0;
|
||||
if (maxX >= minX) {
|
||||
int width = maxX - minX + 1;
|
||||
int height = maxY - minY + 1;
|
||||
image = QImage(width, height, QImage::Format_Indexed8);
|
||||
width = maxX - minX + 1;
|
||||
height = maxY - minY + 1;
|
||||
delta = QByteArray(width * height, 0);
|
||||
char* dest = delta.data();
|
||||
src = _contents.constData() + minY * size + minX;
|
||||
for (int y = 0; y < height; y++) {
|
||||
memcpy(image.scanLine(y), src, width);
|
||||
src += size;
|
||||
for (int y = 0; y < height; y++, src += size, dest += width) {
|
||||
memcpy(dest, src, width);
|
||||
}
|
||||
}
|
||||
image.setOffset(QPoint(minX + 1, minY + 1));
|
||||
reference->setEncodedDelta(encodeHeightfieldImage(image, true));
|
||||
reference->setEncodedDelta(encodeTexture(minX + 1, minY + 1, width, height, delta));
|
||||
reference->setDeltaData(HeightfieldDataPointer(this));
|
||||
}
|
||||
out << reference->getEncodedDelta().size();
|
||||
|
@ -1030,15 +1052,11 @@ void HeightfieldTextureData::writeDelta(Bitstream& out, const HeightfieldTexture
|
|||
}
|
||||
|
||||
void HeightfieldTextureData::read(Bitstream& in, int bytes) {
|
||||
set(decodeHeightfieldImage(_encoded = in.readAligned(bytes)));
|
||||
int offsetX, offsetY, width, height;
|
||||
_contents = decodeTexture(_encoded = in.readAligned(bytes), offsetX, offsetY, width, height);
|
||||
in >> _textures;
|
||||
}
|
||||
|
||||
void HeightfieldTextureData::set(const QImage& image) {
|
||||
_contents.resize(image.width() * image.height());
|
||||
memcpy(_contents.data(), image.constBits(), _contents.size());
|
||||
}
|
||||
|
||||
HeightfieldTexture::HeightfieldTexture() {
|
||||
}
|
||||
|
||||
|
|
|
@ -540,7 +540,6 @@ public:
|
|||
private:
|
||||
|
||||
void read(Bitstream& in, int bytes);
|
||||
void set(const QImage& image);
|
||||
|
||||
QVector<SharedObjectPointer> _textures;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue