mirror of
https://github.com/overte-org/overte.git
synced 2025-08-11 23:51:49 +02:00
start fixing asynch issue, fixes model loading!
This commit is contained in:
parent
3a6e84e681
commit
a6b7578c3c
8 changed files with 102 additions and 101 deletions
|
@ -329,25 +329,27 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityRenderer::doRenderUpdateAsynchronous(const EntityItemPointer& entity) {
|
void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) {
|
||||||
auto transparent = isTransparent();
|
withWriteLock([&] {
|
||||||
if (_prevIsTransparent && !transparent) {
|
auto transparent = isTransparent();
|
||||||
_isFading = false;
|
if (_prevIsTransparent && !transparent) {
|
||||||
}
|
_isFading = false;
|
||||||
_prevIsTransparent = transparent;
|
}
|
||||||
|
_prevIsTransparent = transparent;
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
auto bound = entity->getAABox(success);
|
auto bound = entity->getAABox(success);
|
||||||
if (success) {
|
if (success) {
|
||||||
_bound = bound;
|
_bound = bound;
|
||||||
}
|
}
|
||||||
auto newModelTransform = entity->getTransformToCenter(success);
|
auto newModelTransform = entity->getTransformToCenter(success);
|
||||||
if (success) {
|
if (success) {
|
||||||
_modelTransform = newModelTransform;
|
_modelTransform = newModelTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
_moving = entity->isMovingRelativeToParent();
|
_moving = entity->isMovingRelativeToParent();
|
||||||
_visible = entity->getVisible();
|
_visible = entity->getVisible();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityRenderer::onAddToScene(const EntityItemPointer& entity) {
|
void EntityRenderer::onAddToScene(const EntityItemPointer& entity) {
|
||||||
|
|
|
@ -73,12 +73,12 @@ protected:
|
||||||
|
|
||||||
// Will be called on the main thread from updateInScene. This can be used to fetch things like
|
// Will be called on the main thread from updateInScene. This can be used to fetch things like
|
||||||
// network textures or model geometry from resource caches
|
// network textures or model geometry from resource caches
|
||||||
virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { }
|
virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity);
|
||||||
|
|
||||||
// Will be called by the lambda posted to the scene in updateInScene.
|
// Will be called by the lambda posted to the scene in updateInScene.
|
||||||
// This function will execute on the rendering thread, so you cannot use network caches to fetch
|
// This function will execute on the rendering thread, so you cannot use network caches to fetch
|
||||||
// data in this method if using multi-threaded rendering
|
// data in this method if using multi-threaded rendering
|
||||||
virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity);
|
virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity) { }
|
||||||
|
|
||||||
// Called by the `render` method after `needsRenderUpdate`
|
// Called by the `render` method after `needsRenderUpdate`
|
||||||
virtual void doRender(RenderArgs* args) = 0;
|
virtual void doRender(RenderArgs* args) = 0;
|
||||||
|
|
|
@ -1145,7 +1145,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
model->removeFromScene(scene, transaction);
|
model->removeFromScene(scene, transaction);
|
||||||
withWriteLock([&] { _model.reset(); });
|
withWriteLock([&] { _model.reset(); });
|
||||||
}
|
}
|
||||||
emit requestRenderUpdate();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,7 +1168,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
|
|
||||||
// Nothing else to do unless the model is loaded
|
// Nothing else to do unless the model is loaded
|
||||||
if (!model->isLoaded()) {
|
if (!model->isLoaded()) {
|
||||||
emit needsRenderUpdate();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,31 +88,33 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
}
|
}
|
||||||
|
|
||||||
_color = vec4(toGlm(entity->getXColor()), entity->getLocalRenderAlpha());
|
_color = vec4(toGlm(entity->getXColor()), entity->getLocalRenderAlpha());
|
||||||
|
|
||||||
|
_shape = entity->getShape();
|
||||||
|
_position = entity->getPosition();
|
||||||
|
_dimensions = entity->getDimensions();
|
||||||
|
_orientation = entity->getOrientation();
|
||||||
|
|
||||||
|
if (_shape == entity::Sphere) {
|
||||||
|
_modelTransform.postScale(SPHERE_ENTITY_SCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
_modelTransform.postScale(_dimensions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||||
if (_procedural.isEnabled() && _procedural.isFading()) {
|
withReadLock([&] {
|
||||||
float isFading = Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f;
|
if (_procedural.isEnabled() && _procedural.isFading()) {
|
||||||
_procedural.setIsFading(isFading);
|
float isFading = Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f;
|
||||||
}
|
_procedural.setIsFading(isFading);
|
||||||
|
}
|
||||||
_shape = entity->getShape();
|
});
|
||||||
_position = entity->getPosition();
|
|
||||||
_dimensions = entity->getDimensions();
|
|
||||||
_orientation = entity->getOrientation();
|
|
||||||
|
|
||||||
if (_shape == entity::Sphere) {
|
|
||||||
_modelTransform.postScale(SPHERE_ENTITY_SCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
_modelTransform.postScale(_dimensions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShapeEntityRenderer::isTransparent() const {
|
bool ShapeEntityRenderer::isTransparent() const {
|
||||||
if (_procedural.isEnabled() && _procedural.isFading()) {
|
if (_procedural.isEnabled() && _procedural.isFading()) {
|
||||||
return Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f;
|
return Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return _entity->getLocalRenderAlpha() < 1.0f || Parent::isTransparent();
|
// return _entity->getLocalRenderAlpha() < 1.0f || Parent::isTransparent();
|
||||||
return Parent::isTransparent();
|
return Parent::isTransparent();
|
||||||
|
@ -126,15 +128,16 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
||||||
|
|
||||||
gpu::Batch& batch = *args->_batch;
|
gpu::Batch& batch = *args->_batch;
|
||||||
|
|
||||||
auto geometryShape = MAPPING[_shape];
|
GeometryCache::Shape geometryShape;
|
||||||
batch.setModelTransform(_modelTransform); // use a transform with scale, rotation, registration point and translation
|
|
||||||
|
|
||||||
bool proceduralRender = false;
|
bool proceduralRender = false;
|
||||||
glm::vec4 outColor = _color;
|
glm::vec4 outColor;
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
|
geometryShape = MAPPING[_shape];
|
||||||
|
batch.setModelTransform(_modelTransform); // use a transform with scale, rotation, registration point and translation
|
||||||
|
outColor = _color;
|
||||||
if (_procedural.isReady()) {
|
if (_procedural.isReady()) {
|
||||||
_procedural.prepare(batch, _position, _dimensions, _orientation);
|
_procedural.prepare(batch, _position, _dimensions, _orientation);
|
||||||
auto outColor = _procedural.getColor(_color);
|
outColor = _procedural.getColor(_color);
|
||||||
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
||||||
proceduralRender = true;
|
proceduralRender = true;
|
||||||
}
|
}
|
||||||
|
@ -149,13 +152,13 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// FIXME, support instanced multi-shape rendering using multidraw indirect
|
// FIXME, support instanced multi-shape rendering using multidraw indirect
|
||||||
_color.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
auto pipeline = _color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
|
auto pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
|
||||||
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
|
if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
|
||||||
geometryCache->renderWireShapeInstance(args, batch, geometryShape, _color, pipeline);
|
geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline);
|
||||||
} else {
|
} else {
|
||||||
geometryCache->renderSolidShapeInstance(args, batch, geometryShape, _color, pipeline);
|
geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,31 +118,30 @@ void WebEntityRenderer::onTimeout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||||
// This work must be done on the main thread
|
withWriteLock([&] {
|
||||||
if (!hasWebSurface()) {
|
// This work must be done on the main thread
|
||||||
buildWebSurface(entity);
|
if (!hasWebSurface()) {
|
||||||
}
|
buildWebSurface(entity);
|
||||||
|
}
|
||||||
|
|
||||||
if (_contextPosition != entity->getPosition()) {
|
if (_contextPosition != entity->getPosition()) {
|
||||||
// update globalPosition
|
// update globalPosition
|
||||||
_contextPosition = entity->getPosition();
|
_contextPosition = entity->getPosition();
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
|
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(_contextPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_lastSourceUrl != entity->getSourceUrl()) {
|
if (_lastSourceUrl != entity->getSourceUrl()) {
|
||||||
_lastSourceUrl = entity->getSourceUrl();
|
_lastSourceUrl = entity->getSourceUrl();
|
||||||
loadSourceURL();
|
loadSourceURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastDPI = entity->getDPI();
|
_lastDPI = entity->getDPI();
|
||||||
|
|
||||||
glm::vec2 windowSize = getWindowSize(entity);
|
glm::vec2 windowSize = getWindowSize(entity);
|
||||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||||
}
|
|
||||||
|
|
||||||
void WebEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
_modelTransform.postScale(entity->getDimensions());
|
||||||
Parent::doRenderUpdateAsynchronousTyped(entity);
|
});
|
||||||
_modelTransform.postScale(entity->getDimensions());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebEntityRenderer::doRender(RenderArgs* args) {
|
void WebEntityRenderer::doRender(RenderArgs* args) {
|
||||||
|
@ -180,7 +179,9 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
||||||
static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f);
|
static const glm::vec2 texMin(0.0f), texMax(1.0f), topLeft(-0.5f), bottomRight(0.5f);
|
||||||
|
|
||||||
gpu::Batch& batch = *args->_batch;
|
gpu::Batch& batch = *args->_batch;
|
||||||
batch.setModelTransform(_modelTransform);
|
withReadLock([&] {
|
||||||
|
batch.setModelTransform(_modelTransform);
|
||||||
|
});
|
||||||
batch.setResourceTexture(0, _texture);
|
batch.setResourceTexture(0, _texture);
|
||||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||||
batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
|
batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
|
||||||
|
@ -190,7 +191,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebEntityRenderer::hasWebSurface() {
|
bool WebEntityRenderer::hasWebSurface() {
|
||||||
return resultWithReadLock<bool>([&] { return (bool)_webSurface; });
|
return (bool)_webSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
|
@ -213,11 +214,8 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
QSharedPointer<OffscreenQmlSurface> webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
|
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
|
||||||
webSurface->create();
|
_webSurface->create();
|
||||||
withWriteLock([&] {
|
|
||||||
_webSurface = webSurface;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
|
||||||
|
@ -322,33 +320,31 @@ glm::vec2 WebEntityRenderer::getWindowSize(const TypedEntityPointer& entity) con
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebEntityRenderer::loadSourceURL() {
|
void WebEntityRenderer::loadSourceURL() {
|
||||||
withWriteLock([&] {
|
const QUrl sourceUrl(_lastSourceUrl);
|
||||||
const QUrl sourceUrl(_lastSourceUrl);
|
if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" ||
|
||||||
if (sourceUrl.scheme() == "http" || sourceUrl.scheme() == "https" ||
|
_lastSourceUrl.toLower().endsWith(".htm") || _lastSourceUrl.toLower().endsWith(".html")) {
|
||||||
_lastSourceUrl.toLower().endsWith(".htm") || _lastSourceUrl.toLower().endsWith(".html")) {
|
_contentType = htmlContent;
|
||||||
_contentType = htmlContent;
|
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "qml/controls/"));
|
||||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "qml/controls/"));
|
|
||||||
|
|
||||||
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
|
// We special case YouTube URLs since we know they are videos that we should play with at least 30 FPS.
|
||||||
if (sourceUrl.host().endsWith("youtube.com", Qt::CaseInsensitive)) {
|
if (sourceUrl.host().endsWith("youtube.com", Qt::CaseInsensitive)) {
|
||||||
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);
|
_webSurface->setMaxFps(YOUTUBE_MAX_FPS);
|
||||||
} else {
|
|
||||||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
|
||||||
}
|
|
||||||
|
|
||||||
_webSurface->load("WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
|
||||||
item->setProperty("url", _lastSourceUrl);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
_contentType = qmlContent;
|
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath()));
|
|
||||||
_webSurface->load(_lastSourceUrl);
|
|
||||||
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
|
|
||||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
|
||||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
_webSurface->load("WebEntityView.qml", [this](QQmlContext* context, QObject* item) {
|
||||||
|
item->setProperty("url", _lastSourceUrl);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_contentType = qmlContent;
|
||||||
|
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath()));
|
||||||
|
_webSurface->load(_lastSourceUrl);
|
||||||
|
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
|
||||||
|
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||||
|
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebEntityRenderer::handlePointerEvent(const TypedEntityPointer& entity, const PointerEvent& event) {
|
void WebEntityRenderer::handlePointerEvent(const TypedEntityPointer& entity, const PointerEvent& event) {
|
||||||
|
|
|
@ -29,7 +29,6 @@ protected:
|
||||||
virtual bool needsRenderUpdate() const override;
|
virtual bool needsRenderUpdate() const override;
|
||||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
|
||||||
virtual void doRender(RenderArgs* args) override;
|
virtual void doRender(RenderArgs* args) override;
|
||||||
virtual bool isTransparent() const override;
|
virtual bool isTransparent() const override;
|
||||||
|
|
||||||
|
|
|
@ -267,6 +267,11 @@ void Model::updateRenderItems() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::setRenderItemsNeedUpdate() {
|
||||||
|
_renderItemsNeedUpdate = true;
|
||||||
|
emit requestRenderUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
void Model::initJointTransforms() {
|
void Model::initJointTransforms() {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
|
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
|
||||||
|
@ -814,13 +819,11 @@ void Model::setTextures(const QVariantMap& textures) {
|
||||||
_needsUpdateTextures = true;
|
_needsUpdateTextures = true;
|
||||||
_needsFixupInScene = true;
|
_needsFixupInScene = true;
|
||||||
_renderGeometry->setTextures(textures);
|
_renderGeometry->setTextures(textures);
|
||||||
emit requestRenderUpdate();
|
|
||||||
} else {
|
} else {
|
||||||
// FIXME(Huffman): Disconnect previously connected lambdas so we don't set textures multiple
|
// FIXME(Huffman): Disconnect previously connected lambdas so we don't set textures multiple
|
||||||
// after the geometry has finished loading.
|
// after the geometry has finished loading.
|
||||||
connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, [this, textures]() {
|
connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, [this, textures]() {
|
||||||
_renderGeometry->setTextures(textures);
|
_renderGeometry->setTextures(textures);
|
||||||
emit requestRenderUpdate();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ public:
|
||||||
bool isLayeredInFront() const { return _isLayeredInFront; }
|
bool isLayeredInFront() const { return _isLayeredInFront; }
|
||||||
|
|
||||||
virtual void updateRenderItems();
|
virtual void updateRenderItems();
|
||||||
void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; emit requestRenderUpdate(); }
|
void setRenderItemsNeedUpdate();
|
||||||
bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; }
|
bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; }
|
||||||
AABox getRenderableMeshBound() const;
|
AABox getRenderableMeshBound() const;
|
||||||
const render::ItemIDs& fetchRenderItemIDs() const;
|
const render::ItemIDs& fetchRenderItemIDs() const;
|
||||||
|
|
Loading…
Reference in a new issue