mirror of
https://github.com/overte-org/overte.git
synced 2025-08-19 04:16:47 +02:00
Merge branch 'master' into 20399
This commit is contained in:
commit
c114442325
39 changed files with 959 additions and 259 deletions
|
@ -158,6 +158,7 @@ option(GET_GLM "Get GLM library automatically as external project" 1)
|
|||
option(GET_GVERB "Get Gverb library automatically as external project" 1)
|
||||
option(GET_SOXR "Get Soxr library automatically as external project" 1)
|
||||
option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
|
||||
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
||||
|
||||
if (WIN32)
|
||||
option(GET_GLEW "Get GLEW library automatically as external project" 1)
|
||||
|
|
|
@ -1036,6 +1036,13 @@ void OctreeServer::readConfiguration() {
|
|||
strcpy(_persistFilename, qPrintable(persistFilename));
|
||||
qDebug("persistFilename=%s", _persistFilename);
|
||||
|
||||
QString persistAsFileType;
|
||||
if (!readOptionString(QString("persistAsFileType"), settingsSectionObject, persistAsFileType)) {
|
||||
persistAsFileType = "svo";
|
||||
}
|
||||
_persistAsFileType = persistAsFileType;
|
||||
qDebug() << "persistAsFileType=" << _persistAsFileType;
|
||||
|
||||
_persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL;
|
||||
readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval);
|
||||
qDebug() << "persistInterval=" << _persistInterval;
|
||||
|
@ -1131,7 +1138,7 @@ void OctreeServer::run() {
|
|||
|
||||
// now set up PersistThread
|
||||
_persistThread = new OctreePersistThread(_tree, _persistFilename, _persistInterval,
|
||||
_wantBackup, _settings, _debugTimestampNow);
|
||||
_wantBackup, _settings, _debugTimestampNow, _persistAsFileType);
|
||||
if (_persistThread) {
|
||||
_persistThread->initialize(true);
|
||||
}
|
||||
|
|
|
@ -157,6 +157,7 @@ protected:
|
|||
QString _statusHost;
|
||||
|
||||
char _persistFilename[MAX_FILENAME_LENGTH];
|
||||
QString _persistAsFileType;
|
||||
int _packetsPerClientPerInterval;
|
||||
int _packetsTotalPerInterval;
|
||||
Octree* _tree; // this IS a reaveraging tree
|
||||
|
|
|
@ -325,6 +325,24 @@
|
|||
"default": "resources/models.svo",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "persistAsFileType",
|
||||
"label": "File format for entity server's persistent data",
|
||||
"help": "This defines how the entity server will save entities to disk.",
|
||||
"default": "svo",
|
||||
"type": "select",
|
||||
"options": [
|
||||
{
|
||||
"value": "svo",
|
||||
"label": "Entity server persists data as SVO"
|
||||
},
|
||||
{
|
||||
"value": "json",
|
||||
"label": "Entity server persists data as JSON"
|
||||
}
|
||||
],
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "persistInterval",
|
||||
"label": "Save Check Interval",
|
||||
|
|
|
@ -766,6 +766,10 @@ function setupModelMenus() {
|
|||
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
|
||||
afterItem: "Allow Selecting of Small Models", isCheckable: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities In Box", shortcutKey: "CTRL+SHIFT+META+A",
|
||||
afterItem: "Allow Selecting of Lights" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities Touching Box", shortcutKey: "CTRL+SHIFT+META+T",
|
||||
afterItem: "Select All Entities In Box" });
|
||||
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||
|
@ -795,6 +799,8 @@ function cleanupModelMenus() {
|
|||
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
|
||||
Menu.removeMenuItem("Edit", "Select All Entities In Box");
|
||||
Menu.removeMenuItem("Edit", "Select All Entities Touching Box");
|
||||
|
||||
Menu.removeSeparator("File", "Models");
|
||||
Menu.removeMenuItem("File", "Export Entities");
|
||||
|
@ -830,6 +836,45 @@ Script.update.connect(function (deltaTime) {
|
|||
selectionDisplay.checkMove();
|
||||
});
|
||||
|
||||
function insideBox(center, dimensions, point) {
|
||||
return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0))
|
||||
&& (Math.abs(point.y - center.y) <= (dimensions.y / 2.0))
|
||||
&& (Math.abs(point.z - center.z) <= (dimensions.z / 2.0));
|
||||
}
|
||||
|
||||
function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) {
|
||||
if (selectionManager.hasSelection()) {
|
||||
// Get all entities touching the bounding box of the current selection
|
||||
var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition,
|
||||
Vec3.multiply(selectionManager.worldDimensions, 0.5));
|
||||
var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions);
|
||||
|
||||
if (!keepIfTouching) {
|
||||
var isValid;
|
||||
if (selectionManager.localPosition === null) {
|
||||
isValid = function(position) {
|
||||
return insideBox(selectionManager.worldPosition, selectionManager.worldDimensions, position);
|
||||
}
|
||||
} else {
|
||||
isValid = function(position) {
|
||||
var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation),
|
||||
Vec3.subtract(position,
|
||||
selectionManager.localPosition));
|
||||
return insideBox({ x: 0, y: 0, z: 0 }, selectionManager.localDimensions, localPosition);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < entities.length; ++i) {
|
||||
var properties = Entities.getEntityProperties(entities[i]);
|
||||
if (!isValid(properties.position)) {
|
||||
entities.splice(i, 1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
selectionManager.setSelections(entities);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteSelectedEntities() {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
print(" Delete Entities");
|
||||
|
@ -888,6 +933,10 @@ function handeMenuEvent(menuItem) {
|
|||
}
|
||||
} else if (menuItem == "Entity List...") {
|
||||
entityListTool.toggleVisible();
|
||||
} else if (menuItem == "Select All Entities In Box") {
|
||||
selectAllEtitiesInCurrentSelectionBox(false);
|
||||
} else if (menuItem == "Select All Entities Touching Box") {
|
||||
selectAllEtitiesInCurrentSelectionBox(true);
|
||||
} else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) {
|
||||
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
|
||||
}
|
||||
|
|
|
@ -220,15 +220,17 @@ else (APPLE)
|
|||
find_package(GLEW REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} ${NSIGHT_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib)
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib)
|
||||
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
find_package(NSIGHT)
|
||||
if (NSIGHT_FOUND)
|
||||
include_directories(${NSIGHT_INCLUDE_DIRS})
|
||||
add_definitions(-DNSIGHT_FOUND)
|
||||
target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
|
||||
endif ()
|
||||
if (USE_NSIGHT)
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
find_package(NSIGHT)
|
||||
if (NSIGHT_FOUND)
|
||||
include_directories(${NSIGHT_INCLUDE_DIRS})
|
||||
add_definitions(-DNSIGHT_FOUND)
|
||||
target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endif (APPLE)
|
||||
|
|
|
@ -1798,7 +1798,7 @@ bool Application::importEntities(const QString& urlOrFilename) {
|
|||
url = QUrl::fromLocalFile(urlOrFilename);
|
||||
}
|
||||
|
||||
bool success = _entityClipboard.readFromSVOURL(url.toString());
|
||||
bool success = _entityClipboard.readFromURL(url.toString());
|
||||
if (success) {
|
||||
_entityClipboard.reaverageOctreeElements();
|
||||
}
|
||||
|
@ -2137,10 +2137,12 @@ void Application::updateCursor(float deltaTime) {
|
|||
}
|
||||
|
||||
void Application::updateCursorVisibility() {
|
||||
if (!_cursorVisible || Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) {
|
||||
_glWidget->setCursor(Qt::BlankCursor);
|
||||
if (!_cursorVisible ||
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode) ||
|
||||
Menu::getInstance()->isOptionChecked(MenuOption::Enable3DTVMode)) {
|
||||
_window->setCursor(Qt::BlankCursor);
|
||||
} else {
|
||||
_glWidget->unsetCursor();
|
||||
_window->unsetCursor();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2793,7 +2795,27 @@ QImage Application::renderAvatarBillboard() {
|
|||
return image;
|
||||
}
|
||||
|
||||
// FIXME, preprocessor guard this check to occur only in DEBUG builds
|
||||
static QThread * activeRenderingThread = nullptr;
|
||||
|
||||
ViewFrustum* Application::getViewFrustum() {
|
||||
if (QThread::currentThread() == activeRenderingThread) {
|
||||
// FIXME, should this be an assert?
|
||||
qWarning() << "Calling Application::getViewFrustum() from the active rendering thread, did you mean Application::getDisplayViewFrustum()?";
|
||||
}
|
||||
return &_viewFrustum;
|
||||
}
|
||||
|
||||
ViewFrustum* Application::getDisplayViewFrustum() {
|
||||
if (QThread::currentThread() != activeRenderingThread) {
|
||||
// FIXME, should this be an assert?
|
||||
qWarning() << "Calling Application::getDisplayViewFrustum() from outside the active rendering thread or outside rendering, did you mean Application::getViewFrustum()?";
|
||||
}
|
||||
return &_displayViewFrustum;
|
||||
}
|
||||
|
||||
void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs::RenderSide renderSide) {
|
||||
activeRenderingThread = QThread::currentThread();
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
PerformanceTimer perfTimer("display");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
|
||||
|
@ -3018,6 +3040,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
_overlays.renderWorld(true);
|
||||
}
|
||||
activeRenderingThread = nullptr;
|
||||
}
|
||||
|
||||
void Application::updateUntranslatedViewMatrix(const glm::vec3& viewMatrixTranslation) {
|
||||
|
@ -3110,17 +3133,25 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
|
||||
|
||||
} else { // HEAD zoom level
|
||||
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
|
||||
if (_myAvatar->getSkeletonModel().isActive() && _myAvatar->getHead()->getFaceModel().isActive()) {
|
||||
// as a hack until we have a better way of dealing with coordinate precision issues, reposition the
|
||||
// face/body so that the average eye position lies at the origin
|
||||
eyeRelativeCamera = true;
|
||||
_mirrorCamera.setPosition(_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
|
||||
// FIXME note that the positioing of the camera relative to the avatar can suffer limited
|
||||
// precision as the user's position moves further away from the origin. Thus at
|
||||
// /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways
|
||||
// wildly as you rotate your avatar because the floating point values are becoming
|
||||
// larger, squeezing out the available digits of precision you have available at the
|
||||
// human scale for camera positioning.
|
||||
|
||||
} else {
|
||||
_mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
|
||||
}
|
||||
// Previously there was a hack to correct this using the mechanism of repositioning
|
||||
// the avatar at the origin of the world for the purposes of rendering the mirror,
|
||||
// but it resulted in failing to render the avatar's head model in the mirror view
|
||||
// when in first person mode. Presumably this was because of some missed culling logic
|
||||
// that was not accounted for in the hack.
|
||||
|
||||
// This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further
|
||||
// investigated in order to adapt the technique while fixing the head rendering issue,
|
||||
// but the complexity of the hack suggests that a better approach
|
||||
_mirrorCamera.setFieldOfView(MIRROR_FIELD_OF_VIEW); // degrees
|
||||
_mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() +
|
||||
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
|
||||
}
|
||||
_mirrorCamera.setAspectRatio((float)region.width() / region.height());
|
||||
|
||||
|
@ -3147,58 +3178,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
|
||||
// render rear mirror view
|
||||
glPushMatrix();
|
||||
if (eyeRelativeCamera) {
|
||||
// save absolute translations
|
||||
glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation();
|
||||
glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation();
|
||||
|
||||
// get the neck position so we can translate the face relative to it
|
||||
glm::vec3 neckPosition;
|
||||
_myAvatar->getSkeletonModel().setTranslation(glm::vec3());
|
||||
_myAvatar->getSkeletonModel().getNeckPosition(neckPosition);
|
||||
|
||||
// get the eye position relative to the body
|
||||
glm::vec3 eyePosition = _myAvatar->getHead()->getEyePosition();
|
||||
float eyeHeight = eyePosition.y - _myAvatar->getPosition().y;
|
||||
|
||||
// set the translation of the face relative to the neck position
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation(neckPosition - glm::vec3(0, eyeHeight, 0));
|
||||
|
||||
// translate the neck relative to the face
|
||||
_myAvatar->getSkeletonModel().setTranslation(_myAvatar->getHead()->getFaceModel().getTranslation() -
|
||||
neckPosition);
|
||||
|
||||
// update the attachments to match
|
||||
QVector<glm::vec3> absoluteAttachmentTranslations;
|
||||
glm::vec3 delta = _myAvatar->getSkeletonModel().getTranslation() - absoluteSkeletonTranslation;
|
||||
foreach (Model* attachment, _myAvatar->getAttachmentModels()) {
|
||||
absoluteAttachmentTranslations.append(attachment->getTranslation());
|
||||
attachment->setTranslation(attachment->getTranslation() + delta);
|
||||
}
|
||||
|
||||
// and lo, even the shadow matrices
|
||||
glm::mat4 savedShadowMatrices[CASCADED_SHADOW_MATRIX_COUNT];
|
||||
for (int i = 0; i < CASCADED_SHADOW_MATRIX_COUNT; i++) {
|
||||
savedShadowMatrices[i] = _shadowMatrices[i];
|
||||
_shadowMatrices[i] = glm::transpose(glm::transpose(_shadowMatrices[i]) * glm::translate(-delta));
|
||||
}
|
||||
|
||||
displaySide(_mirrorCamera, true);
|
||||
|
||||
// restore absolute translations
|
||||
_myAvatar->getSkeletonModel().setTranslation(absoluteSkeletonTranslation);
|
||||
_myAvatar->getHead()->getFaceModel().setTranslation(absoluteFaceTranslation);
|
||||
for (int i = 0; i < absoluteAttachmentTranslations.size(); i++) {
|
||||
_myAvatar->getAttachmentModels().at(i)->setTranslation(absoluteAttachmentTranslations.at(i));
|
||||
}
|
||||
|
||||
// restore the shadow matrices
|
||||
for (int i = 0; i < CASCADED_SHADOW_MATRIX_COUNT; i++) {
|
||||
_shadowMatrices[i] = savedShadowMatrices[i];
|
||||
}
|
||||
} else {
|
||||
displaySide(_mirrorCamera, true);
|
||||
}
|
||||
displaySide(_mirrorCamera, true);
|
||||
glPopMatrix();
|
||||
|
||||
if (!billboard) {
|
||||
|
|
|
@ -174,8 +174,12 @@ public:
|
|||
bool isThrottleRendering() const { return _glWidget->isThrottleRendering(); }
|
||||
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
ViewFrustum* getViewFrustum() { return &_viewFrustum; }
|
||||
ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; }
|
||||
// Represents the current view frustum of the avatar.
|
||||
ViewFrustum* getViewFrustum();
|
||||
// Represents the view frustum of the current rendering pass,
|
||||
// which might be different from the viewFrustum, i.e. shadowmap
|
||||
// passes, mirror window passes, etc
|
||||
ViewFrustum* getDisplayViewFrustum();
|
||||
ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; }
|
||||
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||
EntityTreeRenderer* getEntities() { return &_entities; }
|
||||
|
|
|
@ -338,7 +338,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool
|
|||
// simple frustum check
|
||||
float boundingRadius = getBillboardSize();
|
||||
ViewFrustum* frustum = (renderMode == Avatar::SHADOW_RENDER_MODE) ?
|
||||
Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getViewFrustum();
|
||||
Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getDisplayViewFrustum();
|
||||
if (frustum->sphereInFrustum(getPosition(), boundingRadius) == ViewFrustum::OUTSIDE) {
|
||||
return;
|
||||
}
|
||||
|
@ -1013,16 +1013,18 @@ float Avatar::getSkeletonHeight() const {
|
|||
}
|
||||
|
||||
float Avatar::getHeadHeight() const {
|
||||
Extents extents = getHead()->getFaceModel().getBindExtents();
|
||||
Extents extents = getHead()->getFaceModel().getMeshExtents();
|
||||
if (!extents.isEmpty()) {
|
||||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
extents = _skeletonModel.getMeshExtents();
|
||||
glm::vec3 neckPosition;
|
||||
glm::vec3 headPosition;
|
||||
if (_skeletonModel.getNeckPosition(neckPosition) && _skeletonModel.getHeadPosition(headPosition)) {
|
||||
return glm::distance(neckPosition, headPosition);
|
||||
if (!extents.isEmpty() && _skeletonModel.getNeckPosition(neckPosition)) {
|
||||
return extents.maximum.y / 2.0f - neckPosition.y + _position.y;
|
||||
}
|
||||
const float DEFAULT_HEAD_HEIGHT = 0.1f;
|
||||
|
||||
const float DEFAULT_HEAD_HEIGHT = 0.25f;
|
||||
return DEFAULT_HEAD_HEIGHT;
|
||||
}
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ void Overlays::renderWorld(bool drawFront, RenderArgs::RenderMode renderMode, Re
|
|||
float myAvatarScale = 1.0f;
|
||||
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(),
|
||||
RenderArgs args = { NULL, Application::getInstance()->getDisplayViewFrustum(),
|
||||
lodManager->getOctreeSizeScale(),
|
||||
lodManager->getBoundaryLevelAdjust(),
|
||||
renderMode, renderSide,
|
||||
|
|
|
@ -812,7 +812,7 @@ void AudioClient::handleAudioInput() {
|
|||
}
|
||||
|
||||
emit inputReceived(QByteArray(reinterpret_cast<const char*>(networkAudioSamples),
|
||||
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL));
|
||||
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * sizeof(AudioConstants::AudioSample)));
|
||||
|
||||
} else {
|
||||
// our input loudness is 0, since we're muted
|
||||
|
|
|
@ -169,6 +169,8 @@ void Player::setupAudioThread() {
|
|||
_audioThread->setObjectName("Player Audio Thread");
|
||||
_options.position = _avatar->getPosition();
|
||||
_options.orientation = _avatar->getOrientation();
|
||||
_options.stereo = _recording->numberAudioChannel() == 2;
|
||||
|
||||
_injector.reset(new AudioInjector(_recording->getAudioData(), _options), &QObject::deleteLater);
|
||||
_injector->moveToThread(_audioThread);
|
||||
_audioThread->start();
|
||||
|
@ -309,7 +311,7 @@ void Player::setCurrentFrame(int currentFrame) {
|
|||
|
||||
if (isPlaying()) {
|
||||
_timer.start();
|
||||
setAudionInjectorPosition();
|
||||
setAudioInjectorPosition();
|
||||
} else {
|
||||
_pausedFrame = _currentFrame;
|
||||
}
|
||||
|
@ -349,15 +351,7 @@ void Player::setCurrentTime(int currentTime) {
|
|||
}
|
||||
}
|
||||
|
||||
_currentFrame = lowestBound;
|
||||
_timerOffset = _recording->getFrameTimestamp(lowestBound);
|
||||
|
||||
if (isPlaying()) {
|
||||
_timer.start();
|
||||
setAudionInjectorPosition();
|
||||
} else {
|
||||
_pausedFrame = lowestBound;
|
||||
}
|
||||
setCurrentFrame(lowestBound);
|
||||
}
|
||||
|
||||
void Player::setVolume(float volume) {
|
||||
|
@ -372,11 +366,9 @@ void Player::setAudioOffset(int audioOffset) {
|
|||
_audioOffset = audioOffset;
|
||||
}
|
||||
|
||||
void Player::setAudionInjectorPosition() {
|
||||
void Player::setAudioInjectorPosition() {
|
||||
int MSEC_PER_SEC = 1000;
|
||||
int SAMPLE_SIZE = 2; // 16 bits
|
||||
int CHANNEL_COUNT = 1;
|
||||
int FRAME_SIZE = SAMPLE_SIZE * CHANNEL_COUNT;
|
||||
int FRAME_SIZE = sizeof(AudioConstants::AudioSample) * _recording->numberAudioChannel();
|
||||
int currentAudioFrame = elapsed() * FRAME_SIZE * (AudioConstants::SAMPLE_RATE / MSEC_PER_SEC);
|
||||
_injector->setCurrentSendPosition(currentAudioFrame);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ private:
|
|||
void setupAudioThread();
|
||||
void cleanupAudioThread();
|
||||
void loopRecording();
|
||||
void setAudionInjectorPosition();
|
||||
void setAudioInjectorPosition();
|
||||
bool computeCurrentFrame();
|
||||
|
||||
AvatarData* _avatar;
|
||||
|
|
|
@ -65,6 +65,15 @@ const RecordingFrame& Recording::getFrame(int i) const {
|
|||
return _frames[i];
|
||||
}
|
||||
|
||||
|
||||
int Recording::numberAudioChannel() const {
|
||||
// Check for stereo audio
|
||||
int MSEC_PER_SEC = 1000;
|
||||
int channelLength = (getLength() / MSEC_PER_SEC) *
|
||||
AudioConstants::SAMPLE_RATE * sizeof(AudioConstants::AudioSample);
|
||||
return glm::round((float)channelLength / (float)getAudioData().size());
|
||||
}
|
||||
|
||||
void Recording::addFrame(int timestamp, RecordingFrame &frame) {
|
||||
_timestamps << timestamp;
|
||||
_frames << frame;
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
qint32 getFrameTimestamp(int i) const;
|
||||
const RecordingFrame& getFrame(int i) const;
|
||||
const QByteArray& getAudioData() const { return _audioData; }
|
||||
int numberAudioChannel() const;
|
||||
|
||||
protected:
|
||||
void addFrame(int timestamp, RecordingFrame& frame);
|
||||
|
|
|
@ -187,7 +187,9 @@ void buildStringToShapeTypeLookup() {
|
|||
}
|
||||
|
||||
QString EntityItemProperties::getShapeTypeAsString() const {
|
||||
return QString(shapeTypeNames[_shapeType]);
|
||||
if (_shapeType < sizeof(shapeTypeNames) / sizeof(char *))
|
||||
return QString(shapeTypeNames[_shapeType]);
|
||||
return QString("none");
|
||||
}
|
||||
|
||||
void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <VariantMapToScriptValue.h>
|
||||
|
||||
#include "EntityScriptingInterface.h"
|
||||
#include "EntityTree.h"
|
||||
#include "LightEntityItem.h"
|
||||
|
@ -204,8 +206,7 @@ EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center
|
|||
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
|
||||
_entityTree->unlock();
|
||||
if (closestEntity) {
|
||||
result.id = closestEntity->getID();
|
||||
result.isKnownID = true;
|
||||
result = closestEntity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -227,10 +228,25 @@ QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& ce
|
|||
QVector<const EntityItem*> entities;
|
||||
_entityTree->findEntities(center, radius, entities);
|
||||
_entityTree->unlock();
|
||||
|
||||
|
||||
foreach (const EntityItem* entity, entities) {
|
||||
EntityItemID thisEntityItemID(entity->getID(), UNKNOWN_ENTITY_TOKEN, true);
|
||||
result << thisEntityItemID;
|
||||
result << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVector<EntityItemID> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {
|
||||
QVector<EntityItemID> result;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
AABox box(corner, dimensions);
|
||||
QVector<EntityItem*> entities;
|
||||
_entityTree->findEntities(box, entities);
|
||||
_entityTree->unlock();
|
||||
|
||||
foreach (const EntityItem* entity, entities) {
|
||||
result << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -87,10 +87,14 @@ public slots:
|
|||
/// will return a EntityItemID.isKnownID = false if no models are in the radius
|
||||
/// this function will not find any models in script engine contexts which don't have access to models
|
||||
Q_INVOKABLE EntityItemID findClosestEntity(const glm::vec3& center, float radius) const;
|
||||
|
||||
|
||||
/// finds models within the search sphere specified by the center point and radius
|
||||
/// this function will not find any models in script engine contexts which don't have access to models
|
||||
Q_INVOKABLE QVector<EntityItemID> findEntities(const glm::vec3& center, float radius) const;
|
||||
|
||||
/// finds models within the search sphere specified by the center point and radius
|
||||
/// this function will not find any models in script engine contexts which don't have access to models
|
||||
Q_INVOKABLE QVector<EntityItemID> findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
|
||||
|
||||
/// If the scripting context has visible entities, this will determine a ray intersection, the results
|
||||
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
|
||||
|
|
|
@ -10,13 +10,18 @@
|
|||
//
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <QDateTime>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "EntitySimulation.h"
|
||||
#include "VariantMapToScriptValue.h"
|
||||
|
||||
#include "AddEntityOperator.h"
|
||||
#include "MovingEntitiesOperator.h"
|
||||
#include "UpdateEntityOperator.h"
|
||||
#include "QVariantGLM.h"
|
||||
#include "RecurseOctreeToMapOperator.h"
|
||||
|
||||
EntityTree::EntityTree(bool shouldReaverage) :
|
||||
Octree(shouldReaverage),
|
||||
|
@ -236,21 +241,28 @@ void EntityTree::setSimulation(EntitySimulation* simulation) {
|
|||
_simulation = simulation;
|
||||
}
|
||||
|
||||
void EntityTree::deleteEntity(const EntityItemID& entityID, bool force) {
|
||||
void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ignoreWarnings) {
|
||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||
if (!containingElement) {
|
||||
qDebug() << "UNEXPECTED!!!! EntityTree::deleteEntity() entityID doesn't exist!!! entityID=" << entityID;
|
||||
if (!ignoreWarnings) {
|
||||
qDebug() << "UNEXPECTED!!!! EntityTree::deleteEntity() entityID doesn't exist!!! entityID=" << entityID;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
if (!existingEntity) {
|
||||
qDebug() << "UNEXPECTED!!!! don't call EntityTree::deleteEntity() on entity items that don't exist. entityID=" << entityID;
|
||||
if (!ignoreWarnings) {
|
||||
qDebug() << "UNEXPECTED!!!! don't call EntityTree::deleteEntity() on entity items that don't exist. "
|
||||
"entityID=" << entityID;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingEntity->getLocked() && !force) {
|
||||
qDebug() << "ERROR! EntityTree::deleteEntity() trying to delete locked entity. entityID=" << entityID;
|
||||
if (!ignoreWarnings) {
|
||||
qDebug() << "ERROR! EntityTree::deleteEntity() trying to delete locked entity. entityID=" << entityID;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -263,24 +275,31 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force) {
|
|||
_isDirty = true;
|
||||
}
|
||||
|
||||
void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force) {
|
||||
void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool ignoreWarnings) {
|
||||
// NOTE: callers must lock the tree before using this method
|
||||
DeleteEntityOperator theOperator(this);
|
||||
foreach(const EntityItemID& entityID, entityIDs) {
|
||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||
if (!containingElement) {
|
||||
qDebug() << "UNEXPECTED!!!! EntityTree::deleteEntities() entityID doesn't exist!!! entityID=" << entityID;
|
||||
if (!ignoreWarnings) {
|
||||
qDebug() << "UNEXPECTED!!!! EntityTree::deleteEntities() entityID doesn't exist!!! entityID=" << entityID;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
if (!existingEntity) {
|
||||
qDebug() << "UNEXPECTED!!!! don't call EntityTree::deleteEntities() on entity items that don't exist. entityID=" << entityID;
|
||||
if (!ignoreWarnings) {
|
||||
qDebug() << "UNEXPECTED!!!! don't call EntityTree::deleteEntities() on entity items that don't exist. "
|
||||
"entityID=" << entityID;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existingEntity->getLocked() && !force) {
|
||||
qDebug() << "ERROR! EntityTree::deleteEntities() trying to delete locked entity. entityID=" << entityID;
|
||||
if (!ignoreWarnings) {
|
||||
qDebug() << "ERROR! EntityTree::deleteEntities() trying to delete locked entity. entityID=" << entityID;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -452,7 +471,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData)
|
|||
return true; // keep searching in case children have closer entities
|
||||
}
|
||||
|
||||
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
|
||||
// if this element doesn't contain the point, then none of its children can contain the point, so stop searching
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -528,6 +547,35 @@ void EntityTree::findEntities(const AACube& cube, QVector<EntityItem*>& foundEnt
|
|||
foundEntities.swap(args._foundEntities);
|
||||
}
|
||||
|
||||
class FindEntitiesInBoxArgs {
|
||||
public:
|
||||
FindEntitiesInBoxArgs(const AABox& box)
|
||||
: _box(box), _foundEntities() {
|
||||
}
|
||||
|
||||
AABox _box;
|
||||
QVector<EntityItem*> _foundEntities;
|
||||
};
|
||||
|
||||
bool EntityTree::findInBoxOperation(OctreeElement* element, void* extraData) {
|
||||
FindEntitiesInBoxArgs* args = static_cast<FindEntitiesInBoxArgs*>(extraData);
|
||||
if (element->getAACube().touches(args->_box)) {
|
||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||
entityTreeElement->getEntities(args->_box, args->_foundEntities);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: assumes caller has handled locking
|
||||
void EntityTree::findEntities(const AABox& box, QVector<EntityItem*>& foundEntities) {
|
||||
FindEntitiesInBoxArgs args(box);
|
||||
// NOTE: This should use recursion, since this is a spatial operation
|
||||
recurseTreeWithOperation(findInBoxOperation, &args);
|
||||
// swap the two lists of entity pointers instead of copy
|
||||
foundEntities.swap(args._foundEntities);
|
||||
}
|
||||
|
||||
EntityItem* EntityTree::findEntityByID(const QUuid& id) {
|
||||
EntityItemID entityID(id);
|
||||
return findEntityByEntityItemID(entityID);
|
||||
|
@ -849,10 +897,9 @@ int EntityTree::processEraseMessage(const QByteArray& dataByteArray, const Share
|
|||
EntityItemID entityItemID(entityID);
|
||||
entityItemIDsToDelete << entityItemID;
|
||||
}
|
||||
deleteEntities(entityItemIDsToDelete);
|
||||
deleteEntities(entityItemIDsToDelete, true, true);
|
||||
}
|
||||
unlock();
|
||||
|
||||
return processedBytes;
|
||||
}
|
||||
|
||||
|
@ -889,7 +936,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons
|
|||
EntityItemID entityItemID(entityID);
|
||||
entityItemIDsToDelete << entityItemID;
|
||||
}
|
||||
deleteEntities(entityItemIDsToDelete);
|
||||
deleteEntities(entityItemIDsToDelete, true, true);
|
||||
}
|
||||
return processedBytes;
|
||||
}
|
||||
|
@ -1038,3 +1085,41 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElement* element) {
|
||||
entityDescription["Entities"] = QVariantList();
|
||||
QScriptEngine scriptEngine;
|
||||
RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EntityTree::readFromMap(QVariantMap& map) {
|
||||
// map will have a top-level list keyed as "Entities". This will be extracted
|
||||
// and iterated over. Each member of this list is converted to a QVariantMap, then
|
||||
// to a QScriptValue, and then to EntityItemProperties. These properties are used
|
||||
// to add the new entity to the EnitytTree.
|
||||
QVariantList entitiesQList = map["Entities"].toList();
|
||||
QScriptEngine scriptEngine;
|
||||
|
||||
foreach (QVariant entityVariant, entitiesQList) {
|
||||
// QVariantMap --> QScriptValue --> EntityItemProperties --> Entity
|
||||
QVariantMap entityMap = entityVariant.toMap();
|
||||
QScriptValue entityScriptValue = variantMapToScriptValue(entityMap, scriptEngine);
|
||||
EntityItemProperties properties;
|
||||
EntityItemPropertiesFromScriptValue(entityScriptValue, properties);
|
||||
|
||||
EntityItemID entityItemID;
|
||||
if (entityMap.contains("id")) {
|
||||
entityItemID = EntityItemID(QUuid(entityMap["id"].toString()));
|
||||
} else {
|
||||
entityItemID = EntityItemID(QUuid::createUuid());
|
||||
}
|
||||
|
||||
EntityItem* entity = addEntity(entityItemID, properties);
|
||||
if (!entity) {
|
||||
qDebug() << "adding Entity failed:" << entityItemID << entity->getType();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -92,8 +92,8 @@ public:
|
|||
// use this method if you have a pointer to the entity (avoid an extra entity lookup)
|
||||
bool updateEntity(EntityItem* entity, const EntityItemProperties& properties, bool allowLockChange);
|
||||
|
||||
void deleteEntity(const EntityItemID& entityID, bool force = false);
|
||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false);
|
||||
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
|
||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
|
||||
void removeEntityFromSimulation(EntityItem* entity);
|
||||
|
||||
/// \param position point of query in world-frame (meters)
|
||||
|
@ -111,12 +111,18 @@ public:
|
|||
/// \param foundEntities[out] vector of const EntityItem*
|
||||
/// \remark Side effect: any initial contents in foundEntities will be lost
|
||||
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities);
|
||||
|
||||
|
||||
/// finds all entities that touch a cube
|
||||
/// \param cube the query cube in world-frame (meters)
|
||||
/// \param foundEntities[out] vector of non-const EntityItem*
|
||||
/// \remark Side effect: any initial contents in entities will be lost
|
||||
void findEntities(const AACube& cube, QVector<EntityItem*>& foundEntities);
|
||||
|
||||
/// finds all entities that touch a box
|
||||
/// \param box the query box in world-frame (meters)
|
||||
/// \param foundEntities[out] vector of non-const EntityItem*
|
||||
/// \remark Side effect: any initial contents in entities will be lost
|
||||
void findEntities(const AABox& box, QVector<EntityItem*>& foundEntities);
|
||||
|
||||
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||
|
@ -158,6 +164,9 @@ public:
|
|||
bool wantEditLogging() const { return _wantEditLogging; }
|
||||
void setWantEditLogging(bool value) { _wantEditLogging = value; }
|
||||
|
||||
bool writeToMap(QVariantMap& entityDescription, OctreeElement* element);
|
||||
bool readFromMap(QVariantMap& entityDescription);
|
||||
|
||||
signals:
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
|
@ -173,6 +182,7 @@ private:
|
|||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInBoxOperation(OctreeElement* element, void* extraData);
|
||||
static bool sendEntitiesOperation(OctreeElement* element, void* extraData);
|
||||
|
||||
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
|
||||
|
|
|
@ -111,6 +111,8 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_EXPONENT, float, _exponent);
|
||||
READ_ENTITY_PROPERTY(PROP_CUTOFF, float, _cutoff);
|
||||
|
||||
(void) ignoredAttenuation; // suppress compiler warning
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, _isSpotlight);
|
||||
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
|
||||
|
|
54
libraries/entities/src/RecurseOctreeToMapOperator.cpp
Normal file
54
libraries/entities/src/RecurseOctreeToMapOperator.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// RecurseOctreeToMapOperator.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 3/16/15.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "RecurseOctreeToMapOperator.h"
|
||||
|
||||
|
||||
RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine) :
|
||||
RecurseOctreeOperator(),
|
||||
_map(map),
|
||||
_top(top),
|
||||
_engine(engine)
|
||||
{
|
||||
// if some element "top" was given, only save information for that element and it's children.
|
||||
if (_top) {
|
||||
_withinTop = false;
|
||||
} else {
|
||||
// top was NULL, export entire tree.
|
||||
_withinTop = true;
|
||||
}
|
||||
};
|
||||
|
||||
bool RecurseOctreeToMapOperator::preRecursion(OctreeElement* element) {
|
||||
if (element == _top) {
|
||||
_withinTop = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) {
|
||||
|
||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
|
||||
|
||||
QVariantList entitiesQList = qvariant_cast<QVariantList>(_map["Entities"]);
|
||||
|
||||
foreach (EntityItem* entityItem, entities) {
|
||||
EntityItemProperties properties = entityItem->getProperties();
|
||||
QScriptValue qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties);
|
||||
entitiesQList << qScriptValues.toVariant();
|
||||
}
|
||||
_map["Entities"] = entitiesQList;
|
||||
if (element == _top) {
|
||||
_withinTop = false;
|
||||
}
|
||||
return true;
|
||||
}
|
24
libraries/entities/src/RecurseOctreeToMapOperator.h
Normal file
24
libraries/entities/src/RecurseOctreeToMapOperator.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// RecurseOctreeToMapOperator.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by Seth Alves on 3/16/15.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "EntityTree.h"
|
||||
|
||||
class RecurseOctreeToMapOperator : public RecurseOctreeOperator {
|
||||
public:
|
||||
RecurseOctreeToMapOperator(QVariantMap& map, OctreeElement *top, QScriptEngine *engine);
|
||||
bool preRecursion(OctreeElement* element);
|
||||
bool postRecursion(OctreeElement* element);
|
||||
private:
|
||||
QVariantMap& _map;
|
||||
OctreeElement *_top;
|
||||
QScriptEngine *_engine;
|
||||
bool _withinTop;
|
||||
};
|
|
@ -31,6 +31,7 @@ public:
|
|||
};
|
||||
int nextToken();
|
||||
const QByteArray& getDatum() const { return _datum; }
|
||||
bool isNextTokenFloat();
|
||||
void skipLine() { _device->readLine(); }
|
||||
void pushBackToken(int token) { _pushedBackToken = token; }
|
||||
void ungetChar(char ch) { _device->ungetChar(ch); }
|
||||
|
@ -91,14 +92,32 @@ int OBJTokenizer::nextToken() {
|
|||
return NO_TOKEN;
|
||||
}
|
||||
|
||||
bool OBJTokenizer::isNextTokenFloat() {
|
||||
if (nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
return false;
|
||||
}
|
||||
QByteArray token = getDatum();
|
||||
pushBackToken(OBJTokenizer::DATUM_TOKEN);
|
||||
bool ok;
|
||||
token.toFloat(&ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry) {
|
||||
bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
|
||||
FBXGeometry &geometry, QVector<glm::vec3>& faceNormals, QVector<int>& faceNormalIndexes) {
|
||||
FBXMesh &mesh = geometry.meshes[0];
|
||||
mesh.parts.append(FBXMeshPart());
|
||||
FBXMeshPart &meshPart = mesh.parts.last();
|
||||
bool sawG = false;
|
||||
bool result = true;
|
||||
|
||||
meshPart.diffuseColor = glm::vec3(1, 1, 1);
|
||||
meshPart.specularColor = glm::vec3(1, 1, 1);
|
||||
meshPart.emissiveColor = glm::vec3(0, 0, 0);
|
||||
meshPart.emissiveParams = glm::vec2(0, 1);
|
||||
meshPart.shininess = 40;
|
||||
meshPart.opacity = 1;
|
||||
|
||||
meshPart.materialID = QString("dontknow") + QString::number(mesh.parts.count());
|
||||
meshPart.opacity = 1.0;
|
||||
meshPart._material = model::MaterialPointer(new model::Material());
|
||||
|
@ -131,11 +150,27 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeom
|
|||
break;
|
||||
}
|
||||
float x = std::stof(tokenizer.getDatum().data());
|
||||
// notice the order of z and y -- in OBJ files, up is the 3rd value
|
||||
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
}
|
||||
float y = std::stof(tokenizer.getDatum().data());
|
||||
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
}
|
||||
float z = std::stof(tokenizer.getDatum().data());
|
||||
|
||||
while (tokenizer.isNextTokenFloat()) {
|
||||
// the spec(s) get(s) vague here. might be w, might be a color... chop it off.
|
||||
tokenizer.nextToken();
|
||||
}
|
||||
mesh.vertices.append(glm::vec3(x, y, z));
|
||||
} else if (token == "vn") {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
}
|
||||
float x = std::stof(tokenizer.getDatum().data());
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
}
|
||||
|
@ -143,35 +178,74 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeom
|
|||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
break;
|
||||
}
|
||||
// the spec gets vague here. might be w, might be a color... chop it off.
|
||||
tokenizer.skipLine();
|
||||
mesh.vertices.append(glm::vec3(x, y, z));
|
||||
mesh.colors.append(glm::vec3(1, 1, 1));
|
||||
float z = std::stof(tokenizer.getDatum().data());
|
||||
|
||||
while (tokenizer.isNextTokenFloat()) {
|
||||
// the spec gets vague here. might be w
|
||||
tokenizer.nextToken();
|
||||
}
|
||||
faceNormals.append(glm::vec3(x, y, z));
|
||||
} else if (token == "f") {
|
||||
// a face can have 3 or more vertices
|
||||
QVector<int> indices;
|
||||
QVector<int> normalIndices;
|
||||
while (true) {
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { goto done; }
|
||||
try {
|
||||
int vertexIndex = std::stoi(tokenizer.getDatum().data());
|
||||
// negative indexes count backward from the current end of the vertex list
|
||||
vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1);
|
||||
// obj index is 1 based
|
||||
assert(vertexIndex >= 1);
|
||||
indices.append(vertexIndex - 1);
|
||||
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
|
||||
if (indices.count() == 0) {
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch(const std::exception& e) {
|
||||
// wasn't a number, but it back.
|
||||
// faces can be:
|
||||
// vertex-index
|
||||
// vertex-index/texture-index
|
||||
// vertex-index/texture-index/surface-normal-index
|
||||
|
||||
QByteArray token = tokenizer.getDatum();
|
||||
QList<QByteArray> parts = token.split('/');
|
||||
assert(parts.count() >= 1);
|
||||
assert(parts.count() <= 3);
|
||||
QByteArray vertIndexBA = parts[ 0 ];
|
||||
|
||||
bool ok;
|
||||
int vertexIndex = vertIndexBA.toInt(&ok);
|
||||
if (!ok) {
|
||||
// it wasn't #/#/#, put it back and exit this loop.
|
||||
tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
|
||||
break;
|
||||
}
|
||||
|
||||
// if (parts.count() > 1) {
|
||||
// QByteArray textureIndexBA = parts[ 1 ];
|
||||
// }
|
||||
|
||||
if (parts.count() > 2) {
|
||||
QByteArray normalIndexBA = parts[ 2 ];
|
||||
bool ok;
|
||||
int normalIndex = normalIndexBA.toInt(&ok);
|
||||
if (ok) {
|
||||
normalIndices.append(normalIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// negative indexes count backward from the current end of the vertex list
|
||||
vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1);
|
||||
// obj index is 1 based
|
||||
assert(vertexIndex >= 1);
|
||||
indices.append(vertexIndex - 1);
|
||||
}
|
||||
|
||||
if (indices.count() == 3) {
|
||||
// flip these around (because of the y/z swap above) so our triangles face outward
|
||||
meshPart.triangleIndices.append(indices[0]);
|
||||
meshPart.triangleIndices.append(indices[2]);
|
||||
meshPart.triangleIndices.append(indices[1]);
|
||||
meshPart.triangleIndices.append(indices[2]);
|
||||
if (normalIndices.count() == 3) {
|
||||
faceNormalIndexes.append(normalIndices[0]);
|
||||
faceNormalIndexes.append(normalIndices[1]);
|
||||
faceNormalIndexes.append(normalIndices[2]);
|
||||
} else {
|
||||
// hmm.
|
||||
}
|
||||
} else if (indices.count() == 4) {
|
||||
meshPart.quadIndices << indices;
|
||||
} else {
|
||||
|
@ -205,6 +279,10 @@ FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) {
|
|||
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
|
||||
FBXGeometry geometry;
|
||||
OBJTokenizer tokenizer(device);
|
||||
QVector<int> faceNormalIndexes;
|
||||
QVector<glm::vec3> faceNormals;
|
||||
|
||||
faceNormalIndexes.clear();
|
||||
|
||||
geometry.meshExtents.reset();
|
||||
geometry.meshes.append(FBXMesh());
|
||||
|
@ -216,7 +294,7 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
|
|||
// add a new meshPart to the geometry's single mesh.
|
||||
bool success = true;
|
||||
while (success) {
|
||||
success = parseOBJGroup(tokenizer, mapping, geometry);
|
||||
success = parseOBJGroup(tokenizer, mapping, geometry, faceNormals, faceNormalIndexes);
|
||||
}
|
||||
|
||||
FBXMesh &mesh = geometry.meshes[0];
|
||||
|
@ -229,31 +307,34 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
|
|||
|
||||
geometry.joints.resize(1);
|
||||
geometry.joints[0].isFree = false;
|
||||
// geometry.joints[0].freeLineage;
|
||||
geometry.joints[0].parentIndex = -1;
|
||||
geometry.joints[0].distanceToParent = 0;
|
||||
geometry.joints[0].boneRadius = 0;
|
||||
geometry.joints[0].translation = glm::vec3(0, 0, 0);
|
||||
// geometry.joints[0].preTransform = ;
|
||||
geometry.joints[0].preRotation = glm::quat(0, 0, 0, 1);
|
||||
geometry.joints[0].rotation = glm::quat(0, 0, 0, 1);
|
||||
geometry.joints[0].postRotation = glm::quat(0, 0, 0, 1);
|
||||
// geometry.joints[0].postTransform = ;
|
||||
// geometry.joints[0].transform = ;
|
||||
geometry.joints[0].rotationMin = glm::vec3(0, 0, 0);
|
||||
geometry.joints[0].rotationMax = glm::vec3(0, 0, 0);
|
||||
geometry.joints[0].inverseDefaultRotation = glm::quat(0, 0, 0, 1);
|
||||
geometry.joints[0].inverseBindRotation = glm::quat(0, 0, 0, 1);
|
||||
// geometry.joints[0].bindTransform = ;
|
||||
geometry.joints[0].name = "OBJ";
|
||||
geometry.joints[0].shapePosition = glm::vec3(0, 0, 0);
|
||||
geometry.joints[0].shapeRotation = glm::quat(0, 0, 0, 1);
|
||||
geometry.joints[0].shapeType = SPHERE_SHAPE;
|
||||
geometry.joints[0].isSkeletonJoint = false;
|
||||
geometry.joints[0].isSkeletonJoint = true;
|
||||
|
||||
geometry.jointIndices["x"] = 1;
|
||||
|
||||
FBXCluster cluster;
|
||||
cluster.jointIndex = 0;
|
||||
cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mesh.clusters.append(cluster);
|
||||
|
||||
// The OBJ format has normals for faces. The FBXGeometry structure has normals for points.
|
||||
// run through all the faces, look-up (or determine) a normal and set the normal for the points
|
||||
// that make up each face.
|
||||
QVector<glm::vec3> pointNormalsSums;
|
||||
|
||||
// add bogus normal data for this mesh
|
||||
mesh.normals.fill(glm::vec3(0,0,0), mesh.vertices.count());
|
||||
mesh.tangents.fill(glm::vec3(0,0,0), mesh.vertices.count());
|
||||
pointNormalsSums.fill(glm::vec3(0,0,0), mesh.vertices.count());
|
||||
|
||||
foreach (FBXMeshPart meshPart, mesh.parts) {
|
||||
int triCount = meshPart.triangleIndices.count() / 3;
|
||||
|
@ -262,21 +343,43 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
|
|||
int p1Index = meshPart.triangleIndices[i*3+1];
|
||||
int p2Index = meshPart.triangleIndices[i*3+2];
|
||||
|
||||
glm::vec3 p0 = mesh.vertices[p0Index];
|
||||
glm::vec3 p1 = mesh.vertices[p1Index];
|
||||
glm::vec3 p2 = mesh.vertices[p2Index];
|
||||
assert(p0Index < mesh.vertices.count());
|
||||
assert(p1Index < mesh.vertices.count());
|
||||
assert(p2Index < mesh.vertices.count());
|
||||
|
||||
glm::vec3 n = glm::cross(p1 - p0, p2 - p0);
|
||||
glm::vec3 t = glm::cross(p2 - p0, n);
|
||||
glm::vec3 n0, n1, n2;
|
||||
if (i < faceNormalIndexes.count()) {
|
||||
int n0Index = faceNormalIndexes[i*3];
|
||||
int n1Index = faceNormalIndexes[i*3+1];
|
||||
int n2Index = faceNormalIndexes[i*3+2];
|
||||
n0 = faceNormals[n0Index];
|
||||
n1 = faceNormals[n1Index];
|
||||
n2 = faceNormals[n2Index];
|
||||
} else {
|
||||
// We didn't read normals, add bogus normal data for this face
|
||||
glm::vec3 p0 = mesh.vertices[p0Index];
|
||||
glm::vec3 p1 = mesh.vertices[p1Index];
|
||||
glm::vec3 p2 = mesh.vertices[p2Index];
|
||||
n0 = glm::cross(p1 - p0, p2 - p0);
|
||||
n1 = n0;
|
||||
n2 = n0;
|
||||
}
|
||||
|
||||
mesh.normals[p0Index] = n;
|
||||
mesh.normals[p1Index] = n;
|
||||
mesh.normals[p2Index] = n;
|
||||
|
||||
mesh.tangents[p0Index] = t;
|
||||
mesh.tangents[p1Index] = t;
|
||||
mesh.tangents[p2Index] = t;
|
||||
// we sum up the normal for each point and then divide by the count to get an average
|
||||
pointNormalsSums[p0Index] += n0;
|
||||
pointNormalsSums[p1Index] += n1;
|
||||
pointNormalsSums[p2Index] += n2;
|
||||
}
|
||||
|
||||
int vertCount = mesh.vertices.count();
|
||||
for (int i = 0; i < vertCount; i++) {
|
||||
float length = glm::length(pointNormalsSums[i]);
|
||||
if (length > FLT_EPSILON) {
|
||||
mesh.normals[i] = glm::normalize(pointNormalsSums[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX do same normal calculation for quadCount
|
||||
}
|
||||
}
|
||||
catch(const std::exception& e) {
|
||||
|
@ -285,3 +388,77 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
|
|||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void fbxDebugDump(const FBXGeometry& fbxgeo) {
|
||||
qDebug() << "---------------- fbxGeometry ----------------";
|
||||
qDebug() << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints;
|
||||
qDebug() << " offset =" << fbxgeo.offset;
|
||||
qDebug() << " attachments.count() = " << fbxgeo.attachments.count();
|
||||
qDebug() << " meshes.count() =" << fbxgeo.meshes.count();
|
||||
foreach (FBXMesh mesh, fbxgeo.meshes) {
|
||||
qDebug() << " vertices.count() =" << mesh.vertices.count();
|
||||
qDebug() << " normals.count() =" << mesh.normals.count();
|
||||
if (mesh.normals.count() == mesh.vertices.count()) {
|
||||
for (int i = 0; i < mesh.normals.count(); i++) {
|
||||
qDebug() << " " << mesh.vertices[ i ] << mesh.normals[ i ];
|
||||
}
|
||||
}
|
||||
qDebug() << " tangents.count() =" << mesh.tangents.count();
|
||||
qDebug() << " colors.count() =" << mesh.colors.count();
|
||||
qDebug() << " texCoords.count() =" << mesh.texCoords.count();
|
||||
qDebug() << " texCoords1.count() =" << mesh.texCoords1.count();
|
||||
qDebug() << " clusterIndices.count() =" << mesh.clusterIndices.count();
|
||||
qDebug() << " clusterWeights.count() =" << mesh.clusterWeights.count();
|
||||
qDebug() << " meshExtents =" << mesh.meshExtents;
|
||||
qDebug() << " modelTransform =" << mesh.modelTransform;
|
||||
qDebug() << " parts.count() =" << mesh.parts.count();
|
||||
foreach (FBXMeshPart meshPart, mesh.parts) {
|
||||
qDebug() << " quadIndices.count() =" << meshPart.quadIndices.count();
|
||||
qDebug() << " triangleIndices.count() =" << meshPart.triangleIndices.count();
|
||||
qDebug() << " diffuseColor =" << meshPart.diffuseColor;
|
||||
qDebug() << " specularColor =" << meshPart.specularColor;
|
||||
qDebug() << " emissiveColor =" << meshPart.emissiveColor;
|
||||
qDebug() << " emissiveParams =" << meshPart.emissiveParams;
|
||||
qDebug() << " shininess =" << meshPart.shininess;
|
||||
qDebug() << " opacity =" << meshPart.opacity;
|
||||
qDebug() << " materialID =" << meshPart.materialID;
|
||||
}
|
||||
qDebug() << " clusters.count() =" << mesh.clusters.count();
|
||||
foreach (FBXCluster cluster, mesh.clusters) {
|
||||
qDebug() << " jointIndex =" << cluster.jointIndex;
|
||||
qDebug() << " inverseBindMatrix =" << cluster.inverseBindMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << " jointIndices =" << fbxgeo.jointIndices;
|
||||
qDebug() << " joints.count() =" << fbxgeo.joints.count();
|
||||
|
||||
foreach (FBXJoint joint, fbxgeo.joints) {
|
||||
qDebug() << " isFree =" << joint.isFree;
|
||||
qDebug() << " freeLineage" << joint.freeLineage;
|
||||
qDebug() << " parentIndex" << joint.parentIndex;
|
||||
qDebug() << " distanceToParent" << joint.distanceToParent;
|
||||
qDebug() << " boneRadius" << joint.boneRadius;
|
||||
qDebug() << " translation" << joint.translation;
|
||||
qDebug() << " preTransform" << joint.preTransform;
|
||||
qDebug() << " preRotation" << joint.preRotation;
|
||||
qDebug() << " rotation" << joint.rotation;
|
||||
qDebug() << " postRotation" << joint.postRotation;
|
||||
qDebug() << " postTransform" << joint.postTransform;
|
||||
qDebug() << " transform" << joint.transform;
|
||||
qDebug() << " rotationMin" << joint.rotationMin;
|
||||
qDebug() << " rotationMax" << joint.rotationMax;
|
||||
qDebug() << " inverseDefaultRotation" << joint.inverseDefaultRotation;
|
||||
qDebug() << " inverseBindRotation" << joint.inverseBindRotation;
|
||||
qDebug() << " bindTransform" << joint.bindTransform;
|
||||
qDebug() << " name" << joint.name;
|
||||
qDebug() << " shapePosition" << joint.shapePosition;
|
||||
qDebug() << " shapeRotation" << joint.shapeRotation;
|
||||
qDebug() << " shapeType" << joint.shapeType;
|
||||
qDebug() << " isSkeletonJoint" << joint.isSkeletonJoint;
|
||||
}
|
||||
|
||||
qDebug() << "\n";
|
||||
}
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
|
||||
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping);
|
||||
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping);
|
||||
void fbxDebugDump(const FBXGeometry& fbxgeo);
|
||||
|
|
|
@ -19,13 +19,15 @@ elseif (WIN32)
|
|||
|
||||
target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} opengl32.lib)
|
||||
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
find_package(NSIGHT)
|
||||
if (NSIGHT_FOUND)
|
||||
include_directories(${NSIGHT_INCLUDE_DIRS})
|
||||
add_definitions(-DNSIGHT_FOUND)
|
||||
target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
|
||||
endif ()
|
||||
if (USE_NSIGHT)
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
find_package(NSIGHT)
|
||||
if (NSIGHT_FOUND)
|
||||
include_directories(${NSIGHT_INCLUDE_DIRS})
|
||||
add_definitions(-DNSIGHT_FOUND)
|
||||
target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
|
||||
endif ()
|
||||
endif()
|
||||
elseif (ANDROID)
|
||||
target_link_libraries(${TARGET_NAME} "-lGLESv3" "-lEGL")
|
||||
else ()
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
|
||||
#if defined(NSIGHT_FOUND)
|
||||
#include "nvToolsExt.h"
|
||||
class ProfileRange {
|
||||
public:
|
||||
ProfileRange(const char *name) {
|
||||
nvtxRangePush(name);
|
||||
}
|
||||
~ProfileRange() {
|
||||
nvtxRangePop();
|
||||
}
|
||||
class ProfileRange {
|
||||
public:
|
||||
ProfileRange(const char *name) {
|
||||
nvtxRangePush(name);
|
||||
}
|
||||
~ProfileRange() {
|
||||
nvtxRangePop();
|
||||
}
|
||||
};
|
||||
|
||||
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
||||
|
@ -114,17 +114,17 @@ public:
|
|||
// For now, instead of calling the raw glCall, use the equivalent call on the batch so the call is beeing recorded
|
||||
// THe implementation of these functions is in GLBackend.cpp
|
||||
|
||||
void _glEnable(GLenum cap);
|
||||
void _glDisable(GLenum cap);
|
||||
|
||||
void _glEnable(GLenum cap);
|
||||
void _glDisable(GLenum cap);
|
||||
|
||||
void _glEnableClientState(GLenum array);
|
||||
void _glDisableClientState(GLenum array);
|
||||
|
||||
void _glCullFace(GLenum mode);
|
||||
void _glAlphaFunc(GLenum func, GLclampf ref);
|
||||
|
||||
void _glDepthFunc(GLenum func);
|
||||
void _glDepthMask(GLboolean flag);
|
||||
void _glDepthFunc(GLenum func);
|
||||
void _glDepthMask(GLboolean flag);
|
||||
void _glDepthRange(GLclampd zNear, GLclampd zFar);
|
||||
|
||||
void _glBindBuffer(GLenum target, GLuint buffer);
|
||||
|
@ -138,14 +138,14 @@ public:
|
|||
void _glUniform1f(GLint location, GLfloat v0);
|
||||
void _glUniform2f(GLint location, GLfloat v0, GLfloat v1);
|
||||
void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value);
|
||||
void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
|
||||
void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
|
||||
|
||||
void _glDrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||
void _glDrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||
void _glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);
|
||||
|
||||
void _glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
|
||||
void _glNormalPointer(GLenum type, GLsizei stride, const void *pointer);
|
||||
void _glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
|
||||
|
||||
void _glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
|
||||
void _glNormalPointer(GLenum type, GLsizei stride, const void *pointer);
|
||||
void _glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
|
||||
void _glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
|
||||
|
||||
void _glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
|
||||
|
@ -175,44 +175,44 @@ public:
|
|||
// TODO: As long as we have gl calls explicitely issued from interface
|
||||
// code, we need to be able to record and batch these calls. THe long
|
||||
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
|
||||
COMMAND_glEnable,
|
||||
COMMAND_glDisable,
|
||||
|
||||
COMMAND_glEnableClientState,
|
||||
COMMAND_glDisableClientState,
|
||||
|
||||
COMMAND_glCullFace,
|
||||
COMMAND_glAlphaFunc,
|
||||
|
||||
COMMAND_glDepthFunc,
|
||||
COMMAND_glDepthMask,
|
||||
COMMAND_glDepthRange,
|
||||
|
||||
COMMAND_glBindBuffer,
|
||||
|
||||
COMMAND_glBindTexture,
|
||||
COMMAND_glActiveTexture,
|
||||
|
||||
COMMAND_glDrawBuffers,
|
||||
|
||||
COMMAND_glUseProgram,
|
||||
COMMAND_glUniform1f,
|
||||
COMMAND_glUniform2f,
|
||||
COMMAND_glUniform4fv,
|
||||
COMMAND_glUniformMatrix4fv,
|
||||
|
||||
COMMAND_glDrawArrays,
|
||||
COMMAND_glDrawRangeElements,
|
||||
|
||||
COMMAND_glColorPointer,
|
||||
COMMAND_glNormalPointer,
|
||||
COMMAND_glTexCoordPointer,
|
||||
COMMAND_glVertexPointer,
|
||||
|
||||
COMMAND_glVertexAttribPointer,
|
||||
COMMAND_glEnableVertexAttribArray,
|
||||
COMMAND_glDisableVertexAttribArray,
|
||||
|
||||
COMMAND_glEnable,
|
||||
COMMAND_glDisable,
|
||||
|
||||
COMMAND_glEnableClientState,
|
||||
COMMAND_glDisableClientState,
|
||||
|
||||
COMMAND_glCullFace,
|
||||
COMMAND_glAlphaFunc,
|
||||
|
||||
COMMAND_glDepthFunc,
|
||||
COMMAND_glDepthMask,
|
||||
COMMAND_glDepthRange,
|
||||
|
||||
COMMAND_glBindBuffer,
|
||||
|
||||
COMMAND_glBindTexture,
|
||||
COMMAND_glActiveTexture,
|
||||
|
||||
COMMAND_glDrawBuffers,
|
||||
|
||||
COMMAND_glUseProgram,
|
||||
COMMAND_glUniform1f,
|
||||
COMMAND_glUniform2f,
|
||||
COMMAND_glUniform4fv,
|
||||
COMMAND_glUniformMatrix4fv,
|
||||
|
||||
COMMAND_glDrawArrays,
|
||||
COMMAND_glDrawRangeElements,
|
||||
|
||||
COMMAND_glColorPointer,
|
||||
COMMAND_glNormalPointer,
|
||||
COMMAND_glTexCoordPointer,
|
||||
COMMAND_glVertexPointer,
|
||||
|
||||
COMMAND_glVertexAttribPointer,
|
||||
COMMAND_glEnableVertexAttribArray,
|
||||
COMMAND_glDisableVertexAttribArray,
|
||||
|
||||
COMMAND_glColor4f,
|
||||
|
||||
NUM_COMMANDS,
|
||||
|
|
|
@ -27,8 +27,7 @@
|
|||
const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
|
||||
const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";
|
||||
|
||||
Setting::Handle<QUrl> currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address");
|
||||
|
||||
Setting::Handle<QUrl> currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_HIFI_ADDRESS);
|
||||
|
||||
AddressManager::AddressManager() :
|
||||
_rootPlaceName(),
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QVector>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
|
||||
#include <GeometryUtil.h>
|
||||
#include <LogHandler.h>
|
||||
|
@ -35,6 +39,7 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <Shape.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "CoverageMap.h"
|
||||
#include "OctreeConstants.h"
|
||||
|
@ -42,6 +47,9 @@
|
|||
#include "Octree.h"
|
||||
#include "ViewFrustum.h"
|
||||
|
||||
|
||||
QVector<QString> PERSIST_EXTENSIONS = {"svo", "json"};
|
||||
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) {
|
||||
return voxelSizeScale / powf(2, renderLevel);
|
||||
}
|
||||
|
@ -1841,21 +1849,22 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
|
|||
return bytesAtThisLevel;
|
||||
}
|
||||
|
||||
bool Octree::readFromSVOFile(const char* fileName) {
|
||||
bool Octree::readFromFile(const char* fileName) {
|
||||
bool fileOk = false;
|
||||
|
||||
QFile file(fileName);
|
||||
QString qFileName = findMostRecentFileExtension(fileName, PERSIST_EXTENSIONS);
|
||||
QFile file(qFileName);
|
||||
fileOk = file.open(QIODevice::ReadOnly);
|
||||
|
||||
if(fileOk) {
|
||||
QDataStream fileInputStream(&file);
|
||||
QFileInfo fileInfo(fileName);
|
||||
QFileInfo fileInfo(qFileName);
|
||||
unsigned long fileLength = fileInfo.size();
|
||||
|
||||
emit importSize(1.0f, 1.0f, 1.0f);
|
||||
emit importProgress(0);
|
||||
|
||||
qDebug("Loading file %s...", fileName);
|
||||
qDebug() << "Loading file" << qFileName << "...";
|
||||
|
||||
fileOk = readFromStream(fileLength, fileInputStream);
|
||||
|
||||
|
@ -1866,14 +1875,14 @@ bool Octree::readFromSVOFile(const char* fileName) {
|
|||
return fileOk;
|
||||
}
|
||||
|
||||
bool Octree::readFromSVOURL(const QString& urlString) {
|
||||
bool Octree::readFromURL(const QString& urlString) {
|
||||
bool readOk = false;
|
||||
|
||||
// determine if this is a local file or a network resource
|
||||
QUrl url(urlString);
|
||||
|
||||
if (url.isLocalFile()) {
|
||||
readOk = readFromSVOFile(qPrintable(url.toLocalFile()));
|
||||
readOk = readFromFile(qPrintable(url.toLocalFile()));
|
||||
} else {
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
@ -1899,6 +1908,23 @@ bool Octree::readFromSVOURL(const QString& urlString) {
|
|||
|
||||
|
||||
bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream) {
|
||||
|
||||
// decide if this is SVO or JSON
|
||||
QIODevice *device = inputStream.device();
|
||||
char firstChar;
|
||||
device->getChar(&firstChar);
|
||||
device->ungetChar(firstChar);
|
||||
|
||||
if (firstChar == (char) PacketTypeEntityData) {
|
||||
return readSVOFromStream(streamLength, inputStream);
|
||||
} else {
|
||||
return readJSONFromStream(streamLength, inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStream) {
|
||||
|
||||
bool fileOk = false;
|
||||
|
||||
PacketVersion gotVersion = 0;
|
||||
|
@ -2026,6 +2052,53 @@ bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream
|
|||
return fileOk;
|
||||
}
|
||||
|
||||
bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputStream) {
|
||||
char *rawData = new char[streamLength];
|
||||
inputStream.readRawData(rawData, streamLength);
|
||||
QJsonDocument d = QJsonDocument::fromJson(rawData);
|
||||
QVariant v = d.toVariant();
|
||||
QVariantMap m = v.toMap();
|
||||
readFromMap(m);
|
||||
delete rawData;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Octree::writeToFile(const char* fileName, OctreeElement* element, QString persistAsFileType) {
|
||||
// make the sure file extension makes sense
|
||||
QString qFileName = fileNameWithoutExtension(QString(fileName), PERSIST_EXTENSIONS) + "." + persistAsFileType;
|
||||
QByteArray byteArray = qFileName.toUtf8();
|
||||
const char* cFileName = byteArray.constData();
|
||||
|
||||
if (persistAsFileType == "svo") {
|
||||
writeToSVOFile(fileName, element);
|
||||
} else if (persistAsFileType == "json") {
|
||||
writeToJSONFile(cFileName, element);
|
||||
} else {
|
||||
qDebug() << "unable to write octree to file of type" << persistAsFileType;
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::writeToJSONFile(const char* fileName, OctreeElement* element) {
|
||||
QFile persistFile(fileName);
|
||||
QVariantMap entityDescription;
|
||||
|
||||
qDebug("Saving to file %s...", fileName);
|
||||
|
||||
OctreeElement* top;
|
||||
if (element) {
|
||||
top = element;
|
||||
} else {
|
||||
top = _rootElement;
|
||||
}
|
||||
|
||||
bool entityDescriptionSuccess = writeToMap(entityDescription, top);
|
||||
if (entityDescriptionSuccess && persistFile.open(QIODevice::WriteOnly)) {
|
||||
persistFile.write(QJsonDocument::fromVariant(entityDescription).toJson());
|
||||
} else {
|
||||
qCritical("Could not write to JSON description of entities.");
|
||||
}
|
||||
}
|
||||
|
||||
void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) {
|
||||
std::ofstream file(fileName, std::ios::out|std::ios::binary);
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ class Shape;
|
|||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
|
||||
extern QVector<QString> PERSIST_EXTENSIONS;
|
||||
|
||||
/// derive from this class to use the Octree::recurseTreeWithOperator() method
|
||||
class RecurseOctreeOperator {
|
||||
public:
|
||||
|
@ -324,13 +327,19 @@ public:
|
|||
// Note: this assumes the fileFormat is the HIO individual voxels code files
|
||||
void loadOctreeFile(const char* fileName, bool wantColorRandomizer);
|
||||
|
||||
// these will read/write files that match the wireformat, excluding the 'V' leading
|
||||
// Octree exporters
|
||||
void writeToFile(const char* filename, OctreeElement* element = NULL, QString persistAsFileType = "svo");
|
||||
void writeToJSONFile(const char* filename, OctreeElement* element = NULL);
|
||||
void writeToSVOFile(const char* filename, OctreeElement* element = NULL);
|
||||
virtual bool writeToMap(QVariantMap& entityDescription, OctreeElement* element) = 0;
|
||||
|
||||
bool readFromSVOFile(const char* filename);
|
||||
bool readFromSVOURL(const QString& url); // will support file urls as well...
|
||||
// Octree importers
|
||||
bool readFromFile(const char* filename);
|
||||
bool readFromURL(const QString& url); // will support file urls as well...
|
||||
bool readFromStream(unsigned long streamLength, QDataStream& inputStream);
|
||||
|
||||
bool readSVOFromStream(unsigned long streamLength, QDataStream& inputStream);
|
||||
bool readJSONFromStream(unsigned long streamLength, QDataStream& inputStream);
|
||||
virtual bool readFromMap(QVariantMap& entityDescription) = 0;
|
||||
|
||||
unsigned long getOctreeElementsCount();
|
||||
|
||||
|
|
|
@ -20,16 +20,19 @@
|
|||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "OctreePersistThread.h"
|
||||
|
||||
const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
|
||||
|
||||
OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename, int persistInterval,
|
||||
bool wantBackup, const QJsonObject& settings, bool debugTimestampNow) :
|
||||
bool wantBackup, const QJsonObject& settings, bool debugTimestampNow,
|
||||
QString persistAsFileType) :
|
||||
_tree(tree),
|
||||
_filename(filename),
|
||||
_persistInterval(persistInterval),
|
||||
|
@ -38,9 +41,14 @@ OctreePersistThread::OctreePersistThread(Octree* tree, const QString& filename,
|
|||
_lastCheck(0),
|
||||
_wantBackup(wantBackup),
|
||||
_debugTimestampNow(debugTimestampNow),
|
||||
_lastTimeDebug(0)
|
||||
_lastTimeDebug(0),
|
||||
_persistAsFileType(persistAsFileType)
|
||||
{
|
||||
parseSettings(settings);
|
||||
|
||||
// in case the persist filename has an extension that doesn't match the file type
|
||||
QString sansExt = fileNameWithoutExtension(_filename, PERSIST_EXTENSIONS);
|
||||
_filename = sansExt + "." + _persistAsFileType;
|
||||
}
|
||||
|
||||
void OctreePersistThread::parseSettings(const QJsonObject& settings) {
|
||||
|
@ -140,7 +148,7 @@ bool OctreePersistThread::process() {
|
|||
qDebug() << "Loading Octree... lock file removed:" << lockFileName;
|
||||
}
|
||||
|
||||
persistantFileRead = _tree->readFromSVOFile(_filename.toLocal8Bit().constData());
|
||||
persistantFileRead = _tree->readFromFile(qPrintable(_filename.toLocal8Bit()));
|
||||
_tree->pruneTree();
|
||||
}
|
||||
_tree->unlock();
|
||||
|
@ -242,9 +250,7 @@ void OctreePersistThread::persist() {
|
|||
if(lockFile.is_open()) {
|
||||
qDebug() << "saving Octree lock file created at:" << lockFileName;
|
||||
|
||||
qDebug() << "saving Octree to file " << _filename << "...";
|
||||
|
||||
_tree->writeToSVOFile(qPrintable(_filename));
|
||||
_tree->writeToFile(qPrintable(_filename), NULL, _persistAsFileType);
|
||||
time(&_lastPersistTime);
|
||||
_tree->clearDirtyBit(); // tree is clean after saving
|
||||
qDebug() << "DONE saving Octree to file...";
|
||||
|
@ -346,8 +352,8 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) {
|
|||
QString backupExtensionNplusOne = rule.extensionFormat;
|
||||
backupExtensionN.replace(QString("%N"), QString::number(n));
|
||||
backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1));
|
||||
|
||||
QString backupFilenameN = _filename + backupExtensionN;
|
||||
|
||||
QString backupFilenameN = findMostRecentFileExtension(_filename, PERSIST_EXTENSIONS) + backupExtensionN;
|
||||
QString backupFilenameNplusOne = _filename + backupExtensionNplusOne;
|
||||
|
||||
QFile backupFileN(backupFilenameN);
|
||||
|
|
|
@ -34,8 +34,8 @@ public:
|
|||
static const int DEFAULT_PERSIST_INTERVAL;
|
||||
|
||||
OctreePersistThread(Octree* tree, const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL,
|
||||
bool wantBackup = false, const QJsonObject& settings = QJsonObject(),
|
||||
bool debugTimestampNow = false);
|
||||
bool wantBackup = false, const QJsonObject& settings = QJsonObject(),
|
||||
bool debugTimestampNow = false, QString persistAsFileType="svo");
|
||||
|
||||
bool isInitialLoadComplete() const { return _initialLoadComplete; }
|
||||
quint64 getLoadElapsedTime() const { return _loadTimeUSecs; }
|
||||
|
@ -72,6 +72,8 @@ private:
|
|||
|
||||
bool _debugTimestampNow;
|
||||
quint64 _lastTimeDebug;
|
||||
|
||||
QString _persistAsFileType;
|
||||
};
|
||||
|
||||
#endif // hifi_OctreePersistThread_h
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include <QCoreApplication>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "PathUtils.h"
|
||||
|
||||
|
@ -23,3 +26,30 @@ QString& PathUtils::resourcesPath() {
|
|||
#endif
|
||||
return staticResourcePath;
|
||||
}
|
||||
|
||||
|
||||
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions) {
|
||||
foreach (const QString possibleExtension, possibleExtensions) {
|
||||
if (fileName.endsWith(possibleExtension) ||
|
||||
fileName.endsWith(possibleExtension.toUpper()) ||
|
||||
fileName.endsWith(possibleExtension.toLower())) {
|
||||
return fileName.left(fileName.count() - possibleExtension.count() - 1);
|
||||
}
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions) {
|
||||
QString sansExt = fileNameWithoutExtension(originalFileName, possibleExtensions);
|
||||
QString newestFileName = originalFileName;
|
||||
QDateTime newestTime = QDateTime::fromMSecsSinceEpoch(0);
|
||||
foreach (QString possibleExtension, possibleExtensions) {
|
||||
QString fileName = sansExt + "." + possibleExtension;
|
||||
QFileInfo fileInfo(fileName);
|
||||
if (fileInfo.exists() && fileInfo.lastModified() > newestTime) {
|
||||
newestFileName = fileName;
|
||||
newestTime = fileInfo.lastModified();
|
||||
}
|
||||
}
|
||||
return newestFileName;
|
||||
}
|
||||
|
|
|
@ -19,4 +19,7 @@ namespace PathUtils {
|
|||
QString& resourcesPath();
|
||||
}
|
||||
|
||||
#endif // hifi_PathUtils_h
|
||||
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions);
|
||||
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions);
|
||||
|
||||
#endif // hifi_PathUtils_h
|
||||
|
|
48
libraries/shared/src/QVariantGLM.cpp
Normal file
48
libraries/shared/src/QVariantGLM.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// QVariantGLM.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Seth Alves on 3/9/15.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
#include "OctalCode.h"
|
||||
|
||||
QVariantList glmToQList(const glm::vec3& g) {
|
||||
return QVariantList() << g[0] << g[1] << g[2];
|
||||
}
|
||||
|
||||
QVariantList glmToQList(const glm::quat& g) {
|
||||
return QVariantList() << g.x << g.y << g.z << g.w;
|
||||
}
|
||||
|
||||
QVariantList rgbColorToQList(rgbColor& v) {
|
||||
return QVariantList() << (int)(v[0]) << (int)(v[1]) << (int)(v[2]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::vec3 qListToGlmVec3(const QVariant q) {
|
||||
QVariantList qList = q.toList();
|
||||
return glm::vec3(qList[RED_INDEX].toFloat(), qList[GREEN_INDEX].toFloat(), qList[BLUE_INDEX].toFloat());
|
||||
}
|
||||
|
||||
glm::quat qListToGlmQuat(const QVariant q) {
|
||||
QVariantList qList = q.toList();
|
||||
float x = qList[0].toFloat();
|
||||
float y = qList[1].toFloat();
|
||||
float z = qList[2].toFloat();
|
||||
float w = qList[3].toFloat();
|
||||
return glm::quat(w, x, y, z);
|
||||
}
|
||||
|
||||
void qListtoRgbColor(const QVariant q, rgbColor returnValue) {
|
||||
QVariantList qList = q.toList();
|
||||
returnValue[RED_INDEX] = qList[RED_INDEX].toInt();
|
||||
returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt();
|
||||
returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt();
|
||||
}
|
26
libraries/shared/src/QVariantGLM.h
Normal file
26
libraries/shared/src/QVariantGLM.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// QVariantGLM.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Seth Alves on 3/9/15.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include "SharedUtil.h"
|
||||
|
||||
QVariantList glmToQList(const glm::vec3& g);
|
||||
QVariantList glmToQList(const glm::quat& g);
|
||||
QVariantList rgbColorToQList(rgbColor& v);
|
||||
|
||||
glm::vec3 qListToGlmVec3(const QVariant q);
|
||||
glm::quat qListToGlmQuat(const QVariant q);
|
||||
void qListtoRgbColor(const QVariant q, rgbColor returnValue);
|
47
libraries/shared/src/VariantMapToScriptValue.cpp
Normal file
47
libraries/shared/src/VariantMapToScriptValue.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// VariantMapToScriptValue.cpp
|
||||
// libraries/shared/src/
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/6/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDebug>
|
||||
#include "VariantMapToScriptValue.h"
|
||||
|
||||
QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine) {
|
||||
QScriptValue scriptValue = scriptEngine.newObject();
|
||||
|
||||
for (QVariantMap::const_iterator iter = variantMap.begin(); iter != variantMap.end(); ++iter) {
|
||||
QString key = iter.key();
|
||||
QVariant qValue = iter.value();
|
||||
|
||||
switch(qValue.type()) {
|
||||
case QVariant::Bool:
|
||||
scriptValue.setProperty(key, qValue.toBool());
|
||||
break;
|
||||
case QVariant::Int:
|
||||
scriptValue.setProperty(key, qValue.toInt());
|
||||
break;
|
||||
case QVariant::Double:
|
||||
scriptValue.setProperty(key, qValue.toDouble());
|
||||
break;
|
||||
case QVariant::String: {
|
||||
scriptValue.setProperty(key, scriptEngine.newVariant(qValue));
|
||||
break;
|
||||
}
|
||||
case QVariant::Map: {
|
||||
QVariantMap childMap = qValue.toMap();
|
||||
scriptValue.setProperty(key, variantMapToScriptValue(childMap, scriptEngine));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qDebug() << "unhandled QScript type" << qValue.type();
|
||||
}
|
||||
}
|
||||
|
||||
return scriptValue;
|
||||
}
|
16
libraries/shared/src/VariantMapToScriptValue.h
Normal file
16
libraries/shared/src/VariantMapToScriptValue.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// VariantMapToScriptValue.h
|
||||
// libraries/shared/src/
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 12/6/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QVariant>
|
||||
#include <QScriptValue>
|
||||
#include <QScriptEngine>
|
||||
|
||||
QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scriptEngine);
|
|
@ -50,15 +50,13 @@ bool writeOBJ(QString outFileName, QVector<QVector<VHACD::IVHACD::ConvexHull>>&
|
|||
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
||||
out << "v ";
|
||||
out << formatFloat(hull.m_points[i*3]) << " ";
|
||||
// swap y and z because up is 3rd value in OBJ
|
||||
out << formatFloat(hull.m_points[i*3+2]) << " ";
|
||||
out << formatFloat(hull.m_points[i*3+1]) << "\n";
|
||||
out << formatFloat(hull.m_points[i*3+1]) << " ";
|
||||
out << formatFloat(hull.m_points[i*3+2]) << "\n";
|
||||
}
|
||||
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
|
||||
out << "f ";
|
||||
// change order to flip normal (due to swapping y and z, above)
|
||||
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
|
||||
out << hull.m_triangles[i*3] + 1 + pointStartOffset << " ";
|
||||
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
|
||||
out << hull.m_triangles[i*3+2] + 1 + pointStartOffset << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
|
|
Loading…
Reference in a new issue