From c246205692e94c4e4500788baabc66a0c3986d06 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 15:04:45 -0700 Subject: [PATCH 01/72] Base implementation of ArrayBuffer --- libraries/script-engine/src/ArrayBuffer.cpp | 105 ++++++++++++++++++ libraries/script-engine/src/ArrayBuffer.h | 56 ++++++++++ .../src/ArrayBufferPrototype.cpp | 29 +++++ .../script-engine/src/ArrayBufferPrototype.h | 32 ++++++ 4 files changed, 222 insertions(+) create mode 100644 libraries/script-engine/src/ArrayBuffer.cpp create mode 100644 libraries/script-engine/src/ArrayBuffer.h create mode 100644 libraries/script-engine/src/ArrayBufferPrototype.cpp create mode 100644 libraries/script-engine/src/ArrayBufferPrototype.h diff --git a/libraries/script-engine/src/ArrayBuffer.cpp b/libraries/script-engine/src/ArrayBuffer.cpp new file mode 100644 index 0000000000..561a8cd5bf --- /dev/null +++ b/libraries/script-engine/src/ArrayBuffer.cpp @@ -0,0 +1,105 @@ +// +// ArrayBuffer.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 "ArrayBufferPrototype.h" + +#include "ArrayBuffer.h" + +Q_DECLARE_METATYPE(QByteArray*) +Q_DECLARE_METATYPE(ArrayBuffer*) + +ArrayBuffer::ArrayBuffer(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { + qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); + + _byteLength = engine->toStringHandle(QLatin1String("byteLength")); + +// _proto = engine->newQObject(new ArrayBufferPrototype(this), +// QScriptEngine::QtOwnership, +// QScriptEngine::SkipMethodsInEnumeration +// | QScriptEngine::ExcludeSuperClassMethods +// | QScriptEngine::ExcludeSuperClassProperties); + QScriptValue global = engine->globalObject(); + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); +} + +QScriptValue ArrayBuffer::newInstance(unsigned long size) { + engine()->reportAdditionalMemoryCost(size); + QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBuffer::newInstance(const QByteArray& ba) { + QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBuffer::construct(QScriptContext* ctx, QScriptEngine* eng) { + ArrayBuffer* cls = qscriptvalue_cast(ctx->callee().data()); + QScriptValue arg = ctx->argument(0); + int size = arg.toInt32(); + return cls ? cls->newInstance(size) : QScriptValue(); +} + +QScriptClass::QueryFlags ArrayBuffer::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + // if the property queried is byteLength, only handle read access + return flags &= HandlesReadAccess; + } + return 0; // No access +} + +QScriptValue ArrayBuffer::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + return ba->length(); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBuffer::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString ArrayBuffer::name() const { + return QLatin1String("ArrayBuffer"); +} + +QScriptValue ArrayBuffer::prototype() const { + return _proto; +} + +QScriptValue ArrayBuffer::constructor() const { + return _ctor; +} + +QScriptValue ArrayBuffer::toScriptValue(QScriptEngine* eng, const QByteArray& ba) +{ + QScriptValue ctor = eng->globalObject().property("ArrayBuffer"); + ArrayBuffer *cls = qscriptvalue_cast(ctor.data()); + if (!cls) { + return eng->newVariant(QVariant::fromValue(ba)); + } + return cls->newInstance(ba); +} + +void ArrayBuffer::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { + ba = qvariant_cast(obj.data().toVariant()); +} + diff --git a/libraries/script-engine/src/ArrayBuffer.h b/libraries/script-engine/src/ArrayBuffer.h new file mode 100644 index 0000000000..f951a1248a --- /dev/null +++ b/libraries/script-engine/src/ArrayBuffer.h @@ -0,0 +1,56 @@ +// +// ArrayBuffer.h +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 +// + +#ifndef hifi_ArrayBuffer_h +#define hifi_ArrayBuffer_h + +#include +#include +#include +#include +#include +#include +#include + +class ArrayBuffer : public QObject, public QScriptClass { +public: + ArrayBuffer(QScriptEngine* engine); + QScriptValue newInstance(unsigned long size = 0); + QScriptValue newInstance(const QByteArray& ba); + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue& object, + const QScriptString& name, + uint id); + QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, + uint id); + + QString name() const; + QScriptValue prototype() const; + QScriptValue constructor() const; + +private: + static QScriptValue construct(QScriptContext* ctx, QScriptEngine* eng); + + static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); + static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); + + QScriptValue _proto; + QScriptValue _ctor; + + // JS Object attributes + QScriptString _byteLength; +}; + +#endif // hifi_ArrayBuffer_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp new file mode 100644 index 0000000000..01736cc511 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -0,0 +1,29 @@ +// +// ArrayBufferPrototype.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 + +#include "ArrayBufferPrototype.h" + +ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray ArrayBufferPrototype::slice(long begin, long end) const { + if (end == -1) { + return thisArrayBuffer().mid(begin); + } else { + return thisArrayBuffer().mid(begin, end); + } +} + +QByteArray ArrayBufferPrototype::thisArrayBuffer() const { + return qscriptvalue_cast(thisObject().data()); +} diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h new file mode 100644 index 0000000000..829fb8e3ec --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -0,0 +1,32 @@ +// +// ArrayBufferPrototype.h +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 +// + +#ifndef hifi_ArrayBufferPrototype_h +#define hifi_ArrayBufferPrototype_h + +#include +#include +#include +#include + +class ArrayBufferPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + ArrayBufferPrototype(QObject* parent = NULL); + +public slots: + QByteArray slice(long begin, long end = -1) const; + +private: + QByteArray thisArrayBuffer() const; +}; + +#endif // hifi_ArrayBufferPrototype_h \ No newline at end of file From f9b739a0d1500864495f2da7f6066abcd016b8c4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:02:33 -0700 Subject: [PATCH 02/72] Functional ArrayBuffer class following Qt standard --- libraries/script-engine/src/ArrayBuffer.cpp | 105 ---------------- .../script-engine/src/ArrayBufferClass.cpp | 119 ++++++++++++++++++ .../src/{ArrayBuffer.h => ArrayBufferClass.h} | 27 ++-- .../src/ArrayBufferPrototype.cpp | 14 +-- .../script-engine/src/ArrayBufferPrototype.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 3 + 6 files changed, 143 insertions(+), 127 deletions(-) delete mode 100644 libraries/script-engine/src/ArrayBuffer.cpp create mode 100644 libraries/script-engine/src/ArrayBufferClass.cpp rename libraries/script-engine/src/{ArrayBuffer.h => ArrayBufferClass.h} (67%) diff --git a/libraries/script-engine/src/ArrayBuffer.cpp b/libraries/script-engine/src/ArrayBuffer.cpp deleted file mode 100644 index 561a8cd5bf..0000000000 --- a/libraries/script-engine/src/ArrayBuffer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// -// ArrayBuffer.cpp -// -// -// Created by Clement on 7/3/14. -// Copyright 2014 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 "ArrayBufferPrototype.h" - -#include "ArrayBuffer.h" - -Q_DECLARE_METATYPE(QByteArray*) -Q_DECLARE_METATYPE(ArrayBuffer*) - -ArrayBuffer::ArrayBuffer(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { - qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); - - _byteLength = engine->toStringHandle(QLatin1String("byteLength")); - -// _proto = engine->newQObject(new ArrayBufferPrototype(this), -// QScriptEngine::QtOwnership, -// QScriptEngine::SkipMethodsInEnumeration -// | QScriptEngine::ExcludeSuperClassMethods -// | QScriptEngine::ExcludeSuperClassProperties); - QScriptValue global = engine->globalObject(); - _proto.setPrototype(global.property("Object").property("prototype")); - - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); -} - -QScriptValue ArrayBuffer::newInstance(unsigned long size) { - engine()->reportAdditionalMemoryCost(size); - QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBuffer::newInstance(const QByteArray& ba) { - QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); - return engine()->newObject(this, data); -} - -QScriptValue ArrayBuffer::construct(QScriptContext* ctx, QScriptEngine* eng) { - ArrayBuffer* cls = qscriptvalue_cast(ctx->callee().data()); - QScriptValue arg = ctx->argument(0); - int size = arg.toInt32(); - return cls ? cls->newInstance(size) : QScriptValue(); -} - -QScriptClass::QueryFlags ArrayBuffer::queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - // if the property queried is byteLength, only handle read access - return flags &= HandlesReadAccess; - } - return 0; // No access -} - -QScriptValue ArrayBuffer::property(const QScriptValue &object, - const QScriptString &name, uint id) { - QByteArray* ba = qscriptvalue_cast(object.data()); - if (ba && name == _byteLength) { - return ba->length(); - } - return QScriptValue(); -} - -QScriptValue::PropertyFlags ArrayBuffer::propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id) { - return QScriptValue::Undeletable; -} - -QString ArrayBuffer::name() const { - return QLatin1String("ArrayBuffer"); -} - -QScriptValue ArrayBuffer::prototype() const { - return _proto; -} - -QScriptValue ArrayBuffer::constructor() const { - return _ctor; -} - -QScriptValue ArrayBuffer::toScriptValue(QScriptEngine* eng, const QByteArray& ba) -{ - QScriptValue ctor = eng->globalObject().property("ArrayBuffer"); - ArrayBuffer *cls = qscriptvalue_cast(ctor.data()); - if (!cls) { - return eng->newVariant(QVariant::fromValue(ba)); - } - return cls->newInstance(ba); -} - -void ArrayBuffer::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { - ba = qvariant_cast(obj.data().toVariant()); -} - diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp new file mode 100644 index 0000000000..099e77b485 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -0,0 +1,119 @@ +// +// ArrayBufferClass.cpp +// +// +// Created by Clement on 7/3/14. +// Copyright 2014 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 "ArrayBufferPrototype.h" + +#include "ArrayBufferClass.h" + +static const QString CLASS_NAME = "ArrayBuffer"; +static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; + +Q_DECLARE_METATYPE(QByteArray*) + +ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { + qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); + QScriptValue global = engine->globalObject(); + + // Save string handles for quick lookup + _name = engine->toStringHandle(CLASS_NAME.toLatin1()); + _byteLength = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + + // build prototype + _proto = engine->newQObject(new ArrayBufferPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); + + engine->globalObject().setProperty(name(), _ctor); +} + +QScriptValue ArrayBufferClass::newInstance(unsigned long size) { + engine()->reportAdditionalMemoryCost(size); + QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { + QScriptValue data = engine()->newVariant(QVariant::fromValue(ba)); + return engine()->newObject(this, data); +} + +QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) { + ArrayBufferClass* cls = qscriptvalue_cast(context->callee().data()); + if (!cls) { + return QScriptValue(); + } + + QScriptValue arg = context->argument(0); + unsigned long size = arg.toInt32(); + QScriptValue newObject = cls->newInstance(size); + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + // if the property queried is byteLength, only handle read access + return flags &= HandlesReadAccess; + } + return 0; // No access +} + +QScriptValue ArrayBufferClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* ba = qscriptvalue_cast(object.data()); + if (ba && name == _byteLength) { + return ba->length(); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBufferClass::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString ArrayBufferClass::name() const { + return _name.toString(); +} + +QScriptValue ArrayBufferClass::prototype() const { + return _proto; +} + +QScriptValue ArrayBufferClass::toScriptValue(QScriptEngine* engine, const QByteArray& ba) { + QScriptValue ctor = engine->globalObject().property(CLASS_NAME); + ArrayBufferClass* cls = qscriptvalue_cast(ctor.data()); + if (!cls) { + return engine->newVariant(QVariant::fromValue(ba)); + } + return cls->newInstance(ba); +} + +void ArrayBufferClass::fromScriptValue(const QScriptValue& obj, QByteArray& ba) { + ba = qvariant_cast(obj.data().toVariant()); +} + diff --git a/libraries/script-engine/src/ArrayBuffer.h b/libraries/script-engine/src/ArrayBufferClass.h similarity index 67% rename from libraries/script-engine/src/ArrayBuffer.h rename to libraries/script-engine/src/ArrayBufferClass.h index f951a1248a..b106d27934 100644 --- a/libraries/script-engine/src/ArrayBuffer.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -1,5 +1,5 @@ // -// ArrayBuffer.h +// ArrayBufferClass.h // // // Created by Clement on 7/3/14. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_ArrayBuffer_h -#define hifi_ArrayBuffer_h +#ifndef hifi_ArrayBufferClass_h +#define hifi_ArrayBufferClass_h #include #include @@ -20,28 +20,26 @@ #include #include -class ArrayBuffer : public QObject, public QScriptClass { +class ArrayBufferClass : public QObject, public QScriptClass { + Q_OBJECT public: - ArrayBuffer(QScriptEngine* engine); - QScriptValue newInstance(unsigned long size = 0); + ArrayBufferClass(QScriptEngine* engine); + QScriptValue newInstance(unsigned long size); QScriptValue newInstance(const QByteArray& ba); QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue& object, - const QScriptString& name, - uint id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, - uint id); + const QScriptString& name, uint id); QString name() const; QScriptValue prototype() const; - QScriptValue constructor() const; private: - static QScriptValue construct(QScriptContext* ctx, QScriptEngine* eng); + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); static QScriptValue toScriptValue(QScriptEngine* eng, const QByteArray& ba); static void fromScriptValue(const QScriptValue& obj, QByteArray& ba); @@ -50,7 +48,8 @@ private: QScriptValue _ctor; // JS Object attributes + QScriptString _name; QScriptString _byteLength; }; -#endif // hifi_ArrayBuffer_h \ No newline at end of file +#endif // hifi_ArrayBufferClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 01736cc511..087ab3d177 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -11,19 +11,19 @@ #include +#include "ArrayBufferClass.h" + #include "ArrayBufferPrototype.h" +Q_DECLARE_METATYPE(QByteArray*) + ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } QByteArray ArrayBufferPrototype::slice(long begin, long end) const { - if (end == -1) { - return thisArrayBuffer().mid(begin); - } else { - return thisArrayBuffer().mid(begin, end); - } + return thisArrayBuffer()->mid(begin, end); } -QByteArray ArrayBufferPrototype::thisArrayBuffer() const { - return qscriptvalue_cast(thisObject().data()); +QByteArray* ArrayBufferPrototype::thisArrayBuffer() const { + return qscriptvalue_cast(thisObject().data()); } diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 829fb8e3ec..52caf5c90a 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -26,7 +26,7 @@ public slots: QByteArray slice(long begin, long end = -1) const; private: - QByteArray thisArrayBuffer() const; + QByteArray* thisArrayBuffer() const; }; #endif // hifi_ArrayBufferPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e93a7125b9..fab50784a2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -32,6 +32,7 @@ #include #include "AnimationObject.h" +#include "ArrayBufferClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -211,6 +212,8 @@ void ScriptEngine::init() { return; // only initialize once } + new ArrayBufferClass(&_engine); + _isInitialized = true; _voxelsScriptingInterface.init(); From 96a1390d8f658328158823be0140e216ef5083e7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:58:53 -0700 Subject: [PATCH 03/72] Fixed up ArrayBuffer::slice() behaviour --- .../src/ArrayBufferPrototype.cpp | 25 ++++++++++++++++++- .../script-engine/src/ArrayBufferPrototype.h | 5 ++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 087ab3d177..45810f39c5 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -9,8 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include +#include + #include "ArrayBufferClass.h" #include "ArrayBufferPrototype.h" @@ -21,7 +24,27 @@ ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } QByteArray ArrayBufferPrototype::slice(long begin, long end) const { - return thisArrayBuffer()->mid(begin, end); + QByteArray* ba = thisArrayBuffer(); + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? ba->size() + begin : begin; + end = (end < 0) ? ba->size() + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + end = glm::clamp(end, 0l, (long)(ba->size() - 1)); + + return (end - begin > 0) ? ba->mid(begin, end - begin) : QByteArray(); +} + +QByteArray ArrayBufferPrototype::slice(long begin) const { + QByteArray* ba = thisArrayBuffer(); + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? ba->size() + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + + return ba->mid(begin, -1); } QByteArray* ArrayBufferPrototype::thisArrayBuffer() const { diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 52caf5c90a..6f3c8bbd4d 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -22,8 +22,9 @@ class ArrayBufferPrototype : public QObject, public QScriptable { public: ArrayBufferPrototype(QObject* parent = NULL); -public slots: - QByteArray slice(long begin, long end = -1) const; + public slots: + QByteArray slice(long begin, long end) const; + QByteArray slice(long begin) const; private: QByteArray* thisArrayBuffer() const; From 05d74e2bb1988a87c5d5170a661015ddc8ecc6db Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 7 Jul 2014 18:59:57 -0700 Subject: [PATCH 04/72] Added ArrayBuffer unit tests --- examples/typedArraysUnitTest.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 examples/typedArraysUnitTest.js diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js new file mode 100644 index 0000000000..98f69f3e71 --- /dev/null +++ b/examples/typedArraysUnitTest.js @@ -0,0 +1,22 @@ +// +// typedArraysunitTest.js +// examples +// +// Created by Clément Brisset on 7/7/14 +// Copyright 2014 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 +// + +Script.include("Test.js"); + +test("ArrayBuffer", function(finished) { + this.assertEquals(new ArrayBuffer().byteLength, 0, 'no length'); + + this.assertEquals(typeof(new ArrayBuffer(0)), 'object', 'creation'); + this.assertEquals(typeof(new ArrayBuffer(1)), 'object', 'creation'); + this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); + + this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); +}); \ No newline at end of file From 1f6e1b9509635a49f3d5de90088a8e62484b73d7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:08:11 -0700 Subject: [PATCH 05/72] Modifications and improvments of the ArrayBuffers --- libraries/script-engine/src/ArrayBufferClass.cpp | 9 +++++++-- libraries/script-engine/src/ArrayBufferClass.h | 2 +- .../script-engine/src/ArrayBufferPrototype.cpp | 13 +++++-------- libraries/script-engine/src/ArrayBufferPrototype.h | 8 +++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index 099e77b485..d003125c1e 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -42,7 +42,7 @@ ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QSc engine->globalObject().setProperty(name(), _ctor); } -QScriptValue ArrayBufferClass::newInstance(unsigned long size) { +QScriptValue ArrayBufferClass::newInstance(quint32 size) { engine()->reportAdditionalMemoryCost(size); QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); return engine()->newObject(this, data); @@ -60,7 +60,12 @@ QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* } QScriptValue arg = context->argument(0); - unsigned long size = arg.toInt32(); + + if (!arg.isValid() || !arg.isNumber()) { + return QScriptValue(); + } + + quint32 size = arg.toInt32(); QScriptValue newObject = cls->newInstance(size); if (context->isCalledAsConstructor()) { diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index b106d27934..2f863e77a5 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -24,7 +24,7 @@ class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferClass(QScriptEngine* engine); - QScriptValue newInstance(unsigned long size); + QScriptValue newInstance(quint32 size); QScriptValue newInstance(const QByteArray& ba); QueryFlags queryProperty(const QScriptValue& object, diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 45810f39c5..37db5f3eec 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -9,9 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include - #include #include "ArrayBufferClass.h" @@ -23,26 +20,26 @@ Q_DECLARE_METATYPE(QByteArray*) ArrayBufferPrototype::ArrayBufferPrototype(QObject* parent) : QObject(parent) { } -QByteArray ArrayBufferPrototype::slice(long begin, long end) const { +QByteArray ArrayBufferPrototype::slice(qint32 begin, qint32 end) const { QByteArray* ba = thisArrayBuffer(); // if indices < 0 then they start from the end of the array begin = (begin < 0) ? ba->size() + begin : begin; end = (end < 0) ? ba->size() + end : end; // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); - end = glm::clamp(end, 0l, (long)(ba->size() - 1)); + begin = glm::clamp(begin, 0, (ba->size() - 1)); + end = glm::clamp(end, 0, (ba->size() - 1)); return (end - begin > 0) ? ba->mid(begin, end - begin) : QByteArray(); } -QByteArray ArrayBufferPrototype::slice(long begin) const { +QByteArray ArrayBufferPrototype::slice(qint32 begin) const { QByteArray* ba = thisArrayBuffer(); // if indices < 0 then they start from the end of the array begin = (begin < 0) ? ba->size() + begin : begin; // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0l, (long)(ba->size() - 1)); + begin = glm::clamp(begin, 0, (ba->size() - 1)); return ba->mid(begin, -1); } diff --git a/libraries/script-engine/src/ArrayBufferPrototype.h b/libraries/script-engine/src/ArrayBufferPrototype.h index 6f3c8bbd4d..09d4596f28 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.h +++ b/libraries/script-engine/src/ArrayBufferPrototype.h @@ -12,19 +12,17 @@ #ifndef hifi_ArrayBufferPrototype_h #define hifi_ArrayBufferPrototype_h -#include #include #include -#include class ArrayBufferPrototype : public QObject, public QScriptable { Q_OBJECT public: ArrayBufferPrototype(QObject* parent = NULL); - public slots: - QByteArray slice(long begin, long end) const; - QByteArray slice(long begin) const; +public slots: + QByteArray slice(qint32 begin, qint32 end) const; + QByteArray slice(qint32 begin) const; private: QByteArray* thisArrayBuffer() const; From 13fe8b579fc1eeed993a44731b50e29cc497538c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:09:06 -0700 Subject: [PATCH 06/72] Added ArrayBufferView scriptable objects --- .../src/ArrayBufferViewClass.cpp | 103 ++++++++++++++++++ .../script-engine/src/ArrayBufferViewClass.h | 47 ++++++++ 2 files changed, 150 insertions(+) create mode 100644 libraries/script-engine/src/ArrayBufferViewClass.cpp create mode 100644 libraries/script-engine/src/ArrayBufferViewClass.h diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp new file mode 100644 index 0000000000..73d833dab4 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -0,0 +1,103 @@ +// +// ArrayBufferViewClass.cpp +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 "ArrayBufferViewClass.h" + +Q_DECLARE_METATYPE(QByteArray*) + +ArrayBufferViewClass::ArrayBufferViewClass(QScriptEngine* engine) : +QObject(engine), +QScriptClass(engine) { + // Save string handles for quick lookup + _bufferName = engine->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); + _byteOffsetName = engine->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); + _byteLengthName = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); +} + +QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + if (name == _bufferName || name == _byteOffsetName || name == _byteLengthName) { + return flags &= HandlesReadAccess; // Only keep read access flags + } + return 0; // No access +} + +QScriptValue ArrayBufferViewClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + if (name == _bufferName) { + return object.data().property(_bufferName); + } + if (name == _byteOffsetName) { + return object.data().property(_byteOffsetName); + } + if (name == _byteLengthName) { + return object.data().property(_byteLengthName); + } + return QScriptValue(); +} + +QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +//QScriptClass::QueryFlags DataViewClass::queryProperty(const QScriptValue& object, +// const QScriptString& name, +// QueryFlags flags, uint* id) { +// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); +// bool ok = false; +// int pos = name.toArrayIndex(&ok); +// +// // Check that name is a valid index and arrayBuffer exists +// if (ok && arrayBuffer && pos > 0 && pos < arrayBuffer->size()) { +// *id = pos; // save pos to avoid recomputation +// return HandlesReadAccess | HandlesWriteAccess; // Read/Write access +// } +// +// return ArrayBufferViewClass::queryProperty(object, name, flags, id); +//} +// +//QScriptValue DataViewClass::property(const QScriptValue &object, +// const QScriptString &name, uint id) { +// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); +// bool ok = false; +// name.toArrayIndex(&ok); +// +// if (ok && arrayBuffer) { +// return (*arrayBuffer)[id]; +// } +// +// return ArrayBufferViewClass::queryProperty(object, name, flags, id); +//} +// +//void DataViewClass::setProperty(QScriptValue &object, +// const QScriptString &name, +// uint id, const QScriptValue &value) { +// QByteArray *ba = qscriptvalue_cast(object.data()); +// if (!ba) +// return; +// if (name == length) { +// resize(*ba, value.toInt32()); +// } else { +// qint32 pos = id; +// if (pos < 0) +// return; +// if (ba->size() <= pos) +// resize(*ba, pos + 1); +// (*ba)[pos] = char(value.toInt32()); +// } +//} +// +//QScriptValue::PropertyFlags DataViewClass::propertyFlags(const QScriptValue& object, +// const QScriptString& name, uint id) { +// return QScriptValue::Undeletable; +//} diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h new file mode 100644 index 0000000000..eb4a372843 --- /dev/null +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -0,0 +1,47 @@ +// +// ArrayBufferViewClass.h +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 +// + +#ifndef hifi_ArrayBufferViewClass_h +#define hifi_ArrayBufferViewClass_h + +#include +#include +#include +#include +#include +#include +#include + +static const QString BUFFER_PROPERTY_NAME = "buffer"; +static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset"; +static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; + +class ArrayBufferViewClass : public QObject, public QScriptClass { + Q_OBJECT +public: + ArrayBufferViewClass(QScriptEngine* engine); + + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id); +protected: + // JS Object attributes + QScriptString _bufferName; + QScriptString _byteOffsetName; + QScriptString _byteLengthName; +}; + +#endif // hifi_ArrayBufferViewClass_h \ No newline at end of file From 2e480764088cba80f67dbc9fa7f73a86f0d116cc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:09:45 -0700 Subject: [PATCH 07/72] Added first draft of DataView scriptable objects --- libraries/script-engine/src/DataViewClass.cpp | 81 +++++++ libraries/script-engine/src/DataViewClass.h | 36 +++ .../script-engine/src/DataViewPrototype.cpp | 209 ++++++++++++++++++ .../script-engine/src/DataViewPrototype.h | 68 ++++++ 4 files changed, 394 insertions(+) create mode 100644 libraries/script-engine/src/DataViewClass.cpp create mode 100644 libraries/script-engine/src/DataViewClass.h create mode 100644 libraries/script-engine/src/DataViewPrototype.cpp create mode 100644 libraries/script-engine/src/DataViewPrototype.h diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp new file mode 100644 index 0000000000..7afd43e71c --- /dev/null +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -0,0 +1,81 @@ +// +// DataViewClass.cpp +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 "DataViewPrototype.h" + +#include "DataViewClass.h" + +Q_DECLARE_METATYPE(QByteArray*) + +static const QString DATA_VIEW_NAME = "DataView"; + +DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engine) { + QScriptValue global = engine->globalObject(); + + // Save string handles for quick lookup + _name = engine->toStringHandle(DATA_VIEW_NAME.toLatin1()); + + // build prototype + _proto = engine->newQObject(new DataViewPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine->newFunction(construct, _proto); + _ctor.setData(engine->toScriptValue(this)); + + engine->globalObject().setProperty(name(), _ctor); +} + +QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, byteLentgh); + + return engine()->newObject(this, data); +} + +QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *engine) { + DataViewClass* cls = qscriptvalue_cast(context->callee().data()); + if (!cls || context->argumentCount() < 1) { + return QScriptValue(); + } + + QScriptValue bufferArg = context->argument(0); + QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(-1); + QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(-1); + + QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast(bufferArg.data()) :NULL; + if (!arrayBuffer) { + return QScriptValue(); + } + + QScriptValue newObject = cls->newInstance(bufferArg, byteOffsetArg.toInt32(), byteLengthArg.toInt32()); + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QString DataViewClass::name() const { + return _name.toString(); +} + +QScriptValue DataViewClass::prototype() const { + return _proto; +} \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h new file mode 100644 index 0000000000..7e4d427f09 --- /dev/null +++ b/libraries/script-engine/src/DataViewClass.h @@ -0,0 +1,36 @@ +// +// DataViewClass.h +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 +// + +#ifndef hifi_DataViewClass_h +#define hifi_DataViewClass_h + +#include "ArrayBufferViewClass.h" + +class DataViewClass : public ArrayBufferViewClass { + Q_OBJECT +public: + DataViewClass(QScriptEngine* engine); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset = -1, quint32 byteLength = -1); + + QString name() const; + QScriptValue prototype() const; + +private: + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + + QScriptValue _proto; + QScriptValue _ctor; + + QScriptString _name; +}; + + +#endif // hifi_DataViewClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp new file mode 100644 index 0000000000..345df743b4 --- /dev/null +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -0,0 +1,209 @@ +// +// DataViewPrototype.cpp +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 + +#include + +#include "DataViewClass.h" + +#include "DataViewPrototype.h" + +Q_DECLARE_METATYPE(QByteArray*) + +DataViewPrototype::DataViewPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray* DataViewPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { + quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); + quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); + qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; + qDebug() << "Offset: " << offset << ", Size: " << size; + offset += viewOffset; + qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); + return (offset + size) <= viewOffset + viewLength; +} + +///////////////// GETTERS //////////////////////////// + +qint8 DataViewPrototype::getInt8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(qint8))) { + qDebug() << "Value: " << (qint8)thisArrayBuffer()->at(byteOffset); + + return (qint8)thisArrayBuffer()->at(byteOffset); + } + qDebug() << "42 powaaaa!!!"; + return 42; +} + +quint8 DataViewPrototype::getUint8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(quint8))) { return thisArrayBuffer()->at(byteOffset); + } + return 42; +} + +qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint16))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +quint16 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint16))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint32))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint32))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(float))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { + if (realOffset(byteOffset, sizeof(double))) { + QDataStream stream(*thisArrayBuffer()); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.skipRawData(byteOffset); + + qint16 result; + stream >> result; + return result; + } + return 42; +} + +///////////////// SETTERS //////////////////////////// + +void DataViewPrototype::setInt8(quint32 byteOffset, qint8 value) { + if (realOffset(byteOffset, sizeof(qint8))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint8(quint32 byteOffset, quint8 value) { + if (realOffset(byteOffset, sizeof(quint8))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setInt16(quint32 byteOffset, qint16 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint16))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint16(quint32 byteOffset, quint16 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint16))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setInt32(quint32 byteOffset, quint32 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(qint32))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(quint32))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(float))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + +void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool littleEndian) { + if (realOffset(byteOffset, sizeof(double))) { + QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); + stream.skipRawData(byteOffset); + + stream << value; + } +} + + diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h new file mode 100644 index 0000000000..a024e8938f --- /dev/null +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -0,0 +1,68 @@ +// +// DataViewPrototype.h +// +// +// Created by Clement on 7/8/14. +// Copyright 2014 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 +// + +#ifndef hifi_DataViewPrototype_h +#define hifi_DataViewPrototype_h + +#include +#include + +class DataViewPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + DataViewPrototype(QObject* parent = NULL); + +public slots: + // Gets the value of the given type at the specified byte offset + // from the start of the view. There is no alignment constraint; + // multi-byte values may be fetched from any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether a big-endian or little-endian value should be + // read. If false or undefined, a big-endian value is read. + // + // These methods raise an exception if they would read + // beyond the end of the view. + qint8 getInt8(quint32 byteOffset); + quint8 getUint8(quint32 byteOffset); + qint16 getInt16(quint32 byteOffset, bool littleEndian = false); + quint16 getUint16(quint32 byteOffset, bool littleEndian = false); + qint32 getInt32(quint32 byteOffset, bool littleEndian = false); + quint32 getUint32(quint32 byteOffset, bool littleEndian = false); + float getFloat32(quint32 byteOffset, bool littleEndian = false); + double getFloat64(quint32 byteOffset, bool littleEndian = false); + + // Stores a value of the given type at the specified byte offset + // from the start of the view. There is no alignment constraint; + // multi-byte values may be stored at any offset. + // + // For multi-byte values, the optional littleEndian argument + // indicates whether the value should be stored in big-endian or + // little-endian byte order. If false or undefined, the value is + // stored in big-endian byte order. + // + // These methods raise an exception if they would write + // beyond the end of the view. + void setInt8(quint32 byteOffset, qint8 value); + void setUint8(quint32 byteOffset, quint8 value); + void setInt16(quint32 byteOffset, qint16 value, bool littleEndian = false); + void setUint16(quint32 byteOffset, quint16 value, bool littleEndian = false); + void setInt32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); + void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); + +private: + QByteArray* thisArrayBuffer() const; + bool realOffset(quint32& offset, size_t size) const; +}; + +#endif // hifi_DataViewPrototype_h \ No newline at end of file From 7fe88a4426cb609757733a7cbf087b4608e71620 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:10:22 -0700 Subject: [PATCH 08/72] Hooked up ArrayBuffer, ArrayBufferView and DataView to ScriptEngine --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fab50784a2..59a9563c56 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -33,6 +33,8 @@ #include "AnimationObject.h" #include "ArrayBufferClass.h" +#include "ArrayBufferViewClass.h" +#include "DataViewClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" @@ -213,6 +215,8 @@ void ScriptEngine::init() { } new ArrayBufferClass(&_engine); + new ArrayBufferViewClass(&_engine); + new DataViewClass(&_engine); _isInitialized = true; From 4b2e4b7b28a35a33f0dc167225047e6efbf195b5 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 8 Jul 2014 16:59:27 -0700 Subject: [PATCH 09/72] Some type modifications in DataViewPrototype --- .../script-engine/src/DataViewPrototype.cpp | 66 +++++++++++-------- .../script-engine/src/DataViewPrototype.h | 18 ++--- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 345df743b4..2965f98809 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -38,23 +38,31 @@ bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { ///////////////// GETTERS //////////////////////////// -qint8 DataViewPrototype::getInt8(quint32 byteOffset) { +qint32 DataViewPrototype::getInt8(quint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { - qDebug() << "Value: " << (qint8)thisArrayBuffer()->at(byteOffset); - - return (qint8)thisArrayBuffer()->at(byteOffset); - } - qDebug() << "42 powaaaa!!!"; - return 42; -} - -quint8 DataViewPrototype::getUint8(quint32 byteOffset) { - if (realOffset(byteOffset, sizeof(quint8))) { return thisArrayBuffer()->at(byteOffset); + QDataStream stream(*thisArrayBuffer()); + stream.skipRawData(byteOffset); + + qint8 result; + stream >> result; + return result; } return 42; } -qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint8(quint32 byteOffset) { + if (realOffset(byteOffset, sizeof(quint8))) { + QDataStream stream(*thisArrayBuffer()); + stream.skipRawData(byteOffset); + + quint8 result; + stream >> result; + return result; + } + return 42; +} + +qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); @@ -67,13 +75,13 @@ qint16 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { return 42; } -quint16 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + quint16 result; stream >> result; return result; } @@ -86,7 +94,7 @@ qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + qint32 result; stream >> result; return result; } @@ -99,7 +107,7 @@ quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + quint32 result; stream >> result; return result; } @@ -112,7 +120,7 @@ float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + float result; stream >> result; return result; } @@ -125,7 +133,7 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); - qint16 result; + double result; stream >> result; return result; } @@ -134,48 +142,48 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { ///////////////// SETTERS //////////////////////////// -void DataViewPrototype::setInt8(quint32 byteOffset, qint8 value) { +void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint8)value; } } -void DataViewPrototype::setUint8(quint32 byteOffset, quint8 value) { +void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint8)value; } } -void DataViewPrototype::setInt16(quint32 byteOffset, qint16 value, bool littleEndian) { +void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint16)value; } } -void DataViewPrototype::setUint16(quint32 byteOffset, quint16 value, bool littleEndian) { +void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint16)value; } } -void DataViewPrototype::setInt32(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (qint32)value; } } @@ -184,7 +192,7 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); - stream << value; + stream << (quint32)value; } } diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index a024e8938f..97c94c58ce 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -31,10 +31,10 @@ public slots: // // These methods raise an exception if they would read // beyond the end of the view. - qint8 getInt8(quint32 byteOffset); - quint8 getUint8(quint32 byteOffset); - qint16 getInt16(quint32 byteOffset, bool littleEndian = false); - quint16 getUint16(quint32 byteOffset, bool littleEndian = false); + qint32 getInt8(quint32 byteOffset); + quint32 getUint8(quint32 byteOffset); + qint32 getInt16(quint32 byteOffset, bool littleEndian = false); + quint32 getUint16(quint32 byteOffset, bool littleEndian = false); qint32 getInt32(quint32 byteOffset, bool littleEndian = false); quint32 getUint32(quint32 byteOffset, bool littleEndian = false); float getFloat32(quint32 byteOffset, bool littleEndian = false); @@ -51,11 +51,11 @@ public slots: // // These methods raise an exception if they would write // beyond the end of the view. - void setInt8(quint32 byteOffset, qint8 value); - void setUint8(quint32 byteOffset, quint8 value); - void setInt16(quint32 byteOffset, qint16 value, bool littleEndian = false); - void setUint16(quint32 byteOffset, quint16 value, bool littleEndian = false); - void setInt32(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt8(quint32 byteOffset, qint32 value); + void setUint8(quint32 byteOffset, quint32 value); + void setInt16(quint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint16(quint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt32(quint32 byteOffset, qint32 value, bool littleEndian = false); void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); From 28438d66b1a5e31f25b085cf6bda2fc6fbeefe93 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 10:27:23 -0700 Subject: [PATCH 10/72] Fixed exceptions/endian issues with DataViews --- libraries/script-engine/src/DataViewClass.cpp | 23 ++++++- libraries/script-engine/src/DataViewClass.h | 2 +- .../script-engine/src/DataViewPrototype.cpp | 64 ++++++++++++++----- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 7afd43e71c..38a85e55a8 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -54,15 +54,32 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en } QScriptValue bufferArg = context->argument(0); - QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(-1); - QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(-1); + QScriptValue byteOffsetArg = (context->argumentCount() >= 2) ? context->argument(1) : QScriptValue(); + QScriptValue byteLengthArg = (context->argumentCount() >= 3) ? context->argument(2) : QScriptValue(); QByteArray* arrayBuffer = (bufferArg.isValid()) ? qscriptvalue_cast(bufferArg.data()) :NULL; if (!arrayBuffer) { + engine->evaluate("throw \"TypeError: 1st argument not a ArrayBuffer\""); return QScriptValue(); } + if (byteOffsetArg.isNumber() && + (byteOffsetArg.toInt32() < 0 || + byteOffsetArg.toInt32() >= arrayBuffer->size())) { + engine->evaluate("throw \"RangeError: byteOffset out of range\""); + return QScriptValue(); + } + if (byteLengthArg.isNumber() && + (byteLengthArg.toInt32() < 0 || + byteOffsetArg.toInt32() + byteLengthArg.toInt32() > arrayBuffer->size())) { + engine->evaluate("throw \"RangeError: byteLength out of range\""); + return QScriptValue(); + } - QScriptValue newObject = cls->newInstance(bufferArg, byteOffsetArg.toInt32(), byteLengthArg.toInt32()); + QScriptValue newObject = cls->newInstance(bufferArg, + (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() + : 0, + (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() + : arrayBuffer->size()); if (context->isCalledAsConstructor()) { context->setThisObject(newObject); diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index 7e4d427f09..6b6d6d25e7 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -18,7 +18,7 @@ class DataViewClass : public ArrayBufferViewClass { Q_OBJECT public: DataViewClass(QScriptEngine* engine); - QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset = -1, quint32 byteLength = -1); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); QString name() const; QScriptValue prototype() const; diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 2965f98809..17734e109c 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -29,10 +29,10 @@ QByteArray* DataViewPrototype::thisArrayBuffer() const { bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); - qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; - qDebug() << "Offset: " << offset << ", Size: " << size; + //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; + //qDebug() << "Offset: " << offset << ", Size: " << size; offset += viewOffset; - qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); + //qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); return (offset + size) <= viewOffset + viewLength; } @@ -47,7 +47,8 @@ qint32 DataViewPrototype::getInt8(quint32 byteOffset) { stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint8(quint32 byteOffset) { @@ -59,85 +60,92 @@ quint32 DataViewPrototype::getUint8(quint32 byteOffset) { stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); qint16 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); quint16 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); qint32 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); quint32 result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); float result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); - stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); double result; stream >> result; return result; } - return 42; + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); + return 0; } ///////////////// SETTERS //////////////////////////// @@ -148,6 +156,8 @@ void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { stream.skipRawData(byteOffset); stream << (qint8)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -157,6 +167,8 @@ void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { stream.skipRawData(byteOffset); stream << (quint8)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -164,8 +176,11 @@ void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEn if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (qint16)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -173,8 +188,11 @@ void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool little if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (quint16)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -182,8 +200,11 @@ void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEn if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (qint32)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -191,8 +212,11 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << (quint32)value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -200,8 +224,11 @@ void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleE if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } @@ -209,8 +236,11 @@ void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool little if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); + stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); stream << value; + } else { + thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); } } From a76ebae771c6bd4e0e5e73e94492b8219d224659 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 10:28:50 -0700 Subject: [PATCH 11/72] Added typed array unit test --- examples/typedArraysUnitTest.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 98f69f3e71..6bd35365e8 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -11,12 +11,31 @@ Script.include("Test.js"); -test("ArrayBuffer", function(finished) { - this.assertEquals(new ArrayBuffer().byteLength, 0, 'no length'); +test('ArrayBuffer', function(finished) { + this.assertEquals(new ArrayBuffer(0).byteLength, 0, 'no length'); this.assertEquals(typeof(new ArrayBuffer(0)), 'object', 'creation'); this.assertEquals(typeof(new ArrayBuffer(1)), 'object', 'creation'); this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); -}); \ No newline at end of file +}); + +test('DataView constructors', function (finished) { + var d = new DataView(new ArrayBuffer(8)); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian'); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); + + // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); +}); From efe443e2ff5b930b10bc8c075152af8e7b286b96 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 9 Jul 2014 14:01:46 -0700 Subject: [PATCH 12/72] Very basic metavoxel save/load. --- .../src/metavoxels/MetavoxelServer.cpp | 59 +++++++++++++++++++ .../src/metavoxels/MetavoxelServer.h | 23 ++++++++ libraries/metavoxels/src/Bitstream.cpp | 6 ++ libraries/metavoxels/src/Bitstream.h | 5 ++ 4 files changed, 93 insertions(+) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 14765e2ddc..928a693527 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -10,6 +10,9 @@ // #include +#include +#include +#include #include @@ -44,6 +47,18 @@ void MetavoxelServer::run() { _lastSend = QDateTime::currentMSecsSinceEpoch(); _sendTimer.start(SEND_INTERVAL); + + // initialize Bitstream before using it in multiple threads + Bitstream::preThreadingInit(); + + // create the persister and start the it in its own thread + _persister = new MetavoxelPersister(this); + QThread* persistenceThread = new QThread(this); + _persister->moveToThread(persistenceThread); + persistenceThread->start(); + + // queue up the load + QMetaObject::invokeMethod(_persister, "load"); } void MetavoxelServer::readPendingDatagrams() { @@ -67,6 +82,12 @@ void MetavoxelServer::readPendingDatagrams() { } } +void MetavoxelServer::aboutToFinish() { + QMetaObject::invokeMethod(_persister, "save", Q_ARG(const MetavoxelData&, _data)); + _persister->thread()->quit(); + _persister->thread()->wait(); +} + void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) { if (node->getType() == NodeType::Agent) { QMutexLocker locker(&node->getMutex()); @@ -193,3 +214,41 @@ void MetavoxelSession::sendPacketGroup(int alreadySent) { _sequencer.endPacket(); } } + +MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) : + _server(server) { +} + +const char* SAVE_FILE = "metavoxels.dat"; + +void MetavoxelPersister::load() { + QFile file(SAVE_FILE); + if (!file.exists()) { + return; + } + QDebug debug = qDebug() << "Reading from" << SAVE_FILE << "..."; + file.open(QIODevice::ReadOnly); + QDataStream inStream(&file); + Bitstream in(inStream); + MetavoxelData data; + try { + in >> data; + } catch (const BitstreamException& e) { + debug << "failed, " << e.getDescription(); + return; + } + QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data)); + debug << "done."; +} + +void MetavoxelPersister::save(const MetavoxelData& data) { + QDebug debug = qDebug() << "Writing to" << SAVE_FILE << "..."; + QSaveFile file(SAVE_FILE); + file.open(QIODevice::WriteOnly); + QDataStream outStream(&file); + Bitstream out(outStream); + out << data; + out.flush(); + file.commit(); + debug << "done."; +} diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 6df769227c..9ed765f65f 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -20,6 +20,7 @@ #include class MetavoxelEditMessage; +class MetavoxelPersister; class MetavoxelSession; /// Maintains a shared metavoxel system, accepting change requests and broadcasting updates. @@ -33,11 +34,15 @@ public: void applyEdit(const MetavoxelEditMessage& edit); const MetavoxelData& getData() const { return _data; } + + Q_INVOKABLE void setData(const MetavoxelData& data) { _data = data; } virtual void run(); virtual void readPendingDatagrams(); + virtual void aboutToFinish(); + private slots: void maybeAttachSession(const SharedNodePointer& node); @@ -45,6 +50,8 @@ private slots: private: + MetavoxelPersister* _persister; + QTimer _sendTimer; qint64 _lastSend; @@ -88,4 +95,20 @@ private: int _reliableDeltaID; }; +/// Handles persistence in a separate thread. +class MetavoxelPersister : public QObject { + Q_OBJECT + +public: + + MetavoxelPersister(MetavoxelServer* server); + + Q_INVOKABLE void load(); + Q_INVOKABLE void save(const MetavoxelData& data); + +private: + + MetavoxelServer* _server; +}; + #endif // hifi_MetavoxelServer_h diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index bc662aa890..0d4f8a52b4 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -87,6 +87,12 @@ IDStreamer& IDStreamer::operator>>(int& value) { return *this; } +void Bitstream::preThreadingInit() { + getObjectStreamers(); + getEnumStreamers(); + getEnumStreamersByName(); +} + int Bitstream::registerMetaObject(const char* className, const QMetaObject* metaObject) { getMetaObjects().insert(className, metaObject); diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 70fde94b79..dacfeb2ee9 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -290,6 +290,11 @@ public: QHash sharedObjectValues; }; + /// Performs all of the various lazily initializations (of object streamers, etc.) If multiple threads need to use + /// Bitstream instances, call this beforehand to prevent errors from occurring when multiple threads attempt lazy + /// initialization simultaneously. + static void preThreadingInit(); + /// Registers a metaobject under its name so that instances of it can be streamed. Consider using the REGISTER_META_OBJECT /// at the top level of the source file associated with the class rather than calling this function directly. /// \return zero; the function only returns a value so that it can be used in static initialization From f9036dbaecbff5d7a4eceb018d40e51902febb80 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 9 Jul 2014 14:22:33 -0700 Subject: [PATCH 13/72] Lock the attribute mapping, since it can be accessed from multiple threads. --- libraries/metavoxels/src/AttributeRegistry.cpp | 9 +++++++++ libraries/metavoxels/src/AttributeRegistry.h | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 33ce298859..b43759fa4f 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -9,7 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include +#include #include "AttributeRegistry.h" #include "MetavoxelData.h" @@ -69,6 +71,7 @@ AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute if (!attribute) { return attribute; } + QWriteLocker locker(&_attributesLock); AttributePointer& pointer = _attributes[attribute->getName()]; if (!pointer) { pointer = attribute; @@ -77,9 +80,15 @@ AttributePointer AttributeRegistry::registerAttribute(AttributePointer attribute } void AttributeRegistry::deregisterAttribute(const QString& name) { + QWriteLocker locker(&_attributesLock); _attributes.remove(name); } +AttributePointer AttributeRegistry::getAttribute(const QString& name) { + QReadLocker locker(&_attributesLock); + return _attributes.value(name); +} + QScriptValue AttributeRegistry::getAttribute(QScriptContext* context, QScriptEngine* engine) { return engine->newQObject(getInstance()->getAttribute(context->argument(0).toString()).data(), QScriptEngine::QtOwnership, QScriptEngine::PreferExistingWrapperObject); diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 7dc2e110b8..23e3c1fa97 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -61,11 +62,14 @@ public: void deregisterAttribute(const QString& name); /// Retrieves an attribute by name. - AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); } + AttributePointer getAttribute(const QString& name); /// Returns a reference to the attribute hash. const QHash& getAttributes() const { return _attributes; } + /// Returns a reference to the attributes lock. + QReadWriteLock& getAttributesLock() { return _attributesLock; } + /// Returns a reference to the standard SharedObjectPointer "guide" attribute. const AttributePointer& getGuideAttribute() const { return _guideAttribute; } @@ -92,6 +96,8 @@ private: static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine); QHash _attributes; + QReadWriteLock _attributesLock; + AttributePointer _guideAttribute; AttributePointer _spannersAttribute; AttributePointer _colorAttribute; From 3b7945fc14a83c9bf87a2484651be44d66175a50 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 9 Jul 2014 15:52:23 -0700 Subject: [PATCH 14/72] Gather and print out some stats after loading the data. --- .../src/metavoxels/MetavoxelServer.cpp | 25 ++++++++------- libraries/metavoxels/src/MetavoxelData.cpp | 31 +++++++++++++++++++ libraries/metavoxels/src/MetavoxelData.h | 4 +++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 928a693527..956458404a 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -226,19 +226,22 @@ void MetavoxelPersister::load() { if (!file.exists()) { return; } - QDebug debug = qDebug() << "Reading from" << SAVE_FILE << "..."; - file.open(QIODevice::ReadOnly); - QDataStream inStream(&file); - Bitstream in(inStream); MetavoxelData data; - try { - in >> data; - } catch (const BitstreamException& e) { - debug << "failed, " << e.getDescription(); - return; + { + QDebug debug = qDebug() << "Reading from" << SAVE_FILE << "..."; + file.open(QIODevice::ReadOnly); + QDataStream inStream(&file); + Bitstream in(inStream); + try { + in >> data; + } catch (const BitstreamException& e) { + debug << "failed, " << e.getDescription(); + return; + } + QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data)); + debug << "done."; } - QMetaObject::invokeMethod(_server, "setData", Q_ARG(const MetavoxelData&, data)); - debug << "done."; + data.dumpStats(); } void MetavoxelPersister::save(const MetavoxelData& data) { diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 2d61ede796..4b142d0f5e 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -627,6 +628,25 @@ bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& l return true; } +void MetavoxelData::dumpStats(QDebug debug) const { + QDebugStateSaver saver(debug); + debug.nospace() << "[size=" << _size << ", roots=["; + int totalInternal = 0, totalLeaves = 0; + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + if (it != _roots.constBegin()) { + debug << ", "; + } + debug << it.key()->getName() << " (" << it.key()->metaObject()->className() << "): "; + int internal = 0, leaves = 0; + it.value()->countDescendants(internal, leaves); + debug << internal << " internal, " << leaves << " leaves, " << (internal + leaves) << " total"; + totalInternal += internal; + totalLeaves += leaves; + } + debug << "], totalInternal=" << totalInternal << ", totalLeaves=" << totalLeaves << + ", grandTotal=" << (totalInternal + totalLeaves) << "]"; +} + bool MetavoxelData::operator==(const MetavoxelData& other) const { return _size == other._size && _roots == other._roots; } @@ -1061,6 +1081,17 @@ void MetavoxelNode::getSpanners(const AttributePointer& attribute, const glm::ve } } +void MetavoxelNode::countDescendants(int& internalNodes, int& leaves) const { + if (isLeaf()) { + leaves++; + return; + } + internalNodes++; + for (int i = 0; i < CHILD_COUNT; i++) { + _children[i]->countDescendants(internalNodes, leaves); + } +} + int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth) { return first | (second << 3) | (third << 6) | (fourth << 9) | diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 6a7ba33eb5..347d5a1f0b 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -125,6 +125,8 @@ public: /// shallow comparison). bool deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod = MetavoxelLOD()) const; + void dumpStats(QDebug debug = QDebug(QtDebugMsg)) const; + bool operator==(const MetavoxelData& other) const; bool operator!=(const MetavoxelData& other) const; @@ -221,6 +223,8 @@ public: void getSpanners(const AttributePointer& attribute, const glm::vec3& minimum, float size, const MetavoxelLOD& lod, SharedObjectSet& results) const; + void countDescendants(int& internalNodes, int& leaves) const; + private: Q_DISABLE_COPY(MetavoxelNode) From dbd7ec18b0355fb3c157e0e7c4812830c781e025 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 9 Jul 2014 17:15:05 -0700 Subject: [PATCH 15/72] Show some stats on the client (node counts under LOD, reliable delta download progress). --- interface/src/ui/Stats.cpp | 36 ++++++++++++++++++- .../metavoxels/src/DatagramSequencer.cpp | 11 ++++++ libraries/metavoxels/src/DatagramSequencer.h | 4 +++ .../metavoxels/src/MetavoxelClientManager.cpp | 4 +++ .../metavoxels/src/MetavoxelClientManager.h | 2 ++ libraries/metavoxels/src/MetavoxelData.cpp | 23 ++++++++---- libraries/metavoxels/src/MetavoxelData.h | 6 +++- 7 files changed, 77 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 3de21b449b..b0cbea14a9 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -377,7 +377,7 @@ void Stats::display( MyAvatar* myAvatar = Application::getInstance()->getAvatar(); glm::vec3 avatarPos = myAvatar->getPosition(); - lines = _expanded ? 5 : 3; + lines = _expanded ? 8 : 3; drawBackground(backgroundColor, horizontalOffset, 0, _geoStatsWidth, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; @@ -419,6 +419,40 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); + + + int internal = 0, leaves = 0; + int received = 0, total = 0; + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getType() == NodeType::MetavoxelServer) { + QMutexLocker locker(&node->getMutex()); + MetavoxelClient* client = static_cast(node->getLinkedData()); + if (client) { + client->getData().countNodes(internal, leaves, Application::getInstance()->getMetavoxels()->getLOD()); + int clientReceived = 0, clientTotal = 0; + if (client->getReliableDeltaProgress(clientReceived, clientTotal)) { + received += clientReceived; + total += clientTotal; + } + } + } + } + stringstream nodes; + nodes << "Metavoxels: " << (internal + leaves); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color); + + stringstream nodeTypes; + nodeTypes << "Internal: " << internal << " Leaves: " << leaves; + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color); + + if (total > 0) { + stringstream reliableDelta; + reliableDelta << "Reliable Delta: " << (received * 100 / total) << "%"; + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableDelta.str().c_str(), color); + } } verticalOffset = 0; diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 536cfc9dfb..8c58233eb9 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -692,6 +692,17 @@ void ReliableChannel::sendMessage(const QVariant& message) { endMessage(); } +bool ReliableChannel::getMessageReceiveProgress(int& received, int& total) const { + if (!_messagesEnabled || _buffer.bytesAvailable() < (int)sizeof(quint32)) { + return false; + } + quint32 length; + _buffer.readBytes(_buffer.pos(), sizeof(quint32), (char*)&length); + total = length; + received = _buffer.bytesAvailable(); + return true; +} + void ReliableChannel::sendClearSharedObjectMessage(int id) { ClearSharedObjectMessage message = { id }; sendMessage(QVariant::fromValue(message)); diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index b6dce464f7..998e196a05 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -376,6 +376,10 @@ public: /// writes the message to the bitstream, then calls endMessage). void sendMessage(const QVariant& message); + /// Determines the number of bytes downloaded towards the currently pending message. + /// \return true if there is a message pending, in which case the received and total arguments will be set + bool getMessageReceiveProgress(int& received, int& total) const; + signals: /// Fired when a framed message has been received on this channel. diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index f3ea1ae8c5..94d9116794 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -94,6 +94,10 @@ MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientM SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); } +bool MetavoxelClient::getReliableDeltaProgress(int& received, int& total) const { + return _reliableDeltaChannel && _reliableDeltaChannel->getMessageReceiveProgress(received, total); +} + void MetavoxelClient::guide(MetavoxelVisitor& visitor) { visitor.setLOD(_manager->getLOD()); _data.guide(visitor); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index ad6c86c8fc..809718aa01 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -53,6 +53,8 @@ public: MetavoxelData& getData() { return _data; } + bool getReliableDeltaProgress(int& received, int& total) const; + void guide(MetavoxelVisitor& visitor); void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 4b142d0f5e..1362731a8a 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -628,17 +628,25 @@ bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& l return true; } +void MetavoxelData::countNodes(int& internal, int& leaves, const MetavoxelLOD& lod) const { + glm::vec3 minimum = getMinimum(); + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + it.value()->countNodes(it.key(), minimum, _size, lod, internal, leaves); + } +} + void MetavoxelData::dumpStats(QDebug debug) const { QDebugStateSaver saver(debug); debug.nospace() << "[size=" << _size << ", roots=["; int totalInternal = 0, totalLeaves = 0; + glm::vec3 minimum = getMinimum(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { if (it != _roots.constBegin()) { debug << ", "; } debug << it.key()->getName() << " (" << it.key()->metaObject()->className() << "): "; int internal = 0, leaves = 0; - it.value()->countDescendants(internal, leaves); + it.value()->countNodes(it.key(), minimum, _size, MetavoxelLOD(), internal, leaves); debug << internal << " internal, " << leaves << " leaves, " << (internal + leaves) << " total"; totalInternal += internal; totalLeaves += leaves; @@ -1076,19 +1084,20 @@ void MetavoxelNode::getSpanners(const AttributePointer& attribute, const glm::ve } float nextSize = size * 0.5f; for (int i = 0; i < CHILD_COUNT; i++) { - glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i); - _children[i]->getSpanners(attribute, nextMinimum, nextSize, lod, results); + _children[i]->getSpanners(attribute, getNextMinimum(minimum, nextSize, i), nextSize, lod, results); } } -void MetavoxelNode::countDescendants(int& internalNodes, int& leaves) const { - if (isLeaf()) { +void MetavoxelNode::countNodes(const AttributePointer& attribute, const glm::vec3& minimum, + float size, const MetavoxelLOD& lod, int& internal, int& leaves) const { + if (isLeaf() || !lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) { leaves++; return; } - internalNodes++; + internal++; + float nextSize = size * 0.5f; for (int i = 0; i < CHILD_COUNT; i++) { - _children[i]->countDescendants(internalNodes, leaves); + _children[i]->countNodes(attribute, getNextMinimum(minimum, nextSize, i), nextSize, lod, internal, leaves); } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 347d5a1f0b..f558bf8e80 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -125,6 +125,9 @@ public: /// shallow comparison). bool deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod = MetavoxelLOD()) const; + /// Counts the nodes in the data. + void countNodes(int& internalNodes, int& leaves, const MetavoxelLOD& lod = MetavoxelLOD()) const; + void dumpStats(QDebug debug = QDebug(QtDebugMsg)) const; bool operator==(const MetavoxelData& other) const; @@ -223,7 +226,8 @@ public: void getSpanners(const AttributePointer& attribute, const glm::vec3& minimum, float size, const MetavoxelLOD& lod, SharedObjectSet& results) const; - void countDescendants(int& internalNodes, int& leaves) const; + void countNodes(const AttributePointer& attribute, const glm::vec3& minimum, + float size, const MetavoxelLOD& lod, int& internalNodes, int& leaves) const; private: Q_DISABLE_COPY(MetavoxelNode) From b2b17019658055b9c08221421e3fdff4ebd17eee Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Jul 2014 17:47:09 -0700 Subject: [PATCH 16/72] - Bunch of fixes to typed arrays - First working prototype for Int8Array - Base classes for all other typed arrays --- .../script-engine/src/ArrayBufferClass.cpp | 28 ++- .../script-engine/src/ArrayBufferClass.h | 8 +- .../src/ArrayBufferViewClass.cpp | 13 +- .../script-engine/src/ArrayBufferViewClass.h | 13 +- libraries/script-engine/src/DataViewClass.cpp | 14 +- libraries/script-engine/src/DataViewClass.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 28 ++- libraries/script-engine/src/ScriptEngine.h | 6 + libraries/script-engine/src/TypedArrays.cpp | 233 ++++++++++++++++++ libraries/script-engine/src/TypedArrays.h | 104 ++++++++ 10 files changed, 412 insertions(+), 37 deletions(-) create mode 100644 libraries/script-engine/src/TypedArrays.cpp create mode 100644 libraries/script-engine/src/TypedArrays.h diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index d003125c1e..d86e229a1a 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -9,8 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "ArrayBufferPrototype.h" +#include "ScriptEngine.h" #include "ArrayBufferClass.h" @@ -19,16 +21,20 @@ static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; Q_DECLARE_METATYPE(QByteArray*) -ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QScriptClass(engine) { - qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue); - QScriptValue global = engine->globalObject(); +ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : +QObject(scriptEngine->getScriptEngine()), +QScriptClass(scriptEngine->getScriptEngine()), +_scriptEngine(scriptEngine) { + qDebug() << "Created with engine: " << engine(); + qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); + QScriptValue global = engine()->globalObject(); // Save string handles for quick lookup - _name = engine->toStringHandle(CLASS_NAME.toLatin1()); - _byteLength = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + _name = engine()->toStringHandle(CLASS_NAME.toLatin1()); + _byteLength = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); // build prototype - _proto = engine->newQObject(new ArrayBufferPrototype(this), + _proto = engine()->newQObject(new ArrayBufferPrototype(this), QScriptEngine::QtOwnership, QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | @@ -36,15 +42,17 @@ ArrayBufferClass::ArrayBufferClass(QScriptEngine* engine) : QObject(engine), QSc _proto.setPrototype(global.property("Object").property("prototype")); - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); - engine->globalObject().setProperty(name(), _ctor); + engine()->globalObject().setProperty(name(), _ctor); } QScriptValue ArrayBufferClass::newInstance(quint32 size) { engine()->reportAdditionalMemoryCost(size); - QScriptValue data = engine()->newVariant(QVariant::fromValue(QByteArray(size, 0))); + QScriptEngine* eng = engine(); + QVariant variant = QVariant::fromValue(QByteArray(size, 0)); + QScriptValue data = eng->newVariant(variant); return engine()->newObject(this, data); } diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index 2f863e77a5..c56b659eff 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -20,13 +20,17 @@ #include #include +class ScriptEngine; + class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferClass(QScriptEngine* engine); + ArrayBufferClass(ScriptEngine* scriptEngine); QScriptValue newInstance(quint32 size); QScriptValue newInstance(const QByteArray& ba); + ScriptEngine* getScriptEngine() { return _scriptEngine; } + QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); @@ -50,6 +54,8 @@ private: // JS Object attributes QScriptString _name; QScriptString _byteLength; + + ScriptEngine* _scriptEngine; }; #endif // hifi_ArrayBufferClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp index 73d833dab4..a01f239512 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -13,13 +13,14 @@ Q_DECLARE_METATYPE(QByteArray*) -ArrayBufferViewClass::ArrayBufferViewClass(QScriptEngine* engine) : -QObject(engine), -QScriptClass(engine) { +ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : +QObject(scriptEngine->getScriptEngine()), +QScriptClass(scriptEngine->getScriptEngine()), +_scriptEngine(scriptEngine) { // Save string handles for quick lookup - _bufferName = engine->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); - _byteOffsetName = engine->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); - _byteLengthName = engine->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); + _bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); + _byteOffsetName = engine()->toStringHandle(BYTE_OFFSET_PROPERTY_NAME.toLatin1()); + _byteLengthName = engine()->toStringHandle(BYTE_LENGTH_PROPERTY_NAME.toLatin1()); } QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& object, diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index eb4a372843..acc061c8a3 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -20,6 +20,8 @@ #include #include +#include "ScriptEngine.h" + static const QString BUFFER_PROPERTY_NAME = "buffer"; static const QString BYTE_OFFSET_PROPERTY_NAME = "byteOffset"; static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; @@ -27,21 +29,24 @@ static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; class ArrayBufferViewClass : public QObject, public QScriptClass { Q_OBJECT public: - ArrayBufferViewClass(QScriptEngine* engine); + ArrayBufferViewClass(ScriptEngine* scriptEngine); + ScriptEngine* getScriptEngine() { return _scriptEngine; } - QueryFlags queryProperty(const QScriptValue& object, + virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue &object, + virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); protected: // JS Object attributes QScriptString _bufferName; QScriptString _byteOffsetName; QScriptString _byteLengthName; + + ScriptEngine* _scriptEngine; }; #endif // hifi_ArrayBufferViewClass_h \ No newline at end of file diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 38a85e55a8..4a9420e4e9 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -17,14 +17,14 @@ Q_DECLARE_METATYPE(QByteArray*) static const QString DATA_VIEW_NAME = "DataView"; -DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engine) { - QScriptValue global = engine->globalObject(); +DataViewClass::DataViewClass(ScriptEngine* scriptEngine) : ArrayBufferViewClass(scriptEngine) { + QScriptValue global = engine()->globalObject(); // Save string handles for quick lookup - _name = engine->toStringHandle(DATA_VIEW_NAME.toLatin1()); + _name = engine()->toStringHandle(DATA_VIEW_NAME.toLatin1()); // build prototype - _proto = engine->newQObject(new DataViewPrototype(this), + _proto = engine()->newQObject(new DataViewPrototype(this), QScriptEngine::QtOwnership, QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | @@ -32,10 +32,10 @@ DataViewClass::DataViewClass(QScriptEngine* engine) : ArrayBufferViewClass(engin _proto.setPrototype(global.property("Object").property("prototype")); - _ctor = engine->newFunction(construct, _proto); - _ctor.setData(engine->toScriptValue(this)); + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); - engine->globalObject().setProperty(name(), _ctor); + engine()->globalObject().setProperty(name(), _ctor); } QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLentgh) { diff --git a/libraries/script-engine/src/DataViewClass.h b/libraries/script-engine/src/DataViewClass.h index 6b6d6d25e7..b87803f4b4 100644 --- a/libraries/script-engine/src/DataViewClass.h +++ b/libraries/script-engine/src/DataViewClass.h @@ -17,7 +17,7 @@ class DataViewClass : public ArrayBufferViewClass { Q_OBJECT public: - DataViewClass(QScriptEngine* engine); + DataViewClass(ScriptEngine* scriptEngine); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 byteLength); QString name() const; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f9161b9e51..73983161ff 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -32,13 +32,13 @@ #include #include "AnimationObject.h" -#include "ArrayBufferClass.h" #include "ArrayBufferViewClass.h" #include "DataViewClass.h" #include "MenuItemProperties.h" #include "MIDIEvent.h" #include "LocalVoxels.h" #include "ScriptEngine.h" +#include "TypedArrays.h" #include "XMLHttpRequestClass.h" VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; @@ -93,8 +93,15 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _quatLibrary(), _vec3Library(), _uuidLibrary(), - _animationCache(this) + _animationCache(this), + _arrayBufferClass(NULL) { + _arrayBufferClass = new ArrayBufferClass(this); + qDebug() << "Engine: " << this; + qDebug() << "ArrayBuffer: " <<_arrayBufferClass; + qDebug() << "DataView: " << new DataViewClass(this); + qDebug() << "Int8Array: " << new Int8ArrayClass(this); + qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -118,13 +125,21 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _quatLibrary(), _vec3Library(), _uuidLibrary(), - _animationCache(this) + _animationCache(this), + _arrayBufferClass(NULL) { QString scriptURLString = scriptURL.toString(); _fileNameString = scriptURLString; QUrl url(scriptURL); - + + _arrayBufferClass = new ArrayBufferClass(this); + qDebug() << "Engine: " << this; + qDebug() << "ArrayBuffer: " <<_arrayBufferClass; + qDebug() << "DataView: " << new DataViewClass(this); + qDebug() << "Int8Array: " << new Int8ArrayClass(this); + qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { @@ -141,6 +156,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QTextStream in(&scriptFile); _scriptContents = in.readAll(); } else { + qDebug() << "ERROR Loading file:" << fileName; emit errorMessage("ERROR Loading file:" + fileName); } @@ -213,10 +229,6 @@ void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once } - - new ArrayBufferClass(&_engine); - new ArrayBufferViewClass(&_engine); - new DataViewClass(&_engine); _isInitialized = true; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5b01b8124a..3dda172044 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -25,6 +25,7 @@ #include #include "AbstractControllerScriptingInterface.h" +#include "ArrayBufferClass.h" #include "Quat.h" #include "ScriptUUID.h" #include "Vec3.h" @@ -56,6 +57,9 @@ public: /// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } + QScriptEngine* getScriptEngine() { return &_engine; } + ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } + /// sets the script contents, will return false if failed, will fail if script is already running bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString("")); @@ -147,6 +151,8 @@ private: Vec3 _vec3Library; ScriptUUID _uuidLibrary; AnimationCache _animationCache; + + ArrayBufferClass* _arrayBufferClass; QHash _outgoingScriptAudioSequenceNumbers; }; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp new file mode 100644 index 0000000000..dc208635b8 --- /dev/null +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -0,0 +1,233 @@ +// +// TypedArrays.cpp +// +// +// Created by Clement on 7/9/14. +// Copyright 2014 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 "ScriptEngine.h" + +#include "TypedArrays.h" + +Q_DECLARE_METATYPE(QByteArray*) + +TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferViewClass(scriptEngine) { + _bytesPerElementName = engine()->toStringHandle(BYTES_PER_ELEMENT_PROPERTY_NAME.toLatin1()); + _lengthName = engine()->toStringHandle(LENGTH_PROPERTY_NAME.toLatin1()); + _name = engine()->toStringHandle(name.toLatin1()); +} + +QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { + TypedArray* cls = qscriptvalue_cast(context->callee().data()); + if (!cls || context->argumentCount() < 1) { + return QScriptValue(); + } + + QScriptValue newObject; + QScriptValue bufferArg = context->argument(0); + QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); + TypedArray* typedArray = qscriptvalue_cast(bufferArg); + + if (arrayBuffer) { + if (context->argumentCount() == 1) { + // Case for entire ArrayBuffer + newObject = cls->newInstance(bufferArg, 0, arrayBuffer->size()); + } else { + QScriptValue byteOffsetArg = context->argument(1); + if (!byteOffsetArg.isNumber()) { + engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\""); + return QScriptValue(); + } + if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() >= arrayBuffer->size()) { + engine->evaluate("throw \"RangeError: byteOffset out of range\""); + return QScriptValue(); + } + quint32 byteOffset = byteOffsetArg.toInt32(); + + if (context->argumentCount() == 2) { + // case for end of ArrayBuffer + newObject = cls->newInstance(bufferArg, byteOffset, arrayBuffer->size() - byteOffset); + } else { + + QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue(); + if (!lengthArg.isNumber()) { + engine->evaluate("throw \"ArgumentError: 3nd arg is not a number\""); + return QScriptValue(); + } + if (lengthArg.toInt32() < 0 || + byteOffsetArg.toInt32() + lengthArg.toInt32() * cls->_bytesPerElement > arrayBuffer->size()) { + engine->evaluate("throw \"RangeError: byteLength out of range\""); + return QScriptValue(); + } + quint32 length = lengthArg.toInt32() * cls->_bytesPerElement; + + // case for well-defined range + newObject = cls->newInstance(bufferArg, byteOffset, length); + } + } + } else if (typedArray) { + // case for another reference to another TypedArray + newObject = cls->newInstance(bufferArg, true); + } else if (context->argument(0).isNumber()) { + // case for new ArrayBuffer + newObject = cls->newInstance(context->argument(0).toInt32()); + } else { + // TODO: Standard array case + } + + if (context->isCalledAsConstructor()) { + context->setThisObject(newObject); + return engine->undefinedValue(); + } + + return newObject; +} + +QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + if (name == _bytesPerElementName || name == _lengthName) { + return flags &= HandlesReadAccess; // Only keep read access flags + } + return ArrayBufferViewClass::queryProperty(object, name, flags, id); +} + +QScriptValue TypedArray::property(const QScriptValue &object, + const QScriptString &name, uint id) { + if (name == _bytesPerElementName) { + return QScriptValue(_bytesPerElement); + } + if (name == _lengthName) { + return object.data().property(_lengthName); + } + return ArrayBufferViewClass::property(object, name, id); +} + +QScriptValue::PropertyFlags TypedArray::propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id) { + return QScriptValue::Undeletable; +} + +QString TypedArray::name() const { + return _name.toString(); +} + +QScriptValue TypedArray::prototype() const { + return _proto; +} + +Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { + QScriptValue global = engine()->globalObject(); + + _bytesPerElement = sizeof(qint8); + + // build prototype +// _proto = engine->newQObject(new DataViewPrototype(this), +// QScriptEngine::QtOwnership, +// QScriptEngine::SkipMethodsInEnumeration | +// QScriptEngine::ExcludeSuperClassMethods | +// QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); + + engine()->globalObject().setProperty(name(), _ctor); +} + +QScriptValue Int8ArrayClass::newInstance(quint32 length) { + ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); + QScriptValue buffer = array->newInstance(length * _bytesPerElement); + return newInstance(buffer, 0, length); +} + +QScriptValue Int8ArrayClass::newInstance(QScriptValue array, bool isTypedArray) { + // TODO + assert(false); + if (isTypedArray) { + QByteArray* originalArray = qscriptvalue_cast(array.data().property(_bufferName).data()); + ArrayBufferClass* arrayBufferClass = reinterpret_cast(engine())->getArrayBufferClass(); + quint32 byteOffset = array.data().property(_byteOffsetName).toInt32(); + quint32 byteLength = array.data().property(_byteLengthName).toInt32(); + quint32 length = byteLength / _bytesPerElement; + QByteArray newArray = originalArray->mid(byteOffset, byteLength); + + if (byteLength % _bytesPerElement != 0) { + length += 1; + byteLength = length * _bytesPerElement; + } + + + return newInstance(arrayBufferClass->newInstance(newArray), + array.data().property(_byteOffsetName).toInt32(), + array.data().property(_lengthName).toInt32()); + } + return QScriptValue(); +} + +QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, length * _bytesPerElement); + data.setProperty(_lengthName, length); + + return engine()->newObject(this, data); +} + +QScriptClass::QueryFlags Int8ArrayClass::queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id) { + quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); + quint32 length = object.data().property(_lengthName).toInt32(); + bool ok = false; + int pos = name.toArrayIndex(&ok); + + // Check that name is a valid index and arrayBuffer exists + if (ok && pos >= 0 && pos < length) { + *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation + return HandlesReadAccess | HandlesWriteAccess; // Read/Write access + } + + return TypedArray::queryProperty(object, name, flags, id); +} + +QScriptValue Int8ArrayClass::property(const QScriptValue &object, + const QScriptString &name, uint id) { + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + + qint8 result; + stream >> result; + return result; + } + + return TypedArray::property(object, name, id); +} + +void Int8ArrayClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value) { + qDebug() << "ID: " << id << ", Value: " << value.toInt32(); + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + + qDebug() << "Adding: " << (qint8)value.toInt32(); + stream << (qint8)value.toInt32(); + } +} + + + diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h new file mode 100644 index 0000000000..ea43ca2494 --- /dev/null +++ b/libraries/script-engine/src/TypedArrays.h @@ -0,0 +1,104 @@ +// +// TypedArrays.h +// +// +// Created by Clement on 7/9/14. +// Copyright 2014 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 +// + +#ifndef hifi_TypedArrays_h +#define hifi_TypedArrays_h + +#include "ArrayBufferViewClass.h" + +static const QString BYTES_PER_ELEMENT_PROPERTY_NAME = "BYTES_PER_ELEMENT"; +static const QString LENGTH_PROPERTY_NAME = "length"; + +static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array"; + +class TypedArray : public ArrayBufferViewClass { + Q_OBJECT +public: + TypedArray(ScriptEngine* scriptEngine, QString name); + virtual QScriptValue newInstance(quint32 length) = 0; + virtual QScriptValue newInstance(QScriptValue array, bool isTypedArray) = 0; + virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) = 0; + + virtual QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, + const QScriptString& name, uint id); + + QString name() const; + QScriptValue prototype() const; + +protected: + static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + + QScriptValue _proto; + QScriptValue _ctor; + + QScriptString _name; + QScriptString _bytesPerElementName; + QScriptString _lengthName; + + quint32 _bytesPerElement; +}; + +class Int8ArrayClass : public TypedArray { + Q_OBJECT +public: + Int8ArrayClass(ScriptEngine* scriptEngine); + QScriptValue newInstance(quint32 length); + QScriptValue newInstance(QScriptValue array, bool isTypedArray); + QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); + + QueryFlags queryProperty(const QScriptValue& object, + const QScriptString& name, + QueryFlags flags, uint* id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + void setProperty(QScriptValue &object, + const QScriptString &name, + uint id, const QScriptValue &value); +}; + +class Uint8ArrayClass : public TypedArray { + +}; + +class Uint8ClampedArrayClass : public TypedArray { + +}; + +class Int16ArrayClass : public TypedArray { + +}; + +class Uint16ArrayClass : public TypedArray { + +}; + +class Int32ArrayClass : public TypedArray { + +}; + +class Uint32ArrayClass : public TypedArray { + +}; + +class Float32ArrayClass : public TypedArray { + +}; + +class Float64ArrayClass : public TypedArray { + +}; + +#endif // hifi_TypedArrays_h \ No newline at end of file From b4eb6a2eb7a618120a83d09fb9aa73b5e7eb5a36 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 10:09:49 -0700 Subject: [PATCH 17/72] Some refactoring --- libraries/script-engine/src/TypedArrays.cpp | 63 +++++++++------------ libraries/script-engine/src/TypedArrays.h | 3 - 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index dc208635b8..121870443f 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -19,6 +19,22 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi _bytesPerElementName = engine()->toStringHandle(BYTES_PER_ELEMENT_PROPERTY_NAME.toLatin1()); _lengthName = engine()->toStringHandle(LENGTH_PROPERTY_NAME.toLatin1()); _name = engine()->toStringHandle(name.toLatin1()); + + QScriptValue global = engine()->globalObject(); + + // build prototype TODO + // _proto = engine->newQObject(new DataViewPrototype(this), + // QScriptEngine::QtOwnership, + // QScriptEngine::SkipMethodsInEnumeration | + // QScriptEngine::ExcludeSuperClassMethods | + // QScriptEngine::ExcludeSuperClassProperties); + + _proto.setPrototype(global.property("Object").property("prototype")); + + _ctor = engine()->newFunction(construct, _proto); + _ctor.setData(engine()->toScriptValue(this)); + + engine()->globalObject().setProperty(_name, _ctor); } QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { @@ -93,6 +109,18 @@ QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, if (name == _bytesPerElementName || name == _lengthName) { return flags &= HandlesReadAccess; // Only keep read access flags } + + quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); + quint32 length = object.data().property(_lengthName).toInt32(); + bool ok = false; + int pos = name.toArrayIndex(&ok); + + // Check that name is a valid index and arrayBuffer exists + if (ok && pos >= 0 && pos < length) { + *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation + return HandlesReadAccess | HandlesWriteAccess; // Read/Write access + } + return ArrayBufferViewClass::queryProperty(object, name, flags, id); } @@ -121,23 +149,7 @@ QScriptValue TypedArray::prototype() const { } Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { - QScriptValue global = engine()->globalObject(); - _bytesPerElement = sizeof(qint8); - - // build prototype -// _proto = engine->newQObject(new DataViewPrototype(this), -// QScriptEngine::QtOwnership, -// QScriptEngine::SkipMethodsInEnumeration | -// QScriptEngine::ExcludeSuperClassMethods | -// QScriptEngine::ExcludeSuperClassProperties); - - _proto.setPrototype(global.property("Object").property("prototype")); - - _ctor = engine()->newFunction(construct, _proto); - _ctor.setData(engine()->toScriptValue(this)); - - engine()->globalObject().setProperty(name(), _ctor); } QScriptValue Int8ArrayClass::newInstance(quint32 length) { @@ -180,23 +192,6 @@ QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset return engine()->newObject(this, data); } -QScriptClass::QueryFlags Int8ArrayClass::queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id) { - quint32 byteOffset = object.data().property(_byteOffsetName).toInt32(); - quint32 length = object.data().property(_lengthName).toInt32(); - bool ok = false; - int pos = name.toArrayIndex(&ok); - - // Check that name is a valid index and arrayBuffer exists - if (ok && pos >= 0 && pos < length) { - *id = byteOffset + pos * _bytesPerElement; // save pos to avoid recomputation - return HandlesReadAccess | HandlesWriteAccess; // Read/Write access - } - - return TypedArray::queryProperty(object, name, flags, id); -} - QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); @@ -218,13 +213,11 @@ QScriptValue Int8ArrayClass::property(const QScriptValue &object, void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { - qDebug() << "ID: " << id << ", Value: " << value.toInt32(); QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); - qDebug() << "Adding: " << (qint8)value.toInt32(); stream << (qint8)value.toInt32(); } } diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index ea43ca2494..2c63d83f93 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -59,9 +59,6 @@ public: QScriptValue newInstance(QScriptValue array, bool isTypedArray); QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); void setProperty(QScriptValue &object, From 817b50c7b8fd9a068a9ef841cb752ff3840f29c7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 10 Jul 2014 15:34:45 -0700 Subject: [PATCH 18/72] Show upload/download stats for all reliable messages, not just reliable delta downloads. --- interface/src/ui/Stats.cpp | 22 +++++++------ .../metavoxels/src/DatagramSequencer.cpp | 32 ++++++++++++++++++- libraries/metavoxels/src/DatagramSequencer.h | 9 ++++++ libraries/metavoxels/src/Endpoint.h | 2 ++ .../metavoxels/src/MetavoxelClientManager.cpp | 4 --- .../metavoxels/src/MetavoxelClientManager.h | 2 -- 6 files changed, 54 insertions(+), 17 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index b0cbea14a9..0d2762f923 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -422,18 +422,15 @@ void Stats::display( int internal = 0, leaves = 0; - int received = 0, total = 0; + int sendProgress = 0, sendTotal = 0; + int receiveProgress = 0, receiveTotal = 0; foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { client->getData().countNodes(internal, leaves, Application::getInstance()->getMetavoxels()->getLOD()); - int clientReceived = 0, clientTotal = 0; - if (client->getReliableDeltaProgress(clientReceived, clientTotal)) { - received += clientReceived; - total += clientTotal; - } + client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal); } } } @@ -447,11 +444,16 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color); - if (total > 0) { - stringstream reliableDelta; - reliableDelta << "Reliable Delta: " << (received * 100 / total) << "%"; + if (sendTotal > 0 || receiveTotal > 0) { + stringstream reliableStats; + if (sendTotal > 0) { + reliableStats << "Upload: " << (sendProgress * 100 / sendTotal) << "% "; + } + if (receiveTotal > 0) { + reliableStats << "Download: " << (receiveProgress * 100 / receiveTotal) << "%"; + } verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableDelta.str().c_str(), color); + drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color); } } diff --git a/libraries/metavoxels/src/DatagramSequencer.cpp b/libraries/metavoxels/src/DatagramSequencer.cpp index 8c58233eb9..c757e131bb 100644 --- a/libraries/metavoxels/src/DatagramSequencer.cpp +++ b/libraries/metavoxels/src/DatagramSequencer.cpp @@ -79,6 +79,24 @@ ReliableChannel* DatagramSequencer::getReliableInputChannel(int index) { return channel; } +void DatagramSequencer::addReliableChannelStats(int& sendProgress, int& sendTotal, + int& receiveProgress, int& receiveTotal) const { + foreach (ReliableChannel* channel, _reliableOutputChannels) { + int sent, total; + if (channel->getMessageSendProgress(sent, total)) { + sendProgress += sent; + sendTotal += total; + } + } + foreach (ReliableChannel* channel, _reliableInputChannels) { + int received, total; + if (channel->getMessageReceiveProgress(received, total)) { + receiveProgress += received; + receiveTotal += total; + } + } +} + int DatagramSequencer::notePacketGroup(int desiredPackets) { // figure out how much data we have enqueued and increase the number of packets desired int totalAvailable = 0; @@ -684,6 +702,8 @@ void ReliableChannel::endMessage() { quint32 length = _buffer.pos() - _messageLengthPlaceholder; _buffer.writeBytes(_messageLengthPlaceholder, sizeof(quint32), (const char*)&length); + _messageReceivedOffset = getBytesWritten(); + _messageSize = length; } void ReliableChannel::sendMessage(const QVariant& message) { @@ -692,6 +712,15 @@ void ReliableChannel::sendMessage(const QVariant& message) { endMessage(); } +bool ReliableChannel::getMessageSendProgress(int& sent, int& total) const { + if (!_messagesEnabled || _offset >= _messageReceivedOffset) { + return false; + } + sent = qMax(0, _messageSize - (_messageReceivedOffset - _offset)); + total = _messageSize; + return true; +} + bool ReliableChannel::getMessageReceiveProgress(int& received, int& total) const { if (!_messagesEnabled || _buffer.bytesAvailable() < (int)sizeof(quint32)) { return false; @@ -728,7 +757,8 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o _offset(0), _writePosition(0), _writePositionResetPacketNumber(0), - _messagesEnabled(true) { + _messagesEnabled(true), + _messageReceivedOffset(0) { _buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly); _dataStream.setByteOrder(QDataStream::LittleEndian); diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 998e196a05..4a01679c68 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -108,6 +108,9 @@ public: /// Returns the intput channel at the specified index, creating it if necessary. ReliableChannel* getReliableInputChannel(int index = 0); + /// Adds stats for all reliable channels to the referenced variables. + void addReliableChannelStats(int& sendProgress, int& sendTotal, int& receiveProgress, int& receiveTotal) const; + /// Notes that we're sending a group of packets. /// \param desiredPackets the number of packets we'd like to write in the group /// \return the number of packets to write in the group @@ -376,6 +379,10 @@ public: /// writes the message to the bitstream, then calls endMessage). void sendMessage(const QVariant& message); + /// Determines the number of bytes uploaded towards the currently pending message. + /// \return true if there is a message pending, in which case the sent and total arguments will be set + bool getMessageSendProgress(int& sent, int& total) const; + /// Determines the number of bytes downloaded towards the currently pending message. /// \return true if there is a message pending, in which case the received and total arguments will be set bool getMessageReceiveProgress(int& received, int& total) const; @@ -420,6 +427,8 @@ private: SpanList _acknowledged; bool _messagesEnabled; int _messageLengthPlaceholder; + int _messageReceivedOffset; + int _messageSize; }; #endif // hifi_DatagramSequencer_h diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index 3c681a7b98..d6999196d8 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -32,6 +32,8 @@ public: PacketRecord* baselineReceiveRecord = NULL); virtual ~Endpoint(); + const DatagramSequencer& getSequencer() const { return _sequencer; } + virtual void update(); virtual int parseData(const QByteArray& packet); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 94d9116794..f3ea1ae8c5 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -94,10 +94,6 @@ MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientM SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); } -bool MetavoxelClient::getReliableDeltaProgress(int& received, int& total) const { - return _reliableDeltaChannel && _reliableDeltaChannel->getMessageReceiveProgress(received, total); -} - void MetavoxelClient::guide(MetavoxelVisitor& visitor) { visitor.setLOD(_manager->getLOD()); _data.guide(visitor); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 809718aa01..ad6c86c8fc 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -53,8 +53,6 @@ public: MetavoxelData& getData() { return _data; } - bool getReliableDeltaProgress(int& received, int& total) const; - void guide(MetavoxelVisitor& visitor); void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); From d858e20af25b2a72d595c2b23c2eaddb7a826fbf Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 10 Jul 2014 16:02:53 -0700 Subject: [PATCH 19/72] For now, tie the metavoxel LOD threshold to the avatar LOD parameter. --- interface/src/MetavoxelSystem.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7a5119a62d..66933643ae 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -48,8 +48,10 @@ void MetavoxelSystem::init() { } MetavoxelLOD MetavoxelSystem::getLOD() const { - const float FIXED_LOD_THRESHOLD = 0.01f; - return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), FIXED_LOD_THRESHOLD); + // the LOD threshold is temporarily tied to the avatar LOD parameter + const float BASE_LOD_THRESHOLD = 0.01f; + return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), + BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier()); } void MetavoxelSystem::simulate(float deltaTime) { From d121c8e07af33811b5ac636a00a2b4635c38c5de Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 10 Jul 2014 17:21:05 -0700 Subject: [PATCH 20/72] Fix for starting with empty spanners (due to LOD), then adding. --- libraries/metavoxels/src/AttributeRegistry.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index b43759fa4f..64b2646261 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -568,6 +568,10 @@ void SpannerSetAttribute::readMetavoxelRoot(MetavoxelData& data, MetavoxelStream } data.insert(state.attribute, object); } + // even if the root is empty, it should still exist + if (!data.getRoot(state.attribute)) { + data.createRoot(state.attribute); + } } void SpannerSetAttribute::writeMetavoxelRoot(const MetavoxelNode& root, MetavoxelStreamState& state) { @@ -586,6 +590,10 @@ void SpannerSetAttribute::readMetavoxelDelta(MetavoxelData& data, } data.toggle(state.attribute, object); } + // even if the root is empty, it should still exist + if (!data.getRoot(state.attribute)) { + data.createRoot(state.attribute); + } } void SpannerSetAttribute::writeMetavoxelDelta(const MetavoxelNode& root, From af3af15084ea77f3dfe90509220b2a3d3ce75c58 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 10 Jul 2014 18:11:51 -0700 Subject: [PATCH 21/72] Comment, formatting fix. --- assignment-client/src/metavoxels/MetavoxelServer.cpp | 2 +- interface/src/ui/Stats.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 956458404a..4d9c45ed6c 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -51,7 +51,7 @@ void MetavoxelServer::run() { // initialize Bitstream before using it in multiple threads Bitstream::preThreadingInit(); - // create the persister and start the it in its own thread + // create the persister and start it in its own thread _persister = new MetavoxelPersister(this); QThread* persistenceThread = new QThread(this); _persister->moveToThread(persistenceThread); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 0d2762f923..eeff76395b 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -420,7 +420,6 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); - int internal = 0, leaves = 0; int sendProgress = 0, sendTotal = 0; int receiveProgress = 0, receiveTotal = 0; From 0e5af5afeab679aad91a891fd03dc9146d089839 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:32:55 -0700 Subject: [PATCH 22/72] More work on typed arrays --- libraries/script-engine/src/TypedArrays.cpp | 341 ++++++++++++++++---- libraries/script-engine/src/TypedArrays.h | 89 ++++- 2 files changed, 351 insertions(+), 79 deletions(-) diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 121870443f..d6ffc8f5a6 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "ScriptEngine.h" #include "TypedArrays.h" @@ -22,12 +24,12 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi QScriptValue global = engine()->globalObject(); - // build prototype TODO - // _proto = engine->newQObject(new DataViewPrototype(this), - // QScriptEngine::QtOwnership, - // QScriptEngine::SkipMethodsInEnumeration | - // QScriptEngine::ExcludeSuperClassMethods | - // QScriptEngine::ExcludeSuperClassProperties); + // build prototype + _proto = engine()->newQObject(new TypedArrayPrototype(this), + QScriptEngine::QtOwnership, + QScriptEngine::SkipMethodsInEnumeration | + QScriptEngine::ExcludeSuperClassMethods | + QScriptEngine::ExcludeSuperClassProperties); _proto.setPrototype(global.property("Object").property("prototype")); @@ -37,16 +39,49 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi engine()->globalObject().setProperty(_name, _ctor); } +QScriptValue TypedArray::newInstance(quint32 length) { + ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); + QScriptValue buffer = array->newInstance(length * _bytesPerElement); + return newInstance(buffer, 0, length); +} + +QScriptValue TypedArray::newInstance(QScriptValue array) { + if (array.property("length").isValid()) { + quint32 length = array.property("length").toInt32(); + QScriptValue newArray = newInstance(length); + for (int i = 0; i < length; ++i) { + QScriptValue value = array.property(QString::number(i)); + setProperty(newArray, engine()->toStringHandle(QString::number(i)), + i * _bytesPerElement, (value.isNumber()) ? value : QScriptValue(0)); + } + return newArray; + } + return QScriptValue(); +} + +QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { + QScriptValue data = engine()->newObject(); + data.setProperty(_bufferName, buffer); + data.setProperty(_byteOffsetName, byteOffset); + data.setProperty(_byteLengthName, length * _bytesPerElement); + data.setProperty(_lengthName, length); + + return engine()->newObject(this, data); +} + QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { TypedArray* cls = qscriptvalue_cast(context->callee().data()); - if (!cls || context->argumentCount() < 1) { + if (!cls) { return QScriptValue(); } + if (context->argumentCount() == 0) { + return cls->newInstance(0); + } + QScriptValue newObject; QScriptValue bufferArg = context->argument(0); QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); - TypedArray* typedArray = qscriptvalue_cast(bufferArg); if (arrayBuffer) { if (context->argumentCount() == 1) { @@ -58,15 +93,22 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin engine->evaluate("throw \"ArgumentError: 2nd arg is not a number\""); return QScriptValue(); } - if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() >= arrayBuffer->size()) { + if (byteOffsetArg.toInt32() < 0 || byteOffsetArg.toInt32() > arrayBuffer->size()) { engine->evaluate("throw \"RangeError: byteOffset out of range\""); return QScriptValue(); } + if (byteOffsetArg.toInt32() % cls->_bytesPerElement != 0) { + engine->evaluate("throw \"RangeError: byteOffset not a multiple of BYTES_PER_ELEMENT\""); + } quint32 byteOffset = byteOffsetArg.toInt32(); if (context->argumentCount() == 2) { // case for end of ArrayBuffer - newObject = cls->newInstance(bufferArg, byteOffset, arrayBuffer->size() - byteOffset); + if ((arrayBuffer->size() - byteOffset) % cls->_bytesPerElement != 0) { + engine->evaluate("throw \"RangeError: byteLength - byteOffset not a multiple of BYTES_PER_ELEMENT\""); + } + quint32 length = (arrayBuffer->size() - byteOffset) / cls->_bytesPerElement; + newObject = cls->newInstance(bufferArg, byteOffset, length); } else { QScriptValue lengthArg = (context->argumentCount() > 2) ? context->argument(2) : QScriptValue(); @@ -79,20 +121,17 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin engine->evaluate("throw \"RangeError: byteLength out of range\""); return QScriptValue(); } - quint32 length = lengthArg.toInt32() * cls->_bytesPerElement; + quint32 length = lengthArg.toInt32(); // case for well-defined range newObject = cls->newInstance(bufferArg, byteOffset, length); } } - } else if (typedArray) { - // case for another reference to another TypedArray - newObject = cls->newInstance(bufferArg, true); } else if (context->argument(0).isNumber()) { // case for new ArrayBuffer newObject = cls->newInstance(context->argument(0).toInt32()); } else { - // TODO: Standard array case + newObject = cls->newInstance(bufferArg); } if (context->isCalledAsConstructor()) { @@ -148,79 +187,257 @@ QScriptValue TypedArray::prototype() const { return _proto; } -Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { - _bytesPerElement = sizeof(qint8); +void TypedArray::setBytesPerElement(quint32 bytesPerElement) { + _bytesPerElement = bytesPerElement; + _ctor.setProperty(_bytesPerElementName, _bytesPerElement); } -QScriptValue Int8ArrayClass::newInstance(quint32 length) { - ArrayBufferClass* array = getScriptEngine()->getArrayBufferClass(); - QScriptValue buffer = array->newInstance(length * _bytesPerElement); - return newInstance(buffer, 0, length); +TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { } -QScriptValue Int8ArrayClass::newInstance(QScriptValue array, bool isTypedArray) { - // TODO - assert(false); - if (isTypedArray) { - QByteArray* originalArray = qscriptvalue_cast(array.data().property(_bufferName).data()); - ArrayBufferClass* arrayBufferClass = reinterpret_cast(engine())->getArrayBufferClass(); - quint32 byteOffset = array.data().property(_byteOffsetName).toInt32(); - quint32 byteLength = array.data().property(_byteLengthName).toInt32(); - quint32 length = byteLength / _bytesPerElement; - QByteArray newArray = originalArray->mid(byteOffset, byteLength); - - if (byteLength % _bytesPerElement != 0) { - length += 1; - byteLength = length * _bytesPerElement; +QByteArray* TypedArrayPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +void TypedArrayPrototype::set(QScriptValue array, quint32 offset) { + TypedArray* typedArray = static_cast(parent()); + if (array.isArray()) { + quint32 length = array.property("length").toInt32(); + if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { + // TODO throw an error maybe? + return; } - - - return newInstance(arrayBufferClass->newInstance(newArray), - array.data().property(_byteOffsetName).toInt32(), - array.data().property(_lengthName).toInt32()); + for (int i = 0; i < length; ++i) { + thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); + } + } + // TODO handle typed arrays +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + end = (end < 0) ? length + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + end = glm::clamp(end, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + length = (end - begin > 0) ? end - begin : 0; + return typedArray->newInstance(arrayBuffer, byteOffset, length); +} + +QScriptValue TypedArrayPrototype::get(quint32 index) { + TypedArray* typedArray = static_cast(parent()); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), + name, + QScriptClass::HandlesReadAccess, &id); + if (QScriptClass::HandlesReadAccess & flags) { + return typedArray->property(thisObject(), name, id); } return QScriptValue(); } -QScriptValue Int8ArrayClass::newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) { - QScriptValue data = engine()->newObject(); - data.setProperty(_bufferName, buffer); - data.setProperty(_byteOffsetName, byteOffset); - data.setProperty(_byteLengthName, length * _bytesPerElement); - data.setProperty(_lengthName, length); - - return engine()->newObject(this, data); +void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue object = thisObject(); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(object, + name, + QScriptClass::HandlesWriteAccess, &id); + if (QScriptClass::HandlesWriteAccess & flags) { + typedArray->setProperty(object, name, id, value); + } } -QScriptValue Int8ArrayClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); +template +QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString &name, uint id) { bool ok = false; name.toArrayIndex(&ok); - + if (ok && arrayBuffer) { QDataStream stream(*arrayBuffer); stream.skipRawData(id); - qint8 result; + T result; stream >> result; return result; } - - return TypedArray::property(object, name, id); + return QScriptValue(); } -void Int8ArrayClass::setProperty(QScriptValue &object, - const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - if (ba && value.isNumber()) { - QDataStream stream(ba, QIODevice::ReadWrite); +template +void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString &name, uint id, const QScriptValue &value) { + if (arrayBuffer && value.isNumber()) { + QDataStream stream(arrayBuffer, QIODevice::ReadWrite); stream.skipRawData(id); - stream << (qint8)value.toInt32(); + stream << (T)value.toNumber(); } } +Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_8_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint8)); +} + +QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint8)); +} + +QScriptValue Uint8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint16)); +} + +QScriptValue Int16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Int16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_16_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint16)); +} + +QScriptValue Uint16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(qint32)); +} + +QScriptValue Int32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Int32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint32)); +} + +QScriptValue Uint32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_32_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(float)); +} + +QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + +Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(double)); +} + +QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + setPropertyHelper(ba, name, id, value); +} + diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 2c63d83f93..37d341b2ad 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -18,20 +18,28 @@ static const QString BYTES_PER_ELEMENT_PROPERTY_NAME = "BYTES_PER_ELEMENT"; static const QString LENGTH_PROPERTY_NAME = "length"; static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array"; +static const QString UINT_8_ARRAY_CLASS_NAME = "Uint8Array"; +static const QString INT_16_ARRAY_CLASS_NAME = "Int16Array"; +static const QString UINT_16_ARRAY_CLASS_NAME = "Uint16Array"; +static const QString INT_32_ARRAY_CLASS_NAME = "Int32Array"; +static const QString UINT_32_ARRAY_CLASS_NAME = "Uint32Array"; +static const QString FLOAT_32_ARRAY_CLASS_NAME = "Float32Array"; +static const QString FLOAT_64_ARRAY_CLASS_NAME = "Float64Array"; class TypedArray : public ArrayBufferViewClass { Q_OBJECT public: TypedArray(ScriptEngine* scriptEngine, QString name); - virtual QScriptValue newInstance(quint32 length) = 0; - virtual QScriptValue newInstance(QScriptValue array, bool isTypedArray) = 0; - virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length) = 0; + virtual QScriptValue newInstance(quint32 length); + virtual QScriptValue newInstance(QScriptValue array); + virtual QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); virtual QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + virtual QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); + virtual void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value) = 0; virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); @@ -41,6 +49,8 @@ public: protected: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); + void setBytesPerElement(quint32 bytesPerElement); + QScriptValue _proto; QScriptValue _ctor; @@ -49,53 +59,98 @@ protected: QScriptString _lengthName; quint32 _bytesPerElement; + + friend class TypedArrayPrototype; +}; + +class TypedArrayPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + TypedArrayPrototype(QObject* parent = NULL); + +public slots: + void set(QScriptValue array, quint32 offset = 0); + QScriptValue subarray(qint32 begin); + QScriptValue subarray(qint32 begin, qint32 end); + + QScriptValue get(quint32 index); + void set(quint32 index, QScriptValue& value); +private: + QByteArray* thisArrayBuffer() const; }; class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue newInstance(quint32 length); - QScriptValue newInstance(QScriptValue array, bool isTypedArray); - QScriptValue newInstance(QScriptValue buffer, quint32 byteOffset, quint32 length); - - QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); - void setProperty(QScriptValue &object, - const QScriptString &name, - uint id, const QScriptValue &value); + + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint8ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint8ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint8ClampedArrayClass : public TypedArray { - + // TODO }; class Int16ArrayClass : public TypedArray { + Q_OBJECT +public: + Int16ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint16ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint16ArrayClass(ScriptEngine* scriptEngine); -}; + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; class Int32ArrayClass : public TypedArray { + Q_OBJECT +public: + Int32ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Uint32ArrayClass : public TypedArray { + Q_OBJECT +public: + Uint32ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Float32ArrayClass : public TypedArray { + Q_OBJECT +public: + Float32ArrayClass(ScriptEngine* scriptEngine); -}; + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; class Float64ArrayClass : public TypedArray { + Q_OBJECT +public: + Float64ArrayClass(ScriptEngine* scriptEngine); + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; #endif // hifi_TypedArrays_h \ No newline at end of file From ff378b60acf9eddebbd680788c1955c82190b93e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:33:24 -0700 Subject: [PATCH 23/72] Added functionnalities to JS UnitTests --- examples/Test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/examples/Test.js b/examples/Test.js index 056ec3cbbf..646ae5c71d 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -65,4 +65,16 @@ UnitTest.prototype.assertNull = function(value, message) { if (value !== null) { throw new AssertionException(value, null, message); } -}; +} + +UnitTest.prototype.arrayEqual = function(array1, array2, message) { + this.numAssertions++; + if (array1.length !== array2.length) { + throw new AssertionException(array1.length , array2.length , message); + } + for (var i = 0; i < array1.length; ++i) { + if (array1[i] !== array2[i]) { + throw new AssertionException(array1[i], array2[i], message); + } + } +} \ No newline at end of file From 3070ac457f12e141a8df6997bcd2580cdf177ead Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:33:57 -0700 Subject: [PATCH 24/72] Typed arrays bug fixing --- libraries/script-engine/src/DataViewClass.cpp | 9 ++---- libraries/script-engine/src/ScriptEngine.cpp | 28 ++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 4a9420e4e9..938770ecf5 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -74,12 +74,9 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en engine->evaluate("throw \"RangeError: byteLength out of range\""); return QScriptValue(); } - - QScriptValue newObject = cls->newInstance(bufferArg, - (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() - : 0, - (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() - : arrayBuffer->size()); + quint32 byteOffset = (byteOffsetArg.isNumber()) ? byteOffsetArg.toInt32() : 0; + quint32 byteLength = (byteLengthArg.isNumber()) ? byteLengthArg.toInt32() : arrayBuffer->size() - byteOffset; + QScriptValue newObject = cls->newInstance(bufferArg, byteOffset, byteLength); if (context->isCalledAsConstructor()) { context->setThisObject(newObject); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 73983161ff..4074beb88e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -97,11 +97,15 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _arrayBufferClass(NULL) { _arrayBufferClass = new ArrayBufferClass(this); - qDebug() << "Engine: " << this; - qDebug() << "ArrayBuffer: " <<_arrayBufferClass; - qDebug() << "DataView: " << new DataViewClass(this); - qDebug() << "Int8Array: " << new Int8ArrayClass(this); - qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + new DataViewClass(this); + new Int8ArrayClass(this); + new Uint8ArrayClass(this); + new Int16ArrayClass(this); + new Uint16ArrayClass(this); + new Int32ArrayClass(this); + new Uint32ArrayClass(this); + new Float32ArrayClass(this); + new Float64ArrayClass(this); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -134,11 +138,15 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QUrl url(scriptURL); _arrayBufferClass = new ArrayBufferClass(this); - qDebug() << "Engine: " << this; - qDebug() << "ArrayBuffer: " <<_arrayBufferClass; - qDebug() << "DataView: " << new DataViewClass(this); - qDebug() << "Int8Array: " << new Int8ArrayClass(this); - qDebug() << "Initial thread: " << QThread::currentThread() << " " << thread(); + new DataViewClass(this); + new Int8ArrayClass(this); + new Uint8ArrayClass(this); + new Int16ArrayClass(this); + new Uint16ArrayClass(this); + new Int32ArrayClass(this); + new Uint32ArrayClass(this); + new Float32ArrayClass(this); + new Float64ArrayClass(this); // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; From 02c951fba5ed9a4c22e1fadc5102454e1f9b45a2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Jul 2014 18:34:17 -0700 Subject: [PATCH 25/72] Added bunch of Typed arrays unit tests --- examples/typedArraysUnitTest.js | 343 ++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 6bd35365e8..de9b487c32 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -19,6 +19,9 @@ test('ArrayBuffer', function(finished) { this.assertEquals(typeof(new ArrayBuffer(123)), 'object', 'creation'); this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); + + // raises(function () { return new ArrayBuffer(-1); }, RangeError, 'negative length'); + // raises(function () { return new ArrayBuffer(0x80000000); }, RangeError, 'absurd length'); }); test('DataView constructors', function (finished) { @@ -39,3 +42,343 @@ test('DataView constructors', function (finished) { // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); }); + + +test('ArrayBufferView', function () { + var ab = new ArrayBuffer(48); + var i32 = new Int32Array(ab, 16); + i32.set([1, 2, 3, 4, 5, 6, 7, 8]); + + this.assertEquals(i32.buffer, ab, 'ArrayBuffers equal'); + this.assertEquals(i32.byteOffset, 16, 'byteOffset'); + this.assertEquals(i32.byteLength, 32, 'byteLength'); + + var da = new DataView(i32.buffer, 8); + this.assertEquals(da.buffer, ab, 'DataView: ArrayBuffers equal'); + this.assertEquals(da.byteOffset, 8, 'DataView: byteOffset'); + this.assertEquals(da.byteLength, 40, 'DataView: byteLength'); +}); + +test('TypedArrays', function () { + var a; + + this.assertEquals(Int8Array.BYTES_PER_ELEMENT, 1, 'Int8Array.BYTES_PER_ELEMENT'); + a = new Int8Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'int8Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int8Array.byteOffset'); + this.assertEquals(a.byteLength, 8, 'int8Array.byteLength'); + + this.assertEquals(Uint8Array.BYTES_PER_ELEMENT, 1, 'Uint8Array.BYTES_PER_ELEMENT'); + a = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1, 'uint8Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint8Array.byteOffset'); + this.assertEquals(a.byteLength, 8, 'uint8Array.byteLength'); + + this.assertEquals(Int16Array.BYTES_PER_ELEMENT, 2, 'Int16Array.BYTES_PER_ELEMENT'); + a = new Int16Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'int16Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int16Array.byteOffset'); + this.assertEquals(a.byteLength, 16, 'int16Array.byteLength'); + + this.assertEquals(Uint16Array.BYTES_PER_ELEMENT, 2, 'Uint16Array.BYTES_PER_ELEMENT'); + a = new Uint16Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 2, 'uint16Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint16Array.byteOffset'); + this.assertEquals(a.byteLength, 16, 'uint16Array.byteLength'); + + this.assertEquals(Int32Array.BYTES_PER_ELEMENT, 4, 'Int32Array.BYTES_PER_ELEMENT'); + a = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'int32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'int32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'int32Array.byteLength'); + + this.assertEquals(Uint32Array.BYTES_PER_ELEMENT, 4, 'Uint32Array.BYTES_PER_ELEMENT'); + a = new Uint32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'uint32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'uint32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'uint32Array.byteLength'); + + this.assertEquals(Float32Array.BYTES_PER_ELEMENT, 4, 'Float32Array.BYTES_PER_ELEMENT'); + a = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 4, 'float32Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'float32Array.byteOffset'); + this.assertEquals(a.byteLength, 32, 'float32Array.byteLength'); + + this.assertEquals(Float64Array.BYTES_PER_ELEMENT, 8, 'Float64Array.BYTES_PER_ELEMENT'); + a = new Float64Array([1, 2, 3, 4, 5, 6, 7, 8]); + this.assertEquals(a.BYTES_PER_ELEMENT, 8, 'float64Array.BYTES_PER_ELEMENT'); + this.assertEquals(a.byteOffset, 0, 'float64Array.byteOffset'); + this.assertEquals(a.byteLength, 64, 'float64Array.byteLength'); +}); + + +test('typed array constructors', function () { + this.arrayEqual(new Int8Array({ length: 3 }), [0, 0, 0], 'array equal -1'); + var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer; + + var int8 = new Int8Array(); + this.assertEquals(int8.length, 0, 'no args 0'); + // raises(function () { return new Int8Array(-1); }, /*Range*/Error, 'bogus length'); + // raises(function () { return new Int8Array(0x80000000); }, /*Range*/Error, 'bogus length'); + + int8 = new Int8Array(4); + this.assertEquals(int8.BYTES_PER_ELEMENT, 1); + this.assertEquals(int8.length, 4, 'length 1'); + this.assertEquals(int8.byteLength, 4, 'length 2'); + this.assertEquals(int8.byteOffset, 0, 'length 3'); + this.assertEquals(int8.get(-1), undefined, 'length, out of bounds 4'); + this.assertEquals(int8.get(4), undefined, 'length, out of bounds 5'); + + int8 = new Int8Array([1, 2, 3, 4, 5, 6]); + this.assertEquals(int8.length, 6, 'array 6'); + this.assertEquals(int8.byteLength, 6, 'array 7'); + this.assertEquals(int8.byteOffset, 0, 'array 8'); + this.assertEquals(int8.get(3), 4, 'array 9'); + this.assertEquals(int8.get(-1), undefined, 'array, out of bounds 10'); + this.assertEquals(int8.get(6), undefined, 'array, out of bounds 11'); + + int8 = new Int8Array(rawbuf); + this.assertEquals(int8.length, 8, 'buffer 12'); + this.assertEquals(int8.byteLength, 8, 'buffer 13'); + this.assertEquals(int8.byteOffset, 0, 'buffer 14'); + this.assertEquals(int8.get(7), 7, 'buffer 15'); + int8.set([111]); + this.assertEquals(int8.get(0), 111, 'buffer 16'); + this.assertEquals(int8.get(-1), undefined, 'buffer, out of bounds 17'); + this.assertEquals(int8.get(8), undefined, 'buffer, out of bounds 18'); + + int8 = new Int8Array(rawbuf, 2); + this.assertEquals(int8.length, 6, 'buffer, byteOffset 19'); + this.assertEquals(int8.byteLength, 6, 'buffer, byteOffset 20'); + this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset 21'); + this.assertEquals(int8.get(5), 7, 'buffer, byteOffset 22'); + int8.set([112]); + this.assertEquals(int8.get(0), 112, 'buffer 23'); + this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, out of bounds 24'); + this.assertEquals(int8.get(6), undefined, 'buffer, byteOffset, out of bounds 25'); + + int8 = new Int8Array(rawbuf, 8); + this.assertEquals(int8.length, 0, 'buffer, byteOffset 26'); + + // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); + // raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); + // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 29'); + // raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); + + int8 = new Int8Array(rawbuf, 2, 4); + this.assertEquals(int8.length, 4, 'buffer, byteOffset, length 31'); + this.assertEquals(int8.byteLength, 4, 'buffer, byteOffset, length 32'); + this.assertEquals(int8.byteOffset, 2, 'buffer, byteOffset, length 33'); + this.assertEquals(int8.get(3), 5, 'buffer, byteOffset, length 34'); + int8.set([113]); + this.assertEquals(int8.get(0), 113, 'buffer, byteOffset, length 35'); + this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, length, out of bounds 36'); + this.assertEquals(int8.get(4), undefined, 'buffer, byteOffset, length, out of bounds 37'); + + // raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + // raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + // raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); +}); + +test('TypedArray clone constructor', function () { + var src = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8]); + var dst = new Int32Array(src); + this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '1'); + src.set([99]); + this.arrayEqual(src, [99, 2, 3, 4, 5, 6, 7, 8], '2'); + this.arrayEqual(dst, [1, 2, 3, 4, 5, 6, 7, 8], '3'); +}); + + +test('conversions', function () { + var uint8 = new Uint8Array([1, 2, 3, 4]), + uint16 = new Uint16Array(uint8.buffer), + uint32 = new Uint32Array(uint8.buffer); + + // Note: can't probe individual bytes without endianness awareness + this.arrayEqual(uint8, [1, 2, 3, 4]); + uint16.set([0xffff]); + this.arrayEqual(uint8, [0xff, 0xff, 3, 4]); + uint16.set([0xeeee], 1); + this.arrayEqual(uint8, [0xff, 0xff, 0xee, 0xee]); + uint32.set([0x11111111]); + this.assertEquals(uint16.get(0), 0x1111); + this.assertEquals(uint16.get(1), 0x1111); + this.arrayEqual(uint8, [0x11, 0x11, 0x11, 0x11]); +}); + + +test('signed/unsigned conversions', function () { + + var int8 = new Int8Array(1), uint8 = new Uint8Array(int8.buffer); + uint8.set([123]); + this.assertEquals(int8.get(0), 123, 'int8/uint8'); + uint8.set([161]); + this.assertEquals(int8.get(0), -95, 'int8/uint8'); + int8.set([-120]); + this.assertEquals(uint8.get(0), 136, 'uint8/int8'); + int8.set([-1]); + this.assertEquals(uint8.get(0), 0xff, 'uint8/int8'); + + var int16 = new Int16Array(1), uint16 = new Uint16Array(int16.buffer); + uint16.set([3210]); + this.assertEquals(int16.get(0), 3210, 'int16/uint16'); + uint16.set([49232]); + this.assertEquals(int16.get(0), -16304, 'int16/uint16'); + int16.set([-16384]); + this.assertEquals(uint16.get(0), 49152, 'uint16/int16'); + int16.set([-1]); + this.assertEquals(uint16.get(0), 0xffff, 'uint16/int16'); + + var int32 = new Int32Array(1), uint32 = new Uint32Array(int32.buffer); + uint32.set([0x80706050]); + this.assertEquals(int32.get(0), -2140118960, 'int32/uint32'); + int32.set([-2023406815]); + this.assertEquals(uint32.get(0), 0x87654321, 'uint32/int32'); + int32.set([-1]); + this.assertEquals(uint32.get(0), 0xffffffff, 'uint32/int32'); +}); + + +test('Int32Array round trips', function () { + var i32 = new Int32Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -456, + 0x80000000 >> 0, + 0x7fffffff >> 0, + 0x12345678 >> 0, + 0x87654321 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i32.set([datum]); + this.assertEquals(datum, i32.get(0), String(datum)); + } +}); + + +test('Int16Array round trips', function () { + var i16 = new Int16Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -456, + 0xffff8000 >> 0, + 0x00007fff >> 0, + 0x00001234 >> 0, + 0xffff8765 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i16.set([datum]); + this.assertEquals(datum, i16.get(0), String(datum)); + } +}); + + +test('Int8Array round trips', function () { + var i8 = new Int8Array([0]); + var data = [ + 0, + 1, + -1, + 123, + -45, + 0xffffff80 >> 0, + 0x0000007f >> 0, + 0x00000012 >> 0, + 0xffffff87 >> 0 + ]; + + for (var i = 0; i < data.length; i += 1) { + var datum = data[i]; + i8.set([datum]); + this.assertEquals(datum, i8.get(0), String(datum)); + } +}); + +test('TypedArray setting', function () { + + var a = new Int32Array([1, 2, 3, 4, 5]); + var b = new Int32Array(5); + b.set(a); + this.arrayEqual(b, [1, 2, 3, 4, 5], '1'); + // raises(function () { b.set(a, 1); }); + + b.set(new Int32Array([99, 98]), 2); + this.arrayEqual(b, [1, 2, 99, 98, 5], '2'); + + b.set(new Int32Array([99, 98, 97]), 2); + this.arrayEqual(b, [1, 2, 99, 98, 97], '3'); + + // raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); + // raises(function () { b.set([101, 102, 103, 104], 4); }); + + // ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ] + // a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ] + // a2 = [ ^, ^, ^, ^ ] + var ab = new ArrayBuffer(8); + var a1 = new Uint8Array(ab); + for (var i = 0; i < a1.length; i += 1) { a1.set([i], i); } + var a2 = new Uint8Array(ab, 4); + a1.set(a2, 2); + this.arrayEqual(a1, [0, 1, 4, 5, 6, 7, 6, 7]); + this.arrayEqual(a2, [6, 7, 6, 7]); +}); + + +test('TypedArray.subarray', function () { + + var a = new Int32Array([1, 2, 3, 4, 5]); + this.arrayEqual(a.subarray(3), [4, 5]); + this.arrayEqual(a.subarray(1, 3), [2, 3]); + this.arrayEqual(a.subarray(-3), [3, 4, 5]); + this.arrayEqual(a.subarray(-3, -1), [3, 4]); + this.arrayEqual(a.subarray(3, 2), []); + this.arrayEqual(a.subarray(-2, -3), []); + this.arrayEqual(a.subarray(4, 1), []); + this.arrayEqual(a.subarray(-1, -4), []); + this.arrayEqual(a.subarray(1).subarray(1), [3, 4, 5]); + this.arrayEqual(a.subarray(1, 4).subarray(1, 2), [3]); +}); + + +test('DataView constructors', function () { + + var d = new DataView(new ArrayBuffer(8)); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0), 0x12345678, 'big endian/big endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0, true), 0x12345678, 'little endian/little endian'); + + d.setUint32(0, 0x12345678, true); + this.assertEquals(d.getUint32(0), 0x78563412, 'little endian/big endian'); + + d.setUint32(0, 0x12345678); + this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); + + // Chrome allows no arguments, throws if non-ArrayBuffer + //stricterEqual(new DataView().buffer.byteLength, 0, 'no arguments'); + + // Safari (iOS 5) does not + //raises(function () { return new DataView(); }, TypeError, 'no arguments'); + + // Chrome raises TypeError, Safari iOS5 raises isDOMException(INDEX_SIZE_ERR) + // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + + // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); +}); + + + + From d36943524064737fab4b03f3ed035c602c4ed867 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 11 Jul 2014 13:25:47 -0700 Subject: [PATCH 26/72] added dirty fix for NaN _nextOutputTrailingLoudness --- assignment-client/src/audio/AudioMixer.cpp | 26 ++++++++++++++++++- .../audio/src/PositionalAudioRingBuffer.cpp | 21 ++++++++++----- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 459f8a4b59..c856969de1 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -366,6 +366,11 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf } void AudioMixer::prepareMixForListeningNode(Node* node) { + + static int k = 0; + k++; + bool debug = (k % 20) == 0; + AvatarAudioRingBuffer* nodeRingBuffer = ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer(); // zero out the client mix for this node @@ -381,11 +386,30 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { for (int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i]; + if ((*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode()) && otherNodeBuffer->willBeAddedToMix() - && otherNodeBuffer->getNextOutputTrailingLoudness() > 0) { + && otherNodeBuffer->getNextOutputTrailingLoudness() > 0.0f) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); + } else { + + //if (debug) { + printf("\nWILL NOT MIX!!!\n"); + printf("listening node = %s\n", node->getUUID().toString().toLatin1().data()); + printf("other node = %s\n", otherNode->getUUID().toString().toLatin1().data()); + + if (otherNodeBuffer->getType() == PositionalAudioRingBuffer::Microphone) + printf("\t avatar buffer...\n"); + else + { + printf("\t inj buffer %s\n", ((InjectedAudioRingBuffer*)otherNodeBuffer)->getStreamIdentifier().toString().toLatin1().data()); + } + + printf("\t\t other==listening || shouldLoopBack: %d\n", (*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode())); + printf("\t\t other will be added to mix: %d\n", otherNodeBuffer->willBeAddedToMix()); + printf("\t\t other trailing loudess: %f\n", otherNodeBuffer->getNextOutputTrailingLoudness()); + //} } } } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 411b02400d..8cba6d72b0 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -32,6 +32,7 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer:: _shouldLoopbackForNode(false), _shouldOutputStarveDebug(true), _isStereo(isStereo), + _nextOutputTrailingLoudness(0.0f), _listenerUnattenuatedZone(NULL), _lastFrameReceivedTime(0), _interframeTimeGapStatsForJitterCalc(TIME_GAPS_FOR_JITTER_CALC_INTERVAL_SAMPLES, TIME_GAPS_FOR_JITTER_CALC_WINDOW_INTERVALS), @@ -121,27 +122,35 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { // ForBoundarySamples means that we expect the number of samples not to roll of the end of the ring buffer float nextLoudness = 0; - for (int i = 0; i < _numFrameSamples; ++i) { - nextLoudness += fabsf(_nextOutput[i]); + if (samplesAvailable() >= _numFrameSamples) { + for (int i = 0; i < _numFrameSamples; ++i) { + nextLoudness += fabsf(_nextOutput[i]); + } + nextLoudness /= _numFrameSamples; + nextLoudness /= MAX_SAMPLE_VALUE; } - nextLoudness /= _numFrameSamples; - nextLoudness /= MAX_SAMPLE_VALUE; - const int TRAILING_AVERAGE_FRAMES = 100; const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; const float LOUDNESS_EPSILON = 0.000001f; + float oldNextOutputTrailingLoudness = _nextOutputTrailingLoudness; if (nextLoudness >= _nextOutputTrailingLoudness) { _nextOutputTrailingLoudness = nextLoudness; } else { _nextOutputTrailingLoudness = (_nextOutputTrailingLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness); - + if (_nextOutputTrailingLoudness < LOUDNESS_EPSILON) { _nextOutputTrailingLoudness = 0; } } + + // fixes bug on Windows where _nextOutputTrailingLoudness sometimes becomes NaN. In that case, + // revert _nextOutputTrailingLoudness to its previous value + if (isNaN(_nextOutputTrailingLoudness)) { + _nextOutputTrailingLoudness = oldNextOutputTrailingLoudness; + } } bool PositionalAudioRingBuffer::shouldBeAddedToMix() { From 90c931ea45f24fb89f58f6fda562d4f5f95aedf3 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 11 Jul 2014 15:07:50 -0700 Subject: [PATCH 27/72] removed Audio.cpp initialization list warning; updated Injector removal conditions --- assignment-client/src/audio/AudioMixer.cpp | 4 ++-- assignment-client/src/audio/AudioMixerClientData.cpp | 3 +-- interface/src/Audio.cpp | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index c856969de1..6df23ca32f 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -394,7 +394,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); } else { - //if (debug) { + if (debug) { printf("\nWILL NOT MIX!!!\n"); printf("listening node = %s\n", node->getUUID().toString().toLatin1().data()); printf("other node = %s\n", otherNode->getUUID().toString().toLatin1().data()); @@ -409,7 +409,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { printf("\t\t other==listening || shouldLoopBack: %d\n", (*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode())); printf("\t\t other will be added to mix: %d\n", otherNodeBuffer->willBeAddedToMix()); printf("\t\t other trailing loudess: %f\n", otherNodeBuffer->getNextOutputTrailingLoudness()); - //} + } } } } diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 94bbdc6a6b..b6e9f8883e 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -154,13 +154,12 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { // this was a used buffer, push the output pointer forwards PositionalAudioRingBuffer* audioBuffer = *i; - const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 100; + const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 200; if (audioBuffer->willBeAddedToMix()) { audioBuffer->shiftReadPosition(audioBuffer->getSamplesPerFrame()); audioBuffer->setWillBeAddedToMix(false); } else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector - && audioBuffer->hasStarted() && audioBuffer->isStarved() && audioBuffer->getConsecutiveNotMixedCount() > INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD) { // this is an empty audio buffer that has starved, safe to delete // also delete its sequence number stats diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 3689ff0143..bc2da96f6e 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -115,9 +115,9 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : _audioMixerAvatarStreamAudioStats(), _outgoingAvatarAudioSequenceNumber(0), _incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH), - _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS), _starveCount(0), - _consecutiveNotMixedCount(0) + _consecutiveNotMixedCount(0), + _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS) { // clear the array of locally injected samples memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); From 38623e108a621fe55e193e4d87108c358697874c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 11 Jul 2014 15:37:59 -0700 Subject: [PATCH 28/72] Delete persister when its thread finishes, copy metavoxel nodes on subdivision so that we'll know that they changed. --- .../src/metavoxels/MetavoxelServer.cpp | 1 + .../metavoxels/src/AttributeRegistry.cpp | 6 ++- libraries/metavoxels/src/MetavoxelData.cpp | 46 +++++++++++++------ libraries/metavoxels/src/MetavoxelData.h | 7 +-- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index 4d9c45ed6c..598d8ea722 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -55,6 +55,7 @@ void MetavoxelServer::run() { _persister = new MetavoxelPersister(this); QThread* persistenceThread = new QThread(this); _persister->moveToThread(persistenceThread); + _persister->connect(persistenceThread, SIGNAL(finished()), SLOT(deleteLater())); persistenceThread->start(); // queue up the load diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 64b2646261..670f0be389 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -213,7 +213,11 @@ void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNo } void Attribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { - data.getRoot(state.attribute)->readSubdivision(state); + // copy if changed + MetavoxelNode* root = data.getRoot(state.attribute); + if (root->readSubdivision(state)) { + data.setRoot(state.attribute, new MetavoxelNode(state.attribute, root)); + } } void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state) { diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 1362731a8a..72d5392a39 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -603,12 +603,18 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO } } -MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { - MetavoxelNode*& root = _roots[attribute]; - if (root) { - root->decrementReferenceCount(attribute); +void MetavoxelData::setRoot(const AttributePointer& attribute, MetavoxelNode* root) { + MetavoxelNode*& rootReference = _roots[attribute]; + if (rootReference) { + rootReference->decrementReferenceCount(attribute); } - return root = new MetavoxelNode(attribute); + rootReference = root; +} + +MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { + MetavoxelNode* root = new MetavoxelNode(attribute); + setRoot(attribute, root); + return root; } bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod) const { @@ -843,10 +849,12 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta _children[i] = new MetavoxelNode(state.attribute); _children[i]->readDelta(*reference._children[i], nextState); } else { - _children[i] = reference._children[i]; - _children[i]->incrementReferenceCount(); - if (nextState.becameSubdivided()) { - _children[i]->readSubdivision(nextState); + if (nextState.becameSubdivided() && reference._children[i]->readSubdivision(nextState)) { + _children[i] = new MetavoxelNode(state.attribute, reference._children[i]); + + } else { + _children[i] = reference._children[i]; + _children[i]->incrementReferenceCount(); } } } @@ -888,7 +896,7 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt } } -void MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { +bool MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { bool leaf; bool subdivideReference = state.shouldSubdivideReference(); if (!subdivideReference) { @@ -897,7 +905,7 @@ void MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { leaf = isLeaf(); } if (leaf) { - clearChildren(state.attribute); + return clearChildren(state.attribute); } else { MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, @@ -909,13 +917,22 @@ void MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { _children[i] = new MetavoxelNode(state.attribute); _children[i]->read(nextState); } + return true; + } else { + bool changed = false; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); if (nextState.becameSubdivided()) { - _children[i]->readSubdivision(nextState); + if (_children[i]->readSubdivision(nextState)) { + MetavoxelNode* oldNode = _children[i]; + _children[i] = new MetavoxelNode(state.attribute, oldNode); + oldNode->decrementReferenceCount(state.attribute); + changed = true; + } } } + return changed; } } } @@ -1042,13 +1059,16 @@ void MetavoxelNode::destroy(const AttributePointer& attribute) { } } -void MetavoxelNode::clearChildren(const AttributePointer& attribute) { +bool MetavoxelNode::clearChildren(const AttributePointer& attribute) { + bool cleared = false; for (int i = 0; i < CHILD_COUNT; i++) { if (_children[i]) { _children[i]->decrementReferenceCount(attribute); _children[i] = NULL; + cleared = true; } } + return cleared; } bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index f558bf8e80..ac8f0cc023 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -118,7 +118,8 @@ public: void writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& out, const MetavoxelLOD& lod) const; - MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); } + void setRoot(const AttributePointer& attribute, MetavoxelNode* root); + MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); } MetavoxelNode* createRoot(const AttributePointer& attribute); /// Performs a deep comparison between this data and the specified other (as opposed to the == operator, which does a @@ -200,7 +201,7 @@ public: void readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state); void writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; - void readSubdivision(MetavoxelStreamState& state); + bool readSubdivision(MetavoxelStreamState& state); void writeSubdivision(MetavoxelStreamState& state) const; void writeSpanners(MetavoxelStreamState& state) const; @@ -216,7 +217,7 @@ public: void destroy(const AttributePointer& attribute); - void clearChildren(const AttributePointer& attribute); + bool clearChildren(const AttributePointer& attribute); /// Performs a deep comparison between this and the specified other node. bool deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, From 5e34bcc4233b8caec3442fcc47c8e25e26915c5b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 11 Jul 2014 15:51:36 -0700 Subject: [PATCH 29/72] Some additional comments. --- libraries/metavoxels/src/DatagramSequencer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/metavoxels/src/DatagramSequencer.h b/libraries/metavoxels/src/DatagramSequencer.h index 4a01679c68..9a1f5a4ae0 100644 --- a/libraries/metavoxels/src/DatagramSequencer.h +++ b/libraries/metavoxels/src/DatagramSequencer.h @@ -426,9 +426,9 @@ private: int _writePositionResetPacketNumber; SpanList _acknowledged; bool _messagesEnabled; - int _messageLengthPlaceholder; - int _messageReceivedOffset; - int _messageSize; + int _messageLengthPlaceholder; ///< the location in the buffer of the message length for the current message + int _messageReceivedOffset; ///< when reached, indicates that the most recent sent message has been received + int _messageSize; ///< the size of the most recent sent message; only valid when _messageReceivedOffset has been set }; #endif // hifi_DatagramSequencer_h From 82e9aa8bb7835a442c37a22fa0796e6fe9f78ea0 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 11 Jul 2014 16:08:15 -0700 Subject: [PATCH 30/72] made stats easier to read; reverted injected stream deletion conditions --- .../src/audio/AudioMixerClientData.cpp | 3 +- interface/src/ui/Stats.cpp | 40 +++++++++++-------- libraries/shared/src/SharedUtil.cpp | 14 +++++++ libraries/shared/src/SharedUtil.h | 4 ++ 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b6e9f8883e..94bbdc6a6b 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -154,12 +154,13 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { // this was a used buffer, push the output pointer forwards PositionalAudioRingBuffer* audioBuffer = *i; - const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 200; + const int INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD = 100; if (audioBuffer->willBeAddedToMix()) { audioBuffer->shiftReadPosition(audioBuffer->getSamplesPerFrame()); audioBuffer->setWillBeAddedToMix(false); } else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector + && audioBuffer->hasStarted() && audioBuffer->isStarved() && audioBuffer->getConsecutiveNotMixedCount() > INJECTOR_CONSECUTIVE_NOT_MIXED_THRESHOLD) { // this is an empty audio buffer that has starved, safe to delete // also delete its sequence number stats diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 15a54e42a6..69e7fde209 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -346,26 +346,28 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamLabelString, color); - char downstreamAudioStatsString[30]; + char downstreamAudioStatsString[512]; AudioStreamStats downstreamAudioStreamStats = audio->getDownstreamAudioStreamStats(); - sprintf(downstreamAudioStatsString, " mix: %.1f%%/%.1f%%, %u/?/%u", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, + sprintf(downstreamAudioStatsString, " mix: %.2f%%/%.2f%%, %u/?/%u", downstreamAudioStreamStats._packetStreamStats.getLostRate()*100.0f, downstreamAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, downstreamAudioStreamStats._ringBufferFramesAvailable, downstreamAudioStreamStats._ringBufferDesiredJitterBufferFrames); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); - sprintf(downstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", downstreamAudioStreamStats._timeGapMin, - downstreamAudioStreamStats._timeGapMax, downstreamAudioStreamStats._timeGapAverage, + sprintf(downstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(downstreamAudioStreamStats._timeGapMin).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapMax).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapAverage).toLatin1().data(), downstreamAudioStreamStats._ringBufferStarveCount, downstreamAudioStreamStats._ringBufferOverflowCount); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downstreamAudioStatsString, color); - sprintf(downstreamAudioStatsString, " %llu/%llu/%.2f, %u/?", downstreamAudioStreamStats._timeGapWindowMin, - downstreamAudioStreamStats._timeGapWindowMax, downstreamAudioStreamStats._timeGapWindowAverage, + sprintf(downstreamAudioStatsString, " %s/%s/%s, %u/?", formatUsecTime(downstreamAudioStreamStats._timeGapWindowMin).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapWindowMax).toLatin1().data(), + formatUsecTime(downstreamAudioStreamStats._timeGapWindowAverage).toLatin1().data(), downstreamAudioStreamStats._ringBufferConsecutiveNotMixedCount); verticalOffset += STATS_PELS_PER_LINE; @@ -376,11 +378,11 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamLabelString, color); - char upstreamAudioStatsString[30]; + char upstreamAudioStatsString[512]; const AudioStreamStats& audioMixerAvatarAudioStreamStats = audio->getAudioMixerAvatarStreamAudioStats(); - sprintf(upstreamAudioStatsString, " mic: %.1f%%/%.1f%%, %u/%u/%u", audioMixerAvatarAudioStreamStats._packetStreamStats.getLostRate()*100.0f, + sprintf(upstreamAudioStatsString, " mic: %.2f%%/%.2f%%, %u/%u/%u", audioMixerAvatarAudioStreamStats._packetStreamStats.getLostRate()*100.0f, audioMixerAvatarAudioStreamStats._packetStreamWindowStats.getLostRate() * 100.0f, audioMixerAvatarAudioStreamStats._ringBufferFramesAvailable, audioMixerAvatarAudioStreamStats._ringBufferCurrentJitterBufferFrames, audioMixerAvatarAudioStreamStats._ringBufferDesiredJitterBufferFrames); @@ -388,15 +390,17 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", audioMixerAvatarAudioStreamStats._timeGapMin, - audioMixerAvatarAudioStreamStats._timeGapMax, audioMixerAvatarAudioStreamStats._timeGapAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapMin).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapMax).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapAverage).toLatin1().data(), audioMixerAvatarAudioStreamStats._ringBufferStarveCount, audioMixerAvatarAudioStreamStats._ringBufferOverflowCount); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", audioMixerAvatarAudioStreamStats._timeGapWindowMin, - audioMixerAvatarAudioStreamStats._timeGapWindowMax, audioMixerAvatarAudioStreamStats._timeGapWindowAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowMin).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowMax).toLatin1().data(), + formatUsecTime(audioMixerAvatarAudioStreamStats._timeGapWindowAverage).toLatin1().data(), audioMixerAvatarAudioStreamStats._ringBufferConsecutiveNotMixedCount, audioMixerAvatarAudioStreamStats._ringBufferSilentFramesDropped); verticalOffset += STATS_PELS_PER_LINE; @@ -404,7 +408,7 @@ void Stats::display( foreach(const AudioStreamStats& injectedStreamAudioStats, audioMixerInjectedStreamAudioStatsMap) { - sprintf(upstreamAudioStatsString, " inj: %.1f%%/%.1f%%, %u/%u/%u", injectedStreamAudioStats._packetStreamStats.getLostRate()*100.0f, + sprintf(upstreamAudioStatsString, " inj: %.2f%%/%.2f%%, %u/%u/%u", injectedStreamAudioStats._packetStreamStats.getLostRate()*100.0f, injectedStreamAudioStats._packetStreamWindowStats.getLostRate() * 100.0f, injectedStreamAudioStats._ringBufferFramesAvailable, injectedStreamAudioStats._ringBufferCurrentJitterBufferFrames, injectedStreamAudioStats._ringBufferDesiredJitterBufferFrames); @@ -412,15 +416,17 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", injectedStreamAudioStats._timeGapMin, - injectedStreamAudioStats._timeGapMax, injectedStreamAudioStats._timeGapAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(injectedStreamAudioStats._timeGapMin).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapMax).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapAverage).toLatin1().data(), injectedStreamAudioStats._ringBufferStarveCount, injectedStreamAudioStats._ringBufferOverflowCount); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, upstreamAudioStatsString, color); - sprintf(upstreamAudioStatsString, " %llu/%llu/%.2f, %u/%u", injectedStreamAudioStats._timeGapWindowMin, - injectedStreamAudioStats._timeGapWindowMax, injectedStreamAudioStats._timeGapWindowAverage, + sprintf(upstreamAudioStatsString, " %s/%s/%s, %u/%u", formatUsecTime(injectedStreamAudioStats._timeGapWindowMin).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapWindowMax).toLatin1().data(), + formatUsecTime(injectedStreamAudioStats._timeGapWindowAverage).toLatin1().data(), injectedStreamAudioStats._ringBufferConsecutiveNotMixedCount, injectedStreamAudioStats._ringBufferSilentFramesDropped); verticalOffset += STATS_PELS_PER_LINE; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index e4d2e1c835..23d33969f4 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -837,3 +837,17 @@ bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, f QByteArray createByteArray(const glm::vec3& vector) { return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z); } + +QString formatUsecTime(float usecs, int prec) { + QString result; + if (usecs > USECS_PER_MINUTE) { + result = QString::number(usecs / USECS_PER_MINUTE, 'f', prec) + "min"; + } else if (usecs > USECS_PER_SECOND) { + result = QString::number(usecs / USECS_PER_SECOND, 'f', prec) + 's'; + } else if (usecs > USECS_PER_MSEC) { + result = QString::number(usecs / USECS_PER_MSEC, 'f', prec) + "ms"; + } else { + result = QString::number(usecs, 'f', prec) + "us"; + } + return result; +} diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index e5c2a0afc9..81ec2ab810 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -57,7 +57,9 @@ static const float METERS_PER_MILLIMETER = 0.001f; static const float MILLIMETERS_PER_METER = 1000.0f; static const quint64 USECS_PER_MSEC = 1000; static const quint64 MSECS_PER_SECOND = 1000; +static const quint64 SECONDS_PER_MINUTE = 60; static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; +static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; const int BITS_IN_BYTE = 8; @@ -189,4 +191,6 @@ bool isNaN(float value); QByteArray createByteArray(const glm::vec3& vector); +QString formatUsecTime(float usecs, int prec = 3); + #endif // hifi_SharedUtil_h From d832f5bec8f7e63aee3167a8299e0d9b697386b9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 11 Jul 2014 16:08:37 -0700 Subject: [PATCH 31/72] fix menu config of visible stats --- interface/src/Menu.cpp | 10 ++++----- interface/src/Menu.h | 12 +++++------ interface/src/ui/Stats.cpp | 43 ++++++++++++++------------------------ 3 files changed, 25 insertions(+), 40 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f2ec217656..f18dde7cbf 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -441,13 +441,11 @@ Menu::Menu() : QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools"); QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer"); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandDisplaySideTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarSimulateTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandAvatarUpdateTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMiscAvatarTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandIdleTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4ca50f71b8..651cf3288a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -357,13 +357,11 @@ namespace MenuOption { const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString EnableVRMode = "Enable VR Mode"; - const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing"; - const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing"; - const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing"; - const QString ExpandDisplaySideTiming = "Expand Display Side Timing"; - const QString ExpandIdleTiming = "Expand Idle Timing"; - const QString ExpandPaintGLTiming = "Expand PaintGL Timing"; - const QString ExpandUpdateTiming = "Expand Update Timing"; + const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; + const QString ExpandMyAvatarTiming = "Expand /myAvatar"; + const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; + const QString ExpandPaintGLTiming = "Expand /paintGL"; + const QString ExpandUpdateTiming = "Expand /update"; const QString Faceplus = "Faceplus"; const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 15a54e42a6..00f9450eda 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -162,36 +162,25 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh } bool Stats::includeTimingRecord(const QString& name) { - bool included = false; if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) { - - if (name == "idle/update") { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming) || - Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming); - } else if (name == "idle/updateGL") { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming); - } else if (name.startsWith("idle/update")) { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming); - } else if (name.startsWith("idle/")) { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandIdleTiming); - } else if (name.startsWith("MyAvatar::simulate")) { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarSimulateTiming); - } else if (name.startsWith("MyAvatar::update/") || name.startsWith("updateMyAvatar")) { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandAvatarUpdateTiming); - } else if (name.startsWith("MyAvatar::")) { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandMiscAvatarTiming); - } else if (name == "paintGL/displaySide") { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming) || - Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); - } else if (name.startsWith("paintGL/displaySide/")) { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandDisplaySideTiming); - } else if (name.startsWith("paintGL/")) { - included = Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); - } else { - included = true; // include everything else + if (name.startsWith("/idle/update/")) { + if (name.startsWith("/idle/update/myAvatar/")) { + if (name.startsWith("/idle/update/myAvatar/simulate/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandMyAvatarSimulateTiming); + } + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandMyAvatarTiming); + } else if (name.startsWith("/idle/update/otherAvatars/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandOtherAvatarTiming); + } + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandUpdateTiming); + } else if (name.startsWith("/idle/updateGL/paintGL/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); + } else if (name.startsWith("/paintGL/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); } + return true; } - return included; + return false; } // display expanded or contracted stats From b9fe5b2ef1dbf03456ea582d034826224b956573 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 16:23:19 -0700 Subject: [PATCH 32/72] Added JS exceptions + fixed up floating precision mess up --- .../script-engine/src/ArrayBufferClass.cpp | 12 +++- .../script-engine/src/ArrayBufferClass.h | 2 +- libraries/script-engine/src/DataViewClass.cpp | 2 +- .../script-engine/src/DataViewPrototype.cpp | 42 ++++++++----- .../script-engine/src/DataViewPrototype.h | 34 +++++----- libraries/script-engine/src/TypedArrays.cpp | 63 ++++++++++++++----- libraries/script-engine/src/TypedArrays.h | 2 +- 7 files changed, 105 insertions(+), 52 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index d86e229a1a..4f85eeeae2 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -48,7 +48,17 @@ _scriptEngine(scriptEngine) { engine()->globalObject().setProperty(name(), _ctor); } -QScriptValue ArrayBufferClass::newInstance(quint32 size) { +QScriptValue ArrayBufferClass::newInstance(qint32 size) { + const qint32 MAX_LENGTH = 100000000; + if (size < 0) { + engine()->evaluate("throw \"ArgumentError: negative length\""); + return QScriptValue(); + } + if (size > MAX_LENGTH) { + engine()->evaluate("throw \"ArgumentError: absurd length\""); + return QScriptValue(); + } + engine()->reportAdditionalMemoryCost(size); QScriptEngine* eng = engine(); QVariant variant = QVariant::fromValue(QByteArray(size, 0)); diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index c56b659eff..c7b4d447dd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -26,7 +26,7 @@ class ArrayBufferClass : public QObject, public QScriptClass { Q_OBJECT public: ArrayBufferClass(ScriptEngine* scriptEngine); - QScriptValue newInstance(quint32 size); + QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); ScriptEngine* getScriptEngine() { return _scriptEngine; } diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index 938770ecf5..efb45daf3f 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -64,7 +64,7 @@ QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *en } if (byteOffsetArg.isNumber() && (byteOffsetArg.toInt32() < 0 || - byteOffsetArg.toInt32() >= arrayBuffer->size())) { + byteOffsetArg.toInt32() > arrayBuffer->size())) { engine->evaluate("throw \"RangeError: byteOffset out of range\""); return QScriptValue(); } diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 17734e109c..95f3fdf3f6 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -26,7 +26,10 @@ QByteArray* DataViewPrototype::thisArrayBuffer() const { return qscriptvalue_cast(bufferObject.data()); } -bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { +bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { + if (offset < 0) { + return false; + } quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; @@ -38,7 +41,7 @@ bool DataViewPrototype::realOffset(quint32& offset, size_t size) const { ///////////////// GETTERS //////////////////////////// -qint32 DataViewPrototype::getInt8(quint32 byteOffset) { +qint32 DataViewPrototype::getInt8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -51,7 +54,7 @@ qint32 DataViewPrototype::getInt8(quint32 byteOffset) { return 0; } -quint32 DataViewPrototype::getUint8(quint32 byteOffset) { +quint32 DataViewPrototype::getUint8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -64,7 +67,7 @@ quint32 DataViewPrototype::getUint8(quint32 byteOffset) { return 0; } -qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { +qint32 DataViewPrototype::getInt16(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -78,7 +81,7 @@ qint32 DataViewPrototype::getInt16(quint32 byteOffset, bool littleEndian) { return 0; } -quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint16(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -92,7 +95,7 @@ quint32 DataViewPrototype::getUint16(quint32 byteOffset, bool littleEndian) { return 0; } -qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { +qint32 DataViewPrototype::getInt32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -106,7 +109,7 @@ qint32 DataViewPrototype::getInt32(quint32 byteOffset, bool littleEndian) { return 0; } -quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { +quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -120,25 +123,28 @@ quint32 DataViewPrototype::getUint32(quint32 byteOffset, bool littleEndian) { return 0; } -float DataViewPrototype::getFloat32(quint32 byteOffset, bool littleEndian) { +float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); float result; stream >> result; + qDebug() << "Get: " << result; return result; } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); return 0; } -double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { +double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); double result; stream >> result; @@ -150,7 +156,7 @@ double DataViewPrototype::getFloat64(quint32 byteOffset, bool littleEndian) { ///////////////// SETTERS //////////////////////////// -void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { +void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -161,7 +167,7 @@ void DataViewPrototype::setInt8(quint32 byteOffset, qint32 value) { } } -void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { +void DataViewPrototype::setUint8(qint32 byteOffset, quint32 value) { if (realOffset(byteOffset, sizeof(quint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -172,7 +178,7 @@ void DataViewPrototype::setUint8(quint32 byteOffset, quint32 value) { } } -void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEndian) { +void DataViewPrototype::setInt16(qint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -184,7 +190,7 @@ void DataViewPrototype::setInt16(quint32 byteOffset, qint32 value, bool littleEn } } -void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setUint16(qint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint16))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -196,7 +202,7 @@ void DataViewPrototype::setUint16(quint32 byteOffset, quint32 value, bool little } } -void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEndian) { +void DataViewPrototype::setInt32(qint32 byteOffset, qint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(qint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -208,7 +214,7 @@ void DataViewPrototype::setInt32(quint32 byteOffset, qint32 value, bool littleEn } } -void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool littleEndian) { +void DataViewPrototype::setUint32(qint32 byteOffset, quint32 value, bool littleEndian) { if (realOffset(byteOffset, sizeof(quint32))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); @@ -220,11 +226,12 @@ void DataViewPrototype::setUint32(quint32 byteOffset, quint32 value, bool little } } -void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleEndian) { +void DataViewPrototype::setFloat32(qint32 byteOffset, float value, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); stream << value; } else { @@ -232,11 +239,12 @@ void DataViewPrototype::setFloat32(quint32 byteOffset, float value, bool littleE } } -void DataViewPrototype::setFloat64(quint32 byteOffset, double value, bool littleEndian) { +void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); stream.skipRawData(byteOffset); stream.setByteOrder((littleEndian) ? QDataStream::LittleEndian : QDataStream::BigEndian); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); stream << value; } else { diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index 97c94c58ce..b7061a388e 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -31,14 +31,14 @@ public slots: // // These methods raise an exception if they would read // beyond the end of the view. - qint32 getInt8(quint32 byteOffset); - quint32 getUint8(quint32 byteOffset); - qint32 getInt16(quint32 byteOffset, bool littleEndian = false); - quint32 getUint16(quint32 byteOffset, bool littleEndian = false); - qint32 getInt32(quint32 byteOffset, bool littleEndian = false); - quint32 getUint32(quint32 byteOffset, bool littleEndian = false); - float getFloat32(quint32 byteOffset, bool littleEndian = false); - double getFloat64(quint32 byteOffset, bool littleEndian = false); + qint32 getInt8(qint32 byteOffset); + quint32 getUint8(qint32 byteOffset); + qint32 getInt16(qint32 byteOffset, bool littleEndian = false); + quint32 getUint16(qint32 byteOffset, bool littleEndian = false); + qint32 getInt32(qint32 byteOffset, bool littleEndian = false); + quint32 getUint32(qint32 byteOffset, bool littleEndian = false); + float getFloat32(qint32 byteOffset, bool littleEndian = false); + double getFloat64(qint32 byteOffset, bool littleEndian = false); // Stores a value of the given type at the specified byte offset // from the start of the view. There is no alignment constraint; @@ -51,18 +51,18 @@ public slots: // // These methods raise an exception if they would write // beyond the end of the view. - void setInt8(quint32 byteOffset, qint32 value); - void setUint8(quint32 byteOffset, quint32 value); - void setInt16(quint32 byteOffset, qint32 value, bool littleEndian = false); - void setUint16(quint32 byteOffset, quint32 value, bool littleEndian = false); - void setInt32(quint32 byteOffset, qint32 value, bool littleEndian = false); - void setUint32(quint32 byteOffset, quint32 value, bool littleEndian = false); - void setFloat32(quint32 byteOffset, float value, bool littleEndian = false); - void setFloat64(quint32 byteOffset, double value, bool littleEndian = false); + void setInt8(qint32 byteOffset, qint32 value); + void setUint8(qint32 byteOffset, quint32 value); + void setInt16(qint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint16(qint32 byteOffset, quint32 value, bool littleEndian = false); + void setInt32(qint32 byteOffset, qint32 value, bool littleEndian = false); + void setUint32(qint32 byteOffset, quint32 value, bool littleEndian = false); + void setFloat32(qint32 byteOffset, float value, bool littleEndian = false); + void setFloat64(qint32 byteOffset, double value, bool littleEndian = false); private: QByteArray* thisArrayBuffer() const; - bool realOffset(quint32& offset, size_t size) const; + bool realOffset(qint32& offset, size_t size) const; }; #endif // hifi_DataViewPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index d6ffc8f5a6..102c477c04 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -56,6 +56,7 @@ QScriptValue TypedArray::newInstance(QScriptValue array) { } return newArray; } + engine()->evaluate("throw \"ArgumentError: not an array\""); return QScriptValue(); } @@ -200,19 +201,23 @@ QByteArray* TypedArrayPrototype::thisArrayBuffer() const { return qscriptvalue_cast(bufferObject.data()); } -void TypedArrayPrototype::set(QScriptValue array, quint32 offset) { +void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { TypedArray* typedArray = static_cast(parent()); - if (array.isArray()) { + if (array.isArray() || typedArray) { + if (offset < 0) { + engine()->evaluate("throw \"ArgumentError: negative offset\""); + } quint32 length = array.property("length").toInt32(); if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { - // TODO throw an error maybe? + engine()->evaluate("throw \"ArgumentError: array does not fit\""); return; } for (int i = 0; i < length; ++i) { thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); } + } else { + engine()->evaluate("throw \"ArgumentError: not an array\""); } - // TODO handle typed arrays } QScriptValue TypedArrayPrototype::subarray(qint32 begin) { @@ -411,15 +416,31 @@ Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); - QScriptValue result = propertyHelper(arrayBuffer, name, id); - return (result.isValid()) ? result : TypedArray::property(object, name, id); + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); + + float result; + stream >> result; + return result; + } + return TypedArray::property(object, name, id); } void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - setPropertyHelper(ba, name, id, value); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::SinglePrecision); + + stream << (float)value.toNumber(); + } } Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, FLOAT_64_ARRAY_CLASS_NAME) { @@ -428,16 +449,30 @@ Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); - QScriptValue result = propertyHelper(arrayBuffer, name, id); - return (result.isValid()) ? result : TypedArray::property(object, name, id); + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; + name.toArrayIndex(&ok); + + if (ok && arrayBuffer) { + QDataStream stream(*arrayBuffer); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); + + double result; + stream >> result; + return result; + } + return TypedArray::property(object, name, id); } void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value) { QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); - setPropertyHelper(ba, name, id, value); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + stream.setFloatingPointPrecision(QDataStream::DoublePrecision); + + stream << (double)value.toNumber(); + } } - - diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 37d341b2ad..2eff3e84b9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -69,7 +69,7 @@ public: TypedArrayPrototype(QObject* parent = NULL); public slots: - void set(QScriptValue array, quint32 offset = 0); + void set(QScriptValue array, qint32 offset = 0); QScriptValue subarray(qint32 begin); QScriptValue subarray(qint32 begin, qint32 end); From 75573a4c06a67cc17f8725296b5589dd52e0219c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 16:23:50 -0700 Subject: [PATCH 33/72] Added exception checking to unit tests --- examples/Test.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/examples/Test.js b/examples/Test.js index 646ae5c71d..caecfc5af7 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -20,7 +20,7 @@ test = function(name, func) { unitTest.run(); print(" Success: " + unitTest.numAssertions + " assertions passed"); } catch (error) { - print(" Failure: " + error.message); + print(" Failure: " + error.name + " " + error.message); } }; @@ -30,6 +30,12 @@ AssertionException = function(expected, actual, message) { this.name = 'AssertionException'; }; +UnthrownException = function(message) { + print("Creating exception"); + this.message = message + "\n"; + this.name = 'UnthrownException'; +}; + UnitTest = function(name, func) { this.numAssertions = 0; this.func = func; @@ -77,4 +83,15 @@ UnitTest.prototype.arrayEqual = function(array1, array2, message) { throw new AssertionException(array1[i], array2[i], message); } } +} + +UnitTest.prototype.raises = function(func, message) { + this.numAssertions++; + try { + func(); + } catch (error) { + return; + } + + throw new UnthrownException(message); } \ No newline at end of file From 9939c00a40fad20c57db1b63cab664b67e74403c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 11 Jul 2014 16:57:08 -0700 Subject: [PATCH 34/72] more guards to prevent crashing in bad particle edit packets --- libraries/particles/src/Particle.cpp | 114 ++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 4dfe4b588e..76890afafe 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -357,15 +357,23 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr processedBytes = 0; // the first part of the data is our octcode... - int octets = numberOfThreeBitSectionsInCode(data); + int octets = numberOfThreeBitSectionsInCode(data, length); int lengthOfOctcode = bytesRequiredForCodeLength(octets); // we don't actually do anything with this octcode... dataAt += lengthOfOctcode; processedBytes += lengthOfOctcode; - + // id uint32_t editID; + + // check to make sure we have enough content to keep reading... + if (processedBytes + sizeof(editID) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } + memcpy(&editID, dataAt, sizeof(editID)); dataAt += sizeof(editID); processedBytes += sizeof(editID); @@ -377,6 +385,14 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // If this is a NEW_PARTICLE, then we assume that there's an additional uint32_t creatorToken, that // we want to send back to the creator as an map to the actual id uint32_t creatorTokenID; + + // check to make sure we have enough content to keep reading... + if (processedBytes + sizeof(creatorTokenID) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } + memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID)); dataAt += sizeof(creatorTokenID); processedBytes += sizeof(creatorTokenID); @@ -409,6 +425,12 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr } // lastEdited + // check to make sure we have enough content to keep reading... + if (processedBytes + sizeof(newParticle._lastEdited) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited)); dataAt += sizeof(newParticle._lastEdited); processedBytes += sizeof(newParticle._lastEdited); @@ -417,6 +439,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // properties included bits uint16_t packetContainsBits = 0; if (!isNewParticle) { + if (processedBytes + sizeof(packetContainsBits) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&packetContainsBits, dataAt, sizeof(packetContainsBits)); dataAt += sizeof(packetContainsBits); processedBytes += sizeof(packetContainsBits); @@ -425,6 +452,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // radius if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { + if (processedBytes + sizeof(newParticle._radius) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius)); dataAt += sizeof(newParticle._radius); processedBytes += sizeof(newParticle._radius); @@ -432,6 +464,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // position if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { + if (processedBytes + sizeof(newParticle._position) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._position, dataAt, sizeof(newParticle._position)); dataAt += sizeof(newParticle._position); processedBytes += sizeof(newParticle._position); @@ -439,6 +476,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // color if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { + if (processedBytes + sizeof(newParticle._color) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(newParticle._color, dataAt, sizeof(newParticle._color)); dataAt += sizeof(newParticle._color); processedBytes += sizeof(newParticle._color); @@ -446,6 +488,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // velocity if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { + if (processedBytes + sizeof(newParticle._velocity) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity)); dataAt += sizeof(newParticle._velocity); processedBytes += sizeof(newParticle._velocity); @@ -453,6 +500,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // gravity if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { + if (processedBytes + sizeof(newParticle._gravity) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity)); dataAt += sizeof(newParticle._gravity); processedBytes += sizeof(newParticle._gravity); @@ -460,6 +512,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // damping if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { + if (processedBytes + sizeof(newParticle._damping) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping)); dataAt += sizeof(newParticle._damping); processedBytes += sizeof(newParticle._damping); @@ -467,6 +524,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // lifetime if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { + if (processedBytes + sizeof(newParticle._lifetime) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._lifetime, dataAt, sizeof(newParticle._lifetime)); dataAt += sizeof(newParticle._lifetime); processedBytes += sizeof(newParticle._lifetime); @@ -475,6 +537,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // TODO: make inHand and shouldDie into single bits // inHand if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { + if (processedBytes + sizeof(newParticle._inHand) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._inHand, dataAt, sizeof(newParticle._inHand)); dataAt += sizeof(newParticle._inHand); processedBytes += sizeof(newParticle._inHand); @@ -482,6 +549,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // shouldDie if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { + if (processedBytes + sizeof(newParticle._shouldDie) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._shouldDie, dataAt, sizeof(newParticle._shouldDie)); dataAt += sizeof(newParticle._shouldDie); processedBytes += sizeof(newParticle._shouldDie); @@ -490,9 +562,20 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // script if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { uint16_t scriptLength; + if (processedBytes + sizeof(scriptLength) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&scriptLength, dataAt, sizeof(scriptLength)); dataAt += sizeof(scriptLength); processedBytes += sizeof(scriptLength); + + if (processedBytes + scriptLength > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } QString tempString((const char*)dataAt); newParticle._script = tempString; dataAt += scriptLength; @@ -502,9 +585,20 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelURL if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) { uint16_t modelURLLength; + if (processedBytes + sizeof(modelURLLength) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&modelURLLength, dataAt, sizeof(modelURLLength)); dataAt += sizeof(modelURLLength); processedBytes += sizeof(modelURLLength); + + if (processedBytes + modelURLLength > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } QString tempString((const char*)dataAt); newParticle._modelURL = tempString; dataAt += modelURLLength; @@ -513,6 +607,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelScale if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) { + if (processedBytes + sizeof(newParticle._modelScale) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._modelScale, dataAt, sizeof(newParticle._modelScale)); dataAt += sizeof(newParticle._modelScale); processedBytes += sizeof(newParticle._modelScale); @@ -520,6 +619,11 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelTranslation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) { + if (processedBytes + sizeof(newParticle._modelTranslation) > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } memcpy(&newParticle._modelTranslation, dataAt, sizeof(newParticle._modelTranslation)); dataAt += sizeof(newParticle._modelTranslation); processedBytes += sizeof(newParticle._modelTranslation); @@ -527,6 +631,12 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelRotation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) { + const int expectedBytesForPackedQuat = sizeof(uint16_t) * 4; // this is how we pack the quats + if (processedBytes + expectedBytesForPackedQuat > length) { + valid = false; + processedBytes = length; + return newParticle; // fail as if we read the entire buffer + } int bytes = unpackOrientationQuatFromBytes(dataAt, newParticle._modelRotation); dataAt += bytes; processedBytes += bytes; From b86890033c2fde9f2ef2b9bd0530216cf0663c4c Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 11 Jul 2014 19:35:17 -0500 Subject: [PATCH 35/72] Moved hair to separate class --- interface/src/Hair.cpp | 203 +++++++++++++++++++++++- interface/src/Hair.h | 33 +++- interface/src/avatar/Avatar.cpp | 254 +++--------------------------- interface/src/avatar/Avatar.h | 14 +- interface/src/avatar/MyAvatar.cpp | 22 ++- 5 files changed, 262 insertions(+), 264 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index a23312eba1..635ea7ee0f 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -15,21 +15,212 @@ #include "Util.h" #include "world.h" +const float HAIR_DAMPING = 0.99f; +const float CONSTRAINT_RELAXATION = 10.0f; +const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); +const float HAIR_ACCELERATION_COUPLING = 0.025f; +const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; +const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; +const float HAIR_STIFFNESS = 0.0000f; +const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); +const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); +const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); +const float MAX_WIND_STRENGTH = 0.02f; -Hair::Hair() { - qDebug() << "Creating Hair"; + +Hair::Hair(int strands, + int links, + float radius, + float linkLength, + float hairThickness) : + _acceleration(0.0f), + _angularVelocity(0.0f), + _gravity(0.0f) +{ + _strands = strands; + _links = links; + _radius = radius; + _linkLength = linkLength; + _hairThickness = hairThickness; + _hairPosition = new glm::vec3[_strands * _links]; + _hairOriginalPosition = new glm::vec3[_strands * _links]; + _hairLastPosition = new glm::vec3[_strands * _links]; + _hairQuadDelta = new glm::vec3[_strands * _links]; + _hairNormals = new glm::vec3[_strands * _links]; + _hairColors = new glm::vec3[_strands * _links]; + _hairIsMoveable = new int[_strands * _links]; + _hairConstraints = new int[_strands * _links * HAIR_CONSTRAINTS]; // Hair can link to two others + const float FACE_WIDTH = PI / 4.0f; + glm::vec3 thisVertex; + for (int strand = 0; strand < _strands; strand++) { + float strandAngle = randFloat() * PI; + float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); + float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); + glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); + thisStrand *= _radius; + + for (int link = 0; link < _links; link++) { + int vertexIndex = strand * _links + link; + // Clear constraints + for (int link2 = 0; link2 < HAIR_CONSTRAINTS; link2++) { + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link2] = -1; + } + if (vertexIndex % _links == 0) { + // start of strand + thisVertex = thisStrand; + } else { + thisVertex+= glm::normalize(thisStrand) * _linkLength; + // Set constraints to vertex before and maybe vertex after in strand + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS] = vertexIndex - 1; + if (link < (_links - 1)) { + _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + 1] = vertexIndex + 1; + } + } + _hairPosition[vertexIndex] = thisVertex; + _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; + _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; + + _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness); + _hairQuadDelta[vertexIndex] *= 1.f - (link / _links); + _hairNormals[vertexIndex] = glm::normalize(randVector()); + if (randFloat() < elevation / PI_OVER_TWO) { + _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links); + } else { + _hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)_links); + } + } + } } void Hair::simulate(float deltaTime) { + deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f); + glm::vec3 acceleration = _acceleration; + if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) { + acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; + } + + float windIntensity = randFloat() * MAX_WIND_STRENGTH; + + for (int strand = 0; strand < _strands; strand++) { + for (int link = 0; link < _links; link++) { + int vertexIndex = strand * _links + link; + if (vertexIndex % _links == 0) { + // Base Joint - no integration + } else { + // + // Vertlet Integration + // + // Add velocity from last position, with damping + glm::vec3 thisPosition = _hairPosition[vertexIndex]; + glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; + _hairPosition[vertexIndex] += diff * HAIR_DAMPING; + // Resolve collision with head sphere + if (glm::length(_hairPosition[vertexIndex]) < _radius) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * + (_radius - glm::length(_hairPosition[vertexIndex])); + } + // Resolve collision with hands + /* + if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * + (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); + } + if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { + _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * + (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); + } + */ + + + // Add a little gravity + _hairPosition[vertexIndex] += HAIR_GRAVITY * deltaTime; + + // Add linear acceleration of the avatar body + _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; + + // Add stiffness (like hair care products do) + _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) + * powf(1.f - link / _links, 2.f) * HAIR_STIFFNESS; + + // Add some wind + glm::vec3 wind = WIND_DIRECTION * windIntensity; + _hairPosition[vertexIndex] += wind * deltaTime; + + const float ANGULAR_VELOCITY_MIN = 0.001f; + // Add angular acceleration of the avatar body + if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) { + glm::vec3 yawVector = _hairPosition[vertexIndex]; + yawVector.y = 0.f; + if (glm::length(yawVector) > EPSILON) { + float radius = glm::length(yawVector); + yawVector = glm::normalize(yawVector); + float angle = atan2f(yawVector.x, -yawVector.z) + PI; + glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + glm::vec3 pitchVector = _hairPosition[vertexIndex]; + pitchVector.x = 0.f; + if (glm::length(pitchVector) > EPSILON) { + float radius = glm::length(pitchVector); + pitchVector = glm::normalize(pitchVector); + float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; + glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + glm::vec3 rollVector = _hairPosition[vertexIndex]; + rollVector.z = 0.f; + if (glm::length(rollVector) > EPSILON) { + float radius = glm::length(rollVector); + pitchVector = glm::normalize(rollVector); + float angle = atan2f(rollVector.x, rollVector.y) + PI; + glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); + _hairPosition[vertexIndex] -= delta * radius * _angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; + } + } + + // Iterate length constraints to other links + for (int link = 0; link < HAIR_CONSTRAINTS; link++) { + if (_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link] > -1) { + // If there is a constraint, try to enforce it + glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link]] - _hairPosition[vertexIndex]; + _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - _linkLength) * CONSTRAINT_RELAXATION * deltaTime; + } + } + // Store start position for next vertlet pass + _hairLastPosition[vertexIndex] = thisPosition; + } + } + } + } void Hair::render() { // // Before calling this function, translate/rotate to the origin of the owning object - glPushMatrix(); - glColor3f(1.0f, 1.0f, 0.0f); - glutSolidSphere(1.0f, 15, 15); - glPopMatrix(); + // + glBegin(GL_QUADS); + for (int strand = 0; strand < _strands; strand++) { + for (int link = 0; link < _links - 1; link++) { + int vertexIndex = strand * _links + link; + glColor3fv(&_hairColors[vertexIndex].x); + glNormal3fv(&_hairNormals[vertexIndex].x); + glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z); + glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z); + + glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z); + glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x, + _hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y, + _hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z); + } + } + glEnd(); } + diff --git a/interface/src/Hair.h b/interface/src/Hair.h index e4dc3778c3..dfb90cae21 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -21,14 +21,45 @@ #include "InterfaceConfig.h" #include "Util.h" +const int HAIR_CONSTRAINTS = 2; + +const int DEFAULT_HAIR_STRANDS = 50; +const int DEFAULT_HAIR_LINKS = 10; +const float DEFAULT_HAIR_RADIUS = 0.25; +const float DEFAULT_HAIR_LINK_LENGTH = 0.05; +const float DEFAULT_HAIR_THICKNESS = 0.015; class Hair { public: - Hair(); + Hair(int strands = DEFAULT_HAIR_STRANDS, + int links = DEFAULT_HAIR_LINKS, + float radius = DEFAULT_HAIR_RADIUS, + float linkLength = DEFAULT_HAIR_LINK_LENGTH, + float hairThickness = DEFAULT_HAIR_THICKNESS); void simulate(float deltaTime); void render(); + void setAcceleration(const glm::vec3& acceleration) { _acceleration = acceleration; } + void setAngularVelocity(const glm::vec3& angularVelocity) { _angularVelocity = angularVelocity; } + void setGravity(const glm::vec3& gravity) { _gravity = gravity; } private: + int _strands; + int _links; + float _linkLength; + float _hairThickness; + float _radius; + glm::vec3* _hairPosition; + glm::vec3* _hairOriginalPosition; + glm::vec3* _hairLastPosition; + glm::vec3* _hairQuadDelta; + glm::vec3* _hairNormals; + glm::vec3* _hairColors; + int* _hairIsMoveable; + int* _hairConstraints; + glm::vec3 _acceleration; + glm::vec3 _angularVelocity; + glm::vec3 _gravity; + }; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4efb03b3f5..a826ad699f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -46,6 +46,7 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; Avatar::Avatar() : AvatarData(), + _hair(), _skeletonModel(this), _bodyYawDelta(0.0f), _velocity(0.0f, 0.0f, 0.0f), @@ -60,9 +61,9 @@ Avatar::Avatar() : _mouseRayDirection(0.0f, 0.0f, 0.0f), _moving(false), _collisionGroups(0), + _numLocalLights(1), _initialized(false), - _shouldRenderBillboard(true), - _numLocalLights(1) + _shouldRenderBillboard(true) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -82,8 +83,7 @@ void Avatar::init() { _skeletonModel.init(); _initialized = true; _shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE); - initializeHair(); - + for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { _localLightColors[i] = glm::vec3(0.0f, 0.0f, 0.0f); _localLightDirections[i] = glm::vec3(0.0f, 0.0f, 0.0f); @@ -168,13 +168,11 @@ void Avatar::simulate(float deltaTime) { } if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { PerformanceTimer perfTimer("hair"); - simulateHair(deltaTime); + _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); + _hair.simulate(deltaTime); } - - foreach (Hair* hair, _hairs) { - hair->simulate(deltaTime); - } - } // update position by velocity, and subtract the change added earlier for gravity @@ -426,233 +424,17 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { getHand()->render(false, modelRenderMode); } getHead()->render(1.0f, modelRenderMode); + if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - renderHair(); - foreach (Hair* hair, _hairs) { - hair->render(); - } - } -} - -// -// Constants for the Hair Simulation -// - -const float HAIR_LENGTH = 0.2f; -const float HAIR_LINK_LENGTH = HAIR_LENGTH / HAIR_LINKS; -const float HAIR_DAMPING = 0.99f; -const float HEAD_RADIUS = 0.21f; -const float CONSTRAINT_RELAXATION = 10.0f; -const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); -const float HAIR_ACCELERATION_COUPLING = 0.025f; -const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; -const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_THICKNESS = 0.015f; -const float HAIR_STIFFNESS = 0.0000f; -const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); -const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); -const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); -const float MAX_WIND_STRENGTH = 0.02f; -const float FINGER_LENGTH = 0.25f; -const float FINGER_RADIUS = 0.10f; - -void Avatar::renderHair() { - // - // Render the avatar's moveable hair - // - - glm::vec3 headPosition = getHead()->getPosition(); - glPushMatrix(); - glTranslatef(headPosition.x, headPosition.y, headPosition.z); - const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glBegin(GL_QUADS); - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - for (int link = 0; link < HAIR_LINKS - 1; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - glColor3fv(&_hairColors[vertexIndex].x); - glNormal3fv(&_hairNormals[vertexIndex].x); - glVertex3f(_hairPosition[vertexIndex].x - _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex].y - _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex].z - _hairQuadDelta[vertexIndex].z); - glVertex3f(_hairPosition[vertexIndex].x + _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex].y + _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex].z + _hairQuadDelta[vertexIndex].z); - - glVertex3f(_hairPosition[vertexIndex + 1].x + _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex + 1].y + _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex + 1].z + _hairQuadDelta[vertexIndex].z); - glVertex3f(_hairPosition[vertexIndex + 1].x - _hairQuadDelta[vertexIndex].x, - _hairPosition[vertexIndex + 1].y - _hairQuadDelta[vertexIndex].y, - _hairPosition[vertexIndex + 1].z - _hairQuadDelta[vertexIndex].z); - } - } - glEnd(); - - glPopMatrix(); - -} - -void Avatar::simulateHair(float deltaTime) { - - deltaTime = glm::clamp(deltaTime, 0.0f, 1.0f / 30.0f); - glm::vec3 acceleration = getAcceleration(); - if (glm::length(acceleration) > HAIR_MAX_LINEAR_ACCELERATION) { - acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; - } - const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); - acceleration = acceleration * rotation; - glm::vec3 angularVelocity = getAngularVelocity() + getHead()->getAngularVelocity(); - - // Get hand positions to allow touching hair - glm::vec3 leftHandPosition, rightHandPosition; - getSkeletonModel().getLeftHandPosition(leftHandPosition); - getSkeletonModel().getRightHandPosition(rightHandPosition); - leftHandPosition -= getHead()->getPosition(); - rightHandPosition -= getHead()->getPosition(); - glm::quat leftRotation, rightRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); - leftHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(leftRotation); - rightHandPosition += glm::vec3(0.0f, FINGER_LENGTH, 0.0f) * glm::inverse(rightRotation); - leftHandPosition = leftHandPosition * rotation; - rightHandPosition = rightHandPosition * rotation; - - float windIntensity = randFloat() * MAX_WIND_STRENGTH; - - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - for (int link = 0; link < HAIR_LINKS; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - if (vertexIndex % HAIR_LINKS == 0) { - // Base Joint - no integration - } else { - // - // Vertlet Integration - // - // Add velocity from last position, with damping - glm::vec3 thisPosition = _hairPosition[vertexIndex]; - glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; - _hairPosition[vertexIndex] += diff * HAIR_DAMPING; - // Resolve collision with head sphere - if (glm::length(_hairPosition[vertexIndex]) < HEAD_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * - (HEAD_RADIUS - glm::length(_hairPosition[vertexIndex])); - } - // Resolve collision with hands - if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); - } - if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); - } - - - // Add a little gravity - _hairPosition[vertexIndex] += HAIR_GRAVITY * rotation * deltaTime; - - // Add linear acceleration of the avatar body - _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; - - // Add stiffness (like hair care products do) - _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) - * powf(1.f - link / HAIR_LINKS, 2.f) * HAIR_STIFFNESS; - - // Add some wind - glm::vec3 wind = WIND_DIRECTION * windIntensity; - _hairPosition[vertexIndex] += wind * deltaTime; - - const float ANGULAR_VELOCITY_MIN = 0.001f; - // Add angular acceleration of the avatar body - if (glm::length(angularVelocity) > ANGULAR_VELOCITY_MIN) { - glm::vec3 yawVector = _hairPosition[vertexIndex]; - yawVector.y = 0.f; - if (glm::length(yawVector) > EPSILON) { - float radius = glm::length(yawVector); - yawVector = glm::normalize(yawVector); - float angle = atan2f(yawVector.x, -yawVector.z) + PI; - glm::vec3 delta = glm::vec3(-1.f, 0.f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 1, 0)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.y * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - glm::vec3 pitchVector = _hairPosition[vertexIndex]; - pitchVector.x = 0.f; - if (glm::length(pitchVector) > EPSILON) { - float radius = glm::length(pitchVector); - pitchVector = glm::normalize(pitchVector); - float angle = atan2f(pitchVector.y, -pitchVector.z) + PI; - glm::vec3 delta = glm::vec3(0.0f, 1.0f, 0.f) * glm::angleAxis(angle, glm::vec3(1, 0, 0)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.x * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - glm::vec3 rollVector = _hairPosition[vertexIndex]; - rollVector.z = 0.f; - if (glm::length(rollVector) > EPSILON) { - float radius = glm::length(rollVector); - pitchVector = glm::normalize(rollVector); - float angle = atan2f(rollVector.x, rollVector.y) + PI; - glm::vec3 delta = glm::vec3(-1.0f, 0.0f, 0.f) * glm::angleAxis(angle, glm::vec3(0, 0, 1)); - _hairPosition[vertexIndex] -= delta * radius * angularVelocity.z * HAIR_ANGULAR_VELOCITY_COUPLING * deltaTime; - } - } - - // Iterate length constraints to other links - for (int link = 0; link < HAIR_MAX_CONSTRAINTS; link++) { - if (_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link] > -1) { - // If there is a constraint, try to enforce it - glm::vec3 vectorBetween = _hairPosition[_hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link]] - _hairPosition[vertexIndex]; - _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - HAIR_LINK_LENGTH) * CONSTRAINT_RELAXATION * deltaTime; - } - } - // Store start position for next vertlet pass - _hairLastPosition[vertexIndex] = thisPosition; - } - } - } -} - -void Avatar::initializeHair() { - const float FACE_WIDTH = PI / 4.0f; - glm::vec3 thisVertex; - for (int strand = 0; strand < HAIR_STRANDS; strand++) { - float strandAngle = randFloat() * PI; - float azimuth = FACE_WIDTH / 2.0f + (randFloat() * (2.0 * PI - FACE_WIDTH)); - float elevation = PI_OVER_TWO - (randFloat() * 0.75 * PI); - glm::vec3 thisStrand(sinf(azimuth) * cosf(elevation), sinf(elevation), -cosf(azimuth) * cosf(elevation)); - thisStrand *= HEAD_RADIUS; - - for (int link = 0; link < HAIR_LINKS; link++) { - int vertexIndex = strand * HAIR_LINKS + link; - // Clear constraints - for (int link2 = 0; link2 < HAIR_MAX_CONSTRAINTS; link2++) { - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + link2] = -1; - } - if (vertexIndex % HAIR_LINKS == 0) { - // start of strand - thisVertex = thisStrand; - } else { - thisVertex+= glm::normalize(thisStrand) * HAIR_LINK_LENGTH; - // Set constraints to vertex before and maybe vertex after in strand - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS] = vertexIndex - 1; - if (link < (HAIR_LINKS - 1)) { - _hairConstraints[vertexIndex * HAIR_MAX_CONSTRAINTS + 1] = vertexIndex + 1; - } - } - _hairPosition[vertexIndex] = thisVertex; - _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; - _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; - - _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * HAIR_THICKNESS, 0.f, sin(strandAngle) * HAIR_THICKNESS); - _hairQuadDelta[vertexIndex] *= 1.f - (link / HAIR_LINKS); - _hairNormals[vertexIndex] = glm::normalize(randVector()); - if (randFloat() < elevation / PI_OVER_TWO) { - _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)HAIR_LINKS); - } else { - _hairColors[vertexIndex] = HAIR_COLOR2 * ((float)(link + 1) / (float)HAIR_LINKS); - } - - } + // Render Hair + glPushMatrix(); + glm::vec3 headPosition = getHead()->getPosition(); + glTranslatef(headPosition.x, headPosition.y, headPosition.z); + const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + _hair.render(); + glPopMatrix(); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 8c14fc9ed0..8dbeda25dc 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -164,7 +164,7 @@ signals: void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision); protected: - QVector _hairs; + Hair _hair; SkeletonModel _skeletonModel; QVector _attachmentModels; float _bodyYawDelta; @@ -210,18 +210,6 @@ protected: virtual void renderAttachments(RenderMode renderMode); virtual void updateJointMappings(); - - glm::vec3 _hairPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairOriginalPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairLastPosition[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairQuadDelta[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairNormals[HAIR_STRANDS * HAIR_LINKS]; - glm::vec3 _hairColors[HAIR_STRANDS * HAIR_LINKS]; - int _hairIsMoveable[HAIR_STRANDS * HAIR_LINKS]; - int _hairConstraints[HAIR_STRANDS * HAIR_LINKS * 2]; // Hair can link to two others - void renderHair(); - void simulateHair(float deltaTime); - void initializeHair(); private: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 04e7b628c9..93463556cc 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -185,10 +185,10 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("hair"); if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - simulateHair(deltaTime); - foreach (Hair* hair, _hairs) { - hair->simulate(deltaTime); - } + _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(getGravity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.simulate(deltaTime); } } @@ -884,11 +884,17 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { const glm::vec3 cameraPos = camera->getPosition() + (camera->getRotation() * glm::vec3(0.0f, 0.0f, 1.0f)) * camera->getDistance(); if (shouldRenderHead(cameraPos, renderMode)) { getHead()->render(1.0f, modelRenderMode); + if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { - renderHair(); - foreach (Hair* hair, _hairs) { - hair->render(); - } + // Render Hair + glPushMatrix(); + glm::vec3 headPosition = getHead()->getPosition(); + glTranslatef(headPosition.x, headPosition.y, headPosition.z); + const glm::quat& rotation = getHead()->getFinalOrientationInWorldFrame(); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + _hair.render(); + glPopMatrix(); } } getHand()->render(true, modelRenderMode); From 5a8569fcd5539d42abd345027ff52785539b10cd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 17:45:52 -0700 Subject: [PATCH 36/72] Handling NaN values for floats --- examples/typedArraysUnitTest.js | 339 +++++++++++++++++- .../script-engine/src/DataViewPrototype.cpp | 22 +- .../script-engine/src/DataViewPrototype.h | 4 +- libraries/script-engine/src/TypedArrays.cpp | 6 + 4 files changed, 343 insertions(+), 28 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index de9b487c32..a466c9104f 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -11,6 +11,26 @@ Script.include("Test.js"); +// e.g. extractbits([0xff, 0x80, 0x00, 0x00], 23, 30); inclusive +function extractbits(bytes, lo, hi) { + var out = 0; + bytes = bytes.slice(); // make a copy + var lsb = bytes.pop(), sc = 0, sh = 0; + + for (; lo > 0; lo--, hi--) { + lsb >>= 1; + if (++sc === 8) { sc = 0; lsb = bytes.pop(); } + } + + for (; hi >= 0; hi--) { + out = out | (lsb & 0x01) << sh++; + lsb >>= 1; + if (++sc === 8) { sc = 0; lsb = bytes.pop(); } + } + + return out; +} + test('ArrayBuffer', function(finished) { this.assertEquals(new ArrayBuffer(0).byteLength, 0, 'no length'); @@ -20,8 +40,8 @@ test('ArrayBuffer', function(finished) { this.assertEquals(new ArrayBuffer(123).byteLength, 123, 'length'); - // raises(function () { return new ArrayBuffer(-1); }, RangeError, 'negative length'); - // raises(function () { return new ArrayBuffer(0x80000000); }, RangeError, 'absurd length'); + this.raises(function () { return new ArrayBuffer(-1); }, 'negative length'); + this.raises(function () { return new ArrayBuffer(0x80000000); }, 'absurd length'); }); test('DataView constructors', function (finished) { @@ -39,8 +59,8 @@ test('DataView constructors', function (finished) { d.setUint32(0, 0x12345678); this.assertEquals(d.getUint32(0, true), 0x78563412, 'big endian/little endian'); - // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); - // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView("bogus"); }, 'non-ArrayBuffer argument'); }); @@ -118,8 +138,8 @@ test('typed array constructors', function () { var int8 = new Int8Array(); this.assertEquals(int8.length, 0, 'no args 0'); - // raises(function () { return new Int8Array(-1); }, /*Range*/Error, 'bogus length'); - // raises(function () { return new Int8Array(0x80000000); }, /*Range*/Error, 'bogus length'); + this.raises(function () { return new Int8Array(-1); }, 'bogus length'); + this.raises(function () { return new Int8Array(0x80000000); }, 'bogus length'); int8 = new Int8Array(4); this.assertEquals(int8.BYTES_PER_ELEMENT, 1); @@ -160,10 +180,10 @@ test('typed array constructors', function () { int8 = new Int8Array(rawbuf, 8); this.assertEquals(int8.length, 0, 'buffer, byteOffset 26'); - // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); - // raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); - // raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 29'); - // raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); + this.raises(function () { return new Int8Array(rawbuf, -1); }, 'invalid byteOffset 27'); + this.raises(function () { return new Int8Array(rawbuf, 9); }, 'invalid byteOffset 28'); + this.raises(function () { return new Int32Array(rawbuf, -1); }, 'invalid byteOffset 29'); + this.raises(function () { return new Int32Array(rawbuf, 5); }, 'invalid byteOffset 30'); int8 = new Int8Array(rawbuf, 2, 4); this.assertEquals(int8.length, 4, 'buffer, byteOffset, length 31'); @@ -175,9 +195,9 @@ test('typed array constructors', function () { this.assertEquals(int8.get(-1), undefined, 'buffer, byteOffset, length, out of bounds 36'); this.assertEquals(int8.get(4), undefined, 'buffer, byteOffset, length, out of bounds 37'); - // raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); - // raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); - // raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + this.raises(function () { return new Int8Array(rawbuf, 9, -1); }, 'invalid byteOffset+length'); }); test('TypedArray clone constructor', function () { @@ -240,6 +260,189 @@ test('signed/unsigned conversions', function () { }); +test('IEEE754 single precision unpacking', function () { + function fromBytes(bytes) { + var uint8 = new Uint8Array(bytes), + dv = new DataView(uint8.buffer); + return dv.getFloat32(0); + } + + this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x01])), true, 'Q-NaN'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xc0, 0x00, 0x00])), true, 'Indeterminate'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xbf, 0xff, 0xff])), true, 'S-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0x80, 0x00, 0x01])), true, 'S-NaN'); + + this.assertEquals(fromBytes([0xff, 0x80, 0x00, 0x00]), -Infinity, '-Infinity'); + + this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized'); + this.assertEquals(fromBytes([0xff, 0x7f, 0xff, 0xff]), -3.4028234663852886E+38, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x80, 0x00, 0x00]), -1.1754943508222875E-38, '-Normalized'); + + // TODO: Denormalized values fail on Safari on iOS/ARM + this.assertEquals(fromBytes([0x80, 0x7f, 0xff, 0xff]), -1.1754942106924411E-38, '-Denormalized'); + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x01]), -1.4012984643248170E-45, '-Denormalized'); + + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00]), -0, '-0'); + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00]), +0, '+0'); + + // TODO: Denormalized values fail on Safari on iOS/ARM + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x01]), 1.4012984643248170E-45, '+Denormalized'); + this.assertEquals(fromBytes([0x00, 0x7f, 0xff, 0xff]), 1.1754942106924411E-38, '+Denormalized'); + + this.assertEquals(fromBytes([0x00, 0x80, 0x00, 0x00]), 1.1754943508222875E-38, '+Normalized'); + this.assertEquals(fromBytes([0x7f, 0x7f, 0xff, 0xff]), 3.4028234663852886E+38, '+Normalized'); + + this.assertEquals(fromBytes([0x7f, 0x80, 0x00, 0x00]), +Infinity, '+Infinity'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0x80, 0x00, 0x01])), true, 'S+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xbf, 0xff, 0xff])), true, 'S+NaN'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xc0, 0x00, 0x00])), true, 'Q+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff])), true, 'Q+NaN'); +}); + +test('IEEE754 single precision packing', function () { + + function toBytes(v) { + var uint8 = new Uint8Array(4), dv = new DataView(uint8.buffer); + dv.setFloat32(0, v); + var bytes = []; + for (var i = 0; i < 4; i += 1) { + bytes.push(uint8.get(i)); + } + return bytes; + } + + this.arrayEqual(toBytes(-Infinity), [0xff, 0x80, 0x00, 0x00], '-Infinity'); + + this.arrayEqual(toBytes(-3.4028235677973366e+38), [0xff, 0x80, 0x00, 0x00], '-Overflow'); + this.arrayEqual(toBytes(-3.402824E+38), [0xff, 0x80, 0x00, 0x00], '-Overflow'); + + this.arrayEqual(toBytes(-3.4028234663852886E+38), [0xff, 0x7f, 0xff, 0xff], '-Normalized'); + this.arrayEqual(toBytes(-1.1754943508222875E-38), [0x80, 0x80, 0x00, 0x00], '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(-1.1754942106924411E-38), [0x80, 0x7f, 0xff, 0xff], '-Denormalized'); + this.arrayEqual(toBytes(-1.4012984643248170E-45), [0x80, 0x00, 0x00, 0x01], '-Denormalized'); + + this.arrayEqual(toBytes(-7.006492321624085e-46), [0x80, 0x00, 0x00, 0x00], '-Underflow'); + + this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00], '-0'); + this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00], '+0'); + + this.arrayEqual(toBytes(7.006492321624085e-46), [0x00, 0x00, 0x00, 0x00], '+Underflow'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(1.4012984643248170E-45), [0x00, 0x00, 0x00, 0x01], '+Denormalized'); + this.arrayEqual(toBytes(1.1754942106924411E-38), [0x00, 0x7f, 0xff, 0xff], '+Denormalized'); + + this.arrayEqual(toBytes(1.1754943508222875E-38), [0x00, 0x80, 0x00, 0x00], '+Normalized'); + this.arrayEqual(toBytes(3.4028234663852886E+38), [0x7f, 0x7f, 0xff, 0xff], '+Normalized'); + + this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow'); + this.arrayEqual(toBytes(+3.402824E+38), [0x7f, 0x80, 0x00, 0x00], '+Overflow'); + this.arrayEqual(toBytes(+Infinity), [0x7f, 0x80, 0x00, 0x00], '+Infinity'); + + // Allow any NaN pattern (exponent all 1's, fraction non-zero) + var nanbytes = toBytes(NaN), + sign = extractbits(nanbytes, 31, 31), + exponent = extractbits(nanbytes, 23, 30), + fraction = extractbits(nanbytes, 0, 22); + this.assertEquals(exponent === 255 && fraction !== 0, true, 'NaN'); +}); + + +test('IEEE754 double precision unpacking', function () { + + function fromBytes(bytes) { + var uint8 = new Uint8Array(bytes), + dv = new DataView(uint8.buffer); + return dv.getFloat64(0); + } + + this.assertEquals(isNaN(fromBytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'Q-NaN'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Indeterminate'); + + this.assertEquals(isNaN(fromBytes([0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S-NaN'); + this.assertEquals(isNaN(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S-NaN'); + + this.assertEquals(fromBytes([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -Infinity, '-Infinity'); + + this.assertEquals(fromBytes([0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -1.7976931348623157E+308, '-Normalized'); + this.assertEquals(fromBytes([0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -2.2250738585072014E-308, '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.assertEquals(fromBytes([0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), -2.2250738585072010E-308, '-Denormalized'); + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), -4.9406564584124654E-324, '-Denormalized'); + + this.assertEquals(fromBytes([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), -0, '-0'); + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +0, '+0'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.assertEquals(fromBytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), 4.9406564584124654E-324, '+Denormalized'); + this.assertEquals(fromBytes([0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 2.2250738585072010E-308, '+Denormalized'); + + this.assertEquals(fromBytes([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), 2.2250738585072014E-308, '+Normalized'); + this.assertEquals(fromBytes([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), 1.7976931348623157E+308, '+Normalized'); + + this.assertEquals(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), +Infinity, '+Infinity'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])), true, 'S+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'S+NaN'); + + this.assertEquals(isNaN(fromBytes([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])), true, 'Q+NaN'); + this.assertEquals(isNaN(fromBytes([0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])), true, 'Q+NaN'); +}); + + +test('IEEE754 double precision packing', function () { + + function toBytes(v) { + var uint8 = new Uint8Array(8), + dv = new DataView(uint8.buffer); + dv.setFloat64(0, v); + var bytes = []; + for (var i = 0; i < 8; i += 1) { + bytes.push(uint8.get(i)); + } + return bytes; + } + + this.arrayEqual(toBytes(-Infinity), [0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Infinity'); + + this.arrayEqual(toBytes(-1.7976931348623157E+308), [0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Normalized'); + this.arrayEqual(toBytes(-2.2250738585072014E-308), [0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-Normalized'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(-2.2250738585072010E-308), [0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '-Denormalized'); + this.arrayEqual(toBytes(-4.9406564584124654E-324), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '-Denormalized'); + + this.arrayEqual(toBytes(-0), [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '-0'); + this.arrayEqual(toBytes(0), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+0'); + + // TODO: Denormalized values fail on Safari iOS/ARM + this.arrayEqual(toBytes(4.9406564584124654E-324), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], '+Denormalized'); + this.arrayEqual(toBytes(2.2250738585072010E-308), [0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Denormalized'); + + this.arrayEqual(toBytes(2.2250738585072014E-308), [0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Normalized'); + this.arrayEqual(toBytes(1.7976931348623157E+308), [0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], '+Normalized'); + + this.arrayEqual(toBytes(+Infinity), [0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], '+Infinity'); + + // Allow any NaN pattern (exponent all 1's, fraction non-zero) + var nanbytes = toBytes(NaN), + sign = extractbits(nanbytes, 63, 63), + exponent = extractbits(nanbytes, 52, 62), + fraction = extractbits(nanbytes, 0, 51); + this.assertEquals(exponent === 2047 && fraction !== 0, true, 'NaN'); +}); + test('Int32Array round trips', function () { var i32 = new Int32Array([0]); var data = [ @@ -319,8 +522,8 @@ test('TypedArray setting', function () { b.set(new Int32Array([99, 98, 97]), 2); this.arrayEqual(b, [1, 2, 99, 98, 97], '3'); - // raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); - // raises(function () { b.set([101, 102, 103, 104], 4); }); + this.raises(function () { b.set(new Int32Array([99, 98, 97, 96]), 2); }); + this.raises(function () { b.set([101, 102, 103, 104], 4); }); // ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ] // a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ] @@ -374,11 +577,113 @@ test('DataView constructors', function () { //raises(function () { return new DataView(); }, TypeError, 'no arguments'); // Chrome raises TypeError, Safari iOS5 raises isDOMException(INDEX_SIZE_ERR) - // raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView({}); }, 'non-ArrayBuffer argument'); - // raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); + this.raises(function () { return new DataView("bogus"); }, TypeError, 'non-ArrayBuffer argument'); }); +test('DataView accessors', function () { + var u = new Uint8Array(8), d = new DataView(u.buffer); + this.arrayEqual(u, [0, 0, 0, 0, 0, 0, 0, 0], '1'); + + d.setUint8(0, 255); + this.arrayEqual(u, [0xff, 0, 0, 0, 0, 0, 0, 0], '2'); + + d.setInt8(1, -1); + this.arrayEqual(u, [0xff, 0xff, 0, 0, 0, 0, 0, 0], '3'); + + d.setUint16(2, 0x1234); + this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0, 0, 0, 0]), '4'; + + d.setInt16(4, -1); + this.arrayEqual(u, [0xff, 0xff, 0x12, 0x34, 0xff, 0xff, 0, 0], '5'); + + d.setUint32(1, 0x12345678); + this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x78, 0xff, 0, 0], '6'); + + d.setInt32(4, -2023406815); + this.arrayEqual(u, [0xff, 0x12, 0x34, 0x56, 0x87, 0x65, 0x43, 0x21], '7'); + + d.setFloat32(2, 1.2E+38); + this.arrayEqual(u, [0xff, 0x12, 0x7e, 0xb4, 0x8e, 0x52, 0x43, 0x21], '8'); + + d.setFloat64(0, -1.2345678E+301); + this.arrayEqual(u, [0xfe, 0x72, 0x6f, 0x51, 0x5f, 0x61, 0x77, 0xe5], '9'); + + u.set([0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87]); + this.assertEquals(d.getUint8(0), 128, '10'); + this.assertEquals(d.getInt8(1), -127, '11'); + this.assertEquals(d.getUint16(2), 33411, '12'); + this.assertEquals(d.getInt16(3), -31868, '13'); + this.assertEquals(d.getUint32(4), 2223343239, '14'); + this.assertEquals(d.getInt32(2), -2105310075, '15'); + this.assertEquals(d.getFloat32(2), -1.932478247535851e-37, '16'); + this.assertEquals(d.getFloat64(0), -3.116851295377095e-306, '17'); + +}); + + +test('DataView endian', function () { + var rawbuf = (new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])).buffer; + var d; + + d = new DataView(rawbuf); + this.assertEquals(d.byteLength, 8, 'buffer'); + this.assertEquals(d.byteOffset, 0, 'buffer'); + this.raises(function () { d.getUint8(-2); }); // Chrome bug for index -, DOMException, 'bounds for buffer'? + this.raises(function () { d.getUint8(8); }, 'bounds for buffer'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer'); + this.raises(function () { d.setUint8(8, 0); }, 'bounds for buffer'); + + d = new DataView(rawbuf, 2); + this.assertEquals(d.byteLength, 6, 'buffer, byteOffset'); + this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset'); + this.assertEquals(d.getUint8(5), 7, 'buffer, byteOffset'); + this.raises(function () { d.getUint8(-2); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.getUint8(6); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset'); + this.raises(function () { d.setUint8(6, 0); }, 'bounds for buffer, byteOffset'); + + d = new DataView(rawbuf, 8); + this.assertEquals(d.byteLength, 0, 'buffer, byteOffset'); + + this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset'); + this.raises(function () { return new DataView(rawbuf, 9); }, 'invalid byteOffset'); + this.raises(function () { return new DataView(rawbuf, -1); }, 'invalid byteOffset'); + + d = new DataView(rawbuf, 2, 4); + this.assertEquals(d.byteLength, 4, 'buffer, byteOffset, length'); + this.assertEquals(d.byteOffset, 2, 'buffer, byteOffset, length'); + this.assertEquals(d.getUint8(3), 5, 'buffer, byteOffset, length'); + this.raises(function () { return d.getUint8(-2); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.getUint8(4); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.setUint8(-2, 0); }, 'bounds for buffer, byteOffset, length'); + this.raises(function () { d.setUint8(4, 0); }, 'bounds for buffer, byteOffset, length'); + + this.raises(function () { return new DataView(rawbuf, 0, 9); }, 'invalid byteOffset+length'); + this.raises(function () { return new DataView(rawbuf, 8, 1); }, 'invalid byteOffset+length'); + this.raises(function () { return new DataView(rawbuf, 9, -1); }, 'invalid byteOffset+length'); +}); + + +test('Typed Array getters/setters', function () { + // Only supported if Object.defineProperty() is fully supported on non-DOM objects. + try { + var o = {}; + Object.defineProperty(o, 'x', { get: function() { return 1; } }); + if (o.x !== 1) throw Error(); + } catch (_) { + ok(true); + return; + } + + var bytes = new Uint8Array([1, 2, 3, 4]), + uint32s = new Uint32Array(bytes.buffer); + + this.assertEquals(bytes[1], 2); + uint32s[0] = 0xffffffff; + this.assertEquals(bytes[1], 0xff); +}); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 95f3fdf3f6..29264e9bc5 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -32,10 +32,7 @@ bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { } quint32 viewOffset = thisObject().data().property(BYTE_OFFSET_PROPERTY_NAME).toInt32(); quint32 viewLength = thisObject().data().property(BYTE_LENGTH_PROPERTY_NAME).toInt32(); - //qDebug() << "View Offset: " << viewOffset << ", View Lenght: " << viewLength; - //qDebug() << "Offset: " << offset << ", Size: " << size; offset += viewOffset; - //qDebug() << "New offset: " << offset << ", bool: " << ((offset + size) <= viewOffset + viewLength); return (offset + size) <= viewOffset + viewLength; } @@ -123,7 +120,7 @@ quint32 DataViewPrototype::getUint32(qint32 byteOffset, bool littleEndian) { return 0; } -float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { +QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(float))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -132,14 +129,17 @@ float DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) { float result; stream >> result; - qDebug() << "Get: " << result; - return result; + if (isNaN(result)) { + return QScriptValue(NAN); + } + + return QScriptValue(result); } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); - return 0; + return QScriptValue(); } -double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { +QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { if (realOffset(byteOffset, sizeof(double))) { QDataStream stream(*thisArrayBuffer()); stream.skipRawData(byteOffset); @@ -148,10 +148,14 @@ double DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) { double result; stream >> result; + if (isNaN(result)) { + return QScriptValue(); + } + return result; } thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); - return 0; + return QScriptValue(); } ///////////////// SETTERS //////////////////////////// diff --git a/libraries/script-engine/src/DataViewPrototype.h b/libraries/script-engine/src/DataViewPrototype.h index b7061a388e..da0261dad4 100644 --- a/libraries/script-engine/src/DataViewPrototype.h +++ b/libraries/script-engine/src/DataViewPrototype.h @@ -37,8 +37,8 @@ public slots: quint32 getUint16(qint32 byteOffset, bool littleEndian = false); qint32 getInt32(qint32 byteOffset, bool littleEndian = false); quint32 getUint32(qint32 byteOffset, bool littleEndian = false); - float getFloat32(qint32 byteOffset, bool littleEndian = false); - double getFloat64(qint32 byteOffset, bool littleEndian = false); + QScriptValue getFloat32(qint32 byteOffset, bool littleEndian = false); + QScriptValue getFloat64(qint32 byteOffset, bool littleEndian = false); // Stores a value of the given type at the specified byte offset // from the start of the view. There is no alignment constraint; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 102c477c04..106b824f76 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -426,6 +426,9 @@ QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScri float result; stream >> result; + if (isNaN(result)) { + return QScriptValue(NAN); + } return result; } return TypedArray::property(object, name, id); @@ -459,6 +462,9 @@ QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScri double result; stream >> result; + if (isNaN(result)) { + return QScriptValue(NAN); + } return result; } return TypedArray::property(object, name, id); From 0b3e41a541ba85feb9d299a9a1f3c5c5bbc09841 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 11 Jul 2014 19:58:13 -0500 Subject: [PATCH 37/72] remove wind from hair, fix gravity --- interface/src/Hair.cpp | 39 +++++++------------------------ interface/src/avatar/MyAvatar.cpp | 2 +- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 635ea7ee0f..bd2092cf3f 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -17,16 +17,12 @@ const float HAIR_DAMPING = 0.99f; const float CONSTRAINT_RELAXATION = 10.0f; -const glm::vec3 HAIR_GRAVITY(0.0f, -0.007f, 0.0f); const float HAIR_ACCELERATION_COUPLING = 0.025f; const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; const float HAIR_STIFFNESS = 0.0000f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); -const glm::vec3 WIND_DIRECTION(0.5f, -1.0f, 0.0f); -const float MAX_WIND_STRENGTH = 0.02f; - Hair::Hair(int strands, int links, @@ -99,8 +95,6 @@ void Hair::simulate(float deltaTime) { acceleration = glm::normalize(acceleration) * HAIR_MAX_LINEAR_ACCELERATION; } - float windIntensity = randFloat() * MAX_WIND_STRENGTH; - for (int strand = 0; strand < _strands; strand++) { for (int link = 0; link < _links; link++) { int vertexIndex = strand * _links + link; @@ -114,40 +108,25 @@ void Hair::simulate(float deltaTime) { glm::vec3 thisPosition = _hairPosition[vertexIndex]; glm::vec3 diff = thisPosition - _hairLastPosition[vertexIndex]; _hairPosition[vertexIndex] += diff * HAIR_DAMPING; - // Resolve collision with head sphere + + // Resolve collisions with sphere if (glm::length(_hairPosition[vertexIndex]) < _radius) { _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex]) * (_radius - glm::length(_hairPosition[vertexIndex])); } - // Resolve collision with hands - /* - if (glm::length(_hairPosition[vertexIndex] - leftHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - leftHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - leftHandPosition)); - } - if (glm::length(_hairPosition[vertexIndex] - rightHandPosition) < FINGER_RADIUS) { - _hairPosition[vertexIndex] += glm::normalize(_hairPosition[vertexIndex] - rightHandPosition) * - (FINGER_RADIUS - glm::length(_hairPosition[vertexIndex] - rightHandPosition)); - } - */ + // Add gravity + _hairPosition[vertexIndex] += _gravity * deltaTime; - // Add a little gravity - _hairPosition[vertexIndex] += HAIR_GRAVITY * deltaTime; - - // Add linear acceleration of the avatar body + // Add linear acceleration _hairPosition[vertexIndex] -= acceleration * HAIR_ACCELERATION_COUPLING * deltaTime; - // Add stiffness (like hair care products do) + // Add stiffness to return to original position _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) * powf(1.f - link / _links, 2.f) * HAIR_STIFFNESS; - // Add some wind - glm::vec3 wind = WIND_DIRECTION * windIntensity; - _hairPosition[vertexIndex] += wind * deltaTime; - + // Add angular acceleration const float ANGULAR_VELOCITY_MIN = 0.001f; - // Add angular acceleration of the avatar body if (glm::length(_angularVelocity) > ANGULAR_VELOCITY_MIN) { glm::vec3 yawVector = _hairPosition[vertexIndex]; yawVector.y = 0.f; @@ -178,7 +157,7 @@ void Hair::simulate(float deltaTime) { } } - // Iterate length constraints to other links + // Impose link constraints for (int link = 0; link < HAIR_CONSTRAINTS; link++) { if (_hairConstraints[vertexIndex * HAIR_CONSTRAINTS + link] > -1) { // If there is a constraint, try to enforce it @@ -186,12 +165,12 @@ void Hair::simulate(float deltaTime) { _hairPosition[vertexIndex] += glm::normalize(vectorBetween) * (glm::length(vectorBetween) - _linkLength) * CONSTRAINT_RELAXATION * deltaTime; } } + // Store start position for next vertlet pass _hairLastPosition[vertexIndex] = thisPosition; } } } - } void Hair::render() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 93463556cc..3ec6652889 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -187,7 +187,7 @@ void MyAvatar::simulate(float deltaTime) { if (Menu::getInstance()->isOptionChecked(MenuOption::StringHair)) { _hair.setAcceleration(getAcceleration() * getHead()->getFinalOrientationInWorldFrame()); _hair.setAngularVelocity(getAngularVelocity() + getHead()->getAngularVelocity() * getHead()->getFinalOrientationInWorldFrame()); - _hair.setGravity(getGravity() * getHead()->getFinalOrientationInWorldFrame()); + _hair.setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()) * getHead()->getFinalOrientationInWorldFrame()); _hair.simulate(deltaTime); } } From d493b92d7b36c462b337fc5705078ce917ffe634 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 11 Jul 2014 20:23:59 -0500 Subject: [PATCH 38/72] tweaked defaults --- interface/src/Hair.cpp | 2 +- interface/src/Hair.h | 4 ++-- interface/src/avatar/Avatar.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index bd2092cf3f..824e273049 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -20,7 +20,7 @@ const float CONSTRAINT_RELAXATION = 10.0f; const float HAIR_ACCELERATION_COUPLING = 0.025f; const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_STIFFNESS = 0.0000f; +const float HAIR_STIFFNESS = 0.0003f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); diff --git a/interface/src/Hair.h b/interface/src/Hair.h index dfb90cae21..0ca507ee23 100644 --- a/interface/src/Hair.h +++ b/interface/src/Hair.h @@ -25,8 +25,8 @@ const int HAIR_CONSTRAINTS = 2; const int DEFAULT_HAIR_STRANDS = 50; const int DEFAULT_HAIR_LINKS = 10; -const float DEFAULT_HAIR_RADIUS = 0.25; -const float DEFAULT_HAIR_LINK_LENGTH = 0.05; +const float DEFAULT_HAIR_RADIUS = 0.15; +const float DEFAULT_HAIR_LINK_LENGTH = 0.03; const float DEFAULT_HAIR_THICKNESS = 0.015; class Hair { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 8dbeda25dc..e29a2de71e 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -222,6 +222,7 @@ private: float getBillboardSize() const; + }; #endif // hifi_Avatar_h From 6d4bb0f4fd96cc5d623b0d623058ba83aafdc862 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 18:27:26 -0700 Subject: [PATCH 39/72] Added Uint8ClampedArray --- examples/Test.js | 2 +- examples/typedArraysUnitTest.js | 19 ++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 2 ++ libraries/script-engine/src/TypedArrays.cpp | 27 ++++++++++++++++++++ libraries/script-engine/src/TypedArrays.h | 8 +++++- 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/examples/Test.js b/examples/Test.js index caecfc5af7..36dee7bd90 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -80,7 +80,7 @@ UnitTest.prototype.arrayEqual = function(array1, array2, message) { } for (var i = 0; i < array1.length; ++i) { if (array1[i] !== array2[i]) { - throw new AssertionException(array1[i], array2[i], message); + throw new AssertionException(array1[i], array2[i], i + " " + message); } } } diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index a466c9104f..859bc9b9b3 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -687,3 +687,22 @@ test('Typed Array getters/setters', function () { this.assertEquals(bytes[1], 0xff); }); + +test('Uint8ClampedArray', function () { + this.assertEquals(Uint8ClampedArray.BYTES_PER_ELEMENT, 1, 'Uint8ClampedArray.BYTES_PER_ELEMENT'); + var a = new Uint8ClampedArray([-Infinity, -Number.MAX_VALUE, -1, -Number.MIN_VALUE, -0, + 0, Number.MIN_VALUE, 1, 1.1, 1.9, 255, 255.1, 255.9, 256, Number.MAX_VALUE, Infinity, + NaN]); + this.assertEquals(a.BYTES_PER_ELEMENT, 1); + this.assertEquals(a.byteOffset, 0); + this.assertEquals(a.byteLength, 17); + this.arrayEqual(a, [0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0], "array test"); +}); + +test('Regression Tests', function() { + // Bug: https://github.com/inexorabletash/polyfill/issues/16 + var minFloat32 = 1.401298464324817e-45; + var truncated = new Float32Array([-minFloat32 / 2 - Math.pow(2, -202)]).get(0); + this.assertEquals(truncated, -minFloat32, 'smallest 32 bit float should not truncate to zero'); +}); + diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 4074beb88e..0279e2ea27 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -100,6 +100,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam new DataViewClass(this); new Int8ArrayClass(this); new Uint8ArrayClass(this); + new Uint8ClampedArrayClass(this); new Int16ArrayClass(this); new Uint16ArrayClass(this); new Int32ArrayClass(this); @@ -141,6 +142,7 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, new DataViewClass(this); new Int8ArrayClass(this); new Uint8ArrayClass(this); + new Uint8ClampedArrayClass(this); new Int16ArrayClass(this); new Uint16ArrayClass(this); new Int32ArrayClass(this); diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 106b824f76..6b461fc6fb 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -342,6 +342,33 @@ void Uint8ArrayClass::setProperty(QScriptValue &object, const QScriptString &nam setPropertyHelper(ba, name, id, value); } +Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, UINT_8_CLAMPED_ARRAY_CLASS_NAME) { + setBytesPerElement(sizeof(quint8)); +} + +QScriptValue Uint8ClampedArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { + + QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); + QScriptValue result = propertyHelper(arrayBuffer, name, id); + return (result.isValid()) ? result : TypedArray::property(object, name, id); +} + +void Uint8ClampedArrayClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) { + QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + if (ba && value.isNumber()) { + QDataStream stream(ba, QIODevice::ReadWrite); + stream.skipRawData(id); + if (value.toNumber() > 255) { + stream << (quint8)255; + } else if (value.toNumber() < 0) { + stream << (quint8)0; + } else { + stream << (quint8)glm::clamp(qRound(value.toNumber()), 0, 255); + } + } +} + Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEngine, INT_16_ARRAY_CLASS_NAME) { setBytesPerElement(sizeof(qint16)); } diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index 2eff3e84b9..bacb067add 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -19,6 +19,7 @@ static const QString LENGTH_PROPERTY_NAME = "length"; static const QString INT_8_ARRAY_CLASS_NAME = "Int8Array"; static const QString UINT_8_ARRAY_CLASS_NAME = "Uint8Array"; +static const QString UINT_8_CLAMPED_ARRAY_CLASS_NAME = "Uint8ClampedArray"; static const QString INT_16_ARRAY_CLASS_NAME = "Int16Array"; static const QString UINT_16_ARRAY_CLASS_NAME = "Uint16Array"; static const QString INT_32_ARRAY_CLASS_NAME = "Int32Array"; @@ -98,7 +99,12 @@ public: }; class Uint8ClampedArrayClass : public TypedArray { - // TODO + Q_OBJECT +public: + Uint8ClampedArrayClass(ScriptEngine* scriptEngine); + + QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); + void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); }; class Int16ArrayClass : public TypedArray { From aa65084ae1c8199f122e03b5d36e43e3d9c56b17 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Jul 2014 23:08:41 -0700 Subject: [PATCH 40/72] Code cleanup --- .../script-engine/src/ArrayBufferClass.cpp | 15 ++++++++++- libraries/script-engine/src/ScriptEngine.cpp | 27 ++----------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index 4f85eeeae2..f8299266fd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -12,12 +12,13 @@ #include #include "ArrayBufferPrototype.h" +#include "DataViewClass.h" #include "ScriptEngine.h" +#include "TypedArrays.h" #include "ArrayBufferClass.h" static const QString CLASS_NAME = "ArrayBuffer"; -static const QString BYTE_LENGTH_PROPERTY_NAME = "byteLength"; Q_DECLARE_METATYPE(QByteArray*) @@ -46,6 +47,18 @@ _scriptEngine(scriptEngine) { _ctor.setData(engine()->toScriptValue(this)); engine()->globalObject().setProperty(name(), _ctor); + + // Registering other array types + new DataViewClass(scriptEngine); + new Int8ArrayClass(scriptEngine); + new Uint8ArrayClass(scriptEngine); + new Uint8ClampedArrayClass(scriptEngine); + new Int16ArrayClass(scriptEngine); + new Uint16ArrayClass(scriptEngine); + new Int32ArrayClass(scriptEngine); + new Uint32ArrayClass(scriptEngine); + new Float32ArrayClass(scriptEngine); + new Float64ArrayClass(scriptEngine); } QScriptValue ArrayBufferClass::newInstance(qint32 size) { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0279e2ea27..a224b9fa33 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,19 +94,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _vec3Library(), _uuidLibrary(), _animationCache(this), - _arrayBufferClass(NULL) + _arrayBufferClass(new ArrayBufferClass(this)) { - _arrayBufferClass = new ArrayBufferClass(this); - new DataViewClass(this); - new Int8ArrayClass(this); - new Uint8ArrayClass(this); - new Uint8ClampedArrayClass(this); - new Int16ArrayClass(this); - new Uint16ArrayClass(this); - new Int32ArrayClass(this); - new Uint32ArrayClass(this); - new Float32ArrayClass(this); - new Float64ArrayClass(this); } ScriptEngine::ScriptEngine(const QUrl& scriptURL, @@ -131,25 +120,13 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, _vec3Library(), _uuidLibrary(), _animationCache(this), - _arrayBufferClass(NULL) + _arrayBufferClass(new ArrayBufferClass(this)) { QString scriptURLString = scriptURL.toString(); _fileNameString = scriptURLString; QUrl url(scriptURL); - _arrayBufferClass = new ArrayBufferClass(this); - new DataViewClass(this); - new Int8ArrayClass(this); - new Uint8ArrayClass(this); - new Uint8ClampedArrayClass(this); - new Int16ArrayClass(this); - new Uint16ArrayClass(this); - new Int32ArrayClass(this); - new Uint32ArrayClass(this); - new Float32ArrayClass(this); - new Float64ArrayClass(this); - // if the scheme length is one or lower, maybe they typed in a file, let's try const int WINDOWS_DRIVE_LETTER_SIZE = 1; if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { From 27cdcd9ada874f56d5be16097e3f09e74df788e4 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 17:03:38 +0200 Subject: [PATCH 41/72] Fix compiler warnings for g++ 4.7. This fixes nine compiler warnings like: /opt/highfidelity/hifi/hifi/libraries/particles/src/Particle.cpp:371:43: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] The best way to avoid this (without using an ugly (int) cast) is to bring everything to one side and compare with zero, which results in the same assembly code after optimization (it's the same to the compiler). --- libraries/particles/src/Particle.cpp | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 76890afafe..814da636c1 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -368,7 +368,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr uint32_t editID; // check to make sure we have enough content to keep reading... - if (processedBytes + sizeof(editID) > length) { + if (length - processedBytes - sizeof(editID) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -387,7 +387,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr uint32_t creatorTokenID; // check to make sure we have enough content to keep reading... - if (processedBytes + sizeof(creatorTokenID) > length) { + if (length - processedBytes - sizeof(creatorTokenID) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -426,7 +426,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // lastEdited // check to make sure we have enough content to keep reading... - if (processedBytes + sizeof(newParticle._lastEdited) > length) { + if (length - processedBytes - sizeof(newParticle._lastEdited) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -439,7 +439,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // properties included bits uint16_t packetContainsBits = 0; if (!isNewParticle) { - if (processedBytes + sizeof(packetContainsBits) > length) { + if (length - processedBytes - sizeof(packetContainsBits) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -452,7 +452,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // radius if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { - if (processedBytes + sizeof(newParticle._radius) > length) { + if (length - processedBytes - sizeof(newParticle._radius) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -464,7 +464,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // position if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { - if (processedBytes + sizeof(newParticle._position) > length) { + if (length - processedBytes - sizeof(newParticle._position) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -476,7 +476,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // color if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { - if (processedBytes + sizeof(newParticle._color) > length) { + if (length - processedBytes - sizeof(newParticle._color) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -488,7 +488,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // velocity if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { - if (processedBytes + sizeof(newParticle._velocity) > length) { + if (length - processedBytes - sizeof(newParticle._velocity) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -500,7 +500,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // gravity if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { - if (processedBytes + sizeof(newParticle._gravity) > length) { + if (length - processedBytes - sizeof(newParticle._gravity) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -512,7 +512,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // damping if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { - if (processedBytes + sizeof(newParticle._damping) > length) { + if (length - processedBytes - sizeof(newParticle._damping) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -524,7 +524,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // lifetime if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { - if (processedBytes + sizeof(newParticle._lifetime) > length) { + if (length - processedBytes - sizeof(newParticle._lifetime) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -537,7 +537,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // TODO: make inHand and shouldDie into single bits // inHand if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { - if (processedBytes + sizeof(newParticle._inHand) > length) { + if (length - processedBytes - sizeof(newParticle._inHand) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -549,7 +549,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // shouldDie if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { - if (processedBytes + sizeof(newParticle._shouldDie) > length) { + if (length - processedBytes - sizeof(newParticle._shouldDie) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -562,7 +562,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // script if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { uint16_t scriptLength; - if (processedBytes + sizeof(scriptLength) > length) { + if (length - processedBytes - sizeof(scriptLength) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -571,7 +571,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr dataAt += sizeof(scriptLength); processedBytes += sizeof(scriptLength); - if (processedBytes + scriptLength > length) { + if (length - processedBytes - scriptLength < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -585,7 +585,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelURL if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) { uint16_t modelURLLength; - if (processedBytes + sizeof(modelURLLength) > length) { + if (length - processedBytes - sizeof(modelURLLength) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -594,7 +594,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr dataAt += sizeof(modelURLLength); processedBytes += sizeof(modelURLLength); - if (processedBytes + modelURLLength > length) { + if (length - processedBytes - modelURLLength < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -607,7 +607,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelScale if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) { - if (processedBytes + sizeof(newParticle._modelScale) > length) { + if (length - processedBytes - sizeof(newParticle._modelScale) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -619,7 +619,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelTranslation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) { - if (processedBytes + sizeof(newParticle._modelTranslation) > length) { + if (length - processedBytes - sizeof(newParticle._modelTranslation) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -632,7 +632,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelRotation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) { const int expectedBytesForPackedQuat = sizeof(uint16_t) * 4; // this is how we pack the quats - if (processedBytes + expectedBytesForPackedQuat > length) { + if (length - processedBytes - expectedBytesForPackedQuat < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer From bd212c26d0960e9a29eaf9e628b693d22a62a6ac Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 17:10:39 +0200 Subject: [PATCH 42/72] Fix initializer list order (avoiding compiler warning). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the compiler warning: In file included from /opt/highfidelity/hifi/hifi/interface/src/Application.h:45:0, from /opt/highfidelity/hifi/hifi/interface/src/Audio.cpp:42: /opt/highfidelity/hifi/hifi/interface/src/Audio.h: In constructor ‘Audio::Audio(int16_t, QObject*)’: /opt/highfidelity/hifi/hifi/interface/src/Audio.h:268:30: warning: ‘Audio::_interframeTimeGapStats’ will be initialized after [-Wreorder] /opt/highfidelity/hifi/hifi/interface/src/Audio.h:259:9: warning: ‘int Audio::_starveCount’ [-Wreorder] /opt/highfidelity/hifi/hifi/interface/src/Audio.cpp:63:1: warning: when initialized here [-Wreorder] by fixing the initializer list of class Audio. Note that I omitted _audioMixerAvatarStreamAudioStats() - which is a default constructor that is called anyway (it's the default!) and therefore doesn't belong in the initializer list. --- interface/src/Audio.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 3689ff0143..6a26563e36 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -112,12 +112,11 @@ Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : _scopeInput(0), _scopeOutputLeft(0), _scopeOutputRight(0), - _audioMixerAvatarStreamAudioStats(), + _starveCount(0), + _consecutiveNotMixedCount(0), _outgoingAvatarAudioSequenceNumber(0), _incomingMixedAudioSequenceNumberStats(INCOMING_SEQ_STATS_HISTORY_LENGTH), - _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS), - _starveCount(0), - _consecutiveNotMixedCount(0) + _interframeTimeGapStats(TIME_GAPS_STATS_INTERVAL_SAMPLES, TIME_GAP_STATS_WINDOW_INTERVALS) { // clear the array of locally injected samples memset(_localProceduralSamples, 0, NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL); From ea98b58b405b1599b1d55449b4b0b3a097fe7cc4 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 20:29:01 +0200 Subject: [PATCH 43/72] Debug support: allow printing of glm types to QDebug. This adds support for serialization to QDebug of glm::vec3, glm::quat and glm::mat4. Output example: qDebug().nospace() << "Calling PhysicsEntity::findRayIntersection(" << origin << ", " << direction << ", &distance) const; numShapes = " << numShapes; leads to: [2014-07-13T20:24:47] Calling PhysicsEntity::findRayIntersection({type='glm::vec3', x=5222.45, y=2159.05, z=6527.79}, {type='glm::vec3', x=0, y=-0.119145, z=-0.992877}, &distance) const; numShapes = 0 Note that we explicitly don't return dbg.space() because we want to be able to print these things comma separated as follows: {...}, {...}, ... as opposed to {...} , {...} etc. I changed the already existing operator<< for Box to the more general case, where it just prints its members and doesn't mess with the internals of its members. The result is more verbose, but also more recognizable when in the future everything will look the same, allowing for speed reading the debug output. The constructor of Box needed to made explicit because it was too annoying that when one forgets to #include "StreamUtils.h" that writing a glm::vec3 resulted in printing out a Box, implicitly converted from the vector. --- libraries/metavoxels/src/MetavoxelUtil.cpp | 6 ++--- libraries/metavoxels/src/MetavoxelUtil.h | 2 +- libraries/shared/src/StreamUtils.cpp | 31 ++++++++++++++++++++++ libraries/shared/src/StreamUtils.h | 13 ++++++--- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index f2f434b24d..b0233f04cd 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -26,6 +26,7 @@ #include "MetavoxelUtil.h" #include "ScriptCache.h" +#include "StreamUtils.h" static int scriptHashType = qRegisterMetaType(); static int parameterizedURLType = qRegisterMetaType(); @@ -310,9 +311,8 @@ Box operator*(const glm::mat4& matrix, const Box& box) { return newBox; } -QDebug& operator<<(QDebug& out, const Box& box) { - return out << '(' << box.minimum.x << box.minimum.y << box.minimum.z << ") (" << - box.maximum.x << box.maximum.y << box.maximum.z << ')'; +QDebug& operator<<(QDebug& dbg, const Box& box) { + return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximumx=" << box.maximum << "}"; } QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { diff --git a/libraries/metavoxels/src/MetavoxelUtil.h b/libraries/metavoxels/src/MetavoxelUtil.h index 34c2bac6be..83aac1318e 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.h +++ b/libraries/metavoxels/src/MetavoxelUtil.h @@ -42,7 +42,7 @@ public: STREAM glm::vec3 minimum; STREAM glm::vec3 maximum; - Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); + explicit Box(const glm::vec3& minimum = glm::vec3(), const glm::vec3& maximum = glm::vec3()); bool contains(const glm::vec3& point) const; diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 5356c45a51..1723068eee 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -95,3 +95,34 @@ std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) { #endif // DEBUG +#ifndef QT_NO_DEBUG_STREAM +#include + +QDebug& operator<<(QDebug& dbg, const glm::vec3& v) { + dbg.nospace() << "{type='glm::vec3'" + ", x=" << v.x << + ", y=" << v.y << + ", z=" << v.z << + "}"; + return dbg; +} + +QDebug& operator<<(QDebug& dbg, const glm::quat& q) { + dbg.nospace() << "{type='glm::quat'" + ", x=" << q.x << + ", y=" << q.y << + ", z=" << q.z << + ", w=" << q.w << + "}"; + return dbg; +} + +QDebug& operator<<(QDebug& dbg, const glm::mat4& m) { + dbg.nospace() << "{type='glm::mat4', ["; + for (int j = 0; j < 4; ++j) { + dbg << ' ' << m[0][j] << ' ' << m[1][j] << ' ' << m[2][j] << ' ' << m[3][j] << ';'; + } + return dbg << " ]}"; +} + +#endif // QT_NO_DEBUG_STREAM diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index 2a42a3ea7b..4f74e895d2 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -38,13 +38,20 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion); // less common utils can be enabled with DEBUG #ifdef DEBUG -#include "CollisionInfo.h" -#include "SphereShape.h" -#include "CapsuleShape.h" +class CollisionInfo; +class SphereShape; +class CapsuleShape.h; std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); std::ostream& operator<<(std::ostream& s, const SphereShape& shape); std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); #endif // DEBUG +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +// Add support for writing these to qDebug()> +QDebug& operator<<(QDebug& s, const glm::vec3& v); +QDebug& operator<<(QDebug& s, const glm::quat& q); +QDebug& operator<<(QDebug& s, const glm::mat4& m); +#endif // QT_NO_DEBUG_STREAM #endif // hifi_StreamUtils_h From 961eca432746bc8e1fb1b949d04cb7c8d3ef2de7 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 20:51:19 +0200 Subject: [PATCH 44/72] Fix typo in previous commit --- libraries/metavoxels/src/MetavoxelUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/MetavoxelUtil.cpp b/libraries/metavoxels/src/MetavoxelUtil.cpp index b0233f04cd..d35b9a698a 100644 --- a/libraries/metavoxels/src/MetavoxelUtil.cpp +++ b/libraries/metavoxels/src/MetavoxelUtil.cpp @@ -312,7 +312,7 @@ Box operator*(const glm::mat4& matrix, const Box& box) { } QDebug& operator<<(QDebug& dbg, const Box& box) { - return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximumx=" << box.maximum << "}"; + return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximum=" << box.maximum << "}"; } QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) { From 13c2c69d0666805a9324a59468037dbe7cd90ba9 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 20:54:27 +0200 Subject: [PATCH 45/72] Fix another typo in previous commit --- libraries/shared/src/StreamUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index 4f74e895d2..c9356eaca2 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -48,7 +48,7 @@ std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); #ifndef QT_NO_DEBUG_STREAM class QDebug; -// Add support for writing these to qDebug()> +// Add support for writing these to qDebug(). QDebug& operator<<(QDebug& s, const glm::vec3& v); QDebug& operator<<(QDebug& s, const glm::quat& q); QDebug& operator<<(QDebug& s, const glm::mat4& m); From 147a4119cbdf8532ed61608a62d9536eca202f77 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Sun, 13 Jul 2014 21:54:07 +0200 Subject: [PATCH 46/72] Fix linker problem This fixes the linker problem: /usr/bin/ld: error: ../../libraries/shared/libshared.a(StreamUtils.cpp.o): multiple definition of 'operator<<(std::ostream&, glm::detail::tvec3 const&)' /usr/bin/ld: CMakeFiles/physics-tests.dir/src/PhysicsTestUtil.cpp.o: previous definition here /usr/bin/ld: error: ../../libraries/shared/libshared.a(StreamUtils.cpp.o): multiple definition of 'operator<<(std::ostream&, glm::detail::tquat const&)' /usr/bin/ld: CMakeFiles/physics-tests.dir/src/PhysicsTestUtil.cpp.o: previous definition here /usr/bin/ld: error: ../../libraries/shared/libshared.a(StreamUtils.cpp.o): multiple definition of 'operator<<(std::ostream&, glm::detail::tmat4x4 const&)' /usr/bin/ld: CMakeFiles/physics-tests.dir/src/PhysicsTestUtil.cpp.o: previous definition here collect2: error: ld returned 1 exit status Note these are the serializers to std::ostream that were already there, not the ones that I added (which are to QDebug). This bug was already there, I only got these errors now after adding some debug code. The reason is obvious: they are already defined in libshared.a (through StreamUtils.cpp). No need to define them again in ./tests/physics/src/PhysicsTestUtil.cpp --- tests/physics/src/PhysicsTestUtil.cpp | 20 +------------------- tests/physics/src/PhysicsTestUtil.h | 3 --- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/tests/physics/src/PhysicsTestUtil.cpp b/tests/physics/src/PhysicsTestUtil.cpp index f176d5e637..a11d4f2add 100644 --- a/tests/physics/src/PhysicsTestUtil.cpp +++ b/tests/physics/src/PhysicsTestUtil.cpp @@ -12,25 +12,7 @@ #include #include "PhysicsTestUtil.h" - -std::ostream& operator<<(std::ostream& s, const glm::vec3& v) { - s << "<" << v.x << "," << v.y << "," << v.z << ">"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const glm::quat& q) { - s << "<" << q.x << "," << q.y << "," << q.z << "," << q.w << ">"; - return s; -} - -std::ostream& operator<<(std::ostream& s, const glm::mat4& m) { - s << "["; - for (int j = 0; j < 4; ++j) { - s << " " << m[0][j] << " " << m[1][j] << " " << m[2][j] << " " << m[3][j] << ";"; - } - s << " ]"; - return s; -} +#include "StreamUtils.h" std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) { s << "[penetration=" << c._penetration diff --git a/tests/physics/src/PhysicsTestUtil.h b/tests/physics/src/PhysicsTestUtil.h index 998a18ff5d..c93545dd76 100644 --- a/tests/physics/src/PhysicsTestUtil.h +++ b/tests/physics/src/PhysicsTestUtil.h @@ -21,9 +21,6 @@ const glm::vec3 xAxis(1.f, 0.f, 0.f); const glm::vec3 yAxis(0.f, 1.f, 0.f); const glm::vec3 zAxis(0.f, 0.f, 1.f); -std::ostream& operator<<(std::ostream& s, const glm::vec3& v); -std::ostream& operator<<(std::ostream& s, const glm::quat& q); -std::ostream& operator<<(std::ostream& s, const glm::mat4& m); std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); #endif // hifi_PhysicsTestUtil_h From 2d4194cae501d3dfba678e17856b161185d76bc4 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Sun, 13 Jul 2014 23:33:02 -0700 Subject: [PATCH 47/72] fixes per review, clap animation requires double-tap to start --- examples/clap.js | 12 +++++------- interface/src/Hair.cpp | 10 ++++------ interface/src/avatar/Avatar.cpp | 1 - 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/clap.js b/examples/clap.js index 28835c1f00..6e70979c2e 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -94,23 +94,21 @@ function playClap(volume, position) { Audio.playSound(claps[clip], options); } -var FASTEST_CLAP_INTERVAL = 100.0; -var SLOWEST_CLAP_INTERVAL = 2000.0; +var FASTEST_CLAP_INTERVAL = 150.0; +var SLOWEST_CLAP_INTERVAL = 750.0; Controller.keyPressEvent.connect(function(event) { if(event.text == "SHIFT") { if (!clickClappingNow) { clickClappingNow = true; clickStartTime = new Date(); - playClap(1.0, Camera.getPosition()); lastClapFrame = 0; - MyAvatar.startAnimation(clapAnimation, clapRate, 1.0, true, false); } else { - // Adjust animation speed for measured clicking interval + // start or adjust clapping speed based on the duration between clicks clickEndTime = new Date(); - var milliseconds = clickEndTime - clickStartTime; + var milliseconds = Math.max(clickEndTime - clickStartTime, FASTEST_CLAP_INTERVAL); clickStartTime = new Date(); - if ((milliseconds < SLOWEST_CLAP_INTERVAL) && (milliseconds > FASTEST_CLAP_INTERVAL)) { + if (milliseconds < SLOWEST_CLAP_INTERVAL) { clapRate = ANIMATION_FRAMES_PER_CLAP * (1000.0 / milliseconds); playClap(1.0, Camera.getPosition()); MyAvatar.stopAnimation(clapAnimation); diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 824e273049..4dc9b9cf98 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -20,7 +20,7 @@ const float CONSTRAINT_RELAXATION = 10.0f; const float HAIR_ACCELERATION_COUPLING = 0.025f; const float HAIR_ANGULAR_VELOCITY_COUPLING = 0.10f; const float HAIR_MAX_LINEAR_ACCELERATION = 4.0f; -const float HAIR_STIFFNESS = 0.0003f; +const float HAIR_STIFFNESS = 0.005f; const glm::vec3 HAIR_COLOR1(0.98f, 0.92f, 0.843f); const glm::vec3 HAIR_COLOR2(0.545f, 0.533f, 0.47f); @@ -72,12 +72,10 @@ Hair::Hair(int strands, _hairConstraints[vertexIndex * HAIR_CONSTRAINTS + 1] = vertexIndex + 1; } } - _hairPosition[vertexIndex] = thisVertex; - _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex]; - _hairOriginalPosition[vertexIndex] = _hairPosition[vertexIndex]; + _hairOriginalPosition[vertexIndex] = _hairLastPosition[vertexIndex] = _hairPosition[vertexIndex] = thisVertex; _hairQuadDelta[vertexIndex] = glm::vec3(cos(strandAngle) * _hairThickness, 0.f, sin(strandAngle) * _hairThickness); - _hairQuadDelta[vertexIndex] *= 1.f - (link / _links); + _hairQuadDelta[vertexIndex] *= 1.f - ((float)link / _links); _hairNormals[vertexIndex] = glm::normalize(randVector()); if (randFloat() < elevation / PI_OVER_TWO) { _hairColors[vertexIndex] = HAIR_COLOR1 * ((float)(link + 1) / (float)_links); @@ -123,7 +121,7 @@ void Hair::simulate(float deltaTime) { // Add stiffness to return to original position _hairPosition[vertexIndex] += (_hairOriginalPosition[vertexIndex] - _hairPosition[vertexIndex]) - * powf(1.f - link / _links, 2.f) * HAIR_STIFFNESS; + * powf(1.f - (float)link / _links, 2.f) * HAIR_STIFFNESS; // Add angular acceleration const float ANGULAR_VELOCITY_MIN = 0.001f; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ff49d7394b..fd8f2d836b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -46,7 +46,6 @@ const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; Avatar::Avatar() : AvatarData(), - _hair(), _skeletonModel(this), _bodyYawDelta(0.0f), _velocity(0.0f, 0.0f, 0.0f), From 4b7ba08932e3584108853fa9ba6d6c794d366370 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Mon, 14 Jul 2014 08:53:10 -0700 Subject: [PATCH 48/72] code review fix --- interface/src/Hair.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/Hair.cpp b/interface/src/Hair.cpp index 4dc9b9cf98..e07b624d89 100644 --- a/interface/src/Hair.cpp +++ b/interface/src/Hair.cpp @@ -29,15 +29,15 @@ Hair::Hair(int strands, float radius, float linkLength, float hairThickness) : + _strands(strands), + _links(links), + _linkLength(linkLength), + _hairThickness(hairThickness), + _radius(radius), _acceleration(0.0f), _angularVelocity(0.0f), _gravity(0.0f) { - _strands = strands; - _links = links; - _radius = radius; - _linkLength = linkLength; - _hairThickness = hairThickness; _hairPosition = new glm::vec3[_strands * _links]; _hairOriginalPosition = new glm::vec3[_strands * _links]; _hairLastPosition = new glm::vec3[_strands * _links]; From a59cef31979f09a07bd11641965a5858df2474b5 Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 14 Jul 2014 10:05:37 -0700 Subject: [PATCH 49/72] removed debug code in AudioMixer --- assignment-client/src/audio/AudioMixer.cpp | 26 +--------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 6df23ca32f..c86d37e283 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -366,11 +366,6 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf } void AudioMixer::prepareMixForListeningNode(Node* node) { - - static int k = 0; - k++; - bool debug = (k % 20) == 0; - AvatarAudioRingBuffer* nodeRingBuffer = ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer(); // zero out the client mix for this node @@ -385,31 +380,12 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { // enumerate the ARBs attached to the otherNode and add all that should be added to mix for (int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i]; - - + if ((*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode()) && otherNodeBuffer->willBeAddedToMix() && otherNodeBuffer->getNextOutputTrailingLoudness() > 0.0f) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); - } else { - - if (debug) { - printf("\nWILL NOT MIX!!!\n"); - printf("listening node = %s\n", node->getUUID().toString().toLatin1().data()); - printf("other node = %s\n", otherNode->getUUID().toString().toLatin1().data()); - - if (otherNodeBuffer->getType() == PositionalAudioRingBuffer::Microphone) - printf("\t avatar buffer...\n"); - else - { - printf("\t inj buffer %s\n", ((InjectedAudioRingBuffer*)otherNodeBuffer)->getStreamIdentifier().toString().toLatin1().data()); - } - - printf("\t\t other==listening || shouldLoopBack: %d\n", (*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode())); - printf("\t\t other will be added to mix: %d\n", otherNodeBuffer->willBeAddedToMix()); - printf("\t\t other trailing loudess: %f\n", otherNodeBuffer->getNextOutputTrailingLoudness()); - } } } } From 30fd4e27d05d08a0a71a1a2bc515ba4868c9ed93 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 14 Jul 2014 10:43:01 -0700 Subject: [PATCH 50/72] pre-CR cleanup --- .../script-engine/src/ArrayBufferClass.cpp | 16 +- .../script-engine/src/ArrayBufferClass.h | 8 +- .../src/ArrayBufferPrototype.cpp | 1 - .../src/ArrayBufferViewClass.cpp | 60 +----- .../script-engine/src/ArrayBufferViewClass.h | 10 +- libraries/script-engine/src/DataViewClass.cpp | 5 +- .../script-engine/src/DataViewPrototype.cpp | 22 +- libraries/script-engine/src/ScriptEngine.cpp | 1 - libraries/script-engine/src/ScriptEngine.h | 2 +- .../script-engine/src/TypedArrayPrototype.cpp | 106 ++++++++++ .../script-engine/src/TypedArrayPrototype.h | 33 +++ libraries/script-engine/src/TypedArrays.cpp | 191 +++++------------- libraries/script-engine/src/TypedArrays.h | 54 ++--- 13 files changed, 241 insertions(+), 268 deletions(-) create mode 100644 libraries/script-engine/src/TypedArrayPrototype.cpp create mode 100644 libraries/script-engine/src/TypedArrayPrototype.h diff --git a/libraries/script-engine/src/ArrayBufferClass.cpp b/libraries/script-engine/src/ArrayBufferClass.cpp index f8299266fd..ab33b5ffe7 100644 --- a/libraries/script-engine/src/ArrayBufferClass.cpp +++ b/libraries/script-engine/src/ArrayBufferClass.cpp @@ -23,10 +23,9 @@ static const QString CLASS_NAME = "ArrayBuffer"; Q_DECLARE_METATYPE(QByteArray*) ArrayBufferClass::ArrayBufferClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getScriptEngine()), -QScriptClass(scriptEngine->getScriptEngine()), +QObject(scriptEngine->getEngine()), +QScriptClass(scriptEngine->getEngine()), _scriptEngine(scriptEngine) { - qDebug() << "Created with engine: " << engine(); qScriptRegisterMetaType(engine(), toScriptValue, fromScriptValue); QScriptValue global = engine()->globalObject(); @@ -40,15 +39,16 @@ _scriptEngine(scriptEngine) { QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); engine()->globalObject().setProperty(name(), _ctor); // Registering other array types + // The script engine is there parent so it'll delete them with itself new DataViewClass(scriptEngine); new Int8ArrayClass(scriptEngine); new Uint8ArrayClass(scriptEngine); @@ -87,11 +87,10 @@ QScriptValue ArrayBufferClass::newInstance(const QByteArray& ba) { QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* engine) { ArrayBufferClass* cls = qscriptvalue_cast(context->callee().data()); if (!cls) { + // return if callee (function called) is not of type ArrayBuffer return QScriptValue(); } - QScriptValue arg = context->argument(0); - if (!arg.isValid() || !arg.isNumber()) { return QScriptValue(); } @@ -100,6 +99,7 @@ QScriptValue ArrayBufferClass::construct(QScriptContext* context, QScriptEngine* QScriptValue newObject = cls->newInstance(size); if (context->isCalledAsConstructor()) { + // if called with keyword new, replace this object. context->setThisObject(newObject); return engine->undefinedValue(); } @@ -118,8 +118,8 @@ QScriptClass::QueryFlags ArrayBufferClass::queryProperty(const QScriptValue& obj return 0; // No access } -QScriptValue ArrayBufferClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue ArrayBufferClass::property(const QScriptValue& object, + const QScriptString& name, uint id) { QByteArray* ba = qscriptvalue_cast(object.data()); if (ba && name == _byteLength) { return ba->length(); diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index c7b4d447dd..f7ad8ad4bd 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -29,19 +29,19 @@ public: QScriptValue newInstance(qint32 size); QScriptValue newInstance(const QByteArray& ba); - ScriptEngine* getScriptEngine() { return _scriptEngine; } - QueryFlags queryProperty(const QScriptValue& object, const QScriptString& name, QueryFlags flags, uint* id); - QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, const QScriptString& name, uint id); QString name() const; QScriptValue prototype() const; + ScriptEngine* getEngine() { return _scriptEngine; } + private: static QScriptValue construct(QScriptContext* context, QScriptEngine* engine); diff --git a/libraries/script-engine/src/ArrayBufferPrototype.cpp b/libraries/script-engine/src/ArrayBufferPrototype.cpp index 37db5f3eec..53ebebc740 100644 --- a/libraries/script-engine/src/ArrayBufferPrototype.cpp +++ b/libraries/script-engine/src/ArrayBufferPrototype.cpp @@ -12,7 +12,6 @@ #include #include "ArrayBufferClass.h" - #include "ArrayBufferPrototype.h" Q_DECLARE_METATYPE(QByteArray*) diff --git a/libraries/script-engine/src/ArrayBufferViewClass.cpp b/libraries/script-engine/src/ArrayBufferViewClass.cpp index a01f239512..aad2e6add7 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.cpp +++ b/libraries/script-engine/src/ArrayBufferViewClass.cpp @@ -14,8 +14,8 @@ Q_DECLARE_METATYPE(QByteArray*) ArrayBufferViewClass::ArrayBufferViewClass(ScriptEngine* scriptEngine) : -QObject(scriptEngine->getScriptEngine()), -QScriptClass(scriptEngine->getScriptEngine()), +QObject(scriptEngine->getEngine()), +QScriptClass(scriptEngine->getEngine()), _scriptEngine(scriptEngine) { // Save string handles for quick lookup _bufferName = engine()->toStringHandle(BUFFER_PROPERTY_NAME.toLatin1()); @@ -32,8 +32,8 @@ QScriptClass::QueryFlags ArrayBufferViewClass::queryProperty(const QScriptValue& return 0; // No access } -QScriptValue ArrayBufferViewClass::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue ArrayBufferViewClass::property(const QScriptValue& object, + const QScriptString& name, uint id) { if (name == _bufferName) { return object.data().property(_bufferName); } @@ -50,55 +50,3 @@ QScriptValue::PropertyFlags ArrayBufferViewClass::propertyFlags(const QScriptVal const QScriptString& name, uint id) { return QScriptValue::Undeletable; } - -//QScriptClass::QueryFlags DataViewClass::queryProperty(const QScriptValue& object, -// const QScriptString& name, -// QueryFlags flags, uint* id) { -// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); -// bool ok = false; -// int pos = name.toArrayIndex(&ok); -// -// // Check that name is a valid index and arrayBuffer exists -// if (ok && arrayBuffer && pos > 0 && pos < arrayBuffer->size()) { -// *id = pos; // save pos to avoid recomputation -// return HandlesReadAccess | HandlesWriteAccess; // Read/Write access -// } -// -// return ArrayBufferViewClass::queryProperty(object, name, flags, id); -//} -// -//QScriptValue DataViewClass::property(const QScriptValue &object, -// const QScriptString &name, uint id) { -// QByteArray* arrayBuffer = qscriptvalue_cast(object.property(_bufferName).data()); -// bool ok = false; -// name.toArrayIndex(&ok); -// -// if (ok && arrayBuffer) { -// return (*arrayBuffer)[id]; -// } -// -// return ArrayBufferViewClass::queryProperty(object, name, flags, id); -//} -// -//void DataViewClass::setProperty(QScriptValue &object, -// const QScriptString &name, -// uint id, const QScriptValue &value) { -// QByteArray *ba = qscriptvalue_cast(object.data()); -// if (!ba) -// return; -// if (name == length) { -// resize(*ba, value.toInt32()); -// } else { -// qint32 pos = id; -// if (pos < 0) -// return; -// if (ba->size() <= pos) -// resize(*ba, pos + 1); -// (*ba)[pos] = char(value.toInt32()); -// } -//} -// -//QScriptValue::PropertyFlags DataViewClass::propertyFlags(const QScriptValue& object, -// const QScriptString& name, uint id) { -// return QScriptValue::Undeletable; -//} diff --git a/libraries/script-engine/src/ArrayBufferViewClass.h b/libraries/script-engine/src/ArrayBufferViewClass.h index acc061c8a3..b673ebf280 100644 --- a/libraries/script-engine/src/ArrayBufferViewClass.h +++ b/libraries/script-engine/src/ArrayBufferViewClass.h @@ -34,12 +34,12 @@ public: ScriptEngine* getScriptEngine() { return _scriptEngine; } virtual QueryFlags queryProperty(const QScriptValue& object, - const QScriptString& name, - QueryFlags flags, uint* id); - virtual QScriptValue property(const QScriptValue &object, - const QScriptString &name, uint id); + const QScriptString& name, + QueryFlags flags, uint* id); + virtual QScriptValue property(const QScriptValue& object, + const QScriptString& name, uint id); virtual QScriptValue::PropertyFlags propertyFlags(const QScriptValue& object, - const QScriptString& name, uint id); + const QScriptString& name, uint id); protected: // JS Object attributes QScriptString _bufferName; diff --git a/libraries/script-engine/src/DataViewClass.cpp b/libraries/script-engine/src/DataViewClass.cpp index efb45daf3f..a65bdff617 100644 --- a/libraries/script-engine/src/DataViewClass.cpp +++ b/libraries/script-engine/src/DataViewClass.cpp @@ -29,12 +29,11 @@ DataViewClass::DataViewClass(ScriptEngine* scriptEngine) : ArrayBufferViewClass( QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); - engine()->globalObject().setProperty(name(), _ctor); } @@ -47,7 +46,7 @@ QScriptValue DataViewClass::newInstance(QScriptValue buffer, quint32 byteOffset, return engine()->newObject(this, data); } -QScriptValue DataViewClass::construct(QScriptContext *context, QScriptEngine *engine) { +QScriptValue DataViewClass::construct(QScriptContext* context, QScriptEngine* engine) { DataViewClass* cls = qscriptvalue_cast(context->callee().data()); if (!cls || context->argumentCount() < 1) { return QScriptValue(); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 29264e9bc5..91f7e01b50 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -36,8 +36,6 @@ bool DataViewPrototype::realOffset(qint32& offset, size_t size) const { return (offset + size) <= viewOffset + viewLength; } -///////////////// GETTERS //////////////////////////// - qint32 DataViewPrototype::getInt8(qint32 byteOffset) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(*thisArrayBuffer()); @@ -149,7 +147,7 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) double result; stream >> result; if (isNaN(result)) { - return QScriptValue(); + return QScriptValue(NAN); } return result; @@ -158,8 +156,6 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) return QScriptValue(); } -///////////////// SETTERS //////////////////////////// - void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { if (realOffset(byteOffset, sizeof(qint8))) { QDataStream stream(thisArrayBuffer(), QIODevice::ReadWrite); @@ -167,7 +163,7 @@ void DataViewPrototype::setInt8(qint32 byteOffset, qint32 value) { stream << (qint8)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -178,7 +174,7 @@ void DataViewPrototype::setUint8(qint32 byteOffset, quint32 value) { stream << (quint8)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -190,7 +186,7 @@ void DataViewPrototype::setInt16(qint32 byteOffset, qint32 value, bool littleEnd stream << (qint16)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -202,7 +198,7 @@ void DataViewPrototype::setUint16(qint32 byteOffset, quint32 value, bool littleE stream << (quint16)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -214,7 +210,7 @@ void DataViewPrototype::setInt32(qint32 byteOffset, qint32 value, bool littleEnd stream << (qint32)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -226,7 +222,7 @@ void DataViewPrototype::setUint32(qint32 byteOffset, quint32 value, bool littleE stream << (quint32)value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -239,7 +235,7 @@ void DataViewPrototype::setFloat32(qint32 byteOffset, float value, bool littleEn stream << value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } @@ -252,7 +248,7 @@ void DataViewPrototype::setFloat64(qint32 byteOffset, double value, bool littleE stream << value; } else { - thisObject().engine()->evaluate("throw \"1RangeError: byteOffset out of range\""); + thisObject().engine()->evaluate("throw \"RangeError: byteOffset out of range\""); } } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a224b9fa33..cd7c2670ec 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -143,7 +143,6 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QTextStream in(&scriptFile); _scriptContents = in.readAll(); } else { - qDebug() << "ERROR Loading file:" << fileName; emit errorMessage("ERROR Loading file:" + fileName); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 3dda172044..0eda74914f 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -57,7 +57,7 @@ public: /// Access the ModelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener static ModelsScriptingInterface* getModelsScriptingInterface() { return &_modelsScriptingInterface; } - QScriptEngine* getScriptEngine() { return &_engine; } + QScriptEngine* getEngine() { return &_engine; } ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } /// sets the script contents, will return false if failed, will fail if script is already running diff --git a/libraries/script-engine/src/TypedArrayPrototype.cpp b/libraries/script-engine/src/TypedArrayPrototype.cpp new file mode 100644 index 0000000000..37274dd080 --- /dev/null +++ b/libraries/script-engine/src/TypedArrayPrototype.cpp @@ -0,0 +1,106 @@ +// +// TypedArrayPrototype.cpp +// +// +// Created by Clement on 7/14/14. +// Copyright 2014 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 "TypedArrays.h" + +#include "TypedArrayPrototype.h" + +Q_DECLARE_METATYPE(QByteArray*) + +TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { +} + +QByteArray* TypedArrayPrototype::thisArrayBuffer() const { + QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); + return qscriptvalue_cast(bufferObject.data()); +} + +void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { + TypedArray* typedArray = static_cast(parent()); + if (array.isArray() || typedArray) { + if (offset < 0) { + engine()->evaluate("throw \"ArgumentError: negative offset\""); + } + quint32 length = array.property("length").toInt32(); + if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { + engine()->evaluate("throw \"ArgumentError: array does not fit\""); + return; + } + for (int i = 0; i < length; ++i) { + thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); + } + } else { + engine()->evaluate("throw \"ArgumentError: not an array\""); + } +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); +} + +QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); + qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); + qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); + qint32 bytesPerElement = typedArray->_bytesPerElement; + + // if indices < 0 then they start from the end of the array + begin = (begin < 0) ? length + begin : begin; + end = (end < 0) ? length + end : end; + + // here we clamp the indices to fit the array + begin = glm::clamp(begin, 0, (length - 1)); + end = glm::clamp(end, 0, (length - 1)); + + byteOffset += begin * bytesPerElement; + length = (end - begin > 0) ? end - begin : 0; + return typedArray->newInstance(arrayBuffer, byteOffset, length); +} + +QScriptValue TypedArrayPrototype::get(quint32 index) { + TypedArray* typedArray = static_cast(parent()); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), + name, + QScriptClass::HandlesReadAccess, &id); + if (QScriptClass::HandlesReadAccess & flags) { + return typedArray->property(thisObject(), name, id); + } + return QScriptValue(); +} + +void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { + TypedArray* typedArray = static_cast(parent()); + QScriptValue object = thisObject(); + QScriptString name = engine()->toStringHandle(QString::number(index)); + uint id; + QScriptClass::QueryFlags flags = typedArray->queryProperty(object, + name, + QScriptClass::HandlesWriteAccess, &id); + if (QScriptClass::HandlesWriteAccess & flags) { + typedArray->setProperty(object, name, id, value); + } +} diff --git a/libraries/script-engine/src/TypedArrayPrototype.h b/libraries/script-engine/src/TypedArrayPrototype.h new file mode 100644 index 0000000000..86d578ace0 --- /dev/null +++ b/libraries/script-engine/src/TypedArrayPrototype.h @@ -0,0 +1,33 @@ +// +// TypedArrayPrototype.h +// +// +// Created by Clement on 7/14/14. +// Copyright 2014 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 +// + +#ifndef hifi_TypedArrayPrototype_h +#define hifi_TypedArrayPrototype_h + +#include "ArrayBufferViewClass.h" + +class TypedArrayPrototype : public QObject, public QScriptable { + Q_OBJECT +public: + TypedArrayPrototype(QObject* parent = NULL); + +public slots: + void set(QScriptValue array, qint32 offset = 0); + QScriptValue subarray(qint32 begin); + QScriptValue subarray(qint32 begin, qint32 end); + + QScriptValue get(quint32 index); + void set(quint32 index, QScriptValue& value); +private: + QByteArray* thisArrayBuffer() const; +}; + +#endif // hifi_TypedArrayPrototype_h \ No newline at end of file diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 6b461fc6fb..72e84859c4 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -12,6 +12,7 @@ #include #include "ScriptEngine.h" +#include "TypedArrayPrototype.h" #include "TypedArrays.h" @@ -30,12 +31,11 @@ TypedArray::TypedArray(ScriptEngine* scriptEngine, QString name) : ArrayBufferVi QScriptEngine::SkipMethodsInEnumeration | QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties); - _proto.setPrototype(global.property("Object").property("prototype")); + // Register constructor _ctor = engine()->newFunction(construct, _proto); _ctor.setData(engine()->toScriptValue(this)); - engine()->globalObject().setProperty(_name, _ctor); } @@ -46,8 +46,9 @@ QScriptValue TypedArray::newInstance(quint32 length) { } QScriptValue TypedArray::newInstance(QScriptValue array) { - if (array.property("length").isValid()) { - quint32 length = array.property("length").toInt32(); + const QString ARRAY_LENGTH_HANDLE = "length"; + if (array.property(ARRAY_LENGTH_HANDLE).isValid()) { + quint32 length = array.property(ARRAY_LENGTH_HANDLE).toInt32(); QScriptValue newArray = newInstance(length); for (int i = 0; i < length; ++i) { QScriptValue value = array.property(QString::number(i)); @@ -70,12 +71,11 @@ QScriptValue TypedArray::newInstance(QScriptValue buffer, quint32 byteOffset, qu return engine()->newObject(this, data); } -QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engine) { +QScriptValue TypedArray::construct(QScriptContext* context, QScriptEngine* engine) { TypedArray* cls = qscriptvalue_cast(context->callee().data()); if (!cls) { return QScriptValue(); } - if (context->argumentCount() == 0) { return cls->newInstance(0); } @@ -84,6 +84,7 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin QScriptValue bufferArg = context->argument(0); QByteArray* arrayBuffer = qscriptvalue_cast(bufferArg.data()); + // parse arguments if (arrayBuffer) { if (context->argumentCount() == 1) { // Case for entire ArrayBuffer @@ -136,6 +137,7 @@ QScriptValue TypedArray::construct(QScriptContext *context, QScriptEngine *engin } if (context->isCalledAsConstructor()) { + // if called with the new keyword, replace this object context->setThisObject(newObject); return engine->undefinedValue(); } @@ -164,8 +166,8 @@ QScriptClass::QueryFlags TypedArray::queryProperty(const QScriptValue& object, return ArrayBufferViewClass::queryProperty(object, name, flags, id); } -QScriptValue TypedArray::property(const QScriptValue &object, - const QScriptString &name, uint id) { +QScriptValue TypedArray::property(const QScriptValue& object, + const QScriptString& name, uint id) { if (name == _bytesPerElementName) { return QScriptValue(_bytesPerElement); } @@ -193,98 +195,10 @@ void TypedArray::setBytesPerElement(quint32 bytesPerElement) { _ctor.setProperty(_bytesPerElementName, _bytesPerElement); } -TypedArrayPrototype::TypedArrayPrototype(QObject* parent) : QObject(parent) { -} - -QByteArray* TypedArrayPrototype::thisArrayBuffer() const { - QScriptValue bufferObject = thisObject().data().property(BUFFER_PROPERTY_NAME); - return qscriptvalue_cast(bufferObject.data()); -} - -void TypedArrayPrototype::set(QScriptValue array, qint32 offset) { - TypedArray* typedArray = static_cast(parent()); - if (array.isArray() || typedArray) { - if (offset < 0) { - engine()->evaluate("throw \"ArgumentError: negative offset\""); - } - quint32 length = array.property("length").toInt32(); - if (offset + length > thisObject().data().property(typedArray->_lengthName).toInt32()) { - engine()->evaluate("throw \"ArgumentError: array does not fit\""); - return; - } - for (int i = 0; i < length; ++i) { - thisObject().setProperty(QString::number(offset + i), array.property(QString::number(i))); - } - } else { - engine()->evaluate("throw \"ArgumentError: not an array\""); - } -} - -QScriptValue TypedArrayPrototype::subarray(qint32 begin) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); - qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); - qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); - qint32 bytesPerElement = typedArray->_bytesPerElement; - - // if indices < 0 then they start from the end of the array - begin = (begin < 0) ? length + begin : begin; - - // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0, (length - 1)); - - byteOffset += begin * bytesPerElement; - return typedArray->newInstance(arrayBuffer, byteOffset, length - begin); -} - -QScriptValue TypedArrayPrototype::subarray(qint32 begin, qint32 end) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue arrayBuffer = thisObject().data().property(typedArray->_bufferName); - qint32 byteOffset = thisObject().data().property(typedArray->_byteOffsetName).toInt32(); - qint32 length = thisObject().data().property(typedArray->_lengthName).toInt32(); - qint32 bytesPerElement = typedArray->_bytesPerElement; - - // if indices < 0 then they start from the end of the array - begin = (begin < 0) ? length + begin : begin; - end = (end < 0) ? length + end : end; - - // here we clamp the indices to fit the array - begin = glm::clamp(begin, 0, (length - 1)); - end = glm::clamp(end, 0, (length - 1)); - - byteOffset += begin * bytesPerElement; - length = (end - begin > 0) ? end - begin : 0; - return typedArray->newInstance(arrayBuffer, byteOffset, length); -} - -QScriptValue TypedArrayPrototype::get(quint32 index) { - TypedArray* typedArray = static_cast(parent()); - QScriptString name = engine()->toStringHandle(QString::number(index)); - uint id; - QScriptClass::QueryFlags flags = typedArray->queryProperty(thisObject(), - name, - QScriptClass::HandlesReadAccess, &id); - if (QScriptClass::HandlesReadAccess & flags) { - return typedArray->property(thisObject(), name, id); - } - return QScriptValue(); -} - -void TypedArrayPrototype::set(quint32 index, QScriptValue& value) { - TypedArray* typedArray = static_cast(parent()); - QScriptValue object = thisObject(); - QScriptString name = engine()->toStringHandle(QString::number(index)); - uint id; - QScriptClass::QueryFlags flags = typedArray->queryProperty(object, - name, - QScriptClass::HandlesWriteAccess, &id); - if (QScriptClass::HandlesWriteAccess & flags) { - typedArray->setProperty(object, name, id, value); - } -} - +// templated helper functions +// don't work for floats as they require single precision settings template -QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString &name, uint id) { +QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString& name, uint id) { bool ok = false; name.toArrayIndex(&ok); @@ -300,7 +214,7 @@ QScriptValue propertyHelper(const QByteArray* arrayBuffer, const QScriptString & } template -void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString &name, uint id, const QScriptValue &value) { +void setPropertyHelper(QByteArray* arrayBuffer, const QScriptString& name, uint id, const QScriptValue& value) { if (arrayBuffer && value.isNumber()) { QDataStream stream(arrayBuffer, QIODevice::ReadWrite); stream.skipRawData(id); @@ -313,15 +227,15 @@ Int8ArrayClass::Int8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scriptEn setBytesPerElement(sizeof(qint8)); } -QScriptValue Int8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { +QScriptValue Int8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } void Int8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -329,16 +243,15 @@ Uint8ArrayClass::Uint8ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(quint8)); } -QScriptValue Uint8ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint8ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint8ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint8ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -346,16 +259,15 @@ Uint8ClampedArrayClass::Uint8ClampedArrayClass(ScriptEngine* scriptEngine) : Typ setBytesPerElement(sizeof(quint8)); } -QScriptValue Uint8ClampedArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint8ClampedArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint8ClampedArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint8ClampedArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); @@ -373,16 +285,15 @@ Int16ArrayClass::Int16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(qint16)); } -QScriptValue Int16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Int16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Int16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Int16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -390,16 +301,15 @@ Uint16ArrayClass::Uint16ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scri setBytesPerElement(sizeof(quint16)); } -QScriptValue Uint16ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Uint16ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint16ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint16ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -407,16 +317,15 @@ Int32ArrayClass::Int32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(script setBytesPerElement(sizeof(qint32)); } -QScriptValue Int32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Int32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Int32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Int32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -424,16 +333,16 @@ Uint32ArrayClass::Uint32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(scri setBytesPerElement(sizeof(quint32)); } -QScriptValue Uint32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { +QScriptValue Uint32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data()); QScriptValue result = propertyHelper(arrayBuffer, name, id); return (result.isValid()) ? result : TypedArray::property(object, name, id); } -void Uint32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Uint32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); setPropertyHelper(ba, name, id, value); } @@ -441,8 +350,7 @@ Float32ArrayClass::Float32ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc setBytesPerElement(sizeof(float)); } -QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; name.toArrayIndex(&ok); @@ -461,9 +369,9 @@ QScriptValue Float32ArrayClass::property(const QScriptValue &object, const QScri return TypedArray::property(object, name, id); } -void Float32ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Float32ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); @@ -477,8 +385,7 @@ Float64ArrayClass::Float64ArrayClass(ScriptEngine* scriptEngine) : TypedArray(sc setBytesPerElement(sizeof(double)); } -QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScriptString &name, uint id) { - +QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScriptString& name, uint id) { QByteArray* arrayBuffer = qscriptvalue_cast(object.data().property(_bufferName).data());bool ok = false; name.toArrayIndex(&ok); @@ -497,9 +404,9 @@ QScriptValue Float64ArrayClass::property(const QScriptValue &object, const QScri return TypedArray::property(object, name, id); } -void Float64ArrayClass::setProperty(QScriptValue &object, const QScriptString &name, - uint id, const QScriptValue &value) { - QByteArray *ba = qscriptvalue_cast(object.data().property(_bufferName).data()); +void Float64ArrayClass::setProperty(QScriptValue& object, const QScriptString& name, + uint id, const QScriptValue& value) { + QByteArray* ba = qscriptvalue_cast(object.data().property(_bufferName).data()); if (ba && value.isNumber()) { QDataStream stream(ba, QIODevice::ReadWrite); stream.skipRawData(id); diff --git a/libraries/script-engine/src/TypedArrays.h b/libraries/script-engine/src/TypedArrays.h index bacb067add..a1a288c3c9 100644 --- a/libraries/script-engine/src/TypedArrays.h +++ b/libraries/script-engine/src/TypedArrays.h @@ -64,29 +64,13 @@ protected: friend class TypedArrayPrototype; }; -class TypedArrayPrototype : public QObject, public QScriptable { - Q_OBJECT -public: - TypedArrayPrototype(QObject* parent = NULL); - -public slots: - void set(QScriptValue array, qint32 offset = 0); - QScriptValue subarray(qint32 begin); - QScriptValue subarray(qint32 begin, qint32 end); - - QScriptValue get(quint32 index); - void set(quint32 index, QScriptValue& value); -private: - QByteArray* thisArrayBuffer() const; -}; - class Int8ArrayClass : public TypedArray { Q_OBJECT public: Int8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint8ArrayClass : public TypedArray { @@ -94,8 +78,8 @@ class Uint8ArrayClass : public TypedArray { public: Uint8ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint8ClampedArrayClass : public TypedArray { @@ -103,8 +87,8 @@ class Uint8ClampedArrayClass : public TypedArray { public: Uint8ClampedArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Int16ArrayClass : public TypedArray { @@ -112,8 +96,8 @@ class Int16ArrayClass : public TypedArray { public: Int16ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint16ArrayClass : public TypedArray { @@ -121,16 +105,17 @@ class Uint16ArrayClass : public TypedArray { public: Uint16ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); +}; class Int32ArrayClass : public TypedArray { Q_OBJECT public: Int32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Uint32ArrayClass : public TypedArray { @@ -138,8 +123,8 @@ class Uint32ArrayClass : public TypedArray { public: Uint32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; class Float32ArrayClass : public TypedArray { @@ -147,16 +132,17 @@ class Float32ArrayClass : public TypedArray { public: Float32ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value);}; + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); +}; class Float64ArrayClass : public TypedArray { Q_OBJECT public: Float64ArrayClass(ScriptEngine* scriptEngine); - QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); - void setProperty(QScriptValue &object, const QScriptString &name, uint id, const QScriptValue &value); + QScriptValue property(const QScriptValue& object, const QScriptString& name, uint id); + void setProperty(QScriptValue& object, const QScriptString& name, uint id, const QScriptValue& value); }; #endif // hifi_TypedArrays_h \ No newline at end of file From cb5a9bf6688e3728ac1783422e033b82e55d463b Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 14 Jul 2014 10:43:02 -0700 Subject: [PATCH 51/72] moved some const defs to avoid redefinition --- libraries/shared/src/SharedUtil.cpp | 3 +++ libraries/shared/src/SharedUtil.h | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 23d33969f4..b5be502ed5 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -839,6 +839,9 @@ QByteArray createByteArray(const glm::vec3& vector) { } QString formatUsecTime(float usecs, int prec) { + static const quint64 SECONDS_PER_MINUTE = 60; + static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; + QString result; if (usecs > USECS_PER_MINUTE) { result = QString::number(usecs / USECS_PER_MINUTE, 'f', prec) + "min"; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 81ec2ab810..6bb39f7e12 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -57,9 +57,7 @@ static const float METERS_PER_MILLIMETER = 0.001f; static const float MILLIMETERS_PER_METER = 1000.0f; static const quint64 USECS_PER_MSEC = 1000; static const quint64 MSECS_PER_SECOND = 1000; -static const quint64 SECONDS_PER_MINUTE = 60; static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; -static const quint64 USECS_PER_MINUTE = USECS_PER_SECOND * SECONDS_PER_MINUTE; const int BITS_IN_BYTE = 8; From afa43a9d7e8ce19f0f35aa708cea7b19a1c0fcba Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 14 Jul 2014 11:06:10 -0700 Subject: [PATCH 52/72] Fix win build fail --- examples/typedArraysUnitTest.js | 2 +- libraries/script-engine/src/DataViewPrototype.cpp | 4 ++-- libraries/script-engine/src/TypedArrays.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/typedArraysUnitTest.js b/examples/typedArraysUnitTest.js index 859bc9b9b3..e86a07289d 100644 --- a/examples/typedArraysUnitTest.js +++ b/examples/typedArraysUnitTest.js @@ -514,7 +514,7 @@ test('TypedArray setting', function () { var b = new Int32Array(5); b.set(a); this.arrayEqual(b, [1, 2, 3, 4, 5], '1'); - // raises(function () { b.set(a, 1); }); + this.raises(function () { b.set(a, 1); }); b.set(new Int32Array([99, 98]), 2); this.arrayEqual(b, [1, 2, 99, 98, 5], '2'); diff --git a/libraries/script-engine/src/DataViewPrototype.cpp b/libraries/script-engine/src/DataViewPrototype.cpp index 91f7e01b50..8bab574f33 100644 --- a/libraries/script-engine/src/DataViewPrototype.cpp +++ b/libraries/script-engine/src/DataViewPrototype.cpp @@ -128,7 +128,7 @@ QScriptValue DataViewPrototype::getFloat32(qint32 byteOffset, bool littleEndian) float result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return QScriptValue(result); @@ -147,7 +147,7 @@ QScriptValue DataViewPrototype::getFloat64(qint32 byteOffset, bool littleEndian) double result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; diff --git a/libraries/script-engine/src/TypedArrays.cpp b/libraries/script-engine/src/TypedArrays.cpp index 72e84859c4..751d1385e5 100644 --- a/libraries/script-engine/src/TypedArrays.cpp +++ b/libraries/script-engine/src/TypedArrays.cpp @@ -362,7 +362,7 @@ QScriptValue Float32ArrayClass::property(const QScriptValue& object, const QScri float result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; } @@ -397,7 +397,7 @@ QScriptValue Float64ArrayClass::property(const QScriptValue& object, const QScri double result; stream >> result; if (isNaN(result)) { - return QScriptValue(NAN); + return QScriptValue(); } return result; } From a37e24aeb48981dd6252b5a0f0ee03ca24a5eeef Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 14 Jul 2014 11:08:00 -0700 Subject: [PATCH 53/72] updated domain page stats to use formated usec time strings --- .../src/audio/AudioMixerClientData.cpp | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 94bbdc6a6b..6559b57959 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -280,12 +280,12 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { + " silents_dropped: ?" + " lost%:" + QString::number(streamStats._packetStreamStats.getLostRate() * 100.0f, 'f', 2) + " lost%_30s:" + QString::number(streamStats._packetStreamWindowStats.getLostRate() * 100.0f, 'f', 2) - + " min_gap:" + QString::number(streamStats._timeGapMin) - + " max_gap:" + QString::number(streamStats._timeGapMax) - + " avg_gap:" + QString::number(streamStats._timeGapAverage, 'f', 2) - + " min_gap_30s:" + QString::number(streamStats._timeGapWindowMin) - + " max_gap_30s:" + QString::number(streamStats._timeGapWindowMax) - + " avg_gap_30s:" + QString::number(streamStats._timeGapWindowAverage, 'f', 2); + + " min_gap:" + formatUsecTime(streamStats._timeGapMin) + + " max_gap:" + formatUsecTime(streamStats._timeGapMax) + + " avg_gap:" + formatUsecTime(streamStats._timeGapAverage) + + " min_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMin) + + " max_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMax) + + " avg_gap_30s:" + formatUsecTime(streamStats._timeGapWindowAverage); AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); if (avatarRingBuffer) { @@ -299,12 +299,12 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { + " silents_dropped:" + QString::number(streamStats._ringBufferSilentFramesDropped) + " lost%:" + QString::number(streamStats._packetStreamStats.getLostRate() * 100.0f, 'f', 2) + " lost%_30s:" + QString::number(streamStats._packetStreamWindowStats.getLostRate() * 100.0f, 'f', 2) - + " min_gap:" + QString::number(streamStats._timeGapMin) - + " max_gap:" + QString::number(streamStats._timeGapMax) - + " avg_gap:" + QString::number(streamStats._timeGapAverage, 'f', 2) - + " min_gap_30s:" + QString::number(streamStats._timeGapWindowMin) - + " max_gap_30s:" + QString::number(streamStats._timeGapWindowMax) - + " avg_gap_30s:" + QString::number(streamStats._timeGapWindowAverage, 'f', 2); + + " min_gap:" + formatUsecTime(streamStats._timeGapMin) + + " max_gap:" + formatUsecTime(streamStats._timeGapMax) + + " avg_gap:" + formatUsecTime(streamStats._timeGapAverage) + + " min_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMin) + + " max_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMax) + + " avg_gap_30s:" + formatUsecTime(streamStats._timeGapWindowAverage); } else { result = "mic unknown"; } @@ -321,12 +321,12 @@ QString AudioMixerClientData::getAudioStreamStatsString() const { + " silents_dropped:" + QString::number(streamStats._ringBufferSilentFramesDropped) + " lost%:" + QString::number(streamStats._packetStreamStats.getLostRate() * 100.0f, 'f', 2) + " lost%_30s:" + QString::number(streamStats._packetStreamWindowStats.getLostRate() * 100.0f, 'f', 2) - + " min_gap:" + QString::number(streamStats._timeGapMin) - + " max_gap:" + QString::number(streamStats._timeGapMax) - + " avg_gap:" + QString::number(streamStats._timeGapAverage, 'f', 2) - + " min_gap_30s:" + QString::number(streamStats._timeGapWindowMin) - + " max_gap_30s:" + QString::number(streamStats._timeGapWindowMax) - + " avg_gap_30s:" + QString::number(streamStats._timeGapWindowAverage, 'f', 2); + + " min_gap:" + formatUsecTime(streamStats._timeGapMin) + + " max_gap:" + formatUsecTime(streamStats._timeGapMax) + + " avg_gap:" + formatUsecTime(streamStats._timeGapAverage) + + " min_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMin) + + " max_gap_30s:" + formatUsecTime(streamStats._timeGapWindowMax) + + " avg_gap_30s:" + formatUsecTime(streamStats._timeGapWindowAverage); } } return result; From 935f28aa35d0be5fe5d6d738473205529f207925 Mon Sep 17 00:00:00 2001 From: Aleric Inglewood Date: Mon, 14 Jul 2014 21:18:37 +0200 Subject: [PATCH 54/72] Type fix. That that even compiled :/ --- libraries/shared/src/StreamUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index c9356eaca2..6d00c0e354 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -40,7 +40,7 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion); #ifdef DEBUG class CollisionInfo; class SphereShape; -class CapsuleShape.h; +class CapsuleShape; std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); std::ostream& operator<<(std::ostream& s, const SphereShape& shape); std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); From 1aa20dff607afbe796c112a1220856ad40c455ba Mon Sep 17 00:00:00 2001 From: TonyPeng Date: Mon, 14 Jul 2014 13:59:57 -0700 Subject: [PATCH 55/72] Expose light directions and colors for the avatar list in AvatarManager. Light parameter changes within the script will effect all the avatars currently in AvatarManager. --- examples/avatarLocalLight.js | 131 +++++++++++++++++++------ interface/src/Application.cpp | 2 + interface/src/avatar/Avatar.cpp | 19 +--- interface/src/avatar/AvatarManager.cpp | 60 +++++++++++ interface/src/avatar/AvatarManager.h | 11 +++ 5 files changed, 176 insertions(+), 47 deletions(-) diff --git a/examples/avatarLocalLight.js b/examples/avatarLocalLight.js index 57f9d84ffe..1ad016f2b3 100644 --- a/examples/avatarLocalLight.js +++ b/examples/avatarLocalLight.js @@ -10,12 +10,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var localLightDirections = [ {x: 1.0, y:0.0, z: 0.0}, {x: 0.0, y:1.0, z: 1.0}, {x: 0.0, y:0.0, z: 1.0}, {x: 1.0, y:1.0, z: 1.0} ]; -var localLightColors = [ {x: 0.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 0.0}, {x: 0.0, y:0.0, z: 0.0} ]; +var localLightDirections = [ {x: 1.0, y:1.0, z: 0.0}, {x: 0.0, y:1.0, z: 1.0} ]; +var localLightColors = [ {x: 0.0, y:1.0, z: 0.0}, {x: 1.0, y:0.0, z: 0.0} ]; var currentSelection = 0; var currentNumLights = 1; var maxNumLights = 2; +var currentNumAvatars = 0; +var avatarHashIDs = []; function keyPressEvent(event) { @@ -43,7 +45,8 @@ function keyPressEvent(event) { localLightColors[currentSelection].x = 0.0; } - MyAvatar.setLocalLightColor(localLightColors[currentSelection], currentSelection); + setAllLightColors(); + print("CHANGE RED light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "6" ) { localLightColors[currentSelection].y += 0.01; @@ -51,7 +54,8 @@ function keyPressEvent(event) { localLightColors[currentSelection].y = 0.0; } - MyAvatar.setLocalLightColor(localLightColors[currentSelection], currentSelection); + setAllLightColors(); + print("CHANGE GREEN light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "7" ) { localLightColors[currentSelection].z += 0.01; @@ -59,7 +63,8 @@ function keyPressEvent(event) { localLightColors[currentSelection].z = 0.0; } - MyAvatar.setLocalLightColor(localLightColors[currentSelection], currentSelection); + setAllLightColors(); + print("CHANGE BLUE light " + currentSelection + " color (" + localLightColors[currentSelection].x + ", " + localLightColors[currentSelection].y + ", " + localLightColors[currentSelection].z + " )" ); } else if (event.text == "8" ) { localLightDirections[currentSelection].x += 0.01; @@ -67,7 +72,8 @@ function keyPressEvent(event) { localLightDirections[currentSelection].x = -1.0; } - MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + setAllLightDirections(); + print("PLUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "9" ) { localLightDirections[currentSelection].x -= 0.01; @@ -75,52 +81,119 @@ function keyPressEvent(event) { localLightDirections[currentSelection].x = 1.0; } - MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + setAllLightDirections(); + print("MINUS X light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } - else if (event.text == "[" ) { + else if (event.text == "0" ) { localLightDirections[currentSelection].y += 0.01; if (localLightDirections[currentSelection].y > 1.0) { localLightDirections[currentSelection].y = -1.0; } - MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + setAllLightDirections(); + print("PLUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } - else if (event.text == "]" ) { + else if (event.text == "-" ) { localLightDirections[currentSelection].y -= 0.01; if (localLightDirections[currentSelection].y < -1.0) { localLightDirections[currentSelection].y = 1.0; } - MyAvatar.setLocalLightDirection(localLightDirections[currentSelection], currentSelection); + setAllLightDirections(); + print("MINUS Y light " + currentSelection + " direction (" + localLightDirections[currentSelection].x + ", " + localLightDirections[currentSelection].y + ", " + localLightDirections[currentSelection].z + " )" ); } else if (event.text == "," ) { if (currentNumLights + 1 <= maxNumLights) { - var darkGrayColor = {x:0.3, y:0.3, z:0.3}; - - // default light - localLightColors[currentNumLights].x = darkGrayColor.x; - localLightColors[currentNumLights].y = darkGrayColor.y; - localLightColors[currentNumLights].z = darkGrayColor.z; - - MyAvatar.addLocalLight(); - MyAvatar.setLocalLightColor(localLightColors[currentNumLights], currentNumLights); - MyAvatar.setLocalLightDirection(localLightDirections[currentNumLights], currentNumLights); - ++currentNumLights; + + for (var i = 0; i < currentNumAvatars; i++) { + AvatarManager.addAvatarLocalLight(i); + + for (var j = 0; j < currentNumLights; j++) { + AvatarManager.setAvatarLightColor(localLightColors[j], j, i); + AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); + } + } } + + print("ADD LIGHT, number of lights " + currentNumLights); } else if (event.text == "." ) { if (currentNumLights - 1 >= 0 ) { - - // no light contribution - localLightColors[currentNumLights - 1].x = 0.0; - localLightColors[currentNumLights - 1].y = 0.0; - localLightColors[currentNumLights - 1].z = 0.0; - - MyAvatar.removeLocalLight(); --currentNumLights; + + for (var i = 0; i < currentNumAvatars; i++) { + AvatarManager.removeAvatarLocalLight(i); + + for (var j = 0; j < currentNumLights; j++) { + AvatarManager.setAvatarLightColor(localLightColors[j], j, i); + AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); + } + } + } + + print("REMOVE LIGHT, number of lights " + currentNumLights); } } +function updateLocalLights() +{ + // new avatars, so add lights + var numAvatars = AvatarManager.getNumAvatars(); + if (numAvatars != currentNumAvatars) { + print("!!! REINITIALIZE LIGHTS !!!"); + + for (var i = 0; i < numAvatars; i++) { + var id = AvatarManager.getAvatarHashKey(i); + + // check if avatar has already been registered + var hasRegistered = false; + for (var j = 0; j < numAvatars; j++) { + if (avatarHashIDs[j] == id) { + hasRegistered = true; + break; + } + } + + // add new id and set light params + if (!hasRegistered) { + print("!!! SAVE NEW AVATAR " + avatarHashIDs.length + " ID = " + id + " !!!"); + + avatarHashIDs.push(id); + AvatarManager.addAvatarLocalLight(i); + + // set color and direction for new avatar + for (var j = 0; j < maxNumLights; j++) { + AvatarManager.setAvatarLightColor(localLightColors[j], j, i); + AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); + } + } + } + + currentNumAvatars = numAvatars; + } +} + +function setAllLightColors() +{ + for (var i = 0; i < currentNumAvatars; i++) { + for (var j = 0; j < maxNumLights; j++) { + AvatarManager.setAvatarLightColor(localLightColors[j], j, i); + } + } +} + +function setAllLightDirections() +{ + for (var i = 0; i < currentNumAvatars; i++) { + for (var j = 0; j < maxNumLights; j++) { + AvatarManager.setAvatarLightDirection(localLightDirections[j], j, i); + } + } +} + +// main +Script.update.connect(updateLocalLights); Controller.keyPressEvent.connect(keyPressEvent); + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f13a92bba0..48181d0244 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3642,6 +3642,8 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("AvatarManager", &_avatarManager); + #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); #endif diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 57b558706e..f6994b2caf 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -60,7 +60,7 @@ Avatar::Avatar() : _mouseRayDirection(0.0f, 0.0f, 0.0f), _moving(false), _collisionGroups(0), - _numLocalLights(2), + _numLocalLights(0), _initialized(false), _shouldRenderBillboard(true) { @@ -83,23 +83,6 @@ void Avatar::init() { _initialized = true; _shouldRenderBillboard = (getLODDistance() >= BILLBOARD_LOD_DISTANCE); initializeHair(); - - for (int i = 0; i < MAX_LOCAL_LIGHTS; i++) { - _localLightColors[i] = glm::vec3(0.0f, 0.0f, 0.0f); - _localLightDirections[i] = glm::vec3(0.0f, 0.0f, 0.0f); - } - - glm::vec3 darkGrayColor(0.4f, 0.4f, 0.4f); - glm::vec3 greenColor(0.0f, 1.0f, 0.0f); - glm::vec3 directionX(1.0f, 0.0f, 0.0f); - glm::vec3 directionY(0.0f, 1.0f, 0.0f); - - // initialize local lights - _localLightColors[0] = darkGrayColor; - _localLightColors[1] = darkGrayColor; - - _localLightDirections[0] = directionX; - _localLightDirections[1] = directionY; } glm::vec3 Avatar::getChestPosition() const { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index debe6489ea..0c2dbbd668 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -158,3 +158,63 @@ void AvatarManager::clearOtherAvatars() { } _myAvatar->clearLookAtTargetAvatar(); } + +Avatar* AvatarManager::getAvatarFromIndex(int avatarIndex) +{ + Avatar* avatar = NULL; + int numAvatars = _avatarHash.count(); + if (avatarIndex < numAvatars) { + QUuid key = (_avatarHash.keys())[avatarIndex]; + + const AvatarSharedPointer& avatarPointer = _avatarHash.value(key); + avatar = static_cast(avatarPointer.data()); + } + + return avatar; +} + +void AvatarManager::addAvatarLocalLight(int avatarIndex) +{ + Avatar* avatar = getAvatarFromIndex(avatarIndex); + if (avatar) { + avatar->addLocalLight(); + } +} + +void AvatarManager::removeAvatarLocalLight(int avatarIndex) +{ + Avatar* avatar = getAvatarFromIndex(avatarIndex); + if (avatar) { + avatar->removeLocalLight(); + } +} + +void AvatarManager::setAvatarLightDirection(const glm::vec3& direction, int lightIndex, int avatarIndex) +{ + Avatar* avatar = getAvatarFromIndex(avatarIndex); + if (avatar) { + avatar->setLocalLightDirection(direction, lightIndex); + } +} + +void AvatarManager::setAvatarLightColor(const glm::vec3& color, int lightIndex, int avatarIndex) +{ + Avatar* avatar = getAvatarFromIndex(avatarIndex); + if (avatar) { + avatar->setLocalLightColor(color, lightIndex); + } +} + +int AvatarManager::getNumAvatars() +{ + return _avatarHash.count(); +} + +QString AvatarManager::getAvatarHashKey(int index) +{ + QString id = ((_avatarHash.keys())[index]).toString(); + std::string idString = id.toStdString(); + return id; +} + + diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 048844ddf2..f2b5904883 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -24,6 +24,7 @@ class MyAvatar; class AvatarManager : public AvatarHashMap { Q_OBJECT + public: AvatarManager(QObject* parent = 0); @@ -36,6 +37,14 @@ public: void clearOtherAvatars(); +public slots: + void setAvatarLightColor(const glm::vec3& color, int lightIndex, int avatarIndex); + void setAvatarLightDirection(const glm::vec3& direction, int lightIndex, int avatarIndex); + void removeAvatarLocalLight(int avatarIndex); + void addAvatarLocalLight(int avatarIndex); + int getNumAvatars(); + QString getAvatarHashKey(int index); + private: AvatarManager(const AvatarManager& other); @@ -44,6 +53,8 @@ private: AvatarSharedPointer newSharedAvatar(); + Avatar* getAvatarFromIndex(int avatarIndex); + // virtual override AvatarHash::iterator erase(const AvatarHash::iterator& iterator); From 6bf06989db1d6c5ec9052d60b27ab917a6e37c14 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 14 Jul 2014 15:24:47 -0600 Subject: [PATCH 56/72] Adding place/user go to logging --- interface/src/location/LocationManager.cpp | 16 ++++++++++++---- libraries/networking/src/UserActivityLogger.cpp | 12 ++++++++++++ libraries/networking/src/UserActivityLogger.h | 1 + 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index 32172d6e38..cbf7d3dfd0 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -13,6 +13,7 @@ #include "Application.h" #include "LocationManager.h" +#include const QString GET_USER_ADDRESS = "/api/v1/users/%1/address"; const QString GET_PLACE_ADDRESS = "/api/v1/places/%1"; @@ -60,23 +61,30 @@ void LocationManager::createNamedLocation(NamedLocation* namedLocation) { } void LocationManager::goTo(QString destination) { - + const QString USER_DESTINATION_TYPE = "user"; + const QString PLACE_DESTINATION_TYPE = "place"; + const QString OTHER_DESTINATION_TYPE = "coordinate_or_username"; + if (destination.startsWith("@")) { // remove '@' and go to user - goToUser(destination.remove(0, 1)); + QString destinationUser = destination.remove(0, 1); + UserActivityLogger::getInstance().wentTo(USER_DESTINATION_TYPE, destinationUser); + goToUser(destinationUser); return; } if (destination.startsWith("#")) { // remove '#' and go to named place - goToPlace(destination.remove(0, 1)); + QString destinationPlace = destination.remove(0, 1); + UserActivityLogger::getInstance().wentTo(PLACE_DESTINATION_TYPE, destinationPlace); + goToPlace(destinationPlace); return; } // go to coordinate destination or to Username if (!goToDestination(destination)) { destination = QString(QUrl::toPercentEncoding(destination)); - + UserActivityLogger::getInstance().wentTo(OTHER_DESTINATION_TYPE, destination); JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "goToAddressFromResponse"; diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 5b20c82263..0217fb9106 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -146,3 +146,15 @@ void UserActivityLogger::loadedScript(QString scriptName) { logAction(ACTION_NAME, actionDetails); } + +void UserActivityLogger::wentTo(QString destinationType, QString destinationName) { + const QString ACTION_NAME = "went_to"; + QJsonObject actionDetails; + const QString DESTINATION_TYPE_KEY = "destination_type"; + const QString DESTINATION_NAME_KEY = "detination_name"; + + actionDetails.insert(DESTINATION_TYPE_KEY, destinationType); + actionDetails.insert(DESTINATION_NAME_KEY, destinationName); + + logAction(ACTION_NAME, actionDetails); +} diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 4823143234..7c48a72b73 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -35,6 +35,7 @@ public slots: void changedDomain(QString domainURL); void connectedDevice(QString typeOfDevice, QString deviceName); void loadedScript(QString scriptName); + void wentTo(QString destinationType, QString destinationName); private slots: void requestFinished(const QJsonObject& object); From e32bab5b0469a741e73da94f279f26c50d419031 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 14 Jul 2014 14:32:55 -0700 Subject: [PATCH 57/72] Beginnings of some metavoxel scriptability, to create some test landscapes. --- interface/src/Application.cpp | 1 + libraries/metavoxels/src/Bitstream.cpp | 7 +++++++ libraries/metavoxels/src/Bitstream.h | 5 +++++ .../metavoxels/src/MetavoxelClientManager.cpp | 19 +++++++++++++++++++ .../metavoxels/src/MetavoxelClientManager.h | 4 ++++ 5 files changed, 36 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f13a92bba0..7219a019d4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3641,6 +3641,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine->registerGlobalObject("AnimationCache", &_animationCache); scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector); scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("Metavoxels", &_metavoxels); #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 0d4f8a52b4..2e9e641d88 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -128,6 +129,12 @@ QList Bitstream::getMetaObjectSubClasses(const QMetaObject* return getMetaObjectSubClasses().values(metaObject); } +void Bitstream::configureScriptEngine(QScriptEngine* engine) { + foreach (const QMetaObject* metaObject, getMetaObjects()) { + engine->globalObject().setProperty(metaObject->className(), engine->newQMetaObject(metaObject)); + } +} + Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, GenericsMode genericsMode, QObject* parent) : QObject(parent), _underlying(underlying), diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index dacfeb2ee9..04b4dc97fc 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -32,6 +32,7 @@ class QByteArray; class QColor; class QDataStream; +class QScriptEngine; class QScriptValue; class QUrl; @@ -320,6 +321,10 @@ public: /// subclasses. static QList getMetaObjectSubClasses(const QMetaObject* metaObject); + /// Configures the supplied script engine with our registered meta-objects, allowing all of them to be instantiated from + /// scripts. + static void configureScriptEngine(QScriptEngine* engine); + enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA }; enum GenericsMode { NO_GENERICS, FALLBACK_GENERICS, ALL_GENERICS }; diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index f3ea1ae8c5..ff02398b85 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "MetavoxelClientManager.h" #include "MetavoxelMessages.h" @@ -53,7 +55,24 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons return closestSpanner; } +void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, QRgb color) { + Sphere* sphere = new Sphere(); + sphere->setTranslation(center); + sphere->setScale(radius); + sphere->setColor(color); + setSpanner(sphere); +} + +void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool reliable) { + MetavoxelEditMessage edit = { QVariant::fromValue(SetSpannerEdit(object)) }; + applyEdit(edit, reliable); +} + void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable)); + return; + } foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index ad6c86c8fc..7537d7f971 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -28,6 +28,10 @@ public: SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); + + Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, QRgb color = 0x808080); + + Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false); Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); From afc573b2482af91d9c9d386befb735e93e496c9c Mon Sep 17 00:00:00 2001 From: TonyPeng Date: Mon, 14 Jul 2014 15:08:29 -0700 Subject: [PATCH 58/72] Took out debug print --- examples/avatarLocalLight.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/avatarLocalLight.js b/examples/avatarLocalLight.js index 1ad016f2b3..225ed4d755 100644 --- a/examples/avatarLocalLight.js +++ b/examples/avatarLocalLight.js @@ -142,7 +142,6 @@ function updateLocalLights() // new avatars, so add lights var numAvatars = AvatarManager.getNumAvatars(); if (numAvatars != currentNumAvatars) { - print("!!! REINITIALIZE LIGHTS !!!"); for (var i = 0; i < numAvatars; i++) { var id = AvatarManager.getAvatarHashKey(i); @@ -158,8 +157,7 @@ function updateLocalLights() // add new id and set light params if (!hasRegistered) { - print("!!! SAVE NEW AVATAR " + avatarHashIDs.length + " ID = " + id + " !!!"); - + avatarHashIDs.push(id); AvatarManager.addAvatarLocalLight(i); From 28854363d44fa2cb65231ddad1b132b9b8cabbfe Mon Sep 17 00:00:00 2001 From: TonyPeng Date: Tue, 15 Jul 2014 09:20:33 -0700 Subject: [PATCH 59/72] Coding standard --- interface/src/avatar/AvatarManager.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0c2dbbd668..bc84aa6b87 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -159,8 +159,7 @@ void AvatarManager::clearOtherAvatars() { _myAvatar->clearLookAtTargetAvatar(); } -Avatar* AvatarManager::getAvatarFromIndex(int avatarIndex) -{ +Avatar* AvatarManager::getAvatarFromIndex(int avatarIndex) { Avatar* avatar = NULL; int numAvatars = _avatarHash.count(); if (avatarIndex < numAvatars) { @@ -173,45 +172,39 @@ Avatar* AvatarManager::getAvatarFromIndex(int avatarIndex) return avatar; } -void AvatarManager::addAvatarLocalLight(int avatarIndex) -{ +void AvatarManager::addAvatarLocalLight(int avatarIndex) { Avatar* avatar = getAvatarFromIndex(avatarIndex); if (avatar) { avatar->addLocalLight(); } } -void AvatarManager::removeAvatarLocalLight(int avatarIndex) -{ +void AvatarManager::removeAvatarLocalLight(int avatarIndex) { Avatar* avatar = getAvatarFromIndex(avatarIndex); if (avatar) { avatar->removeLocalLight(); } } -void AvatarManager::setAvatarLightDirection(const glm::vec3& direction, int lightIndex, int avatarIndex) -{ +void AvatarManager::setAvatarLightDirection(const glm::vec3& direction, int lightIndex, int avatarIndex) { Avatar* avatar = getAvatarFromIndex(avatarIndex); if (avatar) { avatar->setLocalLightDirection(direction, lightIndex); } } -void AvatarManager::setAvatarLightColor(const glm::vec3& color, int lightIndex, int avatarIndex) -{ +void AvatarManager::setAvatarLightColor(const glm::vec3& color, int lightIndex, int avatarIndex) { Avatar* avatar = getAvatarFromIndex(avatarIndex); if (avatar) { avatar->setLocalLightColor(color, lightIndex); } } -int AvatarManager::getNumAvatars() -{ +int AvatarManager::getNumAvatars() { return _avatarHash.count(); } -QString AvatarManager::getAvatarHashKey(int index) -{ +QString AvatarManager::getAvatarHashKey(int index) { QString id = ((_avatarHash.keys())[index]).toString(); std::string idString = id.toStdString(); return id; From 9d5510008797e288ffa4f79620a1fc2059e2b1de Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Jul 2014 09:31:23 -0700 Subject: [PATCH 60/72] Merge (but don't clear redundant nodes) after reading subdivisions. --- libraries/metavoxels/src/MetavoxelData.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 72d5392a39..fdce323708 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -767,7 +767,7 @@ void MetavoxelNode::mergeChildren(const AttributePointer& attribute, bool postRe childValues[i] = _children[i]->_attributeValue; allLeaves &= _children[i]->isLeaf(); } - if (attribute->merge(_attributeValue, childValues, postRead) && allLeaves) { + if (attribute->merge(_attributeValue, childValues, postRead) && allLeaves && !postRead) { clearChildren(attribute); } } @@ -917,6 +917,7 @@ bool MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { _children[i] = new MetavoxelNode(state.attribute); _children[i]->read(nextState); } + mergeChildren(state.attribute, true); return true; } else { @@ -932,6 +933,9 @@ bool MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { } } } + if (changed) { + mergeChildren(state.attribute, true); + } return changed; } } From 0068428c7c7c399c5a3980344db84c0bc9c1bd4e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 15 Jul 2014 10:41:29 -0700 Subject: [PATCH 61/72] fixed some warnings --- interface/src/devices/MIDIManager.cpp | 2 + libraries/particles/src/Particle.cpp | 40 +++++++++---------- .../particles/src/ParticleCollisionSystem.cpp | 3 -- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/interface/src/devices/MIDIManager.cpp b/interface/src/devices/MIDIManager.cpp index 52ae2eb645..54428d273d 100644 --- a/interface/src/devices/MIDIManager.cpp +++ b/interface/src/devices/MIDIManager.cpp @@ -44,7 +44,9 @@ MIDIManager::~MIDIManager() { #endif } +#ifdef HAVE_RTMIDI const int DEFAULT_MIDI_PORT = 0; +#endif void MIDIManager::openDefaultPort() { #ifdef HAVE_RTMIDI diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 814da636c1..5fffefd8b1 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -368,7 +368,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr uint32_t editID; // check to make sure we have enough content to keep reading... - if (length - processedBytes - sizeof(editID) < 0) { + if (length - (processedBytes + (int)sizeof(editID)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -387,7 +387,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr uint32_t creatorTokenID; // check to make sure we have enough content to keep reading... - if (length - processedBytes - sizeof(creatorTokenID) < 0) { + if (length - (processedBytes + (int)sizeof(creatorTokenID)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -426,7 +426,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // lastEdited // check to make sure we have enough content to keep reading... - if (length - processedBytes - sizeof(newParticle._lastEdited) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._lastEdited)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -439,7 +439,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // properties included bits uint16_t packetContainsBits = 0; if (!isNewParticle) { - if (length - processedBytes - sizeof(packetContainsBits) < 0) { + if (length - (processedBytes + (int)sizeof(packetContainsBits)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -452,7 +452,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // radius if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { - if (length - processedBytes - sizeof(newParticle._radius) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._radius)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -464,7 +464,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // position if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { - if (length - processedBytes - sizeof(newParticle._position) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._position)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -476,7 +476,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // color if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { - if (length - processedBytes - sizeof(newParticle._color) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._color)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -488,7 +488,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // velocity if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { - if (length - processedBytes - sizeof(newParticle._velocity) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._velocity)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -500,7 +500,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // gravity if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { - if (length - processedBytes - sizeof(newParticle._gravity) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._gravity)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -512,7 +512,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // damping if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { - if (length - processedBytes - sizeof(newParticle._damping) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._damping)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -524,7 +524,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // lifetime if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { - if (length - processedBytes - sizeof(newParticle._lifetime) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._lifetime)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -537,7 +537,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // TODO: make inHand and shouldDie into single bits // inHand if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { - if (length - processedBytes - sizeof(newParticle._inHand) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._inHand)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -549,7 +549,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // shouldDie if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { - if (length - processedBytes - sizeof(newParticle._shouldDie) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._shouldDie)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -562,7 +562,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // script if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { uint16_t scriptLength; - if (length - processedBytes - sizeof(scriptLength) < 0) { + if (length - (processedBytes + (int)sizeof(scriptLength)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -571,7 +571,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr dataAt += sizeof(scriptLength); processedBytes += sizeof(scriptLength); - if (length - processedBytes - scriptLength < 0) { + if (length - (processedBytes + (int)scriptLength) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -585,7 +585,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelURL if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) { uint16_t modelURLLength; - if (length - processedBytes - sizeof(modelURLLength) < 0) { + if (length - (processedBytes + (int)sizeof(modelURLLength)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -594,7 +594,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr dataAt += sizeof(modelURLLength); processedBytes += sizeof(modelURLLength); - if (length - processedBytes - modelURLLength < 0) { + if (length - (processedBytes + (int)modelURLLength) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -607,7 +607,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelScale if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) { - if (length - processedBytes - sizeof(newParticle._modelScale) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._modelScale)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -619,7 +619,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelTranslation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) { - if (length - processedBytes - sizeof(newParticle._modelTranslation) < 0) { + if (length - (processedBytes + (int)sizeof(newParticle._modelTranslation)) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer @@ -632,7 +632,7 @@ Particle Particle::fromEditPacket(const unsigned char* data, int length, int& pr // modelRotation if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) { const int expectedBytesForPackedQuat = sizeof(uint16_t) * 4; // this is how we pack the quats - if (length - processedBytes - expectedBytesForPackedQuat < 0) { + if (length - (processedBytes + expectedBytesForPackedQuat) < 0) { valid = false; processedBytes = length; return newParticle; // fail as if we read the entire buffer diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 0291690c3d..17d1dd3bc6 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -181,9 +181,6 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) } } -// MIN_VALID_SPEED is obtained by computing speed gained at one gravity after the shortest expected frame -const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f; // 1/60th of a second - void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // particles that are in hand, don't collide with avatars if (!_avatars || particle->getInHand()) { From a1b2bf59e9ebcb5b367fb808ff63f3aad130d9f5 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 15 Jul 2014 12:23:02 -0700 Subject: [PATCH 62/72] disabled SIMD adds in AudioMixer for windows --- assignment-client/src/audio/AudioMixer.cpp | 35 +++++++++++++++++-- .../audio/src/PositionalAudioRingBuffer.cpp | 1 + 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index c86d37e283..60f905f20a 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -231,6 +231,12 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf delayBufferSample[0] = correctBufferSample[0] * weakChannelAmplitudeRatio; delayBufferSample[1] = correctBufferSample[1] * weakChannelAmplitudeRatio; +#ifdef _WIN32 + _clientSamples[s + goodChannelOffset] += correctBufferSample[0]; + _clientSamples[s + goodChannelOffset + SINGLE_STEREO_OFFSET] += correctBufferSample[1]; + _clientSamples[delayedChannelIndex] += delayBufferSample[0]; + _clientSamples[delayedChannelIndex + SINGLE_STEREO_OFFSET] += delayBufferSample[1]; +#else __m64 bufferSamples = _mm_set_pi16(_clientSamples[s + goodChannelOffset], _clientSamples[s + goodChannelOffset + SINGLE_STEREO_OFFSET], _clientSamples[delayedChannelIndex], @@ -247,6 +253,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf _clientSamples[s + goodChannelOffset + SINGLE_STEREO_OFFSET] = shortResults[2]; _clientSamples[delayedChannelIndex] = shortResults[1]; _clientSamples[delayedChannelIndex + SINGLE_STEREO_OFFSET] = shortResults[0]; +#endif } // The following code is pretty gross and redundant, but AFAIK it's the best way to avoid @@ -271,6 +278,13 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf while (i + 3 < numSamplesDelay) { // handle the first cases where we can MMX add four samples at once int parentIndex = i * 2; + +#ifdef _WIN32 + _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; + _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio; + _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 2] * attenuationAndWeakChannelRatio; + _clientSamples[parentIndex + TRIPLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 3] * attenuationAndWeakChannelRatio; +#else __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset], _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset], @@ -286,7 +300,8 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[2]; _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[1]; _clientSamples[parentIndex + TRIPLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[0]; - +#endif + // push the index i += 4; } @@ -296,6 +311,11 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf if (i + 2 < numSamplesDelay) { // MMX add only three delayed samples +#ifdef _WIN32 + _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; + _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio; + _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 2] * attenuationAndWeakChannelRatio; +#else __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset], _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset], @@ -310,8 +330,15 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf _clientSamples[parentIndex + delayedChannelOffset] = shortResults[3]; _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[2]; _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[1]; +#endif + } else if (i + 1 < numSamplesDelay) { + +#ifdef _WIN32 + _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; + _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio; +#else // MMX add two delayed samples __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset], @@ -324,9 +351,12 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf _clientSamples[parentIndex + delayedChannelOffset] = shortResults[3]; _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[2]; - +#endif } else if (i < numSamplesDelay) { // MMX add a single delayed sample +#ifdef _WIN32 + _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; +#else __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], 0, 0, 0); __m64 addSamples = _mm_set_pi16(delayNextOutputStart[i] * attenuationAndWeakChannelRatio, 0, 0, 0); @@ -334,6 +364,7 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf int16_t* shortResults = reinterpret_cast(&mmxResult); _clientSamples[parentIndex + delayedChannelOffset] = shortResults[3]; +#endif } } } else { diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 8cba6d72b0..c9a58b2210 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -149,6 +149,7 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { // fixes bug on Windows where _nextOutputTrailingLoudness sometimes becomes NaN. In that case, // revert _nextOutputTrailingLoudness to its previous value if (isNaN(_nextOutputTrailingLoudness)) { + printf("next output trailling loudness NaN!! --------------------------------------\n"); _nextOutputTrailingLoudness = oldNextOutputTrailingLoudness; } } From 828410b7d6b80f930de864aaace331437e7669a3 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 15 Jul 2014 12:23:54 -0700 Subject: [PATCH 63/72] removed isNan check for _nextOutputTrailingLoudness --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index c9a58b2210..a0ce74dbaf 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -145,13 +145,6 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { _nextOutputTrailingLoudness = 0; } } - - // fixes bug on Windows where _nextOutputTrailingLoudness sometimes becomes NaN. In that case, - // revert _nextOutputTrailingLoudness to its previous value - if (isNaN(_nextOutputTrailingLoudness)) { - printf("next output trailling loudness NaN!! --------------------------------------\n"); - _nextOutputTrailingLoudness = oldNextOutputTrailingLoudness; - } } bool PositionalAudioRingBuffer::shouldBeAddedToMix() { From 871aa2790a6f380e6b256e6bafe22455d208db36 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 15 Jul 2014 12:24:28 -0700 Subject: [PATCH 64/72] forgot unused variable --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index a0ce74dbaf..6c7ecd1cad 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -135,7 +135,6 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; const float LOUDNESS_EPSILON = 0.000001f; - float oldNextOutputTrailingLoudness = _nextOutputTrailingLoudness; if (nextLoudness >= _nextOutputTrailingLoudness) { _nextOutputTrailingLoudness = nextLoudness; } else { From e2c8bdbdbed6468d4c72b056d54faf36ca707e1a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Jul 2014 13:22:50 -0700 Subject: [PATCH 65/72] Create new nodes on subdivision. --- .../metavoxels/src/AttributeRegistry.cpp | 7 +- libraries/metavoxels/src/MetavoxelData.cpp | 97 +++++++++---------- libraries/metavoxels/src/MetavoxelData.h | 2 +- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 670f0be389..1cd41500fc 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -214,9 +214,10 @@ void Attribute::writeMetavoxelDelta(const MetavoxelNode& root, const MetavoxelNo void Attribute::readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state) { // copy if changed - MetavoxelNode* root = data.getRoot(state.attribute); - if (root->readSubdivision(state)) { - data.setRoot(state.attribute, new MetavoxelNode(state.attribute, root)); + MetavoxelNode* oldRoot = data.getRoot(state.attribute); + MetavoxelNode* newRoot = oldRoot->readSubdivision(state); + if (newRoot != oldRoot) { + data.setRoot(state.attribute, newRoot); } } diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index fdce323708..a3ea5ca189 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -849,9 +849,11 @@ void MetavoxelNode::readDelta(const MetavoxelNode& reference, MetavoxelStreamSta _children[i] = new MetavoxelNode(state.attribute); _children[i]->readDelta(*reference._children[i], nextState); } else { - if (nextState.becameSubdivided() && reference._children[i]->readSubdivision(nextState)) { - _children[i] = new MetavoxelNode(state.attribute, reference._children[i]); - + if (nextState.becameSubdivided()) { + _children[i] = reference._children[i]->readSubdivision(nextState); + if (_children[i] == reference._children[i]) { + _children[i]->incrementReferenceCount(); + } } else { _children[i] = reference._children[i]; _children[i]->incrementReferenceCount(); @@ -896,71 +898,68 @@ void MetavoxelNode::writeDelta(const MetavoxelNode& reference, MetavoxelStreamSt } } -bool MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { - bool leaf; - bool subdivideReference = state.shouldSubdivideReference(); - if (!subdivideReference) { +MetavoxelNode* MetavoxelNode::readSubdivision(MetavoxelStreamState& state) { + if (!state.shouldSubdivideReference()) { + bool leaf; state.stream >> leaf; - } else { - leaf = isLeaf(); - } - if (leaf) { - return clearChildren(state.attribute); - - } else { - MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, - state.stream, state.lod, state.referenceLOD }; - if (!subdivideReference) { - clearChildren(state.attribute); - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - _children[i] = new MetavoxelNode(state.attribute); - _children[i]->read(nextState); - } - mergeChildren(state.attribute, true); - return true; + if (leaf) { + return isLeaf() ? this : new MetavoxelNode(getAttributeValue(state.attribute)); } else { - bool changed = false; + MetavoxelNode* newNode = new MetavoxelNode(getAttributeValue(state.attribute)); + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdivided()) { - if (_children[i]->readSubdivision(nextState)) { - MetavoxelNode* oldNode = _children[i]; - _children[i] = new MetavoxelNode(state.attribute, oldNode); - oldNode->decrementReferenceCount(state.attribute); - changed = true; + newNode->_children[i] = new MetavoxelNode(state.attribute); + newNode->_children[i]->read(nextState); + } + return newNode; + } + } else if (!isLeaf()) { + MetavoxelNode* node = this; + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdivided()) { + MetavoxelNode* child = _children[i]->readSubdivision(nextState); + if (child != _children[i]) { + if (node == this) { + node = new MetavoxelNode(state.attribute, this); } + node->_children[i] = child; + _children[i]->decrementReferenceCount(state.attribute); } } - if (changed) { - mergeChildren(state.attribute, true); - } - return changed; } + if (node != this) { + node->mergeChildren(state.attribute, true); + } + return node; } + return this; } void MetavoxelNode::writeSubdivision(MetavoxelStreamState& state) const { bool leaf = isLeaf(); - bool subdivideReference = state.shouldSubdivideReference(); - if (!subdivideReference) { + if (!state.shouldSubdivideReference()) { state.stream << leaf; - } - if (!leaf) { - MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, - state.stream, state.lod, state.referenceLOD }; - if (!subdivideReference) { + if (!leaf) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; for (int i = 0; i < CHILD_COUNT; i++) { nextState.setMinimum(state.minimum, i); _children[i]->write(nextState); } - } else { - for (int i = 0; i < CHILD_COUNT; i++) { - nextState.setMinimum(state.minimum, i); - if (nextState.becameSubdivided()) { - _children[i]->writeSubdivision(nextState); - } + } + } else if (!leaf) { + MetavoxelStreamState nextState = { glm::vec3(), state.size * 0.5f, state.attribute, + state.stream, state.lod, state.referenceLOD }; + for (int i = 0; i < CHILD_COUNT; i++) { + nextState.setMinimum(state.minimum, i); + if (nextState.becameSubdivided()) { + _children[i]->writeSubdivision(nextState); } } } diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index ac8f0cc023..eeab5bd0f4 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -201,7 +201,7 @@ public: void readDelta(const MetavoxelNode& reference, MetavoxelStreamState& state); void writeDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const; - bool readSubdivision(MetavoxelStreamState& state); + MetavoxelNode* readSubdivision(MetavoxelStreamState& state); void writeSubdivision(MetavoxelStreamState& state) const; void writeSpanners(MetavoxelStreamState& state) const; From d5a30ff6eedbb00bf0d850f903a68686b776b7c9 Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 15 Jul 2014 14:43:08 -0700 Subject: [PATCH 66/72] removed SIMD stuff from AudioMixer for all builds --- assignment-client/src/audio/AudioMixer.cpp | 111 +-------------------- 1 file changed, 2 insertions(+), 109 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 60f905f20a..076769d989 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -231,29 +231,10 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf delayBufferSample[0] = correctBufferSample[0] * weakChannelAmplitudeRatio; delayBufferSample[1] = correctBufferSample[1] * weakChannelAmplitudeRatio; -#ifdef _WIN32 _clientSamples[s + goodChannelOffset] += correctBufferSample[0]; _clientSamples[s + goodChannelOffset + SINGLE_STEREO_OFFSET] += correctBufferSample[1]; _clientSamples[delayedChannelIndex] += delayBufferSample[0]; _clientSamples[delayedChannelIndex + SINGLE_STEREO_OFFSET] += delayBufferSample[1]; -#else - __m64 bufferSamples = _mm_set_pi16(_clientSamples[s + goodChannelOffset], - _clientSamples[s + goodChannelOffset + SINGLE_STEREO_OFFSET], - _clientSamples[delayedChannelIndex], - _clientSamples[delayedChannelIndex + SINGLE_STEREO_OFFSET]); - __m64 addedSamples = _mm_set_pi16(correctBufferSample[0], correctBufferSample[1], - delayBufferSample[0], delayBufferSample[1]); - - // perform the MMX add (with saturation) of two correct and delayed samples - __m64 mmxResult = _mm_adds_pi16(bufferSamples, addedSamples); - int16_t* shortResults = reinterpret_cast(&mmxResult); - - // assign the results from the result of the mmx arithmetic - _clientSamples[s + goodChannelOffset] = shortResults[3]; - _clientSamples[s + goodChannelOffset + SINGLE_STEREO_OFFSET] = shortResults[2]; - _clientSamples[delayedChannelIndex] = shortResults[1]; - _clientSamples[delayedChannelIndex + SINGLE_STEREO_OFFSET] = shortResults[0]; -#endif } // The following code is pretty gross and redundant, but AFAIK it's the best way to avoid @@ -273,98 +254,10 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf delayNextOutputStart = bufferStart + ringBufferSampleCapacity - numSamplesDelay; } - int i = 0; - - while (i + 3 < numSamplesDelay) { - // handle the first cases where we can MMX add four samples at once + + for (int i = 0; i < numSamplesDelay; i++) { int parentIndex = i * 2; - -#ifdef _WIN32 _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio; - _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 2] * attenuationAndWeakChannelRatio; - _clientSamples[parentIndex + TRIPLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 3] * attenuationAndWeakChannelRatio; -#else - __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset], - _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset], - _clientSamples[parentIndex + TRIPLE_STEREO_OFFSET + delayedChannelOffset]); - __m64 addSamples = _mm_set_pi16(delayNextOutputStart[i] * attenuationAndWeakChannelRatio, - delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio, - delayNextOutputStart[i + 2] * attenuationAndWeakChannelRatio, - delayNextOutputStart[i + 3] * attenuationAndWeakChannelRatio); - __m64 mmxResult = _mm_adds_pi16(bufferSamples, addSamples); - int16_t* shortResults = reinterpret_cast(&mmxResult); - - _clientSamples[parentIndex + delayedChannelOffset] = shortResults[3]; - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[2]; - _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[1]; - _clientSamples[parentIndex + TRIPLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[0]; -#endif - - // push the index - i += 4; - } - - int parentIndex = i * 2; - - if (i + 2 < numSamplesDelay) { - // MMX add only three delayed samples - -#ifdef _WIN32 - _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio; - _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 2] * attenuationAndWeakChannelRatio; -#else - __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset], - _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset], - 0); - __m64 addSamples = _mm_set_pi16(delayNextOutputStart[i] * attenuationAndWeakChannelRatio, - delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio, - delayNextOutputStart[i + 2] * attenuationAndWeakChannelRatio, - 0); - __m64 mmxResult = _mm_adds_pi16(bufferSamples, addSamples); - int16_t* shortResults = reinterpret_cast(&mmxResult); - - _clientSamples[parentIndex + delayedChannelOffset] = shortResults[3]; - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[2]; - _clientSamples[parentIndex + DOUBLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[1]; -#endif - - - } else if (i + 1 < numSamplesDelay) { - -#ifdef _WIN32 - _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] += delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio; -#else - // MMX add two delayed samples - __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset], - 0, 0); - __m64 addSamples = _mm_set_pi16(delayNextOutputStart[i] * attenuationAndWeakChannelRatio, - delayNextOutputStart[i + 1] * attenuationAndWeakChannelRatio, 0, 0); - - __m64 mmxResult = _mm_adds_pi16(bufferSamples, addSamples); - int16_t* shortResults = reinterpret_cast(&mmxResult); - - _clientSamples[parentIndex + delayedChannelOffset] = shortResults[3]; - _clientSamples[parentIndex + SINGLE_STEREO_OFFSET + delayedChannelOffset] = shortResults[2]; -#endif - } else if (i < numSamplesDelay) { - // MMX add a single delayed sample -#ifdef _WIN32 - _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; -#else - __m64 bufferSamples = _mm_set_pi16(_clientSamples[parentIndex + delayedChannelOffset], 0, 0, 0); - __m64 addSamples = _mm_set_pi16(delayNextOutputStart[i] * attenuationAndWeakChannelRatio, 0, 0, 0); - - __m64 mmxResult = _mm_adds_pi16(bufferSamples, addSamples); - int16_t* shortResults = reinterpret_cast(&mmxResult); - - _clientSamples[parentIndex + delayedChannelOffset] = shortResults[3]; -#endif } } } else { From abca4661f4e6dfdd863dcd6d2964d9d3972ed0ef Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 15 Jul 2014 14:48:26 -0700 Subject: [PATCH 67/72] removed space --- assignment-client/src/audio/AudioMixer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 076769d989..b008a20aa7 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -254,7 +254,6 @@ void AudioMixer::addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuf delayNextOutputStart = bufferStart + ringBufferSampleCapacity - numSamplesDelay; } - for (int i = 0; i < numSamplesDelay; i++) { int parentIndex = i * 2; _clientSamples[parentIndex + delayedChannelOffset] += delayNextOutputStart[i] * attenuationAndWeakChannelRatio; From ebcb8d00d61c2ddf99afbac30dd82f9f5fe94c73 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Jul 2014 15:47:31 -0700 Subject: [PATCH 68/72] Unfortunately, the early test for subdivision doesn't work, which means that, for now, we need to traverse the entire tree to find subdivided nodes. To be revisited. --- libraries/metavoxels/src/MetavoxelData.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index a3ea5ca189..41cf2b2a20 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -47,13 +47,8 @@ bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size, if (!shouldSubdivide(minimum, size, multiplier)) { return false; // this one must be subdivided } - // the general check is whether we've gotten closer (as multiplied by the threshold) to any point in the volume, - // which we approximate as a sphere for simplicity - float halfSize = size * 0.5f; - glm::vec3 center = minimum + glm::vec3(halfSize, halfSize, halfSize); - float radius = sqrtf(3 * halfSize * halfSize); - return qMax(0.0f, glm::distance(position, center) - radius) * threshold <= - qMax(0.0f, glm::distance(reference.position, center) - radius) * reference.threshold; + // TODO: find some way of culling subtrees that can't possibly contain subdivided nodes + return true; } MetavoxelData::MetavoxelData() : _size(1.0f) { From 469e31cc051ff84c40c78f244e46c2859d32e7f9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Jul 2014 16:44:02 -0700 Subject: [PATCH 69/72] Script bindings for QColor. --- .../metavoxels/src/MetavoxelClientManager.cpp | 2 +- .../metavoxels/src/MetavoxelClientManager.h | 2 +- libraries/shared/src/RegisteredMetaTypes.cpp | 26 +++++++++++++++++++ libraries/shared/src/RegisteredMetaTypes.h | 5 ++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index ff02398b85..0b9e3f3d8c 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -55,7 +55,7 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons return closestSpanner; } -void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, QRgb color) { +void MetavoxelClientManager::setSphere(const glm::vec3& center, float radius, const QColor& color) { Sphere* sphere = new Sphere(); sphere->setTranslation(center); sphere->setScale(radius); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 7537d7f971..191af794f7 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -29,7 +29,7 @@ public: SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); - Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, QRgb color = 0x808080); + Q_INVOKABLE void setSphere(const glm::vec3& center, float radius, const QColor& color = QColor(Qt::gray)); Q_INVOKABLE void setSpanner(const SharedObjectPointer& object, bool reliable = false); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 3e21a72fd5..afc439c586 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "RegisteredMetaTypes.h" static int vec4MetaTypeId = qRegisterMetaType(); @@ -25,6 +27,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); + qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); } @@ -101,6 +104,29 @@ void xColorFromScriptValue(const QScriptValue &object, xColor& color) { color.blue = object.property("blue").toVariant().toInt(); } +QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) { + QScriptValue object = engine->newObject(); + object.setProperty("red", color.red()); + object.setProperty("green", color.green()); + object.setProperty("blue", color.blue()); + object.setProperty("alpha", color.alpha()); + return object; +} + +void qColorFromScriptValue(const QScriptValue& object, QColor& color) { + if (object.isNumber()) { + color.setRgb(object.toUInt32()); + + } else if (object.isString()) { + color.setNamedColor(object.toString()); + + } else { + QScriptValue alphaValue = object.property("alpha"); + color.setRgb(object.property("red").toInt32(), object.property("green").toInt32(), object.property("blue").toInt32(), + alphaValue.isNumber() ? alphaValue.toInt32() : 255); + } +} + QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) { QScriptValue obj = engine->newObject(); QScriptValue origin = vec3toScriptValue(engine, pickRay.origin); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index d876965723..b925639e8a 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -19,6 +19,8 @@ #include "CollisionInfo.h" #include "SharedUtil.h" +class QColor; + Q_DECLARE_METATYPE(glm::vec4) Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) @@ -42,6 +44,9 @@ void quatFromScriptValue(const QScriptValue &object, glm::quat& quat); QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color); void xColorFromScriptValue(const QScriptValue &object, xColor& color); +QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color); +void qColorFromScriptValue(const QScriptValue& object, QColor& color); + class PickRay { public: PickRay() : origin(0), direction(0) { }; From ce0210bf0428c135f4684713db5161d8fc954475 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Jul 2014 16:52:17 -0700 Subject: [PATCH 70/72] The weak shared object hash can be accessed by multiple threads, so it must be locked. --- libraries/metavoxels/src/SharedObject.cpp | 5 +++++ libraries/metavoxels/src/SharedObject.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index d0a1842d31..fc72218e2e 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "Bitstream.h" #include "MetavoxelUtil.h" @@ -27,10 +28,12 @@ SharedObject::SharedObject() : _remoteID(0), _remoteOriginID(0) { + QWriteLocker locker(&_weakHashLock); _weakHash.insert(_id, this); } void SharedObject::setID(int id) { + QWriteLocker locker(&_weakHashLock); _weakHash.remove(_id); _weakHash.insert(_id = id, this); } @@ -41,6 +44,7 @@ void SharedObject::incrementReferenceCount() { void SharedObject::decrementReferenceCount() { if (!_referenceCount.deref()) { + QWriteLocker locker(&_weakHashLock); _weakHash.remove(_id); delete this; } @@ -127,6 +131,7 @@ void SharedObject::dump(QDebug debug) const { int SharedObject::_lastID = 0; WeakSharedObjectHash SharedObject::_weakHash; +QReadWriteLock SharedObject::_weakHashLock; void pruneWeakSharedObjectHash(WeakSharedObjectHash& hash) { for (WeakSharedObjectHash::iterator it = hash.begin(); it != hash.end(); ) { diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index ba643b449c..5ff5aa9680 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ public: /// Returns the weak hash under which all local shared objects are registered. static const WeakSharedObjectHash& getWeakHash() { return _weakHash; } + /// Returns a reference to the weak hash lock. + static QReadWriteLock& getWeakHashLock() { return _weakHashLock; } + Q_INVOKABLE SharedObject(); /// Returns the unique local ID for this object. @@ -85,6 +89,7 @@ private: static int _lastID; static WeakSharedObjectHash _weakHash; + static QReadWriteLock _weakHashLock; }; /// Removes the null references from the supplied hash. From a13d8559e6083191f05cb717c3a322a7b5fb3066 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Jul 2014 16:55:52 -0700 Subject: [PATCH 71/72] Better scoping. --- libraries/metavoxels/src/SharedObject.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index fc72218e2e..efc6aa4be6 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -44,8 +44,10 @@ void SharedObject::incrementReferenceCount() { void SharedObject::decrementReferenceCount() { if (!_referenceCount.deref()) { - QWriteLocker locker(&_weakHashLock); - _weakHash.remove(_id); + { + QWriteLocker locker(&_weakHashLock); + _weakHash.remove(_id); + } delete this; } } From 2d4134afafdb629ff01f9b4c1de5375ab9cdb75a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 15 Jul 2014 17:58:31 -0700 Subject: [PATCH 72/72] Provide basic conversions to/from SharedObjectPointer (still have to work out how to handle reference counting correctly). --- libraries/metavoxels/src/Bitstream.cpp | 11 ++++++++++- libraries/metavoxels/src/Bitstream.h | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 2e9e641d88..f49ae1c04f 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -129,10 +129,19 @@ QList Bitstream::getMetaObjectSubClasses(const QMetaObject* return getMetaObjectSubClasses().values(metaObject); } -void Bitstream::configureScriptEngine(QScriptEngine* engine) { +QScriptValue sharedObjectPointerToScriptValue(QScriptEngine* engine, const SharedObjectPointer& pointer) { + return pointer ? engine->newQObject(pointer.data()) : engine->nullValue(); +} + +void sharedObjectPointerFromScriptValue(const QScriptValue& object, SharedObjectPointer& pointer) { + pointer = qobject_cast(object.toQObject()); +} + +void Bitstream::registerTypes(QScriptEngine* engine) { foreach (const QMetaObject* metaObject, getMetaObjects()) { engine->globalObject().setProperty(metaObject->className(), engine->newQMetaObject(metaObject)); } + qScriptRegisterMetaType(engine, sharedObjectPointerToScriptValue, sharedObjectPointerFromScriptValue); } Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, GenericsMode genericsMode, QObject* parent) : diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 04b4dc97fc..7602424ded 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -323,7 +323,7 @@ public: /// Configures the supplied script engine with our registered meta-objects, allowing all of them to be instantiated from /// scripts. - static void configureScriptEngine(QScriptEngine* engine); + static void registerTypes(QScriptEngine* engine); enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index cd7c2670ec..c5d5019b1f 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -229,6 +230,7 @@ void ScriptEngine::init() { registerMenuItemProperties(&_engine); registerAnimationTypes(&_engine); registerAvatarTypes(&_engine); + Bitstream::registerTypes(&_engine); qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);