mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 10:30:28 +02:00
Merged with upstream master
This commit is contained in:
commit
eae604d77e
13 changed files with 549 additions and 299 deletions
|
@ -50,6 +50,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
_hostname(),
|
_hostname(),
|
||||||
_webAuthenticationStateSet(),
|
_webAuthenticationStateSet(),
|
||||||
_cookieSessionHash(),
|
_cookieSessionHash(),
|
||||||
|
_automaticNetworkingSetting(),
|
||||||
_settingsManager()
|
_settingsManager()
|
||||||
{
|
{
|
||||||
LogUtils::init();
|
LogUtils::init();
|
||||||
|
@ -327,17 +328,17 @@ void DomainServer::setupAutomaticNetworking() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString automaticNetworkValue =
|
_automaticNetworkingSetting =
|
||||||
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
|
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
|
||||||
|
|
||||||
if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
|
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
|
||||||
automaticNetworkValue == FULL_AUTOMATIC_NETWORKING_VALUE) {
|
_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
|
||||||
|
|
||||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||||
const QUuid& domainID = nodeList->getSessionUUID();
|
const QUuid& domainID = nodeList->getSessionUUID();
|
||||||
|
|
||||||
if (!domainID.isNull()) {
|
if (!domainID.isNull()) {
|
||||||
qDebug() << "domain-server" << automaticNetworkValue << "automatic networking enabled for ID"
|
qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID"
|
||||||
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
||||||
|
|
||||||
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
||||||
|
@ -347,7 +348,7 @@ void DomainServer::setupAutomaticNetworking() {
|
||||||
QTimer* dynamicIPTimer = new QTimer(this);
|
QTimer* dynamicIPTimer = new QTimer(this);
|
||||||
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
||||||
|
|
||||||
if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
|
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
|
||||||
dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
|
dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
|
||||||
|
|
||||||
// send public socket changes to the data server so nodes can find us at our new IP
|
// send public socket changes to the data server so nodes can find us at our new IP
|
||||||
|
@ -361,11 +362,11 @@ void DomainServer::setupAutomaticNetworking() {
|
||||||
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
|
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
|
||||||
|
|
||||||
// call our sendHeartbeaToIceServer immediately anytime a local or public socket changes
|
// call our sendHeartbeaToIceServer immediately anytime a local or public socket changes
|
||||||
connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer);
|
connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
|
||||||
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer);
|
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
|
||||||
|
|
||||||
// tell the data server which type of automatic networking we are using
|
// send our heartbeat to data server so it knows what our network settings are
|
||||||
updateNetworkingInfoWithDataServer(automaticNetworkValue);
|
sendHeartbeatToDataServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to update our sockets now
|
// attempt to update our sockets now
|
||||||
|
@ -378,8 +379,17 @@ void DomainServer::setupAutomaticNetworking() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateNetworkingInfoWithDataServer(automaticNetworkValue);
|
sendHeartbeatToDataServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting;
|
||||||
|
|
||||||
|
// no matter the auto networking settings we should heartbeat to the data-server every 15s
|
||||||
|
const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000;
|
||||||
|
|
||||||
|
QTimer* dataHeartbeatTimer = new QTimer(this);
|
||||||
|
connect(dataHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToDataServer);
|
||||||
|
dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::loginFailed() {
|
void DomainServer::loginFailed() {
|
||||||
|
@ -1081,10 +1091,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) {
|
||||||
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||||
|
|
||||||
void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) {
|
void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) {
|
||||||
updateNetworkingInfoWithDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString());
|
updateDomainInDataServer(newPublicSockAddr.getAddress().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress) {
|
void DomainServer::updateDomainInDataServer(const QString& networkAddress) {
|
||||||
const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
|
const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
|
||||||
const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID();
|
const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID();
|
||||||
|
|
||||||
|
@ -1097,9 +1107,7 @@ void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting,
|
||||||
domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress;
|
domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Updating automatic networking setting in domain-server to" << newSetting;
|
domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting;
|
||||||
|
|
||||||
domainObject[AUTOMATIC_NETWORKING_KEY] = newSetting;
|
|
||||||
|
|
||||||
QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
|
QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
|
||||||
|
|
||||||
|
@ -1112,11 +1120,11 @@ void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting,
|
||||||
// todo: have data-web respond with ice-server hostname to use
|
// todo: have data-web respond with ice-server hostname to use
|
||||||
|
|
||||||
void DomainServer::performICEUpdates() {
|
void DomainServer::performICEUpdates() {
|
||||||
sendHearbeatToIceServer();
|
sendHeartbeatToIceServer();
|
||||||
sendICEPingPackets();
|
sendICEPingPackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServer::sendHearbeatToIceServer() {
|
void DomainServer::sendHeartbeatToIceServer() {
|
||||||
const HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT);
|
const HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT);
|
||||||
LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR);
|
LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,8 @@ private slots:
|
||||||
void requestCurrentPublicSocketViaSTUN();
|
void requestCurrentPublicSocketViaSTUN();
|
||||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||||
void performICEUpdates();
|
void performICEUpdates();
|
||||||
void sendHearbeatToIceServer();
|
void sendHeartbeatToDataServer() { updateDomainInDataServer(); }
|
||||||
|
void sendHeartbeatToIceServer();
|
||||||
void sendICEPingPackets();
|
void sendICEPingPackets();
|
||||||
private:
|
private:
|
||||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||||
|
@ -76,7 +77,7 @@ private:
|
||||||
bool optionallySetupAssignmentPayment();
|
bool optionallySetupAssignmentPayment();
|
||||||
|
|
||||||
void setupAutomaticNetworking();
|
void setupAutomaticNetworking();
|
||||||
void updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress = QString());
|
void updateDomainInDataServer(const QString& networkAddress = QString());
|
||||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
void processICEHeartbeatResponse(const QByteArray& packet);
|
||||||
|
|
||||||
|
@ -150,6 +151,8 @@ private:
|
||||||
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
||||||
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
||||||
|
|
||||||
|
QString _automaticNetworkingSetting;
|
||||||
|
|
||||||
DomainServerSettingsManager _settingsManager;
|
DomainServerSettingsManager _settingsManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ var leapHands = (function () {
|
||||||
var isOnHMD,
|
var isOnHMD,
|
||||||
LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD",
|
LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD",
|
||||||
LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip
|
LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip
|
||||||
HMD_OFFSET = 0.100, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief
|
HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief
|
||||||
hands,
|
hands,
|
||||||
wrists,
|
wrists,
|
||||||
NUM_HANDS = 2, // 0 = left; 1 = right
|
NUM_HANDS = 2, // 0 = left; 1 = right
|
||||||
|
|
|
@ -107,6 +107,8 @@ static unsigned STARFIELD_SEED = 1;
|
||||||
|
|
||||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
||||||
|
|
||||||
|
const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB
|
||||||
|
|
||||||
static QTimer* idleTimer = NULL;
|
static QTimer* idleTimer = NULL;
|
||||||
|
|
||||||
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
|
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
|
||||||
|
@ -343,9 +345,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS);
|
billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS);
|
||||||
|
|
||||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||||
|
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
||||||
|
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
|
||||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
||||||
networkAccessManager.setCache(cache);
|
networkAccessManager.setCache(cache);
|
||||||
|
|
||||||
|
|
|
@ -961,6 +961,7 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||||
_scale(scale),
|
_scale(scale),
|
||||||
_heightBounds(translation, translation + glm::vec3(scale, scale, scale)),
|
_heightBounds(translation, translation + glm::vec3(scale, scale, scale)),
|
||||||
_colorBounds(_heightBounds),
|
_colorBounds(_heightBounds),
|
||||||
|
_materialBounds(_heightBounds),
|
||||||
_height(height),
|
_height(height),
|
||||||
_color(color),
|
_color(color),
|
||||||
_material(material),
|
_material(material),
|
||||||
|
@ -968,10 +969,12 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||||
_heightTextureID(0),
|
_heightTextureID(0),
|
||||||
_colorTextureID(0),
|
_colorTextureID(0),
|
||||||
_materialTextureID(0),
|
_materialTextureID(0),
|
||||||
_heightSize(glm::sqrt(float(height.size()))),
|
_heightSize(glm::sqrt((float)height.size())),
|
||||||
_heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)),
|
_heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)),
|
||||||
_colorSize(glm::sqrt(float(color.size() / DataBlock::COLOR_BYTES))),
|
_colorSize(glm::sqrt((float)color.size() / DataBlock::COLOR_BYTES)),
|
||||||
_colorIncrement(scale / (_colorSize - SHARED_EDGE)) {
|
_colorIncrement(scale / (_colorSize - SHARED_EDGE)),
|
||||||
|
_materialSize(glm::sqrt((float)material.size())),
|
||||||
|
_materialIncrement(scale / (_materialSize - SHARED_EDGE)) {
|
||||||
|
|
||||||
_heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER;
|
_heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER;
|
||||||
_heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER;
|
_heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER;
|
||||||
|
@ -980,6 +983,9 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
||||||
|
|
||||||
_colorBounds.maximum.x += _colorIncrement * SHARED_EDGE;
|
_colorBounds.maximum.x += _colorIncrement * SHARED_EDGE;
|
||||||
_colorBounds.maximum.z += _colorIncrement * SHARED_EDGE;
|
_colorBounds.maximum.z += _colorIncrement * SHARED_EDGE;
|
||||||
|
|
||||||
|
_materialBounds.maximum.x += _materialIncrement * SHARED_EDGE;
|
||||||
|
_materialBounds.maximum.z += _materialIncrement * SHARED_EDGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldBuffer::~HeightfieldBuffer() {
|
HeightfieldBuffer::~HeightfieldBuffer() {
|
||||||
|
@ -1006,16 +1012,14 @@ QByteArray HeightfieldBuffer::getUnextendedHeight() const {
|
||||||
return unextended;
|
return unextended;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray HeightfieldBuffer::getUnextendedColor() const {
|
QByteArray HeightfieldBuffer::getUnextendedColor(int x, int y) const {
|
||||||
int srcSize = glm::sqrt(float(_color.size() / DataBlock::COLOR_BYTES));
|
int unextendedSize = _heightSize - HEIGHT_EXTENSION;
|
||||||
int destSize = srcSize - 1;
|
QByteArray unextended(unextendedSize * unextendedSize * DataBlock::COLOR_BYTES, 0);
|
||||||
QByteArray unextended(destSize * destSize * DataBlock::COLOR_BYTES, 0);
|
|
||||||
const char* src = _color.constData();
|
|
||||||
int srcStride = srcSize * DataBlock::COLOR_BYTES;
|
|
||||||
char* dest = unextended.data();
|
char* dest = unextended.data();
|
||||||
int destStride = destSize * DataBlock::COLOR_BYTES;
|
const char* src = _color.constData() + (y * _colorSize + x) * unextendedSize * DataBlock::COLOR_BYTES;
|
||||||
for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) {
|
for (int z = 0; z < unextendedSize; z++, dest += unextendedSize * DataBlock::COLOR_BYTES,
|
||||||
memcpy(dest, src, destStride);
|
src += _colorSize * DataBlock::COLOR_BYTES) {
|
||||||
|
memcpy(dest, src, unextendedSize * DataBlock::COLOR_BYTES);
|
||||||
}
|
}
|
||||||
return unextended;
|
return unextended;
|
||||||
}
|
}
|
||||||
|
@ -1702,134 +1706,246 @@ public:
|
||||||
|
|
||||||
HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections);
|
HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections);
|
||||||
|
|
||||||
void init(HeightfieldBuffer* buffer) { _buffer = buffer; }
|
void init(HeightfieldBuffer* buffer);
|
||||||
|
|
||||||
virtual int visit(MetavoxelInfo& info);
|
virtual int visit(MetavoxelInfo& info);
|
||||||
|
virtual bool postVisit(MetavoxelInfo& info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
const QVector<Box>& _intersections;
|
const QVector<Box>& _intersections;
|
||||||
HeightfieldBuffer* _buffer;
|
HeightfieldBuffer* _buffer;
|
||||||
|
|
||||||
|
QVector<int> _depthFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum DepthFlags { HEIGHT_FLAG = 0x01, COLOR_FLAG = 0x02, MATERIAL_FLAG = 0x04 };
|
||||||
|
|
||||||
HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections) :
|
HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections) :
|
||||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>(), lod),
|
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||||
|
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>(), lod),
|
||||||
_intersections(intersections) {
|
_intersections(intersections) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) {
|
||||||
|
_buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
|
int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
|
||||||
Box bounds = info.getBounds();
|
if (!info.getBounds().intersects(_buffer->getHeightBounds())) {
|
||||||
const Box& heightBounds = _buffer->getHeightBounds();
|
|
||||||
if (!bounds.intersects(heightBounds)) {
|
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
if (!info.isLeaf && info.size > _buffer->getScale()) {
|
if (_depthFlags.size() > _depth) {
|
||||||
|
_depthFlags[_depth] = 0;
|
||||||
|
} else {
|
||||||
|
_depthFlags.append(0);
|
||||||
|
}
|
||||||
|
if (!info.isLeaf) {
|
||||||
return DEFAULT_ORDER;
|
return DEFAULT_ORDER;
|
||||||
}
|
}
|
||||||
|
postVisit(info);
|
||||||
|
return STOP_RECURSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) {
|
||||||
HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
||||||
if (!height) {
|
int flags = _depthFlags.at(_depth);
|
||||||
return STOP_RECURSION;
|
if (height) {
|
||||||
|
// to handle borders correctly, make sure we only sample nodes with resolution <= ours
|
||||||
|
int heightSize = glm::sqrt((float)height->getContents().size());
|
||||||
|
float heightIncrement = info.size / heightSize;
|
||||||
|
if (heightIncrement < _buffer->getHeightIncrement() || (flags & HEIGHT_FLAG)) {
|
||||||
|
height.reset();
|
||||||
|
} else {
|
||||||
|
flags |= HEIGHT_FLAG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||||
|
if (color) {
|
||||||
|
int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES);
|
||||||
|
float colorIncrement = info.size / colorSize;
|
||||||
|
if (colorIncrement < _buffer->getColorIncrement() || (flags & COLOR_FLAG)) {
|
||||||
|
color.reset();
|
||||||
|
} else {
|
||||||
|
flags |= COLOR_FLAG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||||
|
if (material) {
|
||||||
|
int materialSize = glm::sqrt((float)material->getContents().size());
|
||||||
|
float materialIncrement = info.size / materialSize;
|
||||||
|
if (materialIncrement < _buffer->getMaterialIncrement() || (flags & MATERIAL_FLAG)) {
|
||||||
|
material.reset();
|
||||||
|
} else {
|
||||||
|
flags |= MATERIAL_FLAG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_depth > 0) {
|
||||||
|
_depthFlags[_depth - 1] |= flags;
|
||||||
|
}
|
||||||
|
if (!(height || color || material)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Box bounds = info.getBounds();
|
||||||
foreach (const Box& intersection, _intersections) {
|
foreach (const Box& intersection, _intersections) {
|
||||||
Box overlap = intersection.getIntersection(bounds);
|
Box overlap = intersection.getIntersection(bounds);
|
||||||
if (overlap.isEmpty()) {
|
if (overlap.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
float heightIncrement = _buffer->getHeightIncrement();
|
if (height) {
|
||||||
int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement;
|
float heightIncrement = _buffer->getHeightIncrement();
|
||||||
int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement;
|
const Box& heightBounds = _buffer->getHeightBounds();
|
||||||
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement);
|
int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement;
|
||||||
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement);
|
int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement;
|
||||||
int heightSize = _buffer->getHeightSize();
|
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement);
|
||||||
char* dest = _buffer->getHeight().data() + destY * heightSize + destX;
|
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement);
|
||||||
|
int heightSize = _buffer->getHeightSize();
|
||||||
const QByteArray& srcHeight = height->getContents();
|
char* dest = _buffer->getHeight().data() + destY * heightSize + destX;
|
||||||
int srcSize = glm::sqrt(float(srcHeight.size()));
|
|
||||||
float srcIncrement = info.size / srcSize;
|
|
||||||
|
|
||||||
if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) {
|
|
||||||
// easy case: same resolution
|
|
||||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
|
||||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
|
||||||
|
|
||||||
const char* src = srcHeight.constData() + srcY * srcSize + srcX;
|
const QByteArray& srcHeight = height->getContents();
|
||||||
for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) {
|
int srcSize = glm::sqrt((float)srcHeight.size());
|
||||||
memcpy(dest, src, destWidth);
|
float srcIncrement = info.size / srcSize;
|
||||||
}
|
|
||||||
} else {
|
if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) {
|
||||||
// more difficult: different resolutions
|
// easy case: same resolution
|
||||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||||
float srcAdvance = heightIncrement / srcIncrement;
|
|
||||||
int shift = 0;
|
const char* src = srcHeight.constData() + srcY * srcSize + srcX;
|
||||||
float size = _buffer->getScale();
|
for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) {
|
||||||
while (size < info.size) {
|
memcpy(dest, src, destWidth);
|
||||||
shift++;
|
}
|
||||||
size *= 2.0f;
|
} else {
|
||||||
}
|
// more difficult: different resolutions
|
||||||
int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale();
|
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||||
for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) {
|
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||||
const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize;
|
float srcAdvance = heightIncrement / srcIncrement;
|
||||||
float lineSrcX = srcX;
|
int shift = 0;
|
||||||
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
float size = _buffer->getScale();
|
||||||
*lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM);
|
while (size < info.size) {
|
||||||
|
shift++;
|
||||||
|
size *= 2.0f;
|
||||||
|
}
|
||||||
|
int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale();
|
||||||
|
for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) {
|
||||||
|
const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize;
|
||||||
|
float lineSrcX = srcX;
|
||||||
|
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
||||||
|
*lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (color) {
|
||||||
int colorSize = _buffer->getColorSize();
|
const Box& colorBounds = _buffer->getColorBounds();
|
||||||
if (colorSize == 0) {
|
overlap = colorBounds.getIntersection(overlap);
|
||||||
continue;
|
float colorIncrement = _buffer->getColorIncrement();
|
||||||
}
|
int destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement;
|
||||||
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
int destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement;
|
||||||
if (!color) {
|
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement);
|
||||||
continue;
|
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement);
|
||||||
}
|
int colorSize = _buffer->getColorSize();
|
||||||
const Box& colorBounds = _buffer->getColorBounds();
|
char* dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES;
|
||||||
overlap = colorBounds.getIntersection(overlap);
|
int destStride = colorSize * DataBlock::COLOR_BYTES;
|
||||||
float colorIncrement = _buffer->getColorIncrement();
|
int destBytes = destWidth * DataBlock::COLOR_BYTES;
|
||||||
destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement;
|
|
||||||
destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement;
|
|
||||||
destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement);
|
|
||||||
destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement);
|
|
||||||
dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES;
|
|
||||||
int destStride = colorSize * DataBlock::COLOR_BYTES;
|
|
||||||
int destBytes = destWidth * DataBlock::COLOR_BYTES;
|
|
||||||
|
|
||||||
const QByteArray& srcColor = color->getContents();
|
|
||||||
srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES));
|
|
||||||
int srcStride = srcSize * DataBlock::COLOR_BYTES;
|
|
||||||
srcIncrement = info.size / srcSize;
|
|
||||||
|
|
||||||
if (srcIncrement == colorIncrement) {
|
|
||||||
// easy case: same resolution
|
|
||||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
|
||||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
|
||||||
|
|
||||||
const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES;
|
const QByteArray& srcColor = color->getContents();
|
||||||
for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) {
|
int srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES));
|
||||||
memcpy(dest, src, destBytes);
|
int srcStride = srcSize * DataBlock::COLOR_BYTES;
|
||||||
}
|
float srcIncrement = info.size / srcSize;
|
||||||
} else {
|
|
||||||
// more difficult: different resolutions
|
if (srcIncrement == colorIncrement) {
|
||||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
// easy case: same resolution
|
||||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||||
float srcAdvance = colorIncrement / srcIncrement;
|
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||||
for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) {
|
|
||||||
const char* src = srcColor.constData() + (int)srcY * srcStride;
|
const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES;
|
||||||
float lineSrcX = srcX;
|
for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) {
|
||||||
for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES,
|
memcpy(dest, src, destBytes);
|
||||||
lineSrcX += srcAdvance) {
|
}
|
||||||
const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES;
|
} else {
|
||||||
lineDest[0] = lineSrc[0];
|
// more difficult: different resolutions
|
||||||
lineDest[1] = lineSrc[1];
|
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||||
lineDest[2] = lineSrc[2];
|
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||||
|
float srcAdvance = colorIncrement / srcIncrement;
|
||||||
|
for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) {
|
||||||
|
const char* src = srcColor.constData() + (int)srcY * srcStride;
|
||||||
|
float lineSrcX = srcX;
|
||||||
|
for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES,
|
||||||
|
lineSrcX += srcAdvance) {
|
||||||
|
const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES;
|
||||||
|
lineDest[0] = lineSrc[0];
|
||||||
|
lineDest[1] = lineSrc[1];
|
||||||
|
lineDest[2] = lineSrc[2];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (material) {
|
||||||
|
const Box& materialBounds = _buffer->getMaterialBounds();
|
||||||
|
overlap = materialBounds.getIntersection(overlap);
|
||||||
|
float materialIncrement = _buffer->getMaterialIncrement();
|
||||||
|
int destX = (overlap.minimum.x - materialBounds.minimum.x) / materialIncrement;
|
||||||
|
int destY = (overlap.minimum.z - materialBounds.minimum.z) / materialIncrement;
|
||||||
|
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / materialIncrement);
|
||||||
|
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / materialIncrement);
|
||||||
|
int materialSize = _buffer->getMaterialSize();
|
||||||
|
char* dest = _buffer->getMaterial().data() + destY * materialSize + destX;
|
||||||
|
|
||||||
|
const QByteArray& srcMaterial = material->getContents();
|
||||||
|
const QVector<SharedObjectPointer> srcMaterials = material->getMaterials();
|
||||||
|
int srcSize = glm::sqrt((float)srcMaterial.size());
|
||||||
|
float srcIncrement = info.size / srcSize;
|
||||||
|
QHash<int, int> materialMappings;
|
||||||
|
|
||||||
|
if (srcIncrement == materialIncrement) {
|
||||||
|
// easy case: same resolution
|
||||||
|
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||||
|
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||||
|
|
||||||
|
const uchar* src = (const uchar*)srcMaterial.constData() + srcY * srcSize + srcX;
|
||||||
|
for (int y = 0; y < destHeight; y++, src += srcSize, dest += materialSize) {
|
||||||
|
const uchar* lineSrc = src;
|
||||||
|
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrc++) {
|
||||||
|
int value = *lineSrc;
|
||||||
|
if (value != 0) {
|
||||||
|
int& mapping = materialMappings[value];
|
||||||
|
if (mapping == 0) {
|
||||||
|
mapping = getMaterialIndex(material->getMaterials().at(value - 1),
|
||||||
|
_buffer->getMaterials(), _buffer->getMaterial());
|
||||||
|
}
|
||||||
|
value = mapping;
|
||||||
|
}
|
||||||
|
*lineDest = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// more difficult: different resolutions
|
||||||
|
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||||
|
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||||
|
float srcAdvance = materialIncrement / srcIncrement;
|
||||||
|
for (int y = 0; y < destHeight; y++, dest += materialSize, srcY += srcAdvance) {
|
||||||
|
const uchar* src = (const uchar*)srcMaterial.constData() + (int)srcY * srcSize;
|
||||||
|
float lineSrcX = srcX;
|
||||||
|
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
||||||
|
int value = src[(int)lineSrcX];
|
||||||
|
if (value != 0) {
|
||||||
|
int& mapping = materialMappings[value];
|
||||||
|
if (mapping == 0) {
|
||||||
|
mapping = getMaterialIndex(material->getMaterials().at(value - 1),
|
||||||
|
_buffer->getMaterials(), _buffer->getMaterial());
|
||||||
|
}
|
||||||
|
value = mapping;
|
||||||
|
}
|
||||||
|
*lineDest = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearUnusedMaterials(_buffer->getMaterials(), _buffer->getMaterial());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return STOP_RECURSION;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HeightfieldRegionVisitor : public MetavoxelVisitor {
|
class HeightfieldRegionVisitor : public MetavoxelVisitor {
|
||||||
|
@ -1841,11 +1957,22 @@ public:
|
||||||
HeightfieldRegionVisitor(const MetavoxelLOD& lod);
|
HeightfieldRegionVisitor(const MetavoxelLOD& lod);
|
||||||
|
|
||||||
virtual int visit(MetavoxelInfo& info);
|
virtual int visit(MetavoxelInfo& info);
|
||||||
|
virtual bool postVisit(MetavoxelInfo& info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void addRegion(const Box& unextended, const Box& extended);
|
void addRegion(const Box& unextended, const Box& extended);
|
||||||
|
|
||||||
|
class DepthInfo {
|
||||||
|
public:
|
||||||
|
float minimumColorIncrement;
|
||||||
|
float minimumMaterialIncrement;
|
||||||
|
|
||||||
|
DepthInfo() : minimumColorIncrement(FLT_MAX), minimumMaterialIncrement(FLT_MAX) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
QVector<DepthInfo> _depthInfo;
|
||||||
|
|
||||||
QVector<Box> _intersections;
|
QVector<Box> _intersections;
|
||||||
HeightfieldFetchVisitor _fetchVisitor;
|
HeightfieldFetchVisitor _fetchVisitor;
|
||||||
};
|
};
|
||||||
|
@ -1861,70 +1988,99 @@ HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) :
|
||||||
}
|
}
|
||||||
|
|
||||||
int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
|
int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
|
||||||
|
DepthInfo depthInfo;
|
||||||
|
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||||
|
if (color) {
|
||||||
|
int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES);
|
||||||
|
depthInfo.minimumColorIncrement = info.size / colorSize;
|
||||||
|
}
|
||||||
|
HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||||
|
if (material) {
|
||||||
|
int materialSize = glm::sqrt((float)material->getContents().size());
|
||||||
|
depthInfo.minimumMaterialIncrement = info.size / materialSize;
|
||||||
|
}
|
||||||
|
if (_depth < _depthInfo.size()) {
|
||||||
|
_depthInfo[_depth] = depthInfo;
|
||||||
|
} else {
|
||||||
|
_depthInfo.append(depthInfo);
|
||||||
|
}
|
||||||
if (!info.isLeaf) {
|
if (!info.isLeaf) {
|
||||||
return DEFAULT_ORDER;
|
return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER;
|
||||||
}
|
}
|
||||||
HeightfieldBuffer* buffer = NULL;
|
postVisit(info);
|
||||||
HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
|
||||||
if (height) {
|
|
||||||
const QByteArray& heightContents = height->getContents();
|
|
||||||
int size = glm::sqrt(float(heightContents.size()));
|
|
||||||
int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION;
|
|
||||||
int heightContentsSize = extendedSize * extendedSize;
|
|
||||||
|
|
||||||
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
|
||||||
int colorContentsSize = 0;
|
|
||||||
if (color) {
|
|
||||||
const QByteArray& colorContents = color->getContents();
|
|
||||||
int colorSize = glm::sqrt(float(colorContents.size() / DataBlock::COLOR_BYTES));
|
|
||||||
int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE;
|
|
||||||
colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES;
|
|
||||||
}
|
|
||||||
|
|
||||||
HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
|
||||||
QByteArray materialContents;
|
|
||||||
QVector<SharedObjectPointer> materials;
|
|
||||||
if (material) {
|
|
||||||
materialContents = material->getContents();
|
|
||||||
materials = material->getMaterials();
|
|
||||||
}
|
|
||||||
|
|
||||||
const HeightfieldBuffer* existingBuffer = static_cast<const HeightfieldBuffer*>(
|
|
||||||
info.inputValues.at(3).getInlineValue<BufferDataPointer>().data());
|
|
||||||
Box bounds = info.getBounds();
|
|
||||||
if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize &&
|
|
||||||
existingBuffer->getColor().size() == colorContentsSize) {
|
|
||||||
// we already have a buffer of the correct resolution
|
|
||||||
addRegion(bounds, existingBuffer->getHeightBounds());
|
|
||||||
buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(),
|
|
||||||
existingBuffer->getColor(), materialContents, materials);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// we must create a new buffer and update its borders
|
|
||||||
buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
|
|
||||||
QByteArray(colorContentsSize, 0), materialContents, materials);
|
|
||||||
const Box& heightBounds = buffer->getHeightBounds();
|
|
||||||
addRegion(bounds, heightBounds);
|
|
||||||
|
|
||||||
_intersections.clear();
|
|
||||||
_intersections.append(Box(heightBounds.minimum,
|
|
||||||
glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
|
|
||||||
_intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
|
|
||||||
glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
|
|
||||||
_intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
|
|
||||||
heightBounds.maximum));
|
|
||||||
_intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
|
|
||||||
glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
|
|
||||||
|
|
||||||
_fetchVisitor.init(buffer);
|
|
||||||
_data->guide(_fetchVisitor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BufferDataPointer pointer(buffer);
|
|
||||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) {
|
||||||
|
const DepthInfo& depthInfo = _depthInfo.at(_depth);
|
||||||
|
if (_depth > 0) {
|
||||||
|
DepthInfo& parentDepthInfo = _depthInfo[_depth - 1];
|
||||||
|
parentDepthInfo.minimumColorIncrement = qMin(parentDepthInfo.minimumColorIncrement, depthInfo.minimumColorIncrement);
|
||||||
|
parentDepthInfo.minimumMaterialIncrement = qMin(parentDepthInfo.minimumMaterialIncrement,
|
||||||
|
depthInfo.minimumMaterialIncrement);
|
||||||
|
}
|
||||||
|
if (_visitations.at(_depth).isInputLeaf(0)) {
|
||||||
|
HeightfieldBuffer* buffer = NULL;
|
||||||
|
HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
||||||
|
if (height) {
|
||||||
|
int heightSize = glm::sqrt((float)height->getContents().size());
|
||||||
|
int extendedHeightSize = heightSize + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||||
|
int heightContentsSize = extendedHeightSize * extendedHeightSize;
|
||||||
|
float minimumColorIncrement = depthInfo.minimumColorIncrement;
|
||||||
|
float minimumMaterialIncrement = depthInfo.minimumMaterialIncrement;
|
||||||
|
for (int i = _depth - 1; i >= 0 && qMax(minimumColorIncrement, minimumMaterialIncrement) == FLT_MAX; i--) {
|
||||||
|
const DepthInfo& ancestorDepthInfo = _depthInfo.at(i);
|
||||||
|
minimumColorIncrement = qMin(minimumColorIncrement, ancestorDepthInfo.minimumColorIncrement);
|
||||||
|
minimumMaterialIncrement = qMin(minimumMaterialIncrement, ancestorDepthInfo.minimumMaterialIncrement);
|
||||||
|
}
|
||||||
|
int colorContentsSize = 0;
|
||||||
|
if (minimumColorIncrement != FLT_MAX) {
|
||||||
|
int colorSize = (int)glm::round(info.size / minimumColorIncrement) + HeightfieldBuffer::SHARED_EDGE;
|
||||||
|
colorContentsSize = colorSize * colorSize * DataBlock::COLOR_BYTES;
|
||||||
|
}
|
||||||
|
int materialContentsSize = 0;
|
||||||
|
if (minimumMaterialIncrement != FLT_MAX) {
|
||||||
|
int materialSize = (int)glm::round(info.size / minimumMaterialIncrement) + HeightfieldBuffer::SHARED_EDGE;
|
||||||
|
materialContentsSize = materialSize * materialSize;
|
||||||
|
}
|
||||||
|
const HeightfieldBuffer* existingBuffer = static_cast<const HeightfieldBuffer*>(
|
||||||
|
info.inputValues.at(3).getInlineValue<BufferDataPointer>().data());
|
||||||
|
Box bounds = info.getBounds();
|
||||||
|
if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize &&
|
||||||
|
existingBuffer->getColor().size() == colorContentsSize &&
|
||||||
|
existingBuffer->getMaterial().size() == materialContentsSize) {
|
||||||
|
// we already have a buffer of the correct resolution
|
||||||
|
addRegion(bounds, existingBuffer->getHeightBounds());
|
||||||
|
buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(),
|
||||||
|
existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// we must create a new buffer and update its borders
|
||||||
|
buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
|
||||||
|
QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0));
|
||||||
|
const Box& heightBounds = buffer->getHeightBounds();
|
||||||
|
addRegion(bounds, heightBounds);
|
||||||
|
|
||||||
|
_intersections.clear();
|
||||||
|
_intersections.append(Box(heightBounds.minimum,
|
||||||
|
glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
|
||||||
|
_intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
|
||||||
|
glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
|
||||||
|
_intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
|
||||||
|
heightBounds.maximum));
|
||||||
|
_intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
|
||||||
|
glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
|
||||||
|
|
||||||
|
_fetchVisitor.init(buffer);
|
||||||
|
_data->guide(_fetchVisitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BufferDataPointer pointer(buffer);
|
||||||
|
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) {
|
void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) {
|
||||||
regions.append(unextended);
|
regions.append(unextended);
|
||||||
regionBounds.add(extended);
|
regionBounds.add(extended);
|
||||||
|
|
|
@ -236,6 +236,7 @@ public:
|
||||||
|
|
||||||
const Box& getHeightBounds() const { return _heightBounds; }
|
const Box& getHeightBounds() const { return _heightBounds; }
|
||||||
const Box& getColorBounds() const { return _colorBounds; }
|
const Box& getColorBounds() const { return _colorBounds; }
|
||||||
|
const Box& getMaterialBounds() const { return _materialBounds; }
|
||||||
|
|
||||||
QByteArray& getHeight() { return _height; }
|
QByteArray& getHeight() { return _height; }
|
||||||
const QByteArray& getHeight() const { return _height; }
|
const QByteArray& getHeight() const { return _height; }
|
||||||
|
@ -246,10 +247,11 @@ public:
|
||||||
QByteArray& getMaterial() { return _material; }
|
QByteArray& getMaterial() { return _material; }
|
||||||
const QByteArray& getMaterial() const { return _material; }
|
const QByteArray& getMaterial() const { return _material; }
|
||||||
|
|
||||||
|
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
|
||||||
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
|
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
|
||||||
|
|
||||||
QByteArray getUnextendedHeight() const;
|
QByteArray getUnextendedHeight() const;
|
||||||
QByteArray getUnextendedColor() const;
|
QByteArray getUnextendedColor(int x = 0, int y = 0) const;
|
||||||
|
|
||||||
int getHeightSize() const { return _heightSize; }
|
int getHeightSize() const { return _heightSize; }
|
||||||
float getHeightIncrement() const { return _heightIncrement; }
|
float getHeightIncrement() const { return _heightIncrement; }
|
||||||
|
@ -257,6 +259,9 @@ public:
|
||||||
int getColorSize() const { return _colorSize; }
|
int getColorSize() const { return _colorSize; }
|
||||||
float getColorIncrement() const { return _colorIncrement; }
|
float getColorIncrement() const { return _colorIncrement; }
|
||||||
|
|
||||||
|
int getMaterialSize() const { return _materialSize; }
|
||||||
|
float getMaterialIncrement() const { return _materialIncrement; }
|
||||||
|
|
||||||
virtual void render(bool cursor = false);
|
virtual void render(bool cursor = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -265,6 +270,7 @@ private:
|
||||||
float _scale;
|
float _scale;
|
||||||
Box _heightBounds;
|
Box _heightBounds;
|
||||||
Box _colorBounds;
|
Box _colorBounds;
|
||||||
|
Box _materialBounds;
|
||||||
QByteArray _height;
|
QByteArray _height;
|
||||||
QByteArray _color;
|
QByteArray _color;
|
||||||
QByteArray _material;
|
QByteArray _material;
|
||||||
|
@ -277,6 +283,8 @@ private:
|
||||||
float _heightIncrement;
|
float _heightIncrement;
|
||||||
int _colorSize;
|
int _colorSize;
|
||||||
float _colorIncrement;
|
float _colorIncrement;
|
||||||
|
int _materialSize;
|
||||||
|
float _materialIncrement;
|
||||||
|
|
||||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||||
static QHash<int, BufferPair> _bufferPairs;
|
static QHash<int, BufferPair> _bufferPairs;
|
||||||
|
|
|
@ -1269,6 +1269,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
||||||
_isPushing = false;
|
_isPushing = false;
|
||||||
float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
|
float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
glm::vec3 newLocalVelocity = localVelocity;
|
||||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||||
(fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
|
(fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
|
||||||
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
||||||
|
@ -1285,31 +1286,47 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
||||||
if (directionLength > EPSILON) {
|
if (directionLength > EPSILON) {
|
||||||
direction /= directionLength;
|
direction /= directionLength;
|
||||||
|
|
||||||
// Compute the target keyboard velocity (which ramps up slowly, and damps very quickly)
|
if (hasFloor) {
|
||||||
// the max magnitude of which depends on what we're doing:
|
// we're walking --> simple exponential decay toward target walk speed
|
||||||
float motorSpeed = glm::length(_keyboardMotorVelocity);
|
const float WALK_ACCELERATION_TIMESCALE = 0.7f; // seconds to decrease delta to 1/e
|
||||||
float finalMaxMotorSpeed = hasFloor ? _scale * MAX_WALKING_SPEED : _scale * MAX_KEYBOARD_MOTOR_SPEED;
|
_keyboardMotorVelocity = MAX_WALKING_SPEED * direction;
|
||||||
float speedGrowthTimescale = 2.0f;
|
motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f);
|
||||||
float speedIncreaseFactor = 1.8f;
|
|
||||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
} else {
|
||||||
const float maxBoostSpeed = _scale * MAX_BOOST_SPEED;
|
// we're flying --> more complex curve
|
||||||
if (motorSpeed < maxBoostSpeed) {
|
float motorSpeed = glm::length(_keyboardMotorVelocity);
|
||||||
// an active keyboard motor should never be slower than this
|
float finalMaxMotorSpeed = _scale * MAX_KEYBOARD_MOTOR_SPEED;
|
||||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
float speedGrowthTimescale = 2.0f;
|
||||||
motorSpeed += MIN_AVATAR_SPEED * boostCoefficient;
|
float speedIncreaseFactor = 1.8f;
|
||||||
motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient;
|
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
||||||
} else if (motorSpeed > finalMaxMotorSpeed) {
|
const float maxBoostSpeed = _scale * MAX_BOOST_SPEED;
|
||||||
motorSpeed = finalMaxMotorSpeed;
|
if (motorSpeed < maxBoostSpeed) {
|
||||||
|
// an active keyboard motor should never be slower than this
|
||||||
|
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||||
|
motorSpeed += MIN_AVATAR_SPEED * boostCoefficient;
|
||||||
|
motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient;
|
||||||
|
} else if (motorSpeed > finalMaxMotorSpeed) {
|
||||||
|
motorSpeed = finalMaxMotorSpeed;
|
||||||
|
}
|
||||||
|
_keyboardMotorVelocity = motorSpeed * direction;
|
||||||
}
|
}
|
||||||
_keyboardMotorVelocity = motorSpeed * direction;
|
|
||||||
_isPushing = true;
|
_isPushing = true;
|
||||||
}
|
}
|
||||||
|
newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity);
|
||||||
} else {
|
} else {
|
||||||
_keyboardMotorVelocity = glm::vec3(0.0f);
|
_keyboardMotorVelocity = glm::vec3(0.0f);
|
||||||
|
newLocalVelocity = (1.0f - motorEfficiency) * localVelocity;
|
||||||
|
if (hasFloor && !_wasPushing) {
|
||||||
|
float speed = glm::length(newLocalVelocity);
|
||||||
|
if (speed > MIN_AVATAR_SPEED) {
|
||||||
|
// add small constant friction to help avatar drift to a stop sooner at low speeds
|
||||||
|
const float CONSTANT_FRICTION_DECELERATION = MIN_AVATAR_SPEED / 0.20f;
|
||||||
|
newLocalVelocity *= (speed - timescale * CONSTANT_FRICTION_DECELERATION) / speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply keyboard motor
|
return newLocalVelocity;
|
||||||
return localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVelocity) {
|
glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVelocity) {
|
||||||
|
|
|
@ -1038,26 +1038,42 @@ void ImportHeightfieldTool::apply() {
|
||||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
|
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
|
||||||
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
|
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
|
||||||
|
|
||||||
QByteArray color;
|
|
||||||
if (buffer->getColor().isEmpty()) {
|
|
||||||
const unsigned char WHITE_VALUE = 0xFF;
|
|
||||||
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
|
|
||||||
} else {
|
|
||||||
color = buffer->getUnextendedColor();
|
|
||||||
}
|
|
||||||
HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color));
|
|
||||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
|
||||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
|
||||||
|
|
||||||
int size = glm::sqrt(float(height.size()));
|
|
||||||
QByteArray material(size * size, 0);
|
|
||||||
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
|
|
||||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue(
|
|
||||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), encodeInline(materialPointer))));
|
|
||||||
|
|
||||||
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
||||||
_translation->getValue() + buffer->getTranslation() * scale, data)) };
|
_translation->getValue() + buffer->getTranslation() * scale, data)) };
|
||||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||||
|
|
||||||
|
int colorUnits = buffer->getColor().isEmpty() ? 1 : (buffer->getColorSize() - HeightfieldBuffer::SHARED_EDGE) /
|
||||||
|
(buffer->getHeightSize() - HeightfieldBuffer::HEIGHT_EXTENSION);
|
||||||
|
float colorScale = scale / colorUnits;
|
||||||
|
|
||||||
|
for (int y = 0; y < colorUnits; y++) {
|
||||||
|
for (int x = 0; x < colorUnits; x++) {
|
||||||
|
MetavoxelData data;
|
||||||
|
data.setSize(colorScale);
|
||||||
|
|
||||||
|
QByteArray color;
|
||||||
|
if (buffer->getColor().isEmpty()) {
|
||||||
|
const unsigned char WHITE_VALUE = 0xFF;
|
||||||
|
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
|
||||||
|
} else {
|
||||||
|
color = buffer->getUnextendedColor(x, y);
|
||||||
|
}
|
||||||
|
HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color));
|
||||||
|
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(
|
||||||
|
AttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
|
||||||
|
encodeInline(colorPointer))));
|
||||||
|
|
||||||
|
QByteArray material(height.size(), 0);
|
||||||
|
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
|
||||||
|
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(
|
||||||
|
AttributeValue(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(),
|
||||||
|
encodeInline(materialPointer))));
|
||||||
|
|
||||||
|
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
||||||
|
_translation->getValue() + buffer->getTranslation() * scale + glm::vec3(x, 0.0f, y) * colorScale, data)) };
|
||||||
|
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,7 +1159,9 @@ void ImportHeightfieldTool::updatePreview() {
|
||||||
float z = 0.0f;
|
float z = 0.0f;
|
||||||
int blockSize = pow(2.0, _blockSize->value());
|
int blockSize = pow(2.0, _blockSize->value());
|
||||||
int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION;
|
int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||||
int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE;
|
int colorScale = glm::round(glm::log(_colorImage.height() / (float)_heightImage.height()) / glm::log(2.0f));
|
||||||
|
int colorBlockSize = blockSize * pow(2.0, qMax(colorScale, 0));
|
||||||
|
int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE;
|
||||||
for (int i = 0; i < _heightImage.height(); i += blockSize, z++) {
|
for (int i = 0; i < _heightImage.height(); i += blockSize, z++) {
|
||||||
float x = 0.0f;
|
float x = 0.0f;
|
||||||
for (int j = 0; j < _heightImage.width(); j += blockSize, x++) {
|
for (int j = 0; j < _heightImage.width(); j += blockSize, x++) {
|
||||||
|
@ -1164,12 +1182,14 @@ void ImportHeightfieldTool::updatePreview() {
|
||||||
}
|
}
|
||||||
QByteArray color;
|
QByteArray color;
|
||||||
if (!_colorImage.isNull()) {
|
if (!_colorImage.isNull()) {
|
||||||
|
int colorI = (i / blockSize) * colorBlockSize;
|
||||||
|
int colorJ = (j / blockSize) * colorBlockSize;
|
||||||
color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0);
|
color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0);
|
||||||
rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
|
rows = qMax(0, qMin(colorSize, _colorImage.height() - colorI));
|
||||||
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
|
columns = qMax(0, qMin(colorSize, _colorImage.width() - colorJ));
|
||||||
for (int y = 0; y < rows; y++) {
|
for (int y = 0; y < rows; y++) {
|
||||||
memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES,
|
memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES,
|
||||||
_colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES,
|
_colorImage.scanLine(colorI + y) + colorJ * DataBlock::COLOR_BYTES,
|
||||||
columns * DataBlock::COLOR_BYTES);
|
columns * DataBlock::COLOR_BYTES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1198,6 +1198,10 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttributeValue HeightfieldAttribute::inherit(const AttributeValue& parentValue) const {
|
||||||
|
return AttributeValue(parentValue.getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) :
|
HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) :
|
||||||
InlineAttribute<HeightfieldColorDataPointer>(name) {
|
InlineAttribute<HeightfieldColorDataPointer>(name) {
|
||||||
}
|
}
|
||||||
|
@ -1337,6 +1341,10 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttributeValue HeightfieldColorAttribute::inherit(const AttributeValue& parentValue) const {
|
||||||
|
return AttributeValue(parentValue.getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) :
|
HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) :
|
||||||
InlineAttribute<HeightfieldMaterialDataPointer>(name) {
|
InlineAttribute<HeightfieldMaterialDataPointer>(name) {
|
||||||
}
|
}
|
||||||
|
@ -1404,6 +1412,71 @@ bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool p
|
||||||
return maxSize == 0;
|
return maxSize == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& parentValue) const {
|
||||||
|
return AttributeValue(parentValue.getAttribute());
|
||||||
|
}
|
||||||
|
|
||||||
|
static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
||||||
|
QHash<uchar, int> counts;
|
||||||
|
for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) {
|
||||||
|
if (*src != 0) {
|
||||||
|
counts[*src]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
||||||
|
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// first look for a matching existing material, noting the first reusable slot
|
||||||
|
int firstEmptyIndex = -1;
|
||||||
|
for (int i = 0; i < materials.size(); i++) {
|
||||||
|
const SharedObjectPointer& existingMaterial = materials.at(i);
|
||||||
|
if (existingMaterial) {
|
||||||
|
if (existingMaterial->equals(material.data())) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
} else if (firstEmptyIndex == -1) {
|
||||||
|
firstEmptyIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if nothing found, use the first empty slot or append
|
||||||
|
if (firstEmptyIndex != -1) {
|
||||||
|
materials[firstEmptyIndex] = material;
|
||||||
|
return firstEmptyIndex + 1;
|
||||||
|
}
|
||||||
|
if (materials.size() < EIGHT_BIT_MAXIMUM) {
|
||||||
|
materials.append(material);
|
||||||
|
return materials.size();
|
||||||
|
}
|
||||||
|
// last resort: find the least-used material and remove it
|
||||||
|
QHash<uchar, int> counts = countIndices(contents);
|
||||||
|
uchar materialIndex = 0;
|
||||||
|
int lowestCount = INT_MAX;
|
||||||
|
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
|
||||||
|
if (it.value() < lowestCount) {
|
||||||
|
materialIndex = it.key();
|
||||||
|
lowestCount = it.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contents.replace((char)materialIndex, (char)0);
|
||||||
|
return materialIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents) {
|
||||||
|
QHash<uchar, int> counts = countIndices(contents);
|
||||||
|
for (int i = 0; i < materials.size(); i++) {
|
||||||
|
if (counts.value(i + 1) == 0) {
|
||||||
|
materials[i] = SharedObjectPointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!(materials.isEmpty() || materials.last())) {
|
||||||
|
materials.removeLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6;
|
const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6;
|
||||||
|
|
||||||
static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ,
|
static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ,
|
||||||
|
|
|
@ -536,6 +536,8 @@ public:
|
||||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||||
|
|
||||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||||
|
|
||||||
|
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
|
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
|
||||||
|
@ -580,6 +582,8 @@ public:
|
||||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||||
|
|
||||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||||
|
|
||||||
|
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QExplicitlySharedDataPointer<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
|
typedef QExplicitlySharedDataPointer<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
|
||||||
|
@ -646,8 +650,17 @@ public:
|
||||||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||||
|
|
||||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||||
|
|
||||||
|
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index,
|
||||||
|
/// creating a new entry in the list if necessary.
|
||||||
|
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents);
|
||||||
|
|
||||||
|
/// Utility method for editing: removes any unused materials from the supplied list.
|
||||||
|
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents);
|
||||||
|
|
||||||
typedef QExplicitlySharedDataPointer<VoxelColorData> VoxelColorDataPointer;
|
typedef QExplicitlySharedDataPointer<VoxelColorData> VoxelColorDataPointer;
|
||||||
|
|
||||||
/// Contains a block of voxel color data.
|
/// Contains a block of voxel color data.
|
||||||
|
|
|
@ -1509,7 +1509,7 @@ bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
||||||
DefaultMetavoxelGuide::DefaultMetavoxelGuide() {
|
DefaultMetavoxelGuide::DefaultMetavoxelGuide() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float lodBase, int encodedOrder) {
|
static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int encodedOrder) {
|
||||||
MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
|
MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
|
||||||
nextVisitation.info.size = visitation.info.size * 0.5f;
|
nextVisitation.info.size = visitation.info.size * 0.5f;
|
||||||
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
|
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
|
||||||
|
@ -1519,14 +1519,14 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float
|
||||||
for (int j = 0; j < visitation.inputNodes.size(); j++) {
|
for (int j = 0; j < visitation.inputNodes.size(); j++) {
|
||||||
MetavoxelNode* node = visitation.inputNodes.at(j);
|
MetavoxelNode* node = visitation.inputNodes.at(j);
|
||||||
const AttributeValue& parentValue = visitation.info.inputValues.at(j);
|
const AttributeValue& parentValue = visitation.info.inputValues.at(j);
|
||||||
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
|
MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase *
|
||||||
parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
||||||
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
|
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
|
||||||
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
|
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
|
||||||
}
|
}
|
||||||
for (int j = 0; j < visitation.outputNodes.size(); j++) {
|
for (int j = 0; j < visitation.outputNodes.size(); j++) {
|
||||||
MetavoxelNode* node = visitation.outputNodes.at(j);
|
MetavoxelNode* node = visitation.outputNodes.at(j);
|
||||||
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
|
MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase *
|
||||||
visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
||||||
nextVisitation.outputNodes[j] = child;
|
nextVisitation.outputNodes[j] = child;
|
||||||
}
|
}
|
||||||
|
@ -1604,9 +1604,10 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float
|
||||||
|
|
||||||
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||||
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
||||||
float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||||
visitation.visitor->getLOD().threshold;
|
visitation.visitor->getLOD().threshold;
|
||||||
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier());
|
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
|
||||||
|
visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||||
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
||||||
int encodedOrder = visitation.visitor->visit(visitation.info);
|
int encodedOrder = visitation.visitor->visit(visitation.info);
|
||||||
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
|
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
|
||||||
|
@ -1628,14 +1629,15 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||||
if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) {
|
if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, lodBase, encodedOrder));
|
return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, encodedOrder));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
||||||
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
||||||
float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||||
visitation.visitor->getLOD().threshold;
|
visitation.visitor->getLOD().threshold;
|
||||||
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier());
|
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
|
||||||
|
visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||||
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
||||||
int encodedOrder = visitation.visitor->visit(visitation.info);
|
int encodedOrder = visitation.visitor->visit(visitation.info);
|
||||||
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
|
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
|
||||||
|
@ -1658,7 +1660,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (encodedOrder & MetavoxelVisitor::ALL_NODES_REST) {
|
if (encodedOrder & MetavoxelVisitor::ALL_NODES_REST) {
|
||||||
return defaultGuideToChildren(visitation, lodBase, encodedOrder);
|
return defaultGuideToChildren(visitation, encodedOrder);
|
||||||
}
|
}
|
||||||
bool onlyVisitDifferent = !(encodedOrder & MetavoxelVisitor::ALL_NODES);
|
bool onlyVisitDifferent = !(encodedOrder & MetavoxelVisitor::ALL_NODES);
|
||||||
MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
|
MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
|
||||||
|
@ -1672,7 +1674,8 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
||||||
for (int j = 0; j < visitation.inputNodes.size(); j++) {
|
for (int j = 0; j < visitation.inputNodes.size(); j++) {
|
||||||
MetavoxelNode* node = visitation.inputNodes.at(j);
|
MetavoxelNode* node = visitation.inputNodes.at(j);
|
||||||
const AttributeValue& parentValue = visitation.info.inputValues.at(j);
|
const AttributeValue& parentValue = visitation.info.inputValues.at(j);
|
||||||
bool expand = (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier());
|
bool expand = (visitation.info.size >= visitation.info.lodBase *
|
||||||
|
parentValue.getAttribute()->getLODThresholdMultiplier());
|
||||||
MetavoxelNode* child = (node && expand) ? node->getChild(index) : NULL;
|
MetavoxelNode* child = (node && expand) ? node->getChild(index) : NULL;
|
||||||
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
|
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
|
||||||
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
|
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
|
||||||
|
@ -1686,7 +1689,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
||||||
}
|
}
|
||||||
for (int j = 0; j < visitation.outputNodes.size(); j++) {
|
for (int j = 0; j < visitation.outputNodes.size(); j++) {
|
||||||
MetavoxelNode* node = visitation.outputNodes.at(j);
|
MetavoxelNode* node = visitation.outputNodes.at(j);
|
||||||
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
|
MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase *
|
||||||
visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
||||||
nextVisitation.outputNodes[j] = child;
|
nextVisitation.outputNodes[j] = child;
|
||||||
}
|
}
|
||||||
|
@ -1911,6 +1914,12 @@ MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous,
|
||||||
MetavoxelVisitation::MetavoxelVisitation() {
|
MetavoxelVisitation::MetavoxelVisitation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MetavoxelVisitation::isInputLeaf(int index) const {
|
||||||
|
MetavoxelNode* node = inputNodes.at(index);
|
||||||
|
return !node || node->isLeaf() || info.size < info.lodBase *
|
||||||
|
info.inputValues.at(index).getAttribute()->getLODThresholdMultiplier();
|
||||||
|
}
|
||||||
|
|
||||||
bool MetavoxelVisitation::allInputNodesLeaves() const {
|
bool MetavoxelVisitation::allInputNodesLeaves() const {
|
||||||
foreach (MetavoxelNode* node, inputNodes) {
|
foreach (MetavoxelNode* node, inputNodes) {
|
||||||
if (node && !node->isLeaf()) {
|
if (node && !node->isLeaf()) {
|
||||||
|
|
|
@ -278,6 +278,7 @@ public:
|
||||||
float size; ///< the size of the voxel in all dimensions
|
float size; ///< the size of the voxel in all dimensions
|
||||||
QVector<AttributeValue> inputValues;
|
QVector<AttributeValue> inputValues;
|
||||||
QVector<OwnedAttributeValue> outputValues;
|
QVector<OwnedAttributeValue> outputValues;
|
||||||
|
float lodBase;
|
||||||
bool isLODLeaf;
|
bool isLODLeaf;
|
||||||
bool isLeaf;
|
bool isLeaf;
|
||||||
|
|
||||||
|
@ -537,6 +538,7 @@ public:
|
||||||
MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize);
|
MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize);
|
||||||
MetavoxelVisitation();
|
MetavoxelVisitation();
|
||||||
|
|
||||||
|
bool isInputLeaf(int index) const;
|
||||||
bool allInputNodesLeaves() const;
|
bool allInputNodesLeaves() const;
|
||||||
AttributeValue getInheritedOutputValue(int index) const;
|
AttributeValue getInheritedOutputValue(int index) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -445,71 +445,11 @@ PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const g
|
||||||
_material(material),
|
_material(material),
|
||||||
_color(color) {
|
_color(color) {
|
||||||
|
|
||||||
glm::vec3 extents(_radius, _radius, _radius);
|
const float LARGE_EXTENT = 100000.0f;
|
||||||
|
glm::vec3 extents(_radius, LARGE_EXTENT, _radius);
|
||||||
_bounds = Box(_position - extents, _position + extents);
|
_bounds = Box(_position - extents, _position + extents);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
|
||||||
QHash<uchar, int> counts;
|
|
||||||
for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) {
|
|
||||||
if (*src != 0) {
|
|
||||||
counts[*src]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return counts;
|
|
||||||
}
|
|
||||||
|
|
||||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
|
||||||
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// first look for a matching existing material, noting the first reusable slot
|
|
||||||
int firstEmptyIndex = -1;
|
|
||||||
for (int i = 0; i < materials.size(); i++) {
|
|
||||||
const SharedObjectPointer& existingMaterial = materials.at(i);
|
|
||||||
if (existingMaterial) {
|
|
||||||
if (existingMaterial->equals(material.data())) {
|
|
||||||
return i + 1;
|
|
||||||
}
|
|
||||||
} else if (firstEmptyIndex == -1) {
|
|
||||||
firstEmptyIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if nothing found, use the first empty slot or append
|
|
||||||
if (firstEmptyIndex != -1) {
|
|
||||||
materials[firstEmptyIndex] = material;
|
|
||||||
return firstEmptyIndex + 1;
|
|
||||||
}
|
|
||||||
if (materials.size() < EIGHT_BIT_MAXIMUM) {
|
|
||||||
materials.append(material);
|
|
||||||
return materials.size();
|
|
||||||
}
|
|
||||||
// last resort: find the least-used material and remove it
|
|
||||||
QHash<uchar, int> counts = countIndices(contents);
|
|
||||||
uchar materialIndex = 0;
|
|
||||||
int lowestCount = INT_MAX;
|
|
||||||
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
|
|
||||||
if (it.value() < lowestCount) {
|
|
||||||
materialIndex = it.key();
|
|
||||||
lowestCount = it.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
contents.replace((char)materialIndex, (char)0);
|
|
||||||
return materialIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
|
||||||
QHash<uchar, int> counts = countIndices(contents);
|
|
||||||
for (int i = 0; i < materials.size(); i++) {
|
|
||||||
if (counts.value(i + 1) == 0) {
|
|
||||||
materials[i] = SharedObjectPointer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (!(materials.isEmpty() || materials.last())) {
|
|
||||||
materials.removeLast();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
||||||
if (!info.getBounds().intersects(_bounds)) {
|
if (!info.getBounds().intersects(_bounds)) {
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
|
@ -581,7 +521,6 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
||||||
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
|
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
|
||||||
float squaredRadius = scaledRadius * scaledRadius;
|
float squaredRadius = scaledRadius * scaledRadius;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
QHash<uchar, int> counts;
|
|
||||||
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
||||||
uchar* dest = lineDest;
|
uchar* dest = lineDest;
|
||||||
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
||||||
|
|
Loading…
Reference in a new issue