mirror of
https://github.com/overte-org/overte.git
synced 2025-06-19 12:40:08 +02:00
procedural vertex shaders
Signed-off-by: Kasen IO <kasenvr@gmail.com>
This commit is contained in:
parent
7f56c090f8
commit
6e961b9f2f
14 changed files with 253 additions and 36 deletions
|
@ -2107,6 +2107,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
EntityTree::setGetUnscaledDimensionsForEntityIDOperator([this](const QUuid& id) {
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return glm::vec3(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entity = getEntities()->getEntity(id);
|
||||||
|
if (entity) {
|
||||||
|
return entity->getUnscaledDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
|
auto avatar = static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(id));
|
||||||
|
if (avatar) {
|
||||||
|
return avatar->getSNScale();
|
||||||
|
}
|
||||||
|
return glm::vec3(1.0f);
|
||||||
|
});
|
||||||
Procedural::opaqueStencil = [](gpu::StatePointer state) { PrepareStencil::testMaskDrawShape(*state); };
|
Procedural::opaqueStencil = [](gpu::StatePointer state) { PrepareStencil::testMaskDrawShape(*state); };
|
||||||
Procedural::transparentStencil = [](gpu::StatePointer state) { PrepareStencil::testMask(*state); };
|
Procedural::transparentStencil = [](gpu::StatePointer state) { PrepareStencil::testMask(*state); };
|
||||||
|
|
||||||
|
|
|
@ -153,13 +153,13 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo
|
||||||
|
|
||||||
if (urlChanged && !usingMaterialData) {
|
if (urlChanged && !usingMaterialData) {
|
||||||
_networkMaterial = DependencyManager::get<MaterialCache>()->getMaterial(_materialURL);
|
_networkMaterial = DependencyManager::get<MaterialCache>()->getMaterial(_materialURL);
|
||||||
auto onMaterialRequestFinished = [this, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) {
|
auto onMaterialRequestFinished = [this, entity, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) {
|
||||||
if (success) {
|
if (success) {
|
||||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||||
_texturesLoaded = false;
|
_texturesLoaded = false;
|
||||||
_parsedMaterials = _networkMaterial->parsedMaterials;
|
_parsedMaterials = _networkMaterial->parsedMaterials;
|
||||||
setCurrentMaterialName(newCurrentMaterialName);
|
setCurrentMaterialName(newCurrentMaterialName);
|
||||||
applyMaterial();
|
applyMaterial(entity);
|
||||||
} else {
|
} else {
|
||||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||||
_retryApply = false;
|
_retryApply = false;
|
||||||
|
@ -183,13 +183,13 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo
|
||||||
_parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), _materialURL);
|
_parsedMaterials = NetworkMaterialResource::parseJSONMaterials(QJsonDocument::fromJson(_materialData.toUtf8()), _materialURL);
|
||||||
// Since our material changed, the current name might not be valid anymore, so we need to update
|
// Since our material changed, the current name might not be valid anymore, so we need to update
|
||||||
setCurrentMaterialName(newCurrentMaterialName);
|
setCurrentMaterialName(newCurrentMaterialName);
|
||||||
applyMaterial();
|
applyMaterial(entity);
|
||||||
} else {
|
} else {
|
||||||
if (deleteNeeded) {
|
if (deleteNeeded) {
|
||||||
deleteMaterial(oldParentID, oldParentMaterialName);
|
deleteMaterial(oldParentID, oldParentMaterialName);
|
||||||
}
|
}
|
||||||
if (addNeeded) {
|
if (addNeeded) {
|
||||||
applyMaterial();
|
applyMaterial(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +382,7 @@ void MaterialEntityRenderer::applyTextureTransform(std::shared_ptr<NetworkMateri
|
||||||
material->setTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat);
|
material->setTextureTransforms(textureTransform, _materialMappingMode, _materialRepeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MaterialEntityRenderer::applyMaterial() {
|
void MaterialEntityRenderer::applyMaterial(const TypedEntityPointer& entity) {
|
||||||
_retryApply = false;
|
_retryApply = false;
|
||||||
|
|
||||||
std::shared_ptr<NetworkMaterial> material = getMaterial();
|
std::shared_ptr<NetworkMaterial> material = getMaterial();
|
||||||
|
@ -396,6 +396,11 @@ void MaterialEntityRenderer::applyMaterial() {
|
||||||
|
|
||||||
graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, _priority);
|
graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, _priority);
|
||||||
|
|
||||||
|
if (auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(material)) {
|
||||||
|
procedural->setBoundOperator([this] { return getBound(); });
|
||||||
|
entity->setHasVertexShader(procedural->hasVertexShader());
|
||||||
|
}
|
||||||
|
|
||||||
// Our parent could be an entity or an avatar
|
// Our parent could be an entity or an avatar
|
||||||
std::string parentMaterialName = _parentMaterialName.toStdString();
|
std::string parentMaterialName = _parentMaterialName.toStdString();
|
||||||
if (EntityTreeRenderer::addMaterialToEntity(parentID, materialLayer, parentMaterialName)) {
|
if (EntityTreeRenderer::addMaterialToEntity(parentID, materialLayer, parentMaterialName)) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ private:
|
||||||
void setCurrentMaterialName(const std::string& currentMaterialName);
|
void setCurrentMaterialName(const std::string& currentMaterialName);
|
||||||
|
|
||||||
void applyTextureTransform(std::shared_ptr<NetworkMaterial>& material);
|
void applyTextureTransform(std::shared_ptr<NetworkMaterial>& material);
|
||||||
void applyMaterial();
|
void applyMaterial(const TypedEntityPointer& entity);
|
||||||
void deleteMaterial(const QUuid& oldParentID, const QString& oldParentMaterialName);
|
void deleteMaterial(const QUuid& oldParentID, const QString& oldParentMaterialName);
|
||||||
|
|
||||||
NetworkMaterialResourcePointer _networkMaterial;
|
NetworkMaterialResourcePointer _networkMaterial;
|
||||||
|
|
|
@ -207,6 +207,18 @@ ShapeKey ShapeEntityRenderer::getShapeKey() {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item::Bound ShapeEntityRenderer::getBound() {
|
||||||
|
auto mat = _materials.find("0");
|
||||||
|
if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() &&
|
||||||
|
mat->second.top().material->isReady()) {
|
||||||
|
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(mat->second.top().material);
|
||||||
|
if (procedural->hasVertexShader() && procedural->hasBoundOperator()) {
|
||||||
|
return procedural->getBound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Parent::getBound();
|
||||||
|
}
|
||||||
|
|
||||||
void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
||||||
PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
|
PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
|
||||||
Q_ASSERT(args->_batch);
|
Q_ASSERT(args->_batch);
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ShapeKey getShapeKey() override;
|
ShapeKey getShapeKey() override;
|
||||||
|
Item::Bound getBound() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool needsRenderUpdate() const override;
|
virtual bool needsRenderUpdate() const override;
|
||||||
|
|
|
@ -3113,6 +3113,7 @@ std::function<QObject*(const QUuid&)> EntityTree::_getEntityObjectOperator = nul
|
||||||
std::function<QSizeF(const QUuid&, const QString&)> EntityTree::_textSizeOperator = nullptr;
|
std::function<QSizeF(const QUuid&, const QString&)> EntityTree::_textSizeOperator = nullptr;
|
||||||
std::function<bool()> EntityTree::_areEntityClicksCapturedOperator = nullptr;
|
std::function<bool()> EntityTree::_areEntityClicksCapturedOperator = nullptr;
|
||||||
std::function<void(const QUuid&, const QVariant&)> EntityTree::_emitScriptEventOperator = nullptr;
|
std::function<void(const QUuid&, const QVariant&)> EntityTree::_emitScriptEventOperator = nullptr;
|
||||||
|
std::function<glm::vec3(const QUuid&)> EntityTree::_getUnscaledDimensionsForEntityIDOperator = nullptr;
|
||||||
|
|
||||||
QObject* EntityTree::getEntityObject(const QUuid& id) {
|
QObject* EntityTree::getEntityObject(const QUuid& id) {
|
||||||
if (_getEntityObjectOperator) {
|
if (_getEntityObjectOperator) {
|
||||||
|
@ -3141,6 +3142,13 @@ void EntityTree::emitScriptEvent(const QUuid& id, const QVariant& message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::vec3 EntityTree::getUnscaledDimensionsForEntityID(const QUuid& id) {
|
||||||
|
if (_getUnscaledDimensionsForEntityIDOperator) {
|
||||||
|
return _getUnscaledDimensionsForEntityIDOperator(id);
|
||||||
|
}
|
||||||
|
return glm::vec3(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
|
void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
|
||||||
MovingEntitiesOperator& moveOperator, bool force, bool tellServer) {
|
MovingEntitiesOperator& moveOperator, bool force, bool tellServer) {
|
||||||
// if the queryBox has changed, tell the entity-server
|
// if the queryBox has changed, tell the entity-server
|
||||||
|
|
|
@ -271,6 +271,9 @@ public:
|
||||||
static void setEmitScriptEventOperator(std::function<void(const QUuid&, const QVariant&)> emitScriptEventOperator) { _emitScriptEventOperator = emitScriptEventOperator; }
|
static void setEmitScriptEventOperator(std::function<void(const QUuid&, const QVariant&)> emitScriptEventOperator) { _emitScriptEventOperator = emitScriptEventOperator; }
|
||||||
static void emitScriptEvent(const QUuid& id, const QVariant& message);
|
static void emitScriptEvent(const QUuid& id, const QVariant& message);
|
||||||
|
|
||||||
|
static void setGetUnscaledDimensionsForEntityIDOperator(std::function<glm::vec3(const QUuid&)> getUnscaledDimensionsForEntityIDOperator) { _getUnscaledDimensionsForEntityIDOperator = getUnscaledDimensionsForEntityIDOperator; }
|
||||||
|
static glm::vec3 getUnscaledDimensionsForEntityID(const QUuid& id);
|
||||||
|
|
||||||
std::map<QString, QString> getNamedPaths() const { return _namedPaths; }
|
std::map<QString, QString> getNamedPaths() const { return _namedPaths; }
|
||||||
|
|
||||||
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
|
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
|
||||||
|
@ -386,6 +389,7 @@ private:
|
||||||
static std::function<QSizeF(const QUuid&, const QString&)> _textSizeOperator;
|
static std::function<QSizeF(const QUuid&, const QString&)> _textSizeOperator;
|
||||||
static std::function<bool()> _areEntityClicksCapturedOperator;
|
static std::function<bool()> _areEntityClicksCapturedOperator;
|
||||||
static std::function<void(const QUuid&, const QVariant&)> _emitScriptEventOperator;
|
static std::function<void(const QUuid&, const QVariant&)> _emitScriptEventOperator;
|
||||||
|
static std::function<glm::vec3(const QUuid&)> _getUnscaledDimensionsForEntityIDOperator;
|
||||||
|
|
||||||
std::vector<int32_t> _staleProxies;
|
std::vector<int32_t> _staleProxies;
|
||||||
|
|
||||||
|
|
|
@ -139,10 +139,10 @@ void MaterialEntityItem::debugDump() const {
|
||||||
|
|
||||||
void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
void MaterialEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
||||||
_desiredDimensions = value;
|
_desiredDimensions = value;
|
||||||
if (_materialMappingMode == MaterialMappingMode::UV) {
|
if (_hasVertexShader || _materialMappingMode == MaterialMappingMode::PROJECTED) {
|
||||||
EntityItem::setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS);
|
|
||||||
} else if (_materialMappingMode == MaterialMappingMode::PROJECTED) {
|
|
||||||
EntityItem::setUnscaledDimensions(value);
|
EntityItem::setUnscaledDimensions(value);
|
||||||
|
} else if (_materialMappingMode == MaterialMappingMode::UV) {
|
||||||
|
EntityItem::setUnscaledDimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,6 +264,13 @@ void MaterialEntityItem::setMaterialRepeat(bool value) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MaterialEntityItem::setParentID(const QUuid& parentID) {
|
||||||
|
if (parentID != getParentID()) {
|
||||||
|
EntityItem::setParentID(parentID);
|
||||||
|
_hasVertexShader = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) {
|
AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) {
|
||||||
AACube aaCube = EntityItem::calculateInitialQueryAACube(success);
|
AACube aaCube = EntityItem::calculateInitialQueryAACube(success);
|
||||||
// A Material entity's queryAACube contains its parent's queryAACube
|
// A Material entity's queryAACube contains its parent's queryAACube
|
||||||
|
@ -278,3 +285,16 @@ AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) {
|
||||||
}
|
}
|
||||||
return aaCube;
|
return aaCube;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MaterialEntityItem::setHasVertexShader(bool hasVertexShader) {
|
||||||
|
bool prevHasVertexShader = _hasVertexShader;
|
||||||
|
_hasVertexShader = hasVertexShader;
|
||||||
|
|
||||||
|
if (hasVertexShader && !prevHasVertexShader) {
|
||||||
|
setLocalPosition(glm::vec3(0.0f));
|
||||||
|
setLocalOrientation(glm::quat());
|
||||||
|
setUnscaledDimensions(EntityTree::getUnscaledDimensionsForEntityID(getParentID()));
|
||||||
|
} else if (!hasVertexShader && prevHasVertexShader) {
|
||||||
|
setUnscaledDimensions(_desiredDimensions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,6 +64,8 @@ public:
|
||||||
QString getParentMaterialName() const;
|
QString getParentMaterialName() const;
|
||||||
void setParentMaterialName(const QString& parentMaterialName);
|
void setParentMaterialName(const QString& parentMaterialName);
|
||||||
|
|
||||||
|
void setParentID(const QUuid& parentID) override;
|
||||||
|
|
||||||
glm::vec2 getMaterialMappingPos() const;
|
glm::vec2 getMaterialMappingPos() const;
|
||||||
void setMaterialMappingPos(const glm::vec2& materialMappingPos);
|
void setMaterialMappingPos(const glm::vec2& materialMappingPos);
|
||||||
glm::vec2 getMaterialMappingScale() const;
|
glm::vec2 getMaterialMappingScale() const;
|
||||||
|
@ -73,6 +75,8 @@ public:
|
||||||
|
|
||||||
AACube calculateInitialQueryAACube(bool& success) override;
|
AACube calculateInitialQueryAACube(bool& success) override;
|
||||||
|
|
||||||
|
void setHasVertexShader(bool hasVertexShader);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// URL for this material. Currently, only JSON format is supported. Set to "materialData" to use the material data to live edit a material.
|
// URL for this material. Currently, only JSON format is supported. Set to "materialData" to use the material data to live edit a material.
|
||||||
// The following fields are supported in the JSON:
|
// The following fields are supported in the JSON:
|
||||||
|
@ -108,6 +112,8 @@ private:
|
||||||
float _materialMappingRot { 0 };
|
float _materialMappingRot { 0 };
|
||||||
QString _materialData;
|
QString _materialData;
|
||||||
|
|
||||||
|
bool _hasVertexShader { false };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_MaterialEntityItem_h
|
#endif // hifi_MaterialEntityItem_h
|
||||||
|
|
|
@ -29,6 +29,7 @@ Q_LOGGING_CATEGORY(proceduralLog, "hifi.gpu.procedural")
|
||||||
|
|
||||||
// User-data parsing constants
|
// User-data parsing constants
|
||||||
static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
|
static const QString PROCEDURAL_USER_DATA_KEY = "ProceduralEntity";
|
||||||
|
static const QString VERTEX_URL_KEY = "vertexShaderURL";
|
||||||
static const QString FRAGMENT_URL_KEY = "fragmentShaderURL";
|
static const QString FRAGMENT_URL_KEY = "fragmentShaderURL";
|
||||||
static const QString URL_KEY = "shaderUrl";
|
static const QString URL_KEY = "shaderUrl";
|
||||||
static const QString VERSION_KEY = "version";
|
static const QString VERSION_KEY = "version";
|
||||||
|
@ -42,6 +43,7 @@ static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION";
|
||||||
bool operator==(const ProceduralData& a, const ProceduralData& b) {
|
bool operator==(const ProceduralData& a, const ProceduralData& b) {
|
||||||
return ((a.version == b.version) &&
|
return ((a.version == b.version) &&
|
||||||
(a.fragmentShaderUrl == b.fragmentShaderUrl) &&
|
(a.fragmentShaderUrl == b.fragmentShaderUrl) &&
|
||||||
|
(a.vertexShaderUrl == b.vertexShaderUrl) &&
|
||||||
(a.uniforms == b.uniforms) &&
|
(a.uniforms == b.uniforms) &&
|
||||||
(a.channels == b.channels));
|
(a.channels == b.channels));
|
||||||
}
|
}
|
||||||
|
@ -101,9 +103,9 @@ void ProceduralData::parse(const QJsonObject& proceduralData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty shader URL isn't valid
|
{ // Vertex shader URL
|
||||||
if (fragmentShaderUrl.isEmpty()) {
|
auto rawShaderUrl = proceduralData[VERTEX_URL_KEY].toString();
|
||||||
return;
|
vertexShaderUrl = DependencyManager::get<ResourceManager>()->normalizeURL(rawShaderUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
uniforms = proceduralData[UNIFORMS_KEY].toObject();
|
uniforms = proceduralData[UNIFORMS_KEY].toObject();
|
||||||
|
@ -172,29 +174,57 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
|
||||||
|
|
||||||
if (proceduralData.fragmentShaderUrl != _data.fragmentShaderUrl) {
|
if (proceduralData.fragmentShaderUrl != _data.fragmentShaderUrl) {
|
||||||
_data.fragmentShaderUrl = proceduralData.fragmentShaderUrl;
|
_data.fragmentShaderUrl = proceduralData.fragmentShaderUrl;
|
||||||
const auto& shaderUrl = _data.fragmentShaderUrl;
|
|
||||||
|
|
||||||
_shaderDirty = true;
|
_shaderDirty = true;
|
||||||
_networkFragmentShader.reset();
|
_networkFragmentShader.reset();
|
||||||
_fragmentShaderPath.clear();
|
_fragmentShaderPath.clear();
|
||||||
_fragmentShaderSource.clear();
|
_fragmentShaderSource.clear();
|
||||||
|
|
||||||
if (shaderUrl.isEmpty() || !shaderUrl.isValid()) {
|
if (!_data.fragmentShaderUrl.isValid()) {
|
||||||
|
qCWarning(proceduralLog) << "Invalid fragment shader URL: " << _data.fragmentShaderUrl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shaderUrl.isLocalFile()) {
|
if (_data.fragmentShaderUrl.isLocalFile()) {
|
||||||
if (!QFileInfo(shaderUrl.toLocalFile()).exists()) {
|
if (!QFileInfo(_data.fragmentShaderUrl.toLocalFile()).exists()) {
|
||||||
|
qCWarning(proceduralLog) << "Invalid fragment shader URL, missing local file: " << _data.fragmentShaderUrl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_fragmentShaderPath = shaderUrl.toLocalFile();
|
_fragmentShaderPath = _data.fragmentShaderUrl.toLocalFile();
|
||||||
} else if (shaderUrl.scheme() == URL_SCHEME_QRC) {
|
} else if (_data.fragmentShaderUrl.scheme() == URL_SCHEME_QRC) {
|
||||||
_fragmentShaderPath = ":" + shaderUrl.path();
|
_fragmentShaderPath = ":" + _data.fragmentShaderUrl.path();
|
||||||
} else {
|
} else {
|
||||||
_networkFragmentShader = ShaderCache::instance().getShader(shaderUrl);
|
_networkFragmentShader = ShaderCache::instance().getShader(_data.fragmentShaderUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (proceduralData.vertexShaderUrl != _data.vertexShaderUrl) {
|
||||||
|
_data.vertexShaderUrl = proceduralData.vertexShaderUrl;
|
||||||
|
|
||||||
|
_shaderDirty = true;
|
||||||
|
_networkVertexShader.reset();
|
||||||
|
_vertexShaderPath.clear();
|
||||||
|
_vertexShaderSource.clear();
|
||||||
|
|
||||||
|
if (!_data.vertexShaderUrl.isValid()) {
|
||||||
|
qCWarning(proceduralLog) << "Invalid vertex shader URL: " << _data.vertexShaderUrl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_data.vertexShaderUrl.isLocalFile()) {
|
||||||
|
if (!QFileInfo(_data.vertexShaderUrl.toLocalFile()).exists()) {
|
||||||
|
qCWarning(proceduralLog) << "Invalid vertex shader URL, missing local file: " << _data.vertexShaderUrl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_vertexShaderPath = _data.vertexShaderUrl.toLocalFile();
|
||||||
|
} else if (_data.vertexShaderUrl.scheme() == URL_SCHEME_QRC) {
|
||||||
|
_vertexShaderPath = ":" + _data.vertexShaderUrl.path();
|
||||||
|
} else {
|
||||||
|
_networkVertexShader = ShaderCache::instance().getShader(_data.vertexShaderUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
_enabled = true;
|
_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,8 +243,12 @@ bool Procedural::isReady() const {
|
||||||
_fadeStartTime = usecTimestampNow();
|
_fadeStartTime = usecTimestampNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we have a network or local shader, and if so, is it loaded?
|
// We need to have at least one shader, and whichever ones we have need to be loaded
|
||||||
if (_fragmentShaderPath.isEmpty() && (!_networkFragmentShader || !_networkFragmentShader->isLoaded())) {
|
bool hasFragmentShader = !_fragmentShaderPath.isEmpty() || _networkFragmentShader;
|
||||||
|
bool fragmentShaderLoaded = !_fragmentShaderPath.isEmpty() || (_networkFragmentShader && _networkFragmentShader->isLoaded());
|
||||||
|
bool hasVertexShader = !_vertexShaderPath.isEmpty() || _networkVertexShader;
|
||||||
|
bool vertexShaderLoaded = !_vertexShaderPath.isEmpty() || (_networkVertexShader && _networkVertexShader->isLoaded());
|
||||||
|
if ((!hasFragmentShader && !hasVertexShader) || (hasFragmentShader && !fragmentShaderLoaded) || (hasVertexShader && !vertexShaderLoaded)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +292,20 @@ void Procedural::prepare(gpu::Batch& batch,
|
||||||
_shaderDirty = true;
|
_shaderDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_vertexShaderPath.isEmpty()) {
|
||||||
|
auto lastModified = (uint64_t)QFileInfo(_vertexShaderPath).lastModified().toMSecsSinceEpoch();
|
||||||
|
if (lastModified > _vertexShaderModified) {
|
||||||
|
QFile file(_vertexShaderPath);
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
_vertexShaderSource = QTextStream(&file).readAll();
|
||||||
|
_shaderDirty = true;
|
||||||
|
_vertexShaderModified = lastModified;
|
||||||
|
}
|
||||||
|
} else if (_vertexShaderSource.isEmpty() && _networkVertexShader && _networkVertexShader->isLoaded()) {
|
||||||
|
_vertexShaderSource = _networkVertexShader->_source;
|
||||||
|
_shaderDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (_shaderDirty) {
|
if (_shaderDirty) {
|
||||||
_proceduralPipelines.clear();
|
_proceduralPipelines.clear();
|
||||||
}
|
}
|
||||||
|
@ -276,25 +324,42 @@ void Procedural::prepare(gpu::Batch& batch,
|
||||||
|
|
||||||
gpu::Shader::Source& fragmentSource = (key.isTransparent() && _transparentFragmentSource.valid()) ? _transparentFragmentSource : _opaqueFragmentSource;
|
gpu::Shader::Source& fragmentSource = (key.isTransparent() && _transparentFragmentSource.valid()) ? _transparentFragmentSource : _opaqueFragmentSource;
|
||||||
|
|
||||||
// Build the fragment shader
|
// Build the fragment and vertex shaders
|
||||||
|
auto versionDefine = "#define PROCEDURAL_V" + std::to_string(_data.version);
|
||||||
fragmentSource.replacements.clear();
|
fragmentSource.replacements.clear();
|
||||||
fragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V" + std::to_string(_data.version);
|
fragmentSource.replacements[PROCEDURAL_VERSION] = versionDefine;
|
||||||
fragmentSource.replacements[PROCEDURAL_BLOCK] = _fragmentShaderSource.toStdString();
|
if (!_fragmentShaderSource.isEmpty()) {
|
||||||
|
fragmentSource.replacements[PROCEDURAL_BLOCK] = _fragmentShaderSource.toStdString();
|
||||||
|
}
|
||||||
|
vertexSource.replacements.clear();
|
||||||
|
vertexSource.replacements[PROCEDURAL_VERSION] = versionDefine;
|
||||||
|
if (!_vertexShaderSource.isEmpty()) {
|
||||||
|
vertexSource.replacements[PROCEDURAL_BLOCK] = _vertexShaderSource.toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
// Set any userdata specified uniforms (if any)
|
// Set any userdata specified uniforms (if any)
|
||||||
if (!_data.uniforms.empty()) {
|
if (!_data.uniforms.empty()) {
|
||||||
// First grab all the possible dialect/variant/Reflections
|
// First grab all the possible dialect/variant/reflections
|
||||||
std::vector<shader::Reflection*> allReflections;
|
std::vector<shader::Reflection*> allFragmentReflections;
|
||||||
for (auto dialectIt = fragmentSource.dialectSources.begin(); dialectIt != fragmentSource.dialectSources.end(); ++dialectIt) {
|
for (auto dialectIt = fragmentSource.dialectSources.begin(); dialectIt != fragmentSource.dialectSources.end(); ++dialectIt) {
|
||||||
for (auto variantIt = (*dialectIt).second.variantSources.begin(); variantIt != (*dialectIt).second.variantSources.end(); ++variantIt) {
|
for (auto variantIt = (*dialectIt).second.variantSources.begin(); variantIt != (*dialectIt).second.variantSources.end(); ++variantIt) {
|
||||||
allReflections.push_back(&(*variantIt).second.reflection);
|
allFragmentReflections.push_back(&(*variantIt).second.reflection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<shader::Reflection*> allVertexReflections;
|
||||||
|
for (auto dialectIt = vertexSource.dialectSources.begin(); dialectIt != vertexSource.dialectSources.end(); ++dialectIt) {
|
||||||
|
for (auto variantIt = (*dialectIt).second.variantSources.begin(); variantIt != (*dialectIt).second.variantSources.end(); ++variantIt) {
|
||||||
|
allVertexReflections.push_back(&(*variantIt).second.reflection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Then fill in every reflections the new custom bindings
|
// Then fill in every reflections the new custom bindings
|
||||||
int customSlot = procedural::slot::uniform::Custom;
|
int customSlot = procedural::slot::uniform::Custom;
|
||||||
for (const auto& key : _data.uniforms.keys()) {
|
for (const auto& key : _data.uniforms.keys()) {
|
||||||
std::string uniformName = key.toLocal8Bit().data();
|
std::string uniformName = key.toLocal8Bit().data();
|
||||||
for (auto reflection : allReflections) {
|
for (auto reflection : allFragmentReflections) {
|
||||||
|
reflection->uniforms[uniformName] = customSlot;
|
||||||
|
}
|
||||||
|
for (auto reflection : allVertexReflections) {
|
||||||
reflection->uniforms[uniformName] = customSlot;
|
reflection->uniforms[uniformName] = customSlot;
|
||||||
}
|
}
|
||||||
++customSlot;
|
++customSlot;
|
||||||
|
@ -303,6 +368,7 @@ void Procedural::prepare(gpu::Batch& batch,
|
||||||
|
|
||||||
// Leave this here for debugging
|
// Leave this here for debugging
|
||||||
//qCDebug(proceduralLog) << "FragmentShader:\n" << fragmentSource.getSource(shader::Dialect::glsl450, shader::Variant::Mono).c_str();
|
//qCDebug(proceduralLog) << "FragmentShader:\n" << fragmentSource.getSource(shader::Dialect::glsl450, shader::Variant::Mono).c_str();
|
||||||
|
//qCDebug(proceduralLog) << "VertexShader:\n" << vertexSource.getSource(shader::Dialect::glsl450, shader::Variant::Mono).c_str();
|
||||||
|
|
||||||
gpu::ShaderPointer vertexShader = gpu::Shader::createVertex(vertexSource);
|
gpu::ShaderPointer vertexShader = gpu::Shader::createVertex(vertexSource);
|
||||||
gpu::ShaderPointer fragmentShader = gpu::Shader::createPixel(fragmentSource);
|
gpu::ShaderPointer fragmentShader = gpu::Shader::createPixel(fragmentSource);
|
||||||
|
@ -453,6 +519,11 @@ glm::vec4 Procedural::getColor(const glm::vec4& entityColor) const {
|
||||||
return entityColor;
|
return entityColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Procedural::hasVertexShader() const {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
return !_data.vertexShaderUrl.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
void graphics::ProceduralMaterial::initializeProcedural() {
|
void graphics::ProceduralMaterial::initializeProcedural() {
|
||||||
_procedural._vertexSource = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural);
|
_procedural._vertexSource = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural);
|
||||||
_procedural._vertexSourceSkinned = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural_deformed);
|
_procedural._vertexSourceSkinned = gpu::Shader::getVertexShaderSource(shader::render_utils::vertex::simple_procedural_deformed);
|
||||||
|
|
|
@ -36,6 +36,8 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
|
||||||
* The data used to define a Procedural shader material.
|
* The data used to define a Procedural shader material.
|
||||||
* @typedef {object} ProceduralData
|
* @typedef {object} ProceduralData
|
||||||
* @property {number} version=1 - The version of the procedural shader.
|
* @property {number} version=1 - The version of the procedural shader.
|
||||||
|
* @property {string} vertexShaderURL - A link to a vertex shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version.
|
||||||
|
* If a procedural material contains a vertex shader, the bounding box of the material entity is used to cull the object to which the material is applied.
|
||||||
* @property {string} fragmentShaderURL - A link to a fragment shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version.
|
* @property {string} fragmentShaderURL - A link to a fragment shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version.
|
||||||
* <code>shaderUrl</code> is an alias.
|
* <code>shaderUrl</code> is an alias.
|
||||||
* @property {string[]} channels=[] - An array of input texture URLs. Currently, up to 4 are supported.
|
* @property {string[]} channels=[] - An array of input texture URLs. Currently, up to 4 are supported.
|
||||||
|
@ -50,6 +52,7 @@ struct ProceduralData {
|
||||||
// Rendering object descriptions, from userData
|
// Rendering object descriptions, from userData
|
||||||
uint8_t version { 0 };
|
uint8_t version { 0 };
|
||||||
QUrl fragmentShaderUrl;
|
QUrl fragmentShaderUrl;
|
||||||
|
QUrl vertexShaderUrl;
|
||||||
QJsonObject uniforms;
|
QJsonObject uniforms;
|
||||||
QJsonArray channels;
|
QJsonArray channels;
|
||||||
};
|
};
|
||||||
|
@ -110,6 +113,11 @@ public:
|
||||||
void setIsFading(bool isFading) { _isFading = isFading; }
|
void setIsFading(bool isFading) { _isFading = isFading; }
|
||||||
void setDoesFade(bool doesFade) { _doesFade = doesFade; }
|
void setDoesFade(bool doesFade) { _doesFade = doesFade; }
|
||||||
|
|
||||||
|
bool hasVertexShader() const;
|
||||||
|
void setBoundOperator(const std::function<AABox()>& boundOperator) { _boundOperator = boundOperator; }
|
||||||
|
bool hasBoundOperator() const { return (bool)_boundOperator; }
|
||||||
|
AABox getBound() { return _boundOperator(); }
|
||||||
|
|
||||||
gpu::Shader::Source _vertexSource;
|
gpu::Shader::Source _vertexSource;
|
||||||
gpu::Shader::Source _vertexSourceSkinned;
|
gpu::Shader::Source _vertexSourceSkinned;
|
||||||
gpu::Shader::Source _vertexSourceSkinnedDQ;
|
gpu::Shader::Source _vertexSourceSkinnedDQ;
|
||||||
|
@ -156,7 +164,11 @@ protected:
|
||||||
uint64_t _firstCompile { 0 };
|
uint64_t _firstCompile { 0 };
|
||||||
int32_t _frameCount { 0 };
|
int32_t _frameCount { 0 };
|
||||||
|
|
||||||
// Rendering object descriptions, from userData
|
// Rendering object descriptions
|
||||||
|
QString _vertexShaderSource;
|
||||||
|
QString _vertexShaderPath;
|
||||||
|
uint64_t _vertexShaderModified { 0 };
|
||||||
|
NetworkShaderPointer _networkVertexShader;
|
||||||
QString _fragmentShaderSource;
|
QString _fragmentShaderSource;
|
||||||
QString _fragmentShaderPath;
|
QString _fragmentShaderPath;
|
||||||
uint64_t _fragmentShaderModified { 0 };
|
uint64_t _fragmentShaderModified { 0 };
|
||||||
|
@ -187,6 +199,9 @@ private:
|
||||||
mutable bool _isFading { false };
|
mutable bool _isFading { false };
|
||||||
bool _doesFade { true };
|
bool _doesFade { true };
|
||||||
ProceduralProgramKey _prevKey;
|
ProceduralProgramKey _prevKey;
|
||||||
|
|
||||||
|
std::function<AABox()> _boundOperator { nullptr };
|
||||||
|
|
||||||
mutable std::mutex _mutex;
|
mutable std::mutex _mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,6 +225,7 @@ public:
|
||||||
bool isFading() const { return _procedural.isFading(); }
|
bool isFading() const { return _procedural.isFading(); }
|
||||||
void setIsFading(bool isFading) { _procedural.setIsFading(isFading); }
|
void setIsFading(bool isFading) { _procedural.setIsFading(isFading); }
|
||||||
uint64_t getFadeStartTime() const { return _procedural.getFadeStartTime(); }
|
uint64_t getFadeStartTime() const { return _procedural.getFadeStartTime(); }
|
||||||
|
bool hasVertexShader() const { return _procedural.hasVertexShader(); }
|
||||||
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation,
|
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation,
|
||||||
const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) {
|
const uint64_t& created, const ProceduralProgramKey key = ProceduralProgramKey()) {
|
||||||
_procedural.prepare(batch, position, size, orientation, created, key);
|
_procedural.prepare(batch, position, size, orientation, created, key);
|
||||||
|
@ -217,6 +233,10 @@ public:
|
||||||
|
|
||||||
void initializeProcedural();
|
void initializeProcedural();
|
||||||
|
|
||||||
|
void setBoundOperator(const std::function<AABox()>& boundOperator) { _procedural.setBoundOperator(boundOperator); }
|
||||||
|
bool hasBoundOperator() const { return _procedural.hasBoundOperator(); }
|
||||||
|
AABox getBound() { return _procedural.getBound(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString _proceduralString;
|
QString _proceduralString;
|
||||||
Procedural _procedural;
|
Procedural _procedural;
|
||||||
|
|
|
@ -60,6 +60,17 @@ LAYOUT_STD140(binding=PROCEDURAL_BUFFER_INPUTS) uniform standardInputsBuffer {
|
||||||
#define iChannelResolution standardInputs.channelResolution
|
#define iChannelResolution standardInputs.channelResolution
|
||||||
#define iWorldOrientation standardInputs.worldOrientation
|
#define iWorldOrientation standardInputs.worldOrientation
|
||||||
|
|
||||||
|
struct ProceduralVertexData {
|
||||||
|
vec4 position;
|
||||||
|
vec4 nonSkinnedPosition; // input only
|
||||||
|
vec3 normal;
|
||||||
|
vec3 nonSkinnedNormal; // input only
|
||||||
|
vec3 tangent; // input only
|
||||||
|
vec3 nonSkinnedTangent; // input only
|
||||||
|
vec4 color;
|
||||||
|
vec2 texCoord0;
|
||||||
|
};
|
||||||
|
|
||||||
struct ProceduralFragment {
|
struct ProceduralFragment {
|
||||||
vec3 normal;
|
vec3 normal;
|
||||||
vec3 diffuse;
|
vec3 diffuse;
|
||||||
|
|
|
@ -110,6 +110,13 @@ ItemKey MeshPartPayload::getKey() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item::Bound MeshPartPayload::getBound() const {
|
Item::Bound MeshPartPayload::getBound() const {
|
||||||
|
graphics::MaterialPointer material = _drawMaterials.empty() ? nullptr : _drawMaterials.top().material;
|
||||||
|
if (material && material->isProcedural() && material->isReady()) {
|
||||||
|
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(_drawMaterials.top().material);
|
||||||
|
if (procedural->hasVertexShader() && procedural->hasBoundOperator()) {
|
||||||
|
return procedural->getBound();
|
||||||
|
}
|
||||||
|
}
|
||||||
return _worldBound;
|
return _worldBound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,6 +182,9 @@ void MeshPartPayload::render(RenderArgs* args) {
|
||||||
|
|
||||||
if (!_drawMaterials.empty() && _drawMaterials.top().material && _drawMaterials.top().material->isProcedural() &&
|
if (!_drawMaterials.empty() && _drawMaterials.top().material && _drawMaterials.top().material->isProcedural() &&
|
||||||
_drawMaterials.top().material->isReady()) {
|
_drawMaterials.top().material->isReady()) {
|
||||||
|
if (!(enableMaterialProceduralShaders && ENABLE_MATERIAL_PROCEDURAL_SHADERS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(_drawMaterials.top().material);
|
auto procedural = std::static_pointer_cast<graphics::ProceduralMaterial>(_drawMaterials.top().material);
|
||||||
auto& schema = _drawMaterials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
auto& schema = _drawMaterials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
||||||
glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity);
|
glm::vec4 outColor = glm::vec4(ColorUtils::tosRGBVec3(schema._albedo), schema._opacity);
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
<@if HIFI_USE_DEFORMED or HIFI_USE_DEFORMEDDQ@>
|
<@if HIFI_USE_DEFORMED or HIFI_USE_DEFORMEDDQ@>
|
||||||
<@include MeshDeformer.slh@>
|
<@include MeshDeformer.slh@>
|
||||||
<@if HIFI_USE_DEFORMED@>
|
<@if HIFI_USE_DEFORMED@>
|
||||||
<$declareMeshDeformer(1, _SCRIBE_NULL, 1, _SCRIBE_NULL, 1)$>
|
<$declareMeshDeformer(1, 1, 1, _SCRIBE_NULL, 1)$>
|
||||||
<@else@>
|
<@else@>
|
||||||
<$declareMeshDeformer(1, _SCRIBE_NULL, 1, 1, 1)$>
|
<$declareMeshDeformer(1, 1, 1, 1, 1)$>
|
||||||
<@endif@>
|
<@endif@>
|
||||||
<$declareMeshDeformerActivation(1, 1)$>
|
<$declareMeshDeformerActivation(1, 1)$>
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
@ -34,24 +34,56 @@ layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS;
|
||||||
layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color;
|
layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color;
|
||||||
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
|
layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01;
|
||||||
|
|
||||||
|
<@include procedural/ProceduralCommon.slh@>
|
||||||
|
|
||||||
|
#line 1001
|
||||||
|
//PROCEDURAL_BLOCK_BEGIN
|
||||||
|
|
||||||
|
void getProceduralVertex(inout ProceduralVertexData proceduralData) {}
|
||||||
|
|
||||||
|
//PROCEDURAL_BLOCK_END
|
||||||
|
|
||||||
|
#line 2030
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec4 positionMS = inPosition;
|
vec4 positionMS = inPosition;
|
||||||
vec3 normalMS = inNormal.xyz;
|
vec3 normalMS = inNormal.xyz;
|
||||||
|
vec3 tangentMS = inTangent.xyz;
|
||||||
|
vec4 color = color_sRGBAToLinear(inColor);
|
||||||
|
vec2 texCoord0 = inTexCoord0.st;
|
||||||
|
|
||||||
<@if HIFI_USE_DEFORMED or HIFI_USE_DEFORMEDDQ@>
|
<@if HIFI_USE_DEFORMED or HIFI_USE_DEFORMEDDQ@>
|
||||||
evalMeshDeformer(inPosition, positionMS, inNormal.xyz, normalMS,
|
evalMeshDeformer(inPosition, positionMS, inNormal.xyz, normalMS, inTangent.xyz, tangentMS,
|
||||||
meshDeformer_doSkinning(_drawCallInfo.y), inSkinClusterIndex, inSkinClusterWeight,
|
meshDeformer_doSkinning(_drawCallInfo.y), inSkinClusterIndex, inSkinClusterWeight,
|
||||||
meshDeformer_doBlendshape(_drawCallInfo.y), gl_VertexID);
|
meshDeformer_doBlendshape(_drawCallInfo.y), gl_VertexID);
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
||||||
|
#if defined(PROCEDURAL_V1) || defined(PROCEDURAL_V2) || defined(PROCEDURAL_V3)
|
||||||
|
ProceduralVertexData proceduralData = ProceduralVertexData(
|
||||||
|
positionMS,
|
||||||
|
inPosition,
|
||||||
|
normalMS,
|
||||||
|
inNormal.xyz,
|
||||||
|
tangentMS,
|
||||||
|
inTangent.xyz,
|
||||||
|
color,
|
||||||
|
texCoord0
|
||||||
|
);
|
||||||
|
|
||||||
|
getProceduralVertex(proceduralData);
|
||||||
|
|
||||||
|
positionMS = proceduralData.position;
|
||||||
|
normalMS = proceduralData.normal;
|
||||||
|
color = proceduralData.color;
|
||||||
|
texCoord0 = proceduralData.texCoord0;
|
||||||
|
#endif
|
||||||
|
|
||||||
_positionMS = positionMS;
|
_positionMS = positionMS;
|
||||||
_normalMS = normalMS;
|
_normalMS = normalMS;
|
||||||
|
_color = color;
|
||||||
|
_texCoord01 = vec4(texCoord0, 0.0, 0.0);
|
||||||
|
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, positionMS, _positionES, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, positionMS, _positionES, gl_Position)$>
|
||||||
<$transformModelToWorldDir(cam, obj, normalMS, _normalWS)$>
|
<$transformModelToWorldDir(cam, obj, normalMS, _normalWS)$>
|
||||||
|
|
||||||
_color = color_sRGBAToLinear(inColor);
|
|
||||||
_texCoord01 = vec4(inTexCoord0.st, 0.0, 0.0);
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue