mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
302 lines
11 KiB
C++
302 lines
11 KiB
C++
//
|
|
// RenderablePolyLineEntityItem.cpp
|
|
// libraries/entities-renderer/src/
|
|
//
|
|
// Created by Eric Levin on 8/10/15
|
|
// Copyright 2015 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#include "RenderablePolyLineEntityItem.h"
|
|
#include <ParticleEffectEntityItem.h>
|
|
|
|
#include <GeometryCache.h>
|
|
#include <StencilMaskPass.h>
|
|
#include <TextureCache.h>
|
|
#include <PathUtils.h>
|
|
#include <PerfStat.h>
|
|
#include <shaders/Shaders.h>
|
|
|
|
#include "paintStroke_Shared.slh"
|
|
|
|
using namespace render;
|
|
using namespace render::entities;
|
|
|
|
std::map<std::pair<render::Args::RenderMethod, bool>, gpu::PipelinePointer> PolyLineEntityRenderer::_pipelines;
|
|
|
|
static const QUrl DEFAULT_POLYLINE_TEXTURE = PathUtils::resourcesUrl("images/paintStroke.png");
|
|
|
|
PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
|
|
_texture = DependencyManager::get<TextureCache>()->getTexture(DEFAULT_POLYLINE_TEXTURE);
|
|
|
|
{ // Initialize our buffers
|
|
_polylineDataBuffer = std::make_shared<gpu::Buffer>();
|
|
_polylineDataBuffer->resize(sizeof(PolylineData));
|
|
PolylineData data { glm::vec2(_faceCamera, _glow), glm::vec2(0.0f) };
|
|
_polylineDataBuffer->setSubData(0, data);
|
|
|
|
_polylineGeometryBuffer = std::make_shared<gpu::Buffer>();
|
|
}
|
|
}
|
|
|
|
void PolyLineEntityRenderer::buildPipelines() {
|
|
// FIXME: opaque pipelines
|
|
|
|
static const std::vector<std::pair<render::Args::RenderMethod, bool>> keys = {
|
|
{ render::Args::DEFERRED, false }, { render::Args::DEFERRED, true }, { render::Args::FORWARD, false }, { render::Args::FORWARD, true },
|
|
};
|
|
|
|
for (auto& key : keys) {
|
|
gpu::ShaderPointer program = gpu::Shader::createProgram(key.first == render::Args::DEFERRED ? shader::entities_renderer::program::paintStroke : shader::entities_renderer::program::paintStroke_forward);
|
|
|
|
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
|
state->setCullMode(gpu::State::CullMode::CULL_NONE);
|
|
state->setDepthTest(true, !key.second, gpu::LESS_EQUAL);
|
|
PrepareStencil::testMask(*state);
|
|
state->setBlendFunction(true,
|
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
|
_pipelines[key] = gpu::Pipeline::create(program, state);
|
|
}
|
|
}
|
|
|
|
ItemKey PolyLineEntityRenderer::getKey() {
|
|
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
|
}
|
|
|
|
ShapeKey PolyLineEntityRenderer::getShapeKey() {
|
|
auto builder = ShapeKey::Builder().withOwnPipeline().withTranslucent().withoutCullFace();
|
|
if (_primitiveMode == PrimitiveMode::LINES) {
|
|
builder.withWireframe();
|
|
}
|
|
return builder.build();
|
|
}
|
|
|
|
bool PolyLineEntityRenderer::needsRenderUpdate() const {
|
|
bool textureLoadedChanged = resultWithReadLock<bool>([&] {
|
|
return (!_textureLoaded && _texture && _texture->isLoaded());
|
|
});
|
|
|
|
if (textureLoadedChanged) {
|
|
return true;
|
|
}
|
|
|
|
return Parent::needsRenderUpdate();
|
|
}
|
|
|
|
bool PolyLineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
|
if (entity->pointsChanged() || entity->widthsChanged() || entity->normalsChanged() || entity->texturesChanged() || entity->colorsChanged()) {
|
|
return true;
|
|
}
|
|
|
|
if (_isUVModeStretch != entity->getIsUVModeStretch() || _glow != entity->getGlow() || _faceCamera != entity->getFaceCamera()) {
|
|
return true;
|
|
}
|
|
|
|
return Parent::needsRenderUpdateFromTypedEntity(entity);
|
|
}
|
|
|
|
void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
|
auto pointsChanged = entity->pointsChanged();
|
|
auto widthsChanged = entity->widthsChanged();
|
|
auto normalsChanged = entity->normalsChanged();
|
|
auto colorsChanged = entity->colorsChanged();
|
|
|
|
bool isUVModeStretch = entity->getIsUVModeStretch();
|
|
bool glow = entity->getGlow();
|
|
bool faceCamera = entity->getFaceCamera();
|
|
|
|
entity->resetPolyLineChanged();
|
|
|
|
// Textures
|
|
if (entity->texturesChanged()) {
|
|
entity->resetTexturesChanged();
|
|
QUrl entityTextures = DEFAULT_POLYLINE_TEXTURE;
|
|
auto textures = entity->getTextures();
|
|
if (!textures.isEmpty()) {
|
|
entityTextures = QUrl(textures);
|
|
}
|
|
withWriteLock([&] {
|
|
_texture = DependencyManager::get<TextureCache>()->getTexture(entityTextures);
|
|
});
|
|
_textureAspectRatio = 1.0f;
|
|
_textureLoaded = false;
|
|
}
|
|
|
|
bool textureChanged = false;
|
|
if (!_textureLoaded && _texture && _texture->isLoaded()) {
|
|
textureChanged = true;
|
|
_textureAspectRatio = (float)_texture->getOriginalHeight() / (float)_texture->getOriginalWidth();
|
|
_textureLoaded = true;
|
|
}
|
|
|
|
// Data
|
|
bool faceCameraChanged = faceCamera != _faceCamera;
|
|
withWriteLock([&] {
|
|
if (faceCameraChanged || glow != _glow) {
|
|
_faceCamera = faceCamera;
|
|
_glow = glow;
|
|
updateData();
|
|
}
|
|
});
|
|
|
|
// Geometry
|
|
if (pointsChanged) {
|
|
_points = entity->getLinePoints();
|
|
}
|
|
if (widthsChanged) {
|
|
_widths = entity->getStrokeWidths();
|
|
}
|
|
if (normalsChanged) {
|
|
_normals = entity->getNormals();
|
|
}
|
|
if (colorsChanged) {
|
|
_colors = entity->getStrokeColors();
|
|
_color = toGlm(entity->getColor());
|
|
}
|
|
|
|
bool uvModeStretchChanged = _isUVModeStretch != isUVModeStretch;
|
|
_isUVModeStretch = isUVModeStretch;
|
|
|
|
bool geometryChanged = uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged;
|
|
|
|
void* key = (void*)this;
|
|
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] () {
|
|
withWriteLock([&] {
|
|
updateModelTransformAndBound();
|
|
_renderTransform = getModelTransform();
|
|
|
|
if (geometryChanged) {
|
|
updateGeometry();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
void PolyLineEntityRenderer::updateGeometry() {
|
|
int maxNumVertices = std::min(_points.length(), _normals.length());
|
|
bool doesStrokeWidthVary = false;
|
|
if (_widths.size() > 0) {
|
|
float prevWidth = _widths[0];
|
|
for (int i = 1; i < maxNumVertices; i++) {
|
|
float width = i < _widths.length() ? _widths[i] : PolyLineEntityItem::DEFAULT_LINE_WIDTH;
|
|
if (width != prevWidth) {
|
|
doesStrokeWidthVary = true;
|
|
break;
|
|
}
|
|
if (i > _widths.length() + 1) {
|
|
break;
|
|
}
|
|
prevWidth = width;
|
|
}
|
|
}
|
|
|
|
float uCoordInc = 1.0f / maxNumVertices;
|
|
float uCoord = 0.0f;
|
|
float accumulatedDistance = 0.0f;
|
|
float accumulatedStrokeWidth = 0.0f;
|
|
glm::vec3 binormal;
|
|
|
|
std::vector<PolylineVertex> vertices;
|
|
vertices.reserve(maxNumVertices);
|
|
|
|
for (int i = 0; i < maxNumVertices; i++) {
|
|
// Position
|
|
glm::vec3 point = _points[i];
|
|
// uCoord
|
|
float width = i < _widths.size() ? _widths[i] : PolyLineEntityItem::DEFAULT_LINE_WIDTH;
|
|
|
|
if (i > 0) { // First uCoord is 0.0f
|
|
if (!_isUVModeStretch) {
|
|
accumulatedDistance += glm::distance(point, _points[i - 1]);
|
|
|
|
if (doesStrokeWidthVary) {
|
|
//If the stroke varies along the line the texture will stretch more or less depending on the speed
|
|
//because it looks better than using the same method as below
|
|
accumulatedStrokeWidth += width;
|
|
float increaseValue = 1;
|
|
if (accumulatedStrokeWidth != 0) {
|
|
float newUcoord = glm::ceil((_textureAspectRatio * accumulatedDistance) / (accumulatedStrokeWidth / i));
|
|
increaseValue = newUcoord - uCoord;
|
|
}
|
|
|
|
increaseValue = increaseValue > 0 ? increaseValue : 1;
|
|
uCoord += increaseValue;
|
|
} else {
|
|
// If the stroke width is constant then the textures should keep the aspect ratio along the line
|
|
uCoord = (_textureAspectRatio * accumulatedDistance) / width;
|
|
}
|
|
} else {
|
|
uCoord += uCoordInc;
|
|
}
|
|
}
|
|
|
|
// Color
|
|
glm::vec3 color = i < _colors.length() ? _colors[i] : _color;
|
|
|
|
// Normal
|
|
glm::vec3 normal = _normals[i];
|
|
|
|
// Binormal
|
|
// For last point we can assume binormals are the same since it represents the last two vertices of quad
|
|
if (i < maxNumVertices - 1) {
|
|
glm::vec3 tangent = _points[i + 1] - point;
|
|
|
|
if (_faceCamera) {
|
|
// In faceCamera mode, we actually pass the tangent, and recompute the binormal in the shader
|
|
binormal = tangent;
|
|
} else {
|
|
binormal = glm::normalize(glm::cross(tangent, normal));
|
|
|
|
// Check to make sure binormal is not a NAN. If it is, don't add to vertices vector
|
|
if (glm::any(glm::isnan(binormal))) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
PolylineVertex vertex = { glm::vec4(point, uCoord), glm::vec4(color, 1.0f), glm::vec4(normal, 0.0f), glm::vec4(binormal, 0.5f * width) };
|
|
vertices.push_back(vertex);
|
|
}
|
|
|
|
_numVertices = vertices.size();
|
|
_polylineGeometryBuffer->setData(vertices.size() * sizeof(PolylineVertex), (const gpu::Byte*) vertices.data());
|
|
}
|
|
|
|
void PolyLineEntityRenderer::updateData() {
|
|
PolylineData data { glm::vec2(_faceCamera, _glow), glm::vec2(0.0f) };
|
|
_polylineDataBuffer->setSubData(0, data);
|
|
}
|
|
|
|
void PolyLineEntityRenderer::doRender(RenderArgs* args) {
|
|
PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render");
|
|
Q_ASSERT(args->_batch);
|
|
gpu::Batch& batch = *args->_batch;
|
|
|
|
size_t numVertices;
|
|
Transform transform;
|
|
gpu::TexturePointer texture;
|
|
withReadLock([&] {
|
|
numVertices = _numVertices;
|
|
transform = _renderTransform;
|
|
texture = _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get<TextureCache>()->getWhiteTexture();
|
|
|
|
batch.setResourceBuffer(0, _polylineGeometryBuffer);
|
|
batch.setUniformBuffer(0, _polylineDataBuffer);
|
|
});
|
|
|
|
if (numVertices < 2) {
|
|
return;
|
|
}
|
|
|
|
if (_pipelines.empty()) {
|
|
buildPipelines();
|
|
}
|
|
|
|
batch.setPipeline(_pipelines[{args->_renderMethod, _glow}]);
|
|
batch.setModelTransform(transform);
|
|
batch.setResourceTexture(0, texture);
|
|
batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * numVertices), 0);
|
|
}
|