mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 06:18:52 +02:00
Heightfield material paint bits.
This commit is contained in:
parent
6dc4abd286
commit
bba042e823
6 changed files with 172 additions and 130 deletions
|
@ -2869,8 +2869,7 @@ void HeightfieldRenderer::render(bool cursor) {
|
||||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount - 1, indexCount, GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
|
|
||||||
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false);
|
Application::getInstance()->getTextureCache()->setPrimaryDrawBuffers(true, false);
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release();
|
DefaultMetavoxelRendererImplementation::getBaseHeightfieldProgram().release();
|
||||||
|
@ -2946,14 +2945,14 @@ void HeightfieldRenderer::render(bool cursor) {
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
|
|
||||||
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release();
|
DefaultMetavoxelRendererImplementation::getSplatHeightfieldProgram().release();
|
||||||
|
|
||||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||||
glDepthMask(true);
|
glDepthMask(true);
|
||||||
glDepthFunc(GL_LESS);
|
glDepthFunc(GL_LESS);
|
||||||
}
|
}
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
|
|
||||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
|
@ -354,6 +354,45 @@ void MetavoxelData::replace(const AttributePointer& attribute, const Box& bounds
|
||||||
guide(visitor);
|
guide(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SpannerFetchVisitor : public SpannerVisitor {
|
||||||
|
public:
|
||||||
|
|
||||||
|
SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds, QVector<SharedObjectPointer>& results);
|
||||||
|
|
||||||
|
virtual bool visit(Spanner* spanner);
|
||||||
|
|
||||||
|
virtual int visit(MetavoxelInfo& info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const Box& _bounds;
|
||||||
|
QVector<SharedObjectPointer>& _results;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpannerFetchVisitor::SpannerFetchVisitor(const AttributePointer& attribute, const Box& bounds,
|
||||||
|
QVector<SharedObjectPointer>& results) :
|
||||||
|
SpannerVisitor(QVector<AttributePointer>() << attribute),
|
||||||
|
_bounds(bounds),
|
||||||
|
_results(results) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpannerFetchVisitor::visit(Spanner* spanner) {
|
||||||
|
if (spanner->getBounds().intersects(_bounds)) {
|
||||||
|
_results.append(spanner);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SpannerFetchVisitor::visit(MetavoxelInfo& info) {
|
||||||
|
return info.getBounds().intersects(_bounds) ? SpannerVisitor::visit(info) : STOP_RECURSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetavoxelData::getIntersecting(const AttributePointer& attribute, const Box& bounds,
|
||||||
|
QVector<SharedObjectPointer>& results) {
|
||||||
|
SpannerFetchVisitor visitor(attribute, bounds, results);
|
||||||
|
guide(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
void MetavoxelData::clear(const AttributePointer& attribute) {
|
void MetavoxelData::clear(const AttributePointer& attribute) {
|
||||||
MetavoxelNode* node = _roots.take(attribute);
|
MetavoxelNode* node = _roots.take(attribute);
|
||||||
if (node) {
|
if (node) {
|
||||||
|
|
|
@ -102,6 +102,9 @@ public:
|
||||||
void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject,
|
void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject,
|
||||||
const SharedObjectPointer& newObject);
|
const SharedObjectPointer& newObject);
|
||||||
|
|
||||||
|
/// Retrieves all spanners that intersect the specified bounds.
|
||||||
|
void getIntersecting(const AttributePointer& attribute, const Box& bounds, QVector<SharedObjectPointer>& results);
|
||||||
|
|
||||||
/// Clears all data in the specified attribute layer.
|
/// Clears all data in the specified attribute layer.
|
||||||
void clear(const AttributePointer& attribute);
|
void clear(const AttributePointer& attribute);
|
||||||
|
|
||||||
|
|
|
@ -242,130 +242,6 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av
|
||||||
averageColor(averageColor) {
|
averageColor(averageColor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor {
|
|
||||||
public:
|
|
||||||
|
|
||||||
PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
|
|
||||||
const SharedObjectPointer& material, const QColor& color);
|
|
||||||
|
|
||||||
virtual int visit(MetavoxelInfo& info);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
glm::vec3 _position;
|
|
||||||
float _radius;
|
|
||||||
SharedObjectPointer _material;
|
|
||||||
QColor _color;
|
|
||||||
Box _bounds;
|
|
||||||
};
|
|
||||||
|
|
||||||
PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
|
|
||||||
const SharedObjectPointer& material, const QColor& color) :
|
|
||||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
|
||||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
|
|
||||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
|
||||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()),
|
|
||||||
_position(position),
|
|
||||||
_radius(radius),
|
|
||||||
_material(material),
|
|
||||||
_color(color) {
|
|
||||||
|
|
||||||
const float LARGE_EXTENT = 100000.0f;
|
|
||||||
glm::vec3 extents(_radius, LARGE_EXTENT, _radius);
|
|
||||||
_bounds = Box(_position - extents, _position + extents);
|
|
||||||
}
|
|
||||||
|
|
||||||
int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
|
||||||
if (!info.getBounds().intersects(_bounds)) {
|
|
||||||
return STOP_RECURSION;
|
|
||||||
}
|
|
||||||
if (!info.isLeaf) {
|
|
||||||
return DEFAULT_ORDER;
|
|
||||||
}
|
|
||||||
HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<HeightfieldColorDataPointer>();
|
|
||||||
if (colorPointer) {
|
|
||||||
QByteArray contents(colorPointer->getContents());
|
|
||||||
int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
|
|
||||||
int highest = size - 1;
|
|
||||||
float heightScale = size / info.size;
|
|
||||||
|
|
||||||
glm::vec3 center = (_position - info.minimum) * heightScale;
|
|
||||||
float scaledRadius = _radius * heightScale;
|
|
||||||
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
|
|
||||||
|
|
||||||
glm::vec3 start = glm::floor(center - extents);
|
|
||||||
glm::vec3 end = glm::ceil(center + extents);
|
|
||||||
|
|
||||||
// paint all points within the radius
|
|
||||||
float z = qMax(start.z, 0.0f);
|
|
||||||
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
|
|
||||||
int stride = size * DataBlock::COLOR_BYTES;
|
|
||||||
char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES;
|
|
||||||
float squaredRadius = scaledRadius * scaledRadius;
|
|
||||||
char red = _color.red(), green = _color.green(), blue = _color.blue();
|
|
||||||
bool changed = false;
|
|
||||||
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
|
||||||
char* dest = lineDest;
|
|
||||||
for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) {
|
|
||||||
float dx = x - center.x, dz = z - center.z;
|
|
||||||
if (dx * dx + dz * dz <= squaredRadius) {
|
|
||||||
dest[0] = red;
|
|
||||||
dest[1] = green;
|
|
||||||
dest[2] = blue;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lineDest += stride;
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
|
|
||||||
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
|
|
||||||
encodeInline<HeightfieldColorDataPointer>(newPointer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<HeightfieldMaterialDataPointer>();
|
|
||||||
if (materialPointer) {
|
|
||||||
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
|
|
||||||
QByteArray contents(materialPointer->getContents());
|
|
||||||
uchar materialIndex = getMaterialIndex(_material, materials, contents);
|
|
||||||
int size = glm::sqrt((float)contents.size());
|
|
||||||
int highest = size - 1;
|
|
||||||
float heightScale = highest / info.size;
|
|
||||||
|
|
||||||
glm::vec3 center = (_position - info.minimum) * heightScale;
|
|
||||||
float scaledRadius = _radius * heightScale;
|
|
||||||
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
|
|
||||||
|
|
||||||
glm::vec3 start = glm::floor(center - extents);
|
|
||||||
glm::vec3 end = glm::ceil(center + extents);
|
|
||||||
|
|
||||||
// paint all points within the radius
|
|
||||||
float z = qMax(start.z, 0.0f);
|
|
||||||
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
|
|
||||||
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
|
|
||||||
float squaredRadius = scaledRadius * scaledRadius;
|
|
||||||
bool changed = false;
|
|
||||||
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
|
||||||
uchar* dest = lineDest;
|
|
||||||
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
|
||||||
float dx = x - center.x, dz = z - center.z;
|
|
||||||
if (dx * dx + dz * dz <= squaredRadius) {
|
|
||||||
*dest = materialIndex;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lineDest += size;
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
clearUnusedMaterials(materials, contents);
|
|
||||||
HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials));
|
|
||||||
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldMaterialDataPointer>(newPointer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return STOP_RECURSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
|
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
|
||||||
const SharedObjectPointer& material, const QColor& averageColor) :
|
const SharedObjectPointer& material, const QColor& averageColor) :
|
||||||
MaterialEdit(material, averageColor),
|
MaterialEdit(material, averageColor),
|
||||||
|
@ -374,8 +250,17 @@ PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& posi
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||||
PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor);
|
glm::vec3 extents(radius, radius, radius);
|
||||||
data.guide(visitor);
|
QVector<SharedObjectPointer> results;
|
||||||
|
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
|
||||||
|
Box(position - extents, position + extents), results);
|
||||||
|
|
||||||
|
foreach (const SharedObjectPointer& spanner, results) {
|
||||||
|
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paint(position, radius, material, averageColor);
|
||||||
|
if (newSpanner != spanner) {
|
||||||
|
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int VOXEL_BLOCK_SIZE = 16;
|
const int VOXEL_BLOCK_SIZE = 16;
|
||||||
|
|
|
@ -108,6 +108,10 @@ bool Spanner::findRayIntersection(const glm::vec3& origin, const glm::vec3& dire
|
||||||
return _bounds.findRayIntersection(origin, direction, distance);
|
return _bounds.findRayIntersection(origin, direction, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spanner* Spanner::paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
bool Spanner::hasOwnColors() const {
|
bool Spanner::hasOwnColors() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1604,6 +1608,112 @@ bool Heightfield::findRayIntersection(const glm::vec3& origin, const glm::vec3&
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spanner* Heightfield::paint(const glm::vec3& position, float radius,
|
||||||
|
const SharedObjectPointer& material, const QColor& color) {
|
||||||
|
if (!_height) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
int heightWidth = _height->getWidth();
|
||||||
|
int heightHeight = _height->getContents().size() / heightWidth;
|
||||||
|
int baseWidth = heightWidth - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE;
|
||||||
|
int baseHeight = heightHeight - HeightfieldHeight::HEIGHT_EXTENSION + HeightfieldData::SHARED_EDGE;
|
||||||
|
Heightfield* newHeightfield = static_cast<Heightfield*>(clone(true));
|
||||||
|
|
||||||
|
int colorWidth = baseWidth, colorHeight = baseHeight;
|
||||||
|
QByteArray colorContents;
|
||||||
|
if (_color) {
|
||||||
|
colorWidth = _color->getWidth();
|
||||||
|
colorHeight = _color->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES);
|
||||||
|
colorContents = _color->getContents();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
colorContents = QByteArray(baseWidth * baseHeight * DataBlock::COLOR_BYTES, 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
int materialWidth = baseWidth, materialHeight = baseHeight;
|
||||||
|
QByteArray materialContents;
|
||||||
|
QVector<SharedObjectPointer> materials;
|
||||||
|
if (_material) {
|
||||||
|
materialWidth = _material->getWidth();
|
||||||
|
materialHeight = _material->getContents().size() / materialWidth;
|
||||||
|
materialContents = _material->getContents();
|
||||||
|
materials = _material->getMaterials();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
materialContents = QByteArray(baseWidth * baseHeight, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int highestX = colorWidth - 1;
|
||||||
|
int highestZ = colorHeight - 1;
|
||||||
|
glm::vec3 inverseScale(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ));
|
||||||
|
glm::vec3 center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale;
|
||||||
|
|
||||||
|
glm::vec3 extents = glm::vec3(radius, radius, radius) * inverseScale;
|
||||||
|
glm::vec3 start = glm::floor(center - extents);
|
||||||
|
glm::vec3 end = glm::ceil(center + extents);
|
||||||
|
|
||||||
|
// paint all points within the radius
|
||||||
|
float z = qMax(start.z, 0.0f);
|
||||||
|
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX);
|
||||||
|
int stride = colorWidth * DataBlock::COLOR_BYTES;
|
||||||
|
uchar* lineDest = (uchar*)colorContents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES;
|
||||||
|
float squaredRadius = extents.x * extents.x;
|
||||||
|
float multiplierZ = inverseScale.x / inverseScale.z;
|
||||||
|
char red = color.red(), green = color.green(), blue = color.blue();
|
||||||
|
bool changed = false;
|
||||||
|
for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) {
|
||||||
|
uchar* dest = lineDest;
|
||||||
|
for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) {
|
||||||
|
float dx = x - center.x, dz = (z - center.z) * multiplierZ;
|
||||||
|
if (dx * dx + dz * dz <= squaredRadius) {
|
||||||
|
dest[0] = red;
|
||||||
|
dest[1] = green;
|
||||||
|
dest[2] = blue;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineDest += stride;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
newHeightfield->setColor(HeightfieldColorPointer(new HeightfieldColor(colorWidth, colorContents)));
|
||||||
|
}
|
||||||
|
|
||||||
|
highestX = materialWidth - 1;
|
||||||
|
highestZ = materialHeight - 1;
|
||||||
|
inverseScale = glm::vec3(highestX / getScale(), 1.0f, highestZ / (getScale() * _aspectZ));
|
||||||
|
center = glm::inverse(getRotation()) * (position - getTranslation()) * inverseScale;
|
||||||
|
|
||||||
|
extents = glm::vec3(radius, radius, radius) * inverseScale;
|
||||||
|
start = glm::floor(center - extents);
|
||||||
|
end = glm::ceil(center + extents);
|
||||||
|
|
||||||
|
// paint all points within the radius
|
||||||
|
z = qMax(start.z, 0.0f);
|
||||||
|
startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highestX);
|
||||||
|
lineDest = (uchar*)materialContents.data() + (int)z * materialWidth + (int)startX;
|
||||||
|
squaredRadius = extents.x * extents.x;
|
||||||
|
uchar materialIndex = getMaterialIndex(material, materials, materialContents);
|
||||||
|
changed = false;
|
||||||
|
for (float endZ = qMin(end.z, (float)highestZ); z <= endZ; z += 1.0f) {
|
||||||
|
uchar* dest = lineDest;
|
||||||
|
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
||||||
|
float dx = x - center.x, dz = (z - center.z) * multiplierZ;
|
||||||
|
if (dx * dx + dz * dz <= squaredRadius) {
|
||||||
|
*dest = materialIndex;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lineDest += materialWidth;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
clearUnusedMaterials(materials, materialContents);
|
||||||
|
newHeightfield->setMaterial(HeightfieldMaterialPointer(new HeightfieldMaterial(materialWidth,
|
||||||
|
materialContents, materials)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newHeightfield;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray Heightfield::getRendererClassName() const {
|
QByteArray Heightfield::getRendererClassName() const {
|
||||||
return "HeightfieldRenderer";
|
return "HeightfieldRenderer";
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,10 @@ public:
|
||||||
/// Finds the intersection between the described ray and this spanner.
|
/// Finds the intersection between the described ray and this spanner.
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||||
|
|
||||||
|
/// Attempts to paint on the spanner.
|
||||||
|
/// \return the modified spanner, or this if no modification was performed
|
||||||
|
virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color);
|
||||||
|
|
||||||
/// Checks whether this spanner has its own colors.
|
/// Checks whether this spanner has its own colors.
|
||||||
virtual bool hasOwnColors() const;
|
virtual bool hasOwnColors() const;
|
||||||
|
|
||||||
|
@ -511,6 +515,8 @@ public:
|
||||||
|
|
||||||
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
|
||||||
|
|
||||||
|
virtual Spanner* paint(const glm::vec3& position, float radius, const SharedObjectPointer& material, const QColor& color);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void aspectYChanged(float aspectY);
|
void aspectYChanged(float aspectY);
|
||||||
|
|
Loading…
Reference in a new issue