From 13c3972a75794e67834b4594bcb9b307612741e7 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 17 Feb 2016 23:47:26 +0200 Subject: [PATCH 1/9] ExtraCompiler: Use a hash for contents Avoid recurring scans of the targets. From a simple test, there is usually a single target. This is mostly done for correctness. Change-Id: Ic025de0825133f1096c400278c929a7e9087c643 Reviewed-by: Ulf Hermann --- src/plugins/projectexplorer/extracompiler.cpp | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index 66b21698e32..409f300050a 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -51,8 +51,8 @@ class ExtraCompilerPrivate public: const Project *project; Utils::FileName source; + QHash contents; Utils::FileNameList targets; - QVector contents; QList issues; QDateTime compileTime; Core::IEditor *lastEditor = 0; @@ -71,7 +71,8 @@ ExtraCompiler::ExtraCompiler(const Project *project, const Utils::FileName &sour d->project = project; d->source = source; d->targets = targets; - d->contents.resize(targets.size()); + foreach (const Utils::FileName &target, targets) + d->contents.insert(target, QByteArray()); d->timer.setSingleShot(true); connect(d->project, &Project::activeTargetChanged, this, &ExtraCompiler::onActiveTargetChanged); @@ -148,11 +149,7 @@ Utils::FileName ExtraCompiler::source() const QByteArray ExtraCompiler::content(const Utils::FileName &file) const { - for (int i = 0; i < d->targets.length(); ++i) { - if (d->targets[i] == file) - return d->contents[i]; - } - return QByteArray(); + return d->contents.value(file); } Utils::FileNameList ExtraCompiler::targets() const @@ -335,13 +332,11 @@ void ExtraCompilerPrivate::updateIssues() void ExtraCompiler::setContent(const Utils::FileName &file, const QByteArray &contents) { - for (int i = 0; i < d->targets.length(); ++i) { - if (d->targets[i] == file) { - if (d->contents[i] != contents) { - d->contents[i] = contents; - emit contentsChanged(file); - } - return; + auto it = d->contents.find(file); + if (it != d->contents.end()) { + if (it.value() != contents) { + it.value() = contents; + emit contentsChanged(file); } } } From 7026b26c4903f9f03c25848920252864b7bc9c60 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 17 Feb 2016 20:44:35 +0200 Subject: [PATCH 2/9] Debugger: Do not set the current executable by default Only when the user requests. Change-Id: Iaae1acb879dc99bcb0cb4d15f92f685b17829a8b Reviewed-by: hjk --- .../debugger/unstartedappwatcherdialog.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/plugins/debugger/unstartedappwatcherdialog.cpp b/src/plugins/debugger/unstartedappwatcherdialog.cpp index b4ec07001b0..31378377d0e 100644 --- a/src/plugins/debugger/unstartedappwatcherdialog.cpp +++ b/src/plugins/debugger/unstartedappwatcherdialog.cpp @@ -103,16 +103,26 @@ UnstartedAppWatcherDialog::UnstartedAppWatcherDialog(QWidget *parent) else if (KitManager::defaultKit()) m_kitChooser->setCurrentKitId(KitManager::defaultKit()->id()); + auto pathLayout = new QHBoxLayout; m_pathChooser = new Utils::PathChooser(this); m_pathChooser->setExpectedKind(Utils::PathChooser::ExistingCommand); - m_pathChooser->setHistoryCompleter(QLatin1String("LocalExecutable")); + m_pathChooser->setHistoryCompleter(QLatin1String("LocalExecutable"), true); m_pathChooser->setMinimumWidth(400); + auto resetExecutable = new QPushButton(tr("Reset")); + resetExecutable->setEnabled(false); + pathLayout->addWidget(m_pathChooser); + pathLayout->addWidget(resetExecutable); if (activeTarget) { if (RunConfiguration *runConfig = activeTarget->activeRunConfiguration()) { const Runnable runnable = runConfig->runnable(); - if (runnable.is() && isLocal(runConfig)) - m_pathChooser->setPath(runnable.as().executable); + if (runnable.is() && isLocal(runConfig)) { + resetExecutable->setEnabled(true); + connect(resetExecutable, &QPushButton::clicked, + this, [this, runnable]() { + m_pathChooser->setPath(runnable.as().executable); + }); + } } } @@ -140,7 +150,7 @@ UnstartedAppWatcherDialog::UnstartedAppWatcherDialog(QWidget *parent) QFormLayout *mainLayout = new QFormLayout(this); mainLayout->addRow(new QLabel(tr("Kit: "), this), m_kitChooser); - mainLayout->addRow(new QLabel(tr("Executable: "), this), m_pathChooser); + mainLayout->addRow(new QLabel(tr("Executable: "), this), pathLayout); mainLayout->addRow(m_hideOnAttachCheckBox); mainLayout->addRow(m_continueOnAttachCheckBox); mainLayout->addRow(m_waitingLabel); From 8cfef9657a112276dd2308bf31e61b17580c5088 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Mon, 22 Feb 2016 17:18:18 +0100 Subject: [PATCH 3/9] Clang: Introduce switching/adding of warning configurations A warning configuration is a list of command line (warning) options for libclang. Three non-editable built-in configurations are provided by default. The user can copy a configuration to customize it. This is still a global setting and it changes take effect after re-opening a document. Both issues will be addressed in follow-up changes. Change-Id: I86667d7dc39ad31b88666454220e6da563797740 Reviewed-by: Leena Miettinen Reviewed-by: Marco Bubke --- .../clangeditordocumentprocessor.cpp | 4 +- .../cpptools/clangdiagnosticconfig.cpp | 78 +++++++ src/plugins/cpptools/clangdiagnosticconfig.h | 63 ++++++ .../cpptools/clangdiagnosticconfigsmodel.cpp | 146 +++++++++++++ .../cpptools/clangdiagnosticconfigsmodel.h | 55 +++++ .../cpptools/clangdiagnosticconfigswidget.cpp | 196 ++++++++++++++++++ .../cpptools/clangdiagnosticconfigswidget.h | 73 +++++++ .../cpptools/clangdiagnosticconfigswidget.ui | 81 ++++++++ src/plugins/cpptools/cppcodemodelsettings.cpp | 102 ++++++--- src/plugins/cpptools/cppcodemodelsettings.h | 15 +- .../cpptools/cppcodemodelsettingspage.cpp | 32 +-- .../cpptools/cppcodemodelsettingspage.h | 5 +- .../cpptools/cppcodemodelsettingspage.ui | 39 +--- src/plugins/cpptools/cpptools.pro | 7 + src/plugins/cpptools/cpptools.qbs | 4 + src/plugins/cpptools/cpptoolsconstants.h | 3 +- 16 files changed, 819 insertions(+), 84 deletions(-) create mode 100644 src/plugins/cpptools/clangdiagnosticconfig.cpp create mode 100644 src/plugins/cpptools/clangdiagnosticconfig.h create mode 100644 src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp create mode 100644 src/plugins/cpptools/clangdiagnosticconfigsmodel.h create mode 100644 src/plugins/cpptools/clangdiagnosticconfigswidget.cpp create mode 100644 src/plugins/cpptools/clangdiagnosticconfigswidget.h create mode 100644 src/plugins/cpptools/clangdiagnosticconfigswidget.ui diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 8cdd19d436a..38f7cb2d51f 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -342,8 +342,8 @@ static QStringList languageOptions(const QString &filePath, CppTools::ProjectPar static QStringList fileArguments(const QString &filePath, CppTools::ProjectPart *projectPart) { - return QStringList(languageOptions(filePath, projectPart)) - + CppTools::codeModelSettings()->extraClangOptions(); + return languageOptions(filePath, projectPart) + + CppTools::codeModelSettings()->clangDiagnosticConfig().commandLineOptions(); } ClangBackEnd::FileContainer diff --git a/src/plugins/cpptools/clangdiagnosticconfig.cpp b/src/plugins/cpptools/clangdiagnosticconfig.cpp new file mode 100644 index 00000000000..62d259c521d --- /dev/null +++ b/src/plugins/cpptools/clangdiagnosticconfig.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangdiagnosticconfig.h" + +namespace CppTools { + +Core::Id ClangDiagnosticConfig::id() const +{ + return m_id; +} + +void ClangDiagnosticConfig::setId(const Core::Id &id) +{ + m_id = id; +} + +QString ClangDiagnosticConfig::displayName() const +{ + return m_displayName; +} + +void ClangDiagnosticConfig::setDisplayName(const QString &displayName) +{ + m_displayName = displayName; +} + +QStringList ClangDiagnosticConfig::commandLineOptions() const +{ + return m_commandLineOptions; +} + +void ClangDiagnosticConfig::setCommandLineOptions(const QStringList &options) +{ + m_commandLineOptions = options; +} + +bool ClangDiagnosticConfig::isReadOnly() const +{ + return m_isReadOnly; +} + +void ClangDiagnosticConfig::setIsReadOnly(bool isReadOnly) +{ + m_isReadOnly = isReadOnly; +} + +bool ClangDiagnosticConfig::operator==(const ClangDiagnosticConfig &other) const +{ + return m_id == other.m_id + && m_displayName == other.m_displayName + && m_commandLineOptions == other.m_commandLineOptions + && m_isReadOnly == other.m_isReadOnly; +} + +} // namespace CppTools diff --git a/src/plugins/cpptools/clangdiagnosticconfig.h b/src/plugins/cpptools/clangdiagnosticconfig.h new file mode 100644 index 00000000000..17a5a768bb4 --- /dev/null +++ b/src/plugins/cpptools/clangdiagnosticconfig.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "cpptools_global.h" + +#include + +#include +#include + +namespace CppTools { + +class CPPTOOLS_EXPORT ClangDiagnosticConfig +{ +public: + Core::Id id() const; + void setId(const Core::Id &id); + + QString displayName() const; + void setDisplayName(const QString &displayName); + + QStringList commandLineOptions() const; + void setCommandLineOptions(const QStringList &commandLineOptions); + + bool isReadOnly() const; + void setIsReadOnly(bool isReadOnly); + + bool operator==(const ClangDiagnosticConfig &other) const; + +private: + Core::Id m_id; + QString m_displayName; + QStringList m_commandLineOptions; + bool m_isReadOnly = false; +}; + +using ClangDiagnosticConfigs = QVector; + +} // namespace CppTools diff --git a/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp b/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp new file mode 100644 index 00000000000..ccd78141d87 --- /dev/null +++ b/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangdiagnosticconfigsmodel.h" + +#include "cpptoolsconstants.h" + +#include + +#include + +namespace CppTools { +namespace Internal { + +static void addConfigForQuestionableConstructs(ClangDiagnosticConfigsModel &model) +{ + ClangDiagnosticConfig config; + config.setId("Builtin.Questionable"); + config.setDisplayName(QCoreApplication::translate("ClangDiagnosticConfigsModel", + "Warnings for questionable constructs")); + config.setIsReadOnly(true); + config.setCommandLineOptions({ + QStringLiteral("-Wall"), + QStringLiteral("-Wextra"), + }); + + model.appendOrUpdate(config); +} + +static void addConfigForPedanticWarnings(ClangDiagnosticConfigsModel &model) +{ + ClangDiagnosticConfig config; + config.setId("Builtin.Pedantic"); + config.setDisplayName(QCoreApplication::translate("ClangDiagnosticConfigsModel", + "Pedantic Warnings")); + config.setIsReadOnly(true); + config.setCommandLineOptions({QStringLiteral("-Wpedantic")}); + + model.appendOrUpdate(config); +} + +static void addConfigForAlmostEveryWarning(ClangDiagnosticConfigsModel &model) +{ + ClangDiagnosticConfig config; + config.setId(Constants::CPP_CLANG_BUILTIN_CONFIG_ID_EVERYTHING_WITH_EXCEPTIONS); + config.setDisplayName(QCoreApplication::translate("ClangDiagnosticConfigsModel", + "Warnings for almost everything")); + config.setIsReadOnly(true); + config.setCommandLineOptions({ + QStringLiteral("-Weverything"), + QStringLiteral("-Wno-c++98-compat"), + QStringLiteral("-Wno-c++98-compat-pedantic"), + QStringLiteral("-Wno-unused-macros"), + QStringLiteral("-Wno-newline-eof"), + QStringLiteral("-Wno-exit-time-destructors"), + QStringLiteral("-Wno-global-constructors"), + QStringLiteral("-Wno-gnu-zero-variadic-macro-arguments"), + QStringLiteral("-Wno-documentation"), + QStringLiteral("-Wno-shadow"), + QStringLiteral("-Wno-missing-prototypes"), // Not optimal for C projects. + }); + + model.appendOrUpdate(config); +} + +static void addBuiltinConfigs(ClangDiagnosticConfigsModel &model) +{ + addConfigForPedanticWarnings(model); + addConfigForQuestionableConstructs(model); + addConfigForAlmostEveryWarning(model); +} + +ClangDiagnosticConfigsModel::ClangDiagnosticConfigsModel(const ClangDiagnosticConfigs &customConfigs) +{ + addBuiltinConfigs(*this); + + foreach (const ClangDiagnosticConfig &config, customConfigs) + m_diagnosticConfigs.append(config); +} + +int ClangDiagnosticConfigsModel::size() const +{ + return m_diagnosticConfigs.size(); +} + +const ClangDiagnosticConfig &ClangDiagnosticConfigsModel::at(int index) const +{ + return m_diagnosticConfigs.at(index); +} + +void ClangDiagnosticConfigsModel::appendOrUpdate(const ClangDiagnosticConfig &config) +{ + const int index = indexOfConfig(config.id()); + + if (index >= 0 && index < m_diagnosticConfigs.size()) + m_diagnosticConfigs.replace(index, config); + else + m_diagnosticConfigs.append(config); +} + +void ClangDiagnosticConfigsModel::removeConfigWithId(const Core::Id &id) +{ + m_diagnosticConfigs.removeOne(configWithId(id)); +} + +ClangDiagnosticConfigs ClangDiagnosticConfigsModel::configs() const +{ + return m_diagnosticConfigs; +} + +const ClangDiagnosticConfig &ClangDiagnosticConfigsModel::configWithId(const Core::Id &id) const +{ + return m_diagnosticConfigs.at(indexOfConfig(id)); +} + +int ClangDiagnosticConfigsModel::indexOfConfig(const Core::Id &id) const +{ + return Utils::indexOf(m_diagnosticConfigs, [&](const ClangDiagnosticConfig &config) { + return config.id() == id; + }); +} + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/clangdiagnosticconfigsmodel.h b/src/plugins/cpptools/clangdiagnosticconfigsmodel.h new file mode 100644 index 00000000000..ffccd0868a2 --- /dev/null +++ b/src/plugins/cpptools/clangdiagnosticconfigsmodel.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangdiagnosticconfig.h" + +namespace CppTools { +namespace Internal { + +class ClangDiagnosticConfigsModel +{ +public: + ClangDiagnosticConfigsModel(const ClangDiagnosticConfigs &customConfigs); + + int size() const; + const ClangDiagnosticConfig &at(int index) const; + + void appendOrUpdate(const ClangDiagnosticConfig &config); + void removeConfigWithId(const Core::Id &id); + + ClangDiagnosticConfigs configs() const; + const ClangDiagnosticConfig &configWithId(const Core::Id &id) const; + +private: + int indexOfConfig(const Core::Id &id) const; + +private: + ClangDiagnosticConfigs m_diagnosticConfigs; +}; + +} // namespace Internal +} // namespace CppTools diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp new file mode 100644 index 00000000000..fbecf049721 --- /dev/null +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangdiagnosticconfigswidget.h" +#include "ui_clangdiagnosticconfigswidget.h" + +#include +#include + +#include +#include +#include + +namespace CppTools { +namespace Internal { + +ClangDiagnosticConfigsWidget::ClangDiagnosticConfigsWidget( + const ClangDiagnosticConfigs &customConfigs, + const Core::Id &configToSelect, + QWidget *parent) + : QWidget(parent) + , m_ui(new Ui::ClangDiagnosticConfigsWidget) + , m_diagnosticConfigsModel(customConfigs) +{ + m_ui->setupUi(this); + + connect(m_ui->configChooserComboBox, + static_cast(&QComboBox::currentIndexChanged), + this, + &ClangDiagnosticConfigsWidget::onCurrentConfigChanged); + connect(m_ui->copyButton, &QPushButton::clicked, + this, &ClangDiagnosticConfigsWidget::onCopyButtonClicked); + connect(m_ui->removeButton, &QPushButton::clicked, + this, &ClangDiagnosticConfigsWidget::onRemoveButtonClicked); + connect(m_ui->diagnosticOptionsTextEdit->document(), &QTextDocument::contentsChanged, + this, &ClangDiagnosticConfigsWidget::onDiagnosticOptionsEdited); + + syncWidgetsToModel(configToSelect); +} + +ClangDiagnosticConfigsWidget::~ClangDiagnosticConfigsWidget() +{ + delete m_ui; +} + +void ClangDiagnosticConfigsWidget::onCurrentConfigChanged(int) +{ + syncOtherWidgetsToComboBox(); +} + +static ClangDiagnosticConfig createCustomConfig(const ClangDiagnosticConfig &config, + const QString &displayName) +{ + ClangDiagnosticConfig copied = config; + copied.setId(Core::Id::fromString(QUuid::createUuid().toString())); + copied.setDisplayName(displayName); + copied.setIsReadOnly(false); + + return copied; +} + +void ClangDiagnosticConfigsWidget::onCopyButtonClicked() +{ + const ClangDiagnosticConfig &config = currentConfig(); + + bool diaglogAccepted = false; + const QString newName = QInputDialog::getText(this, + tr("Copy Diagnostic Configuration"), + tr("Diagnostic configuration name:"), + QLineEdit::Normal, + tr("%1 (Copy)").arg(config.displayName()), + &diaglogAccepted); + if (diaglogAccepted) { + const ClangDiagnosticConfig customConfig = createCustomConfig(config, newName); + m_diagnosticConfigsModel.appendOrUpdate(customConfig); + + syncConfigChooserToModel(customConfig.id()); + m_ui->diagnosticOptionsTextEdit->setFocus(); + } +} + +void ClangDiagnosticConfigsWidget::onRemoveButtonClicked() +{ + m_diagnosticConfigsModel.removeConfigWithId(currentConfigId()); + + syncConfigChooserToModel(); +} + +void ClangDiagnosticConfigsWidget::onDiagnosticOptionsEdited() +{ + const QString diagnosticOptions + = m_ui->diagnosticOptionsTextEdit->document()->toPlainText().trimmed(); + const QStringList updatedCommandLine + = diagnosticOptions.trimmed().split(QLatin1Char(' '), QString::SkipEmptyParts); + + ClangDiagnosticConfig updatedConfig = currentConfig(); + updatedConfig.setCommandLineOptions(updatedCommandLine); + + m_diagnosticConfigsModel.appendOrUpdate(updatedConfig); +} + +void ClangDiagnosticConfigsWidget::syncWidgetsToModel(const Core::Id &configToSelect) +{ + syncConfigChooserToModel(configToSelect); + syncOtherWidgetsToComboBox(); +} + +static QString adaptedDisplayName(const ClangDiagnosticConfig &config) +{ + return config.isReadOnly() + ? QObject::tr("%1 [built-in]").arg(config.displayName()) + : config.displayName(); +} + +void ClangDiagnosticConfigsWidget::syncConfigChooserToModel(const Core::Id &configToSelect) +{ + m_ui->configChooserComboBox->clear(); + int currentIndex = -1; + + const int size = m_diagnosticConfigsModel.size(); + for (int i = 0; i < size; ++i) { + const ClangDiagnosticConfig &config = m_diagnosticConfigsModel.at(i); + m_ui->configChooserComboBox->addItem(adaptedDisplayName(config), config.id().toSetting()); + + if (configToSelect == config.id()) + currentIndex = i; + } + + if (currentIndex != -1) + m_ui->configChooserComboBox->setCurrentIndex(currentIndex); +} + +void ClangDiagnosticConfigsWidget::syncOtherWidgetsToComboBox() +{ + if (isConfigChooserEmpty()) + return; + + const ClangDiagnosticConfig &config = currentConfig(); + + // Update main button row + m_ui->removeButton->setEnabled(!config.isReadOnly()); + + // Update child widgets + const QString commandLineOptions = config.commandLineOptions().join(QLatin1Char(' ')); + m_ui->diagnosticOptionsTextEdit->document()->setPlainText(commandLineOptions); + m_ui->diagnosticOptionsTextEdit->setReadOnly(config.isReadOnly()); +} + +bool ClangDiagnosticConfigsWidget::isConfigChooserEmpty() const +{ + return m_ui->configChooserComboBox->count() == 0; +} + +const ClangDiagnosticConfig &ClangDiagnosticConfigsWidget::currentConfig() const +{ + return m_diagnosticConfigsModel.configWithId(currentConfigId()); +} + +Core::Id ClangDiagnosticConfigsWidget::currentConfigId() const +{ + return Core::Id::fromSetting(m_ui->configChooserComboBox->currentData()); +} + +ClangDiagnosticConfigs ClangDiagnosticConfigsWidget::customConfigs() const +{ + const ClangDiagnosticConfigs allConfigs = m_diagnosticConfigsModel.configs(); + + return Utils::filtered(allConfigs, [](const ClangDiagnosticConfig &config){ + return !config.isReadOnly(); + }); +} + +} // Internal namespace +} // CppTools namespace diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.h b/src/plugins/cpptools/clangdiagnosticconfigswidget.h new file mode 100644 index 00000000000..2938afd2743 --- /dev/null +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangdiagnosticconfig.h" +#include "clangdiagnosticconfigsmodel.h" + +#include + +namespace CppTools { +namespace Internal { + +namespace Ui { class ClangDiagnosticConfigsWidget; } + +class ClangDiagnosticConfigsWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ClangDiagnosticConfigsWidget(const ClangDiagnosticConfigs &customConfigs, + const Core::Id &configToSelect, + QWidget *parent = 0); + + Core::Id currentConfigId() const; + ClangDiagnosticConfigs customConfigs() const; + + ~ClangDiagnosticConfigsWidget(); + +private slots: + void onCurrentConfigChanged(int); + void onCopyButtonClicked(); + void onRemoveButtonClicked(); + + void onDiagnosticOptionsEdited(); + +private: + void syncWidgetsToModel(const Core::Id &configToSelect = Core::Id()); + void syncConfigChooserToModel(const Core::Id &configToSelect = Core::Id()); + void syncOtherWidgetsToComboBox(); + + bool isConfigChooserEmpty() const; + const ClangDiagnosticConfig ¤tConfig() const; + +private: + Ui::ClangDiagnosticConfigsWidget *m_ui; + ClangDiagnosticConfigsModel m_diagnosticConfigsModel; +}; + +} // Internal namespace +} // CppTools namespace diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.ui b/src/plugins/cpptools/clangdiagnosticconfigswidget.ui new file mode 100644 index 00000000000..60776e5d479 --- /dev/null +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.ui @@ -0,0 +1,81 @@ + + + CppTools::Internal::ClangDiagnosticConfigsWidget + + + + 0 + 0 + 545 + 300 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Configuration to use: + + + + + + + + 0 + 0 + + + + + + + + Copy... + + + + + + + Remove + + + + + + + + + + + + For appropriate options, consult the GCC or Clang manual pages or the <a href="https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html">GCC online documentation</a>. + + + true + + + + + + + + diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index 225bcc417f7..246080dc11a 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -24,24 +24,63 @@ ****************************************************************************/ #include "cppcodemodelsettings.h" + +#include "clangdiagnosticconfigsmodel.h" #include "cpptoolsconstants.h" +#include + #include using namespace CppTools; -static QLatin1String cppHeaderMimeType(Constants::CPP_HEADER_MIMETYPE); -static QLatin1String cHeaderMimeType(Constants::C_HEADER_MIMETYPE); -static QLatin1String clangExtraOptionsKey(Constants::CPPTOOLS_EXTRA_CLANG_OPTIONS); +static Core::Id initialClangDiagnosticConfigId() +{ return Core::Id(Constants::CPP_CLANG_BUILTIN_CONFIG_ID_EVERYTHING_WITH_EXCEPTIONS); } + +static CppCodeModelSettings::PCHUsage initialPchUsage() +{ return CppCodeModelSettings::PchUse_None; } + +static QString clangDiagnosticConfigKey() +{ return QStringLiteral("ClangDiagnosticConfig"); } + +static QString clangDiagnosticConfigsArrayKey() +{ return QStringLiteral("ClangDiagnosticConfigs"); } + +static QString clangDiagnosticConfigsArrayIdKey() +{ return QLatin1String("id"); } + +static QString clangDiagnosticConfigsArrayDisplayNameKey() +{ return QLatin1String("displayName"); } + +static QString clangDiagnosticConfigsArrayOptionsKey() +{ return QLatin1String("diagnosticOptions"); } + +static QString pchUsageKey() +{ return QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_PCH_USAGE); } void CppCodeModelSettings::fromSettings(QSettings *s) { s->beginGroup(QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP)); - setExtraClangOptions(s->value(clangExtraOptionsKey, defaultExtraClangOptions()).toStringList()); + const int size = s->beginReadArray(clangDiagnosticConfigsArrayKey()); + for (int i = 0; i < size; ++i) { + s->setArrayIndex(i); - QVariant v = s->value(QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_PCH_USAGE), PchUse_None); - setPCHUsage(static_cast(v.toInt())); + ClangDiagnosticConfig config; + config.setId(Core::Id::fromSetting(s->value(clangDiagnosticConfigsArrayIdKey()))); + config.setDisplayName(s->value(clangDiagnosticConfigsArrayDisplayNameKey()).toString()); + config.setCommandLineOptions(s->value(clangDiagnosticConfigsArrayOptionsKey()).toStringList()); + m_clangCustomDiagnosticConfigs.append(config); + } + s->endArray(); + + const Core::Id diagnosticConfigId = Core::Id::fromSetting( + s->value(clangDiagnosticConfigKey(), + initialClangDiagnosticConfigId().toSetting())); + setClangDiagnosticConfigId(diagnosticConfigId); + + const QVariant pchUsageVariant = s->value(pchUsageKey(), initialPchUsage()); + setPCHUsage(static_cast(pchUsageVariant.toInt())); s->endGroup(); emit changed(); @@ -51,39 +90,50 @@ void CppCodeModelSettings::toSettings(QSettings *s) { s->beginGroup(QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP)); - s->setValue(clangExtraOptionsKey, extraClangOptions()); - s->setValue(QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_PCH_USAGE), pchUsage()); + s->beginWriteArray(clangDiagnosticConfigsArrayKey()); + for (int i = 0, size = m_clangCustomDiagnosticConfigs.size(); i < size; ++i) { + const ClangDiagnosticConfig &config = m_clangCustomDiagnosticConfigs.at(i); + + s->setArrayIndex(i); + s->setValue(clangDiagnosticConfigsArrayIdKey(), config.id().toSetting()); + s->setValue(clangDiagnosticConfigsArrayDisplayNameKey(), config.displayName()); + s->setValue(clangDiagnosticConfigsArrayOptionsKey(), config.commandLineOptions()); + } + s->endArray(); + + s->setValue(clangDiagnosticConfigKey(), clangDiagnosticConfigId().toSetting()); + s->setValue(pchUsageKey(), pchUsage()); s->endGroup(); emit changed(); } -QStringList CppCodeModelSettings::defaultExtraClangOptions() +Core::Id CppCodeModelSettings::clangDiagnosticConfigId() const { - return { - QStringLiteral("-Weverything"), - QStringLiteral("-Wno-c++98-compat"), - QStringLiteral("-Wno-c++98-compat-pedantic"), - QStringLiteral("-Wno-unused-macros"), - QStringLiteral("-Wno-newline-eof"), - QStringLiteral("-Wno-exit-time-destructors"), - QStringLiteral("-Wno-global-constructors"), - QStringLiteral("-Wno-gnu-zero-variadic-macro-arguments"), - QStringLiteral("-Wno-documentation"), - QStringLiteral("-Wno-shadow"), - QStringLiteral("-Wno-missing-prototypes"), // Not optimal for C projects. - }; + return m_clangDiagnosticConfigId; } -QStringList CppCodeModelSettings::extraClangOptions() const +void CppCodeModelSettings::setClangDiagnosticConfigId(const Core::Id &configId) { - return m_extraClangOptions; + m_clangDiagnosticConfigId = configId; } -void CppCodeModelSettings::setExtraClangOptions(const QStringList &extraClangOptions) +const ClangDiagnosticConfig CppCodeModelSettings::clangDiagnosticConfig() const { - m_extraClangOptions = extraClangOptions; + const Internal::ClangDiagnosticConfigsModel configsModel(m_clangCustomDiagnosticConfigs); + + return configsModel.configWithId(clangDiagnosticConfigId()); +} + +ClangDiagnosticConfigs CppCodeModelSettings::clangCustomDiagnosticConfigs() const +{ + return m_clangCustomDiagnosticConfigs; +} + +void CppCodeModelSettings::setClangCustomDiagnosticConfigs(const ClangDiagnosticConfigs &configs) +{ + m_clangCustomDiagnosticConfigs = configs; } CppCodeModelSettings::PCHUsage CppCodeModelSettings::pchUsage() const diff --git a/src/plugins/cpptools/cppcodemodelsettings.h b/src/plugins/cpptools/cppcodemodelsettings.h index c6deaa43c23..81dab011afe 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.h +++ b/src/plugins/cpptools/cppcodemodelsettings.h @@ -28,6 +28,8 @@ #include "cpptools_global.h" +#include "clangdiagnosticconfigsmodel.h" + #include #include @@ -52,9 +54,12 @@ public: void toSettings(QSettings *s); public: - static QStringList defaultExtraClangOptions(); - QStringList extraClangOptions() const; - void setExtraClangOptions(const QStringList &extraClangOptions); + Core::Id clangDiagnosticConfigId() const; + void setClangDiagnosticConfigId(const Core::Id &configId); + const ClangDiagnosticConfig clangDiagnosticConfig() const; + + ClangDiagnosticConfigs clangCustomDiagnosticConfigs() const; + void setClangCustomDiagnosticConfigs(const ClangDiagnosticConfigs &configs); PCHUsage pchUsage() const; void setPCHUsage(PCHUsage pchUsage); @@ -64,10 +69,12 @@ public: // for tests signals: void changed(); + void clangDiagnosticConfigIdChanged(); private: - QStringList m_extraClangOptions; PCHUsage m_pchUsage = PchUse_None; + ClangDiagnosticConfigs m_clangCustomDiagnosticConfigs; + Core::Id m_clangDiagnosticConfigId; }; } // namespace CppTools diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.cpp b/src/plugins/cpptools/cppcodemodelsettingspage.cpp index 93206b39b3c..95d4d933469 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.cpp +++ b/src/plugins/cpptools/cppcodemodelsettingspage.cpp @@ -24,6 +24,8 @@ ****************************************************************************/ #include "cppcodemodelsettingspage.h" + +#include "clangdiagnosticconfigswidget.h" #include "cppmodelmanager.h" #include "cpptoolsconstants.h" #include "ui_cppcodemodelsettingspage.h" @@ -43,10 +45,6 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(QWidget *parent) m_ui->setupUi(this); m_ui->clangSettingsGroupBox->setVisible(true); - connect(m_ui->clangOptionsResetButton, &QPushButton::clicked, [this]() { - const QString options = m_settings->defaultExtraClangOptions().join(QLatin1Char('\n')); - m_ui->clangOptionsToAppendTextEdit->document()->setPlainText(options); - }); } CppCodeModelSettingsWidget::~CppCodeModelSettingsWidget() @@ -73,15 +71,17 @@ void CppCodeModelSettingsWidget::applyToSettings() const m_settings->toSettings(Core::ICore::settings()); } -void CppCodeModelSettingsWidget::setupClangCodeModelWidgets() const +void CppCodeModelSettingsWidget::setupClangCodeModelWidgets() { const bool isClangActive = CppModelManager::instance()->isClangCodeModelActive(); m_ui->activateClangCodeModelPluginHint->setVisible(!isClangActive); m_ui->clangSettingsGroupBox->setEnabled(isClangActive); - const QString extraClangOptions = m_settings->extraClangOptions().join(QLatin1Char('\n')); - m_ui->clangOptionsToAppendTextEdit->document()->setPlainText(extraClangOptions); + m_clangDiagnosticConfigsWidget = new ClangDiagnosticConfigsWidget( + m_settings->clangCustomDiagnosticConfigs(), + m_settings->clangDiagnosticConfigId()); + m_ui->clangSettingsGroupBox->layout()->addWidget(m_clangDiagnosticConfigsWidget); } void CppCodeModelSettingsWidget::setupPchCheckBox() const @@ -94,12 +94,18 @@ bool CppCodeModelSettingsWidget::applyClangCodeModelWidgetsToSettings() const { bool settingsChanged = false; - const QStringList previousOptions = m_settings->extraClangOptions(); - const QString newOptionsAsString = m_ui->clangOptionsToAppendTextEdit->document()->toPlainText(); - const QStringList newOptions = newOptionsAsString.split(QLatin1Char('\n'), - QString::SkipEmptyParts); - if (newOptions != previousOptions) { - m_settings->setExtraClangOptions(newOptions); + const Core::Id oldConfigId = m_settings->clangDiagnosticConfigId(); + const Core::Id currentConfigId = m_clangDiagnosticConfigsWidget->currentConfigId(); + if (oldConfigId != currentConfigId) { + m_settings->setClangDiagnosticConfigId(currentConfigId); + settingsChanged = true; + } + + const ClangDiagnosticConfigs oldDiagnosticConfigs = m_settings->clangCustomDiagnosticConfigs(); + const ClangDiagnosticConfigs currentDiagnosticConfigs + = m_clangDiagnosticConfigsWidget->customConfigs(); + if (oldDiagnosticConfigs != currentDiagnosticConfigs) { + m_settings->setClangCustomDiagnosticConfigs(currentDiagnosticConfigs); settingsChanged = true; } diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.h b/src/plugins/cpptools/cppcodemodelsettingspage.h index 5a9df7dbe3a..6462d634c96 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.h +++ b/src/plugins/cpptools/cppcodemodelsettingspage.h @@ -39,6 +39,8 @@ QT_FORWARD_DECLARE_CLASS(QSettings) namespace CppTools { namespace Internal { +class ClangDiagnosticConfigsWidget; + namespace Ui { class CppCodeModelSettingsPage; } class CppCodeModelSettingsWidget: public QWidget @@ -53,7 +55,7 @@ public: void applyToSettings() const; private: - void setupClangCodeModelWidgets() const; + void setupClangCodeModelWidgets(); void setupPchCheckBox() const; bool applyClangCodeModelWidgetsToSettings() const; @@ -61,6 +63,7 @@ private: private: Ui::CppCodeModelSettingsPage *m_ui; + QPointer m_clangDiagnosticConfigsWidget; QSharedPointer m_settings; }; diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.ui b/src/plugins/cpptools/cppcodemodelsettingspage.ui index 913b505e7ae..974f0f6b6ef 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.ui +++ b/src/plugins/cpptools/cppcodemodelsettingspage.ui @@ -24,47 +24,12 @@ - Clang Code Model + Clang Code Model Warnings false - - - - - Append additional command line options to Clang, one per line. <i>Use this with care.</i> - - - - - - - - - - - - Reset Options - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 67e8d165713..f98a85cde89 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -9,6 +9,9 @@ HEADERS += \ builtineditordocumentparser.h \ builtineditordocumentprocessor.h \ builtinindexingsupport.h \ + clangdiagnosticconfig.h \ + clangdiagnosticconfigsmodel.h \ + clangdiagnosticconfigswidget.h \ commentssettings.h \ completionsettingspage.h \ cppchecksymbols.h \ @@ -84,6 +87,9 @@ SOURCES += \ builtineditordocumentparser.cpp \ builtineditordocumentprocessor.cpp \ builtinindexingsupport.cpp \ + clangdiagnosticconfig.cpp \ + clangdiagnosticconfigsmodel.cpp \ + clangdiagnosticconfigswidget.cpp \ commentssettings.cpp \ completionsettingspage.cpp \ cppchecksymbols.cpp \ @@ -149,6 +155,7 @@ SOURCES += \ compileroptionsbuilder.cpp FORMS += \ + clangdiagnosticconfigswidget.ui \ completionsettingspage.ui \ cppcodemodelsettingspage.ui \ cppcodestylesettingspage.ui \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index f424237957e..84ff91f800e 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -31,6 +31,10 @@ QtcPlugin { "builtineditordocumentparser.cpp", "builtineditordocumentparser.h", "builtineditordocumentprocessor.cpp", "builtineditordocumentprocessor.h", "builtinindexingsupport.cpp", "builtinindexingsupport.h", + "clangdiagnosticconfig.cpp", "clangdiagnosticconfig.h", + "clangdiagnosticconfigsmodel.cpp", "clangdiagnosticconfigsmodel.h", + "clangdiagnosticconfigswidget.cpp", "clangdiagnosticconfigswidget.h", + "clangdiagnosticconfigswidget.ui", "compileroptionsbuilder.cpp", "compileroptionsbuilder.h", "commentssettings.cpp", "commentssettings.h", "completionsettingspage.cpp", "completionsettingspage.h", "completionsettingspage.ui", diff --git a/src/plugins/cpptools/cpptoolsconstants.h b/src/plugins/cpptools/cpptoolsconstants.h index 37e8acb601a..90a62b8f718 100644 --- a/src/plugins/cpptools/cpptoolsconstants.h +++ b/src/plugins/cpptools/cpptoolsconstants.h @@ -49,7 +49,8 @@ const char LOWERCASE_CPPFILES_KEY[] = "LowerCaseFiles"; enum { lowerCaseFilesDefault = 1 }; const char CPPTOOLS_SORT_EDITOR_DOCUMENT_OUTLINE[] = "SortedMethodOverview"; const char CPPTOOLS_MODEL_MANAGER_PCH_USAGE[] = "PCHUsage"; -const char CPPTOOLS_EXTRA_CLANG_OPTIONS[] = "ExtraClangOptions"; + +const char CPP_CLANG_BUILTIN_CONFIG_ID_EVERYTHING_WITH_EXCEPTIONS[] = "Builtin.EverythingWithExceptions"; const char CPP_CODE_STYLE_SETTINGS_ID[] = "A.Cpp.Code Style"; const char CPP_CODE_STYLE_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("CppTools", "Code Style"); From c2dfd0f53bbe3b93ac898f0b0ae34e419426351e Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Thu, 25 Feb 2016 10:54:33 +0100 Subject: [PATCH 4/9] Update Canvas3D application wizard to Qt 5.7 level Upgraded r74 three.js port, which is compatible with Qt 5.6 and Qt 5.7. Some three.js features require Canvas3D 1.1 API, so increased the import version in the template. Change-Id: I1ae37237073d4149f56c1d93579b20a576496e33 Reviewed-by: Miikka Heikkinen Reviewed-by: Eike Ziller Reviewed-by: Alessandro Portale --- .../qmake/qtcanvas3dapplication/main.qml.tpl | 2 +- .../threejs/3rdparty/three.js | 36078 ++++++++++------ .../qtcanvas3dapplication/threejs/glcode.js | 1 - .../qmake/qtcanvas3dapplication/wizard.json | 2 +- src/plugins/qtsupport/baseqtversion.cpp | 3 +- src/plugins/qtsupport/qtsupportconstants.h | 2 +- 6 files changed, 22297 insertions(+), 13791 deletions(-) diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/main.qml.tpl b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/main.qml.tpl index 402c94b80b8..47241e6ebd0 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/main.qml.tpl +++ b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/main.qml.tpl @@ -1,5 +1,5 @@ import QtQuick 2.4 -import QtCanvas3D 1.0 +import QtCanvas3D 1.1 import QtQuick.Window 2.2 import "glcode.js" as GLCode diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/3rdparty/three.js b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/3rdparty/three.js index 9dd4d7527ca..fd129e04bb0 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/3rdparty/three.js +++ b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/3rdparty/three.js @@ -38,17 +38,28 @@ THE SOFTWARE. */ function THREE() {}; -THREE.REVISION = '71' +THREE.REVISION = '74' -// browserify support +// -//if ( typeof module === 'object' ) { +//if ( typeof define === 'function' && define.amd ) { + +// define( 'three', THREE ); + +//} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) { // module.exports = THREE; //} +// -// polyfills +if ( Number.EPSILON === undefined ) { + + Number.EPSILON = Math.pow( 2, - 52 ); + +} + +// if ( Math.sign === undefined ) { @@ -56,18 +67,82 @@ if ( Math.sign === undefined ) { Math.sign = function ( x ) { - return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : +x; + return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; }; } +if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) { -// set the default log handlers -THREE.log = function() { console.log.apply( console, arguments ); } -THREE.warn = function() { console.warn.apply( console, arguments ); } -THREE.error = function() { console.error.apply( console, arguments ); } + // Missing in IE9-11. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name + Object.defineProperty( Function.prototype, 'name', { + + get: function () { + + return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ]; + + } + + } ); + +} + +if ( Object.assign === undefined ) { + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + + Object.defineProperty( Object, 'assign', { + + writable: true, + configurable: true, + + value: function ( target ) { + + 'use strict'; + + if ( target === undefined || target === null ) { + + throw new TypeError( "Cannot convert first argument to object" ); + + } + + var to = Object( target ); + + for ( var i = 1, n = arguments.length; i !== n; ++ i ) { + + var nextSource = arguments[ i ]; + + if ( nextSource === undefined || nextSource === null ) continue; + + nextSource = Object( nextSource ); + + var keysArray = Object.keys( nextSource ); + + for ( var nextIndex = 0, len = keysArray.length; nextIndex !== len; ++ nextIndex ) { + + var nextKey = keysArray[ nextIndex ]; + var desc = Object.getOwnPropertyDescriptor( nextSource, nextKey ); + + if ( desc !== undefined && desc.enumerable ) { + + to[ nextKey ] = nextSource[ nextKey ]; + + } + + } + + } + + return to; + + } + + } ); + +} // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button @@ -99,7 +174,6 @@ THREE.DoubleSide = 2; // shading -THREE.NoShading = 0; THREE.FlatShading = 1; THREE.SmoothShading = 2; @@ -120,7 +194,7 @@ THREE.CustomBlending = 5; // custom blending equations // (numbers start from 100 not to clash with other -// mappings to OpenGL constants defined in Texture.js) +// mappings to OpenGL constants defined in Texture.js) THREE.AddEquation = 100; THREE.SubtractEquation = 101; @@ -151,6 +225,17 @@ THREE.DstColorFactor = 208; THREE.OneMinusDstColorFactor = 209; THREE.SrcAlphaSaturateFactor = 210; +// depth modes + +THREE.NeverDepth = 0; +THREE.AlwaysDepth = 1; +THREE.LessDepth = 2; +THREE.LessEqualDepth = 3; +THREE.EqualDepth = 4; +THREE.GreaterEqualDepth = 5; +THREE.GreaterDepth = 6; +THREE.NotEqualDepth = 7; + // TEXTURE CONSTANTS @@ -228,46 +313,33 @@ THREE.RGB_PVRTC_2BPPV1_Format = 2101; THREE.RGBA_PVRTC_4BPPV1_Format = 2102; THREE.RGBA_PVRTC_2BPPV1_Format = 2103; +// ETC compressed texture formats -// DEPRECATED +THREE.RGB_ETC1_Format = 2151; -THREE.Projector = function () { +// Loop styles for AnimationAction - THREE.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); +THREE.LoopOnce = 2200; +THREE.LoopRepeat = 2201; +THREE.LoopPingPong = 2202; - this.projectVector = function ( vector, camera ) { +// Interpolation - THREE.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); - vector.project( camera ); +THREE.InterpolateDiscrete = 2300; +THREE.InterpolateLinear = 2301; +THREE.InterpolateSmooth = 2302; - }; +// Interpolant ending modes - this.unprojectVector = function ( vector, camera ) { +THREE.ZeroCurvatureEnding = 2400; +THREE.ZeroSlopeEnding = 2401; +THREE.WrapAroundEnding = 2402; - THREE.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); - vector.unproject( camera ); +// Triangle Draw modes - }; - - this.pickingRay = function ( vector, camera ) { - - THREE.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); - - }; - -}; - -THREE.CanvasRenderer = function () { - - THREE.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); - - this.domElement = document.createElement( 'canvas' ); - this.clear = function () {}; - this.render = function () {}; - this.setClearColor = function () {}; - this.setSize = function () {}; - -}; +THREE.TrianglesDrawMode = 0; +THREE.TriangleStripDrawMode = 1; +THREE.TriangleFanDrawMode = 2; // File:src/math/Color.js @@ -279,11 +351,11 @@ THREE.Color = function ( color ) { if ( arguments.length === 3 ) { - return this.setRGB( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ] ); + return this.fromArray( arguments ); } - return this.set( color ) + return this.set( color ); }; @@ -313,6 +385,14 @@ THREE.Color.prototype = { }, + setScalar: function ( scalar ) { + + this.r = scalar; + this.g = scalar; + this.b = scalar; + + }, + setHex: function ( hex ) { hex = Math.floor( hex ); @@ -335,33 +415,170 @@ THREE.Color.prototype = { }, - setHSL: function ( h, s, l ) { + setHSL: function () { - // h,s,l ranges are in 0.0 - 1.0 + function hue2rgb( p, q, t ) { - if ( s === 0 ) { + if ( t < 0 ) t += 1; + if ( t > 1 ) t -= 1; + if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; + if ( t < 1 / 2 ) return q; + if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); + return p; - this.r = this.g = this.b = l; + } - } else { + return function ( h, s, l ) { - var hue2rgb = function ( p, q, t ) { + // h,s,l ranges are in 0.0 - 1.0 + h = THREE.Math.euclideanModulo( h, 1 ); + s = THREE.Math.clamp( s, 0, 1 ); + l = THREE.Math.clamp( l, 0, 1 ); - if ( t < 0 ) t += 1; - if ( t > 1 ) t -= 1; - if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; - if ( t < 1 / 2 ) return q; - if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); - return p; + if ( s === 0 ) { - }; + this.r = this.g = this.b = l; - var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - var q = ( 2 * l ) - p; + } else { - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); + var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + var q = ( 2 * l ) - p; + + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); + + } + + return this; + + }; + + }(), + + setStyle: function ( style ) { + + function handleAlpha( string ) { + + if ( string === undefined ) return; + + if ( parseFloat( string ) < 1 ) { + + console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); + + } + + } + + + var m; + + if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { + + // rgb / hsl + + var color; + var name = m[ 1 ]; + var components = m[ 2 ]; + + switch ( name ) { + + case 'rgb': + case 'rgba': + + if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // rgb(255,0,0) rgba(255,0,0,0.5) + this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; + this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; + this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; + + handleAlpha( color[ 5 ] ); + + return this; + + } + + if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) + this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; + this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; + this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; + + handleAlpha( color[ 5 ] ); + + return this; + + } + + break; + + case 'hsl': + case 'hsla': + + if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) + var h = parseFloat( color[ 1 ] ) / 360; + var s = parseInt( color[ 2 ], 10 ) / 100; + var l = parseInt( color[ 3 ], 10 ) / 100; + + handleAlpha( color[ 5 ] ); + + return this.setHSL( h, s, l ); + + } + + break; + + } + + } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { + + // hex color + + var hex = m[ 1 ]; + var size = hex.length; + + if ( size === 3 ) { + + // #ff0 + this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; + this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; + this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; + + return this; + + } else if ( size === 6 ) { + + // #ff0000 + this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; + this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; + this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; + + return this; + + } + + } + + if ( style && style.length > 0 ) { + + // color keywords + var hex = THREE.ColorKeywords[ style ]; + + if ( hex !== undefined ) { + + // red + this.setHex( hex ); + + } else { + + // unknown color + console.warn( 'THREE.Color: Unknown color ' + style ); + + } } @@ -369,70 +586,9 @@ THREE.Color.prototype = { }, - setStyle: function ( style ) { - - // rgb(255,0,0) - - if ( /^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test( style ) ) { - - var color = /^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec( style ); - - this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; - this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; - this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; - - return this; - - } - - // rgb(100%,0%,0%) - - if ( /^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test( style ) ) { - - var color = /^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec( style ); - - this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; - this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; - this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; - - return this; - - } - - // #ff0000 - - if ( /^\#([0-9a-f]{6})$/i.test( style ) ) { - - var color = /^\#([0-9a-f]{6})$/i.exec( style ); - - this.setHex( parseInt( color[ 1 ], 16 ) ); - - return this; - - } - - // #f00 - - if ( /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test( style ) ) { - - var color = /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec( style ); - - this.setHex( parseInt( color[ 1 ] + color[ 1 ] + color[ 2 ] + color[ 2 ] + color[ 3 ] + color[ 3 ], 16 ) ); - - return this; - - } - - // red - - if ( /^(\w+)$/i.test( style ) ) { - - this.setHex( THREE.ColorKeywords[ style ] ); - - return this; - - } + clone: function () { + return new this.constructor( this.r, this.g, this.b ); }, @@ -635,11 +791,13 @@ THREE.Color.prototype = { }, - fromArray: function ( array ) { + fromArray: function ( array, offset ) { - this.r = array[ 0 ]; - this.g = array[ 1 ]; - this.b = array[ 2 ]; + if ( offset === undefined ) offset = 0; + + this.r = array[ offset ]; + this.g = array[ offset + 1 ]; + this.b = array[ offset + 2 ]; return this; @@ -655,11 +813,6 @@ THREE.Color.prototype = { array[ offset + 2 ] = this.b; return array; - }, - - clone: function () { - - return new THREE.Color().setRGB( this.r, this.g, this.b ); } @@ -696,7 +849,7 @@ THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Quaternion = function ( x, y, z, w ) { @@ -755,6 +908,12 @@ THREE.Quaternion.prototype = { }, + clone: function () { + + return new this.constructor( this._x, this._y, this._z, this._w ); + + }, + copy: function ( quaternion ) { this._x = quaternion.x; @@ -773,6 +932,7 @@ THREE.Quaternion.prototype = { if ( euler instanceof THREE.Euler === false ) { throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } // http://www.mathworks.com/matlabcentral/fileexchange/ @@ -786,42 +946,44 @@ THREE.Quaternion.prototype = { var s2 = Math.sin( euler._y / 2 ); var s3 = Math.sin( euler._z / 2 ); - if ( euler.order === 'XYZ' ) { + var order = euler.order; + + if ( order === 'XYZ' ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; - } else if ( euler.order === 'YXZ' ) { + } else if ( order === 'YXZ' ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; - } else if ( euler.order === 'ZXY' ) { + } else if ( order === 'ZXY' ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; - } else if ( euler.order === 'ZYX' ) { + } else if ( order === 'ZYX' ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; - } else if ( euler.order === 'YZX' ) { + } else if ( order === 'YZX' ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; - } else if ( euler.order === 'XZY' ) { + } else if ( order === 'XZY' ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; @@ -959,7 +1121,7 @@ THREE.Quaternion.prototype = { return this; - } + }; }(), @@ -1033,7 +1195,7 @@ THREE.Quaternion.prototype = { if ( p !== undefined ) { - THREE.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); return this.multiplyQuaternions( q, p ); } @@ -1060,13 +1222,6 @@ THREE.Quaternion.prototype = { }, - multiplyVector3: function ( vector ) { - - THREE.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); - - }, - slerp: function ( qb, t ) { if ( t === 0 ) return this; @@ -1104,7 +1259,6 @@ THREE.Quaternion.prototype = { } - var halfTheta = Math.acos( cosHalfTheta ); var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); if ( Math.abs( sinHalfTheta ) < 0.001 ) { @@ -1118,6 +1272,7 @@ THREE.Quaternion.prototype = { } + var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; @@ -1175,21 +1330,82 @@ THREE.Quaternion.prototype = { }, - onChangeCallback: function () {}, - - clone: function () { - - return new THREE.Quaternion( this._x, this._y, this._z, this._w ); - - } + onChangeCallback: function () {} }; -THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { +Object.assign( THREE.Quaternion, { - return qm.copy( qa ).slerp( qb, t ); + slerp: function( qa, qb, qm, t ) { -} + return qm.copy( qa ).slerp( qb, t ); + + }, + + slerpFlat: function( + dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { + + // fuzz-free, array-based Quaternion SLERP operation + + var x0 = src0[ srcOffset0 + 0 ], + y0 = src0[ srcOffset0 + 1 ], + z0 = src0[ srcOffset0 + 2 ], + w0 = src0[ srcOffset0 + 3 ], + + x1 = src1[ srcOffset1 + 0 ], + y1 = src1[ srcOffset1 + 1 ], + z1 = src1[ srcOffset1 + 2 ], + w1 = src1[ srcOffset1 + 3 ]; + + if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { + + var s = 1 - t, + + cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + + dir = ( cos >= 0 ? 1 : - 1 ), + sqrSin = 1 - cos * cos; + + // Skip the Slerp for tiny steps to avoid numeric problems: + if ( sqrSin > Number.EPSILON ) { + + var sin = Math.sqrt( sqrSin ), + len = Math.atan2( sin, cos * dir ); + + s = Math.sin( s * len ) / sin; + t = Math.sin( t * len ) / sin; + + } + + var tDir = t * dir; + + x0 = x0 * s + x1 * tDir; + y0 = y0 * s + y1 * tDir; + z0 = z0 * s + z1 * tDir; + w0 = w0 * s + w1 * tDir; + + // Normalize in case we just did a lerp: + if ( s === 1 - t ) { + + var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); + + x0 *= f; + y0 *= f; + z0 *= f; + w0 *= f; + + } + + } + + dst[ dstOffset ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; + + } + +} ); // File:src/math/Vector2.js @@ -1211,6 +1427,32 @@ THREE.Vector2.prototype = { constructor: THREE.Vector2, + get width() { + + return this.x; + + }, + + set width( value ) { + + this.x = value; + + }, + + get height() { + + return this.y; + + }, + + set height( value ) { + + this.y = value; + + }, + + // + set: function ( x, y ) { this.x = x; @@ -1220,6 +1462,15 @@ THREE.Vector2.prototype = { }, + setScalar: function ( scalar ) { + + this.x = scalar; + this.y = scalar; + + return this; + + }, + setX: function ( x ) { this.x = x; @@ -1260,6 +1511,12 @@ THREE.Vector2.prototype = { }, + clone: function () { + + return new this.constructor( this.x, this.y ); + + }, + copy: function ( v ) { this.x = v.x; @@ -1273,7 +1530,7 @@ THREE.Vector2.prototype = { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } @@ -1303,11 +1560,20 @@ THREE.Vector2.prototype = { }, + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + + return this; + + }, + sub: function ( v, w ) { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } @@ -1346,10 +1612,19 @@ THREE.Vector2.prototype = { }, - multiplyScalar: function ( s ) { + multiplyScalar: function ( scalar ) { - this.x *= s; - this.y *= s; + if ( isFinite( scalar ) ) { + + this.x *= scalar; + this.y *= scalar; + + } else { + + this.x = 0; + this.y = 0; + + } return this; @@ -1366,37 +1641,14 @@ THREE.Vector2.prototype = { divideScalar: function ( scalar ) { - if ( scalar !== 0 ) { - - var invScalar = 1 / scalar; - - this.x *= invScalar; - this.y *= invScalar; - - } else { - - this.x = 0; - this.y = 0; - - } - - return this; + return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { - if ( this.x > v.x ) { - - this.x = v.x; - - } - - if ( this.y > v.y ) { - - this.y = v.y; - - } + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); return this; @@ -1404,17 +1656,8 @@ THREE.Vector2.prototype = { max: function ( v ) { - if ( this.x < v.x ) { - - this.x = v.x; - - } - - if ( this.y < v.y ) { - - this.y = v.y; - - } + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); return this; @@ -1424,34 +1667,18 @@ THREE.Vector2.prototype = { // This function assumes min < max, if this assumption isn't true it will not operate correctly - if ( this.x < min.x ) { - - this.x = min.x; - - } else if ( this.x > max.x ) { - - this.x = max.x; - - } - - if ( this.y < min.y ) { - - this.y = min.y; - - } else if ( this.y > max.y ) { - - this.y = max.y; - - } + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; + }, - clampScalar: ( function () { + clampScalar: function () { var min, max; - return function ( minVal, maxVal ) { + return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { @@ -1467,7 +1694,17 @@ THREE.Vector2.prototype = { }; - } )(), + }(), + + clampLength: function ( min, max ) { + + var length = this.length(); + + this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); + + return this; + + }, floor: function () { @@ -1532,12 +1769,30 @@ THREE.Vector2.prototype = { }, + lengthManhattan: function() { + + return Math.abs( this.x ) + Math.abs( this.y ); + + }, + normalize: function () { return this.divideScalar( this.length() ); }, + angle: function () { + + // computes the angle in radians with respect to the positive x-axis + + var angle = Math.atan2( this.y, this.x ); + + if ( angle < 0 ) angle += 2 * Math.PI; + + return angle; + + }, + distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); @@ -1551,16 +1806,9 @@ THREE.Vector2.prototype = { }, - setLength: function ( l ) { + setLength: function ( length ) { - var oldLength = this.length(); - - if ( oldLength !== 0 && l !== oldLength ) { - - this.multiplyScalar( l / oldLength ); - } - - return this; + return this.multiplyScalar( length / this.length() ); }, @@ -1623,9 +1871,17 @@ THREE.Vector2.prototype = { }, - clone: function () { + rotateAround: function ( center, angle ) { - return new THREE.Vector2( this.x, this.y ); + var c = Math.cos( angle ), s = Math.sin( angle ); + + var x = this.x - center.x; + var y = this.y - center.y; + + this.x = x * c - y * s + center.x; + this.y = x * s + y * c + center.y; + + return this; } @@ -1664,6 +1920,16 @@ THREE.Vector3.prototype = { }, + setScalar: function ( scalar ) { + + this.x = scalar; + this.y = scalar; + this.z = scalar; + + return this; + + }, + setX: function ( x ) { this.x = x; @@ -1714,6 +1980,12 @@ THREE.Vector3.prototype = { }, + clone: function () { + + return new this.constructor( this.x, this.y, this.z ); + + }, + copy: function ( v ) { this.x = v.x; @@ -1728,7 +2000,7 @@ THREE.Vector3.prototype = { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } @@ -1761,11 +2033,21 @@ THREE.Vector3.prototype = { }, + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + + return this; + + }, + sub: function ( v, w ) { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } @@ -1777,7 +2059,7 @@ THREE.Vector3.prototype = { return this; }, - + subScalar: function ( s ) { this.x -= s; @@ -1802,7 +2084,7 @@ THREE.Vector3.prototype = { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); return this.multiplyVectors( v, w ); } @@ -1817,9 +2099,19 @@ THREE.Vector3.prototype = { multiplyScalar: function ( scalar ) { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; + if ( isFinite( scalar ) ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + + } return this; @@ -1839,11 +2131,11 @@ THREE.Vector3.prototype = { var quaternion; - return function ( euler ) { + return function applyEuler( euler ) { if ( euler instanceof THREE.Euler === false ) { - THREE.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); } @@ -1861,7 +2153,7 @@ THREE.Vector3.prototype = { var quaternion; - return function ( axis, angle ) { + return function applyAxisAngle( axis, angle ) { if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); @@ -1954,7 +2246,7 @@ THREE.Vector3.prototype = { var matrix; - return function ( camera ) { + return function project( camera ) { if ( matrix === undefined ) matrix = new THREE.Matrix4(); @@ -1969,7 +2261,7 @@ THREE.Vector3.prototype = { var matrix; - return function ( camera ) { + return function unproject( camera ) { if ( matrix === undefined ) matrix = new THREE.Matrix4(); @@ -2011,45 +2303,15 @@ THREE.Vector3.prototype = { divideScalar: function ( scalar ) { - if ( scalar !== 0 ) { - - var invScalar = 1 / scalar; - - this.x *= invScalar; - this.y *= invScalar; - this.z *= invScalar; - - } else { - - this.x = 0; - this.y = 0; - this.z = 0; - - } - - return this; + return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { - if ( this.x > v.x ) { - - this.x = v.x; - - } - - if ( this.y > v.y ) { - - this.y = v.y; - - } - - if ( this.z > v.z ) { - - this.z = v.z; - - } + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); return this; @@ -2057,23 +2319,9 @@ THREE.Vector3.prototype = { max: function ( v ) { - if ( this.x < v.x ) { - - this.x = v.x; - - } - - if ( this.y < v.y ) { - - this.y = v.y; - - } - - if ( this.z < v.z ) { - - this.z = v.z; - - } + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); return this; @@ -2083,45 +2331,19 @@ THREE.Vector3.prototype = { // This function assumes min < max, if this assumption isn't true it will not operate correctly - if ( this.x < min.x ) { - - this.x = min.x; - - } else if ( this.x > max.x ) { - - this.x = max.x; - - } - - if ( this.y < min.y ) { - - this.y = min.y; - - } else if ( this.y > max.y ) { - - this.y = max.y; - - } - - if ( this.z < min.z ) { - - this.z = min.z; - - } else if ( this.z > max.z ) { - - this.z = max.z; - - } + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; }, - clampScalar: ( function () { + clampScalar: function () { var min, max; - return function ( minVal, maxVal ) { + return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { @@ -2137,7 +2359,17 @@ THREE.Vector3.prototype = { }; - } )(), + }(), + + clampLength: function ( min, max ) { + + var length = this.length(); + + this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); + + return this; + + }, floor: function () { @@ -2219,16 +2451,9 @@ THREE.Vector3.prototype = { }, - setLength: function ( l ) { + setLength: function ( length ) { - var oldLength = this.length(); - - if ( oldLength !== 0 && l !== oldLength ) { - - this.multiplyScalar( l / oldLength ); - } - - return this; + return this.multiplyScalar( length / this.length() ); }, @@ -2254,7 +2479,7 @@ THREE.Vector3.prototype = { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); return this.crossVectors( v, w ); } @@ -2286,7 +2511,7 @@ THREE.Vector3.prototype = { var v1, dot; - return function ( vector ) { + return function projectOnVector( vector ) { if ( v1 === undefined ) v1 = new THREE.Vector3(); @@ -2304,7 +2529,7 @@ THREE.Vector3.prototype = { var v1; - return function ( planeNormal ) { + return function projectOnPlane( planeNormal ) { if ( v1 === undefined ) v1 = new THREE.Vector3(); @@ -2323,7 +2548,7 @@ THREE.Vector3.prototype = { var v1; - return function ( normal ) { + return function reflect( normal ) { if ( v1 === undefined ) v1 = new THREE.Vector3(); @@ -2335,7 +2560,7 @@ THREE.Vector3.prototype = { angleTo: function ( v ) { - var theta = this.dot( v ) / ( this.length() * v.length() ); + var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); // clamp, to handle numerical problems @@ -2359,41 +2584,6 @@ THREE.Vector3.prototype = { }, - setEulerFromRotationMatrix: function ( m, order ) { - - THREE.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - - }, - - setEulerFromQuaternion: function ( q, order ) { - - THREE.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - - }, - - getPositionFromMatrix: function ( m ) { - - THREE.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - - return this.setFromMatrixPosition( m ); - - }, - - getScaleFromMatrix: function ( m ) { - - THREE.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - - return this.setFromMatrixScale( m ); - }, - - getColumnFromMatrix: function ( index, matrix ) { - - THREE.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - - return this.setFromMatrixColumn( index, matrix ); - - }, - setFromMatrixPosition: function ( m ) { this.x = m.elements[ 12 ]; @@ -2406,8 +2596,8 @@ THREE.Vector3.prototype = { setFromMatrixScale: function ( m ) { - var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); - var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); this.x = sx; @@ -2415,10 +2605,11 @@ THREE.Vector3.prototype = { this.z = sz; return this; + }, setFromMatrixColumn: function ( index, matrix ) { - + var offset = index * 4; var me = matrix.elements; @@ -2474,12 +2665,6 @@ THREE.Vector3.prototype = { return this; - }, - - clone: function () { - - return new THREE.Vector3( this.x, this.y, this.z ); - } }; @@ -2518,6 +2703,17 @@ THREE.Vector4.prototype = { }, + setScalar: function ( scalar ) { + + this.x = scalar; + this.y = scalar; + this.z = scalar; + this.w = scalar; + + return this; + + }, + setX: function ( x ) { this.x = x; @@ -2578,6 +2774,12 @@ THREE.Vector4.prototype = { }, + clone: function () { + + return new this.constructor( this.x, this.y, this.z, this.w ); + + }, + copy: function ( v ) { this.x = v.x; @@ -2593,7 +2795,7 @@ THREE.Vector4.prototype = { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } @@ -2629,11 +2831,22 @@ THREE.Vector4.prototype = { }, + addScaledVector: function ( v, s ) { + + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; + + return this; + + }, + sub: function ( v, w ) { if ( w !== undefined ) { - THREE.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } @@ -2671,10 +2884,21 @@ THREE.Vector4.prototype = { multiplyScalar: function ( scalar ) { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; + if ( isFinite( scalar ) ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + + } return this; @@ -2700,25 +2924,7 @@ THREE.Vector4.prototype = { divideScalar: function ( scalar ) { - if ( scalar !== 0 ) { - - var invScalar = 1 / scalar; - - this.x *= invScalar; - this.y *= invScalar; - this.z *= invScalar; - this.w *= invScalar; - - } else { - - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 1; - - } - - return this; + return this.multiplyScalar( 1 / scalar ); }, @@ -2798,7 +3004,9 @@ THREE.Vector4.prototype = { var xz = ( m13 + m31 ) / 4; var yz = ( m23 + m32 ) / 4; - if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term + if ( ( xx > yy ) && ( xx > zz ) ) { + + // m11 is the largest diagonal term if ( xx < epsilon ) { @@ -2814,7 +3022,9 @@ THREE.Vector4.prototype = { } - } else if ( yy > zz ) { // m22 is the largest diagonal term + } else if ( yy > zz ) { + + // m22 is the largest diagonal term if ( yy < epsilon ) { @@ -2830,7 +3040,9 @@ THREE.Vector4.prototype = { } - } else { // m33 is the largest diagonal term so base result on this + } else { + + // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { @@ -2876,29 +3088,10 @@ THREE.Vector4.prototype = { min: function ( v ) { - if ( this.x > v.x ) { - - this.x = v.x; - - } - - if ( this.y > v.y ) { - - this.y = v.y; - - } - - if ( this.z > v.z ) { - - this.z = v.z; - - } - - if ( this.w > v.w ) { - - this.w = v.w; - - } + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + this.w = Math.min( this.w, v.w ); return this; @@ -2906,29 +3099,10 @@ THREE.Vector4.prototype = { max: function ( v ) { - if ( this.x < v.x ) { - - this.x = v.x; - - } - - if ( this.y < v.y ) { - - this.y = v.y; - - } - - if ( this.z < v.z ) { - - this.z = v.z; - - } - - if ( this.w < v.w ) { - - this.w = v.w; - - } + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + this.w = Math.max( this.w, v.w ); return this; @@ -2938,55 +3112,20 @@ THREE.Vector4.prototype = { // This function assumes min < max, if this assumption isn't true it will not operate correctly - if ( this.x < min.x ) { - - this.x = min.x; - - } else if ( this.x > max.x ) { - - this.x = max.x; - - } - - if ( this.y < min.y ) { - - this.y = min.y; - - } else if ( this.y > max.y ) { - - this.y = max.y; - - } - - if ( this.z < min.z ) { - - this.z = min.z; - - } else if ( this.z > max.z ) { - - this.z = max.z; - - } - - if ( this.w < min.w ) { - - this.w = min.w; - - } else if ( this.w > max.w ) { - - this.w = max.w; - - } + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); + this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; }, - clampScalar: ( function () { + clampScalar: function () { var min, max; - return function ( minVal, maxVal ) { + return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { @@ -3002,9 +3141,9 @@ THREE.Vector4.prototype = { }; - } )(), + }(), - floor: function () { + floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); @@ -3013,9 +3152,9 @@ THREE.Vector4.prototype = { return this; - }, + }, - ceil: function () { + ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); @@ -3024,9 +3163,9 @@ THREE.Vector4.prototype = { return this; - }, + }, - round: function () { + round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); @@ -3035,9 +3174,9 @@ THREE.Vector4.prototype = { return this; - }, + }, - roundToZero: function () { + roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); @@ -3046,7 +3185,7 @@ THREE.Vector4.prototype = { return this; - }, + }, negate: function () { @@ -3089,17 +3228,9 @@ THREE.Vector4.prototype = { }, - setLength: function ( l ) { + setLength: function ( length ) { - var oldLength = this.length(); - - if ( oldLength !== 0 && l !== oldLength ) { - - this.multiplyScalar( l / oldLength ); - - } - - return this; + return this.multiplyScalar( length / this.length() ); }, @@ -3168,12 +3299,6 @@ THREE.Vector4.prototype = { return this; - }, - - clone: function () { - - return new THREE.Vector4( this.x, this.y, this.z, this.w ); - } }; @@ -3249,6 +3374,12 @@ THREE.Euler.prototype = { }, + clone: function () { + + return new this.constructor( this._x, this._y, this._z, this._order ); + + }, + copy: function ( euler ) { this._x = euler._x; @@ -3373,7 +3504,7 @@ THREE.Euler.prototype = { } else { - THREE.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) } @@ -3452,6 +3583,7 @@ THREE.Euler.prototype = { array[ offset + 3 ] = this._order; return array; + }, toVector3: function ( optionalResult ) { @@ -3476,20 +3608,14 @@ THREE.Euler.prototype = { }, - onChangeCallback: function () {}, - - clone: function () { - - return new THREE.Euler( this._x, this._y, this._z, this._order ); - - } + onChangeCallback: function () {} }; // File:src/math/Line3.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Line3 = function ( start, end ) { @@ -3512,6 +3638,12 @@ THREE.Line3.prototype = { }, + clone: function () { + + return new this.constructor().copy( this ); + + }, + copy: function ( line ) { this.start.copy( line.start ); @@ -3605,12 +3737,6 @@ THREE.Line3.prototype = { return line.start.equals( this.start ) && line.end.equals( this.end ); - }, - - clone: function () { - - return new THREE.Line3().copy( this ); - } }; @@ -3618,12 +3744,12 @@ THREE.Line3.prototype = { // File:src/math/Box2.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Box2 = function ( min, max ) { - this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity ); + this.min = ( min !== undefined ) ? min : new THREE.Vector2( + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity ); }; @@ -3647,7 +3773,7 @@ THREE.Box2.prototype = { for ( var i = 0, il = points.length; i < il; i ++ ) { - this.expandByPoint( points[ i ] ) + this.expandByPoint( points[ i ] ); } @@ -3671,6 +3797,12 @@ THREE.Box2.prototype = { }(), + clone: function () { + + return new this.constructor().copy( this ); + + }, + copy: function ( box ) { this.min.copy( box.min ); @@ -3682,14 +3814,14 @@ THREE.Box2.prototype = { makeEmpty: function () { - this.min.x = this.min.y = Infinity; + this.min.x = this.min.y = + Infinity; this.max.x = this.max.y = - Infinity; return this; }, - empty: function () { + isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes @@ -3717,6 +3849,7 @@ THREE.Box2.prototype = { this.max.max( point ); return this; + }, expandByVector: function ( vector ) { @@ -3725,6 +3858,7 @@ THREE.Box2.prototype = { this.max.add( vector ); return this; + }, expandByScalar: function ( scalar ) { @@ -3733,6 +3867,7 @@ THREE.Box2.prototype = { this.max.addScalar( scalar ); return this; + }, containsPoint: function ( point ) { @@ -3775,7 +3910,7 @@ THREE.Box2.prototype = { }, - isIntersectionBox: function ( box ) { + intersectsBox: function ( box ) { // using 6 splitting planes to rule out intersections. @@ -3841,12 +3976,6 @@ THREE.Box2.prototype = { return box.min.equals( this.min ) && box.max.equals( this.max ); - }, - - clone: function () { - - return new THREE.Box2().copy( this ); - } }; @@ -3854,13 +3983,13 @@ THREE.Box2.prototype = { // File:src/math/Box3.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ THREE.Box3 = function ( min, max ) { - this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity ); + this.min = ( min !== undefined ) ? min : new THREE.Vector3( + Infinity, + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity ); }; @@ -3878,13 +4007,46 @@ THREE.Box3.prototype = { }, + setFromArray: function ( array ) { + + this.makeEmpty(); + + var minX = + Infinity; + var minY = + Infinity; + var minZ = + Infinity; + + var maxX = - Infinity; + var maxY = - Infinity; + var maxZ = - Infinity; + + for ( var i = 0, il = array.length; i < il; i += 3 ) { + + var x = array[ i ]; + var y = array[ i + 1 ]; + var z = array[ i + 2 ]; + + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( z < minZ ) minZ = z; + + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + if ( z > maxZ ) maxZ = z; + + } + + this.min.set( minX, minY, minZ ); + this.max.set( maxX, maxY, maxZ ); + + }, + setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { - this.expandByPoint( points[ i ] ) + this.expandByPoint( points[ i ] ); } @@ -3912,54 +4074,36 @@ THREE.Box3.prototype = { setFromObject: function () { // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and childrens', world transforms + // accounting for both the object's, and children's, world transforms - var v1 = new THREE.Vector3(); + var box; return function ( object ) { + if ( box === undefined ) box = new THREE.Box3(); + var scope = this; - object.updateMatrixWorld( true ); - this.makeEmpty(); + object.updateMatrixWorld( true ); + object.traverse( function ( node ) { var geometry = node.geometry; if ( geometry !== undefined ) { - if ( geometry instanceof THREE.Geometry ) { + if ( geometry.boundingBox === null ) { - var vertices = geometry.vertices; - - for ( var i = 0, il = vertices.length; i < il; i ++ ) { - - v1.copy( vertices[ i ] ); - - v1.applyMatrix4( node.matrixWorld ); - - scope.expandByPoint( v1 ); - - } - - } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) { - - var positions = geometry.attributes[ 'position' ].array; - - for ( var i = 0, il = positions.length; i < il; i += 3 ) { - - v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - - v1.applyMatrix4( node.matrixWorld ); - - scope.expandByPoint( v1 ); - - } + geometry.computeBoundingBox(); } + box.copy( geometry.boundingBox ); + box.applyMatrix4( node.matrixWorld ); + scope.union( box ); + } } ); @@ -3970,6 +4114,12 @@ THREE.Box3.prototype = { }(), + clone: function () { + + return new this.constructor().copy( this ); + + }, + copy: function ( box ) { this.min.copy( box.min ); @@ -3981,14 +4131,14 @@ THREE.Box3.prototype = { makeEmpty: function () { - this.min.x = this.min.y = this.min.z = Infinity; + this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; }, - empty: function () { + isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes @@ -4040,8 +4190,8 @@ THREE.Box3.prototype = { containsPoint: function ( point ) { if ( point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ) { + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ) { return false; @@ -4080,13 +4230,13 @@ THREE.Box3.prototype = { }, - isIntersectionBox: function ( box ) { + intersectsBox: function ( box ) { // using 6 splitting planes to rule out intersections. if ( box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ) { + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ) { return false; @@ -4096,6 +4246,71 @@ THREE.Box3.prototype = { }, + intersectsSphere: ( function () { + + var closestPoint; + + return function intersectsSphere( sphere ) { + + if ( closestPoint === undefined ) closestPoint = new THREE.Vector3(); + + // Find the point on the AABB closest to the sphere center. + this.clampPoint( sphere.center, closestPoint ); + + // If that point is inside the sphere, the AABB and sphere intersect. + return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); + + }; + + } )(), + + intersectsPlane: function ( plane ) { + + // We compute the minimum and maximum dot product values. If those values + // are on the same side (back or front) of the plane, then there is no intersection. + + var min, max; + + if ( plane.normal.x > 0 ) { + + min = plane.normal.x * this.min.x; + max = plane.normal.x * this.max.x; + + } else { + + min = plane.normal.x * this.max.x; + max = plane.normal.x * this.min.x; + + } + + if ( plane.normal.y > 0 ) { + + min += plane.normal.y * this.min.y; + max += plane.normal.y * this.max.y; + + } else { + + min += plane.normal.y * this.max.y; + max += plane.normal.y * this.min.y; + + } + + if ( plane.normal.z > 0 ) { + + min += plane.normal.z * this.min.z; + max += plane.normal.z * this.max.z; + + } else { + + min += plane.normal.z * this.max.z; + max += plane.normal.z * this.min.z; + + } + + return ( min <= plane.constant && max >= plane.constant ); + + }, + clampPoint: function ( point, optionalTarget ) { var result = optionalTarget || new THREE.Vector3(); @@ -4174,7 +4389,7 @@ THREE.Box3.prototype = { points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 this.makeEmpty(); this.setFromPoints( points ); @@ -4198,12 +4413,6 @@ THREE.Box3.prototype = { return box.min.equals( this.min ) && box.max.equals( this.max ); - }, - - clone: function () { - - return new THREE.Box3().copy( this ); - } }; @@ -4213,7 +4422,7 @@ THREE.Box3.prototype = { /** * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Matrix3 = function () { @@ -4228,7 +4437,7 @@ THREE.Matrix3 = function () { if ( arguments.length > 0 ) { - THREE.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); + console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); } @@ -4264,6 +4473,12 @@ THREE.Matrix3.prototype = { }, + clone: function () { + + return new this.constructor().fromArray( this.elements ); + + }, + copy: function ( m ) { var me = m.elements; @@ -4280,40 +4495,21 @@ THREE.Matrix3.prototype = { }, - multiplyVector3: function ( vector ) { - - THREE.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); - - }, - - multiplyVector3Array: function ( a ) { - - THREE.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); - return this.applyToVector3Array( a ); - - }, - applyToVector3Array: function () { - var v1 = new THREE.Vector3(); + var v1; return function ( array, offset, length ) { + if ( v1 === undefined ) v1 = new THREE.Vector3(); if ( offset === undefined ) offset = 0; if ( length === undefined ) length = array.length; for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { - v1.x = array[ j ]; - v1.y = array[ j + 1 ]; - v1.z = array[ j + 2 ]; - + v1.fromArray( array, j ); v1.applyMatrix3( this ); - - array[ j ] = v1.x; - array[ j + 1 ] = v1.y; - array[ j + 2 ] = v1.z; + v1.toArray( array, j ); } @@ -4323,6 +4519,34 @@ THREE.Matrix3.prototype = { }(), + applyToBuffer: function () { + + var v1; + + return function applyToBuffer( buffer, offset, length ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = buffer.length / buffer.itemSize; + + for ( var i = 0, j = offset; i < length; i ++, j ++ ) { + + v1.x = buffer.getX( j ); + v1.y = buffer.getY( j ); + v1.z = buffer.getZ( j ); + + v1.applyMatrix3( this ); + + buffer.setXYZ( v1.x, v1.y, v1.z ); + + } + + return buffer; + + }; + + }(), + multiplyScalar: function ( s ) { var te = this.elements; @@ -4347,7 +4571,7 @@ THREE.Matrix3.prototype = { }, - getInverse: function ( matrix, throwOnInvertible ) { + getInverse: function ( matrix, throwOnDegenerate ) { // input: THREE.Matrix4 // ( based on http://code.google.com/p/webgl-mjs/ ) @@ -4371,15 +4595,15 @@ THREE.Matrix3.prototype = { if ( det === 0 ) { - var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0"; + var msg = "THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0"; - if ( throwOnInvertible || false ) { + if ( throwOnDegenerate || false ) { throw new Error( msg ); } else { - THREE.warn( msg ); + console.warn( msg ); } @@ -4411,7 +4635,7 @@ THREE.Matrix3.prototype = { var te = this.elements; - array[ offset ] = te[ 0 ]; + array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; @@ -4473,12 +4697,6 @@ THREE.Matrix3.prototype = { te[ 6 ], te[ 7 ], te[ 8 ] ]; - }, - - clone: function () { - - return new THREE.Matrix3().fromArray( this.elements ); - } }; @@ -4494,7 +4712,7 @@ THREE.Matrix3.prototype = { * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author timknip / http://www.floorplanner.com/ - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ @@ -4511,7 +4729,7 @@ THREE.Matrix4 = function () { if ( arguments.length > 0 ) { - THREE.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); } @@ -4549,6 +4767,12 @@ THREE.Matrix4.prototype = { }, + clone: function () { + + return new THREE.Matrix4().fromArray( this.elements ); + + }, + copy: function ( m ) { this.elements.set( m.elements ); @@ -4557,13 +4781,6 @@ THREE.Matrix4.prototype = { }, - extractPosition: function ( m ) { - - THREE.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); - - }, - copyPosition: function ( m ) { var te = this.elements; @@ -4578,17 +4795,17 @@ THREE.Matrix4.prototype = { }, extractBasis: function ( xAxis, yAxis, zAxis ) { - + var te = this.elements; - + xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] ); yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] ); zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] ); - + return this; - + }, - + makeBasis: function ( xAxis, yAxis, zAxis ) { this.set( @@ -4604,10 +4821,12 @@ THREE.Matrix4.prototype = { extractRotation: function () { - var v1 = new THREE.Vector3(); + var v1; return function ( m ) { + if ( v1 === undefined ) v1 = new THREE.Vector3(); + var te = this.elements; var me = m.elements; @@ -4637,7 +4856,7 @@ THREE.Matrix4.prototype = { if ( euler instanceof THREE.Euler === false ) { - THREE.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); } @@ -4761,14 +4980,6 @@ THREE.Matrix4.prototype = { }, - setRotationFromQuaternion: function ( q ) { - - THREE.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - - return this.makeRotationFromQuaternion( q ); - - }, - makeRotationFromQuaternion: function ( q ) { var te = this.elements; @@ -4808,17 +5019,19 @@ THREE.Matrix4.prototype = { lookAt: function () { - var x = new THREE.Vector3(); - var y = new THREE.Vector3(); - var z = new THREE.Vector3(); + var x, y, z; return function ( eye, target, up ) { + if ( x === undefined ) x = new THREE.Vector3(); + if ( y === undefined ) y = new THREE.Vector3(); + if ( z === undefined ) z = new THREE.Vector3(); + var te = this.elements; z.subVectors( eye, target ).normalize(); - if ( z.length() === 0 ) { + if ( z.lengthSq() === 0 ) { z.z = 1; @@ -4826,7 +5039,7 @@ THREE.Matrix4.prototype = { x.crossVectors( up, z ).normalize(); - if ( x.length() === 0 ) { + if ( x.lengthSq() === 0 ) { z.x += 0.0001; x.crossVectors( up, z ).normalize(); @@ -4850,7 +5063,7 @@ THREE.Matrix4.prototype = { if ( n !== undefined ) { - THREE.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); return this.multiplyMatrices( m, n ); } @@ -4927,47 +5140,21 @@ THREE.Matrix4.prototype = { }, - multiplyVector3: function ( vector ) { - - THREE.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); - return vector.applyProjection( this ); - - }, - - multiplyVector4: function ( vector ) { - - THREE.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - - multiplyVector3Array: function ( a ) { - - THREE.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); - return this.applyToVector3Array( a ); - - }, - applyToVector3Array: function () { - var v1 = new THREE.Vector3(); + var v1; return function ( array, offset, length ) { + if ( v1 === undefined ) v1 = new THREE.Vector3(); if ( offset === undefined ) offset = 0; if ( length === undefined ) length = array.length; for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { - v1.x = array[ j ]; - v1.y = array[ j + 1 ]; - v1.z = array[ j + 2 ]; - + v1.fromArray( array, j ); v1.applyMatrix4( this ); - - array[ j ] = v1.x; - array[ j + 1 ] = v1.y; - array[ j + 2 ] = v1.z; + v1.toArray( array, j ); } @@ -4977,20 +5164,33 @@ THREE.Matrix4.prototype = { }(), - rotateAxis: function ( v ) { + applyToBuffer: function () { - THREE.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + var v1; - v.transformDirection( this ); + return function applyToBuffer( buffer, offset, length ) { - }, + if ( v1 === undefined ) v1 = new THREE.Vector3(); + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = buffer.length / buffer.itemSize; - crossVector: function ( vector ) { + for ( var i = 0, j = offset; i < length; i ++, j ++ ) { - THREE.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + v1.x = buffer.getX( j ); + v1.y = buffer.getY( j ); + v1.z = buffer.getZ( j ); - }, + v1.applyMatrix4( this ); + + buffer.setXYZ( v1.x, v1.y, v1.z ); + + } + + return buffer; + + }; + + }(), determinant: function () { @@ -5063,7 +5263,7 @@ THREE.Matrix4.prototype = { var te = this.elements; - array[ offset ] = te[ 0 ]; + array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; @@ -5089,11 +5289,12 @@ THREE.Matrix4.prototype = { getPosition: function () { - var v1 = new THREE.Vector3(); + var v1; return function () { - THREE.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + if ( v1 === undefined ) v1 = new THREE.Vector3(); + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); var te = this.elements; return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] ); @@ -5144,7 +5345,7 @@ THREE.Matrix4.prototype = { var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ]; - if ( det == 0 ) { + if ( det === 0 ) { var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0"; @@ -5154,13 +5355,14 @@ THREE.Matrix4.prototype = { } else { - THREE.warn( msg ); + console.warn( msg ); } this.identity(); return this; + } this.multiplyScalar( 1 / det ); @@ -5169,36 +5371,6 @@ THREE.Matrix4.prototype = { }, - translate: function ( v ) { - - THREE.error( 'THREE.Matrix4: .translate() has been removed.' ); - - }, - - rotateX: function ( angle ) { - - THREE.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - - }, - - rotateY: function ( angle ) { - - THREE.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - - }, - - rotateZ: function ( angle ) { - - THREE.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - - }, - - rotateByAxis: function ( axis, angle ) { - - THREE.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - - }, - scale: function ( v ) { var te = this.elements; @@ -5221,7 +5393,7 @@ THREE.Matrix4.prototype = { var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - return Math.sqrt( Math.max( scaleXSq, Math.max( scaleYSq, scaleZSq ) ) ); + return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); }, @@ -5341,11 +5513,13 @@ THREE.Matrix4.prototype = { decompose: function () { - var vector = new THREE.Vector3(); - var matrix = new THREE.Matrix4(); + var vector, matrix; return function ( position, quaternion, scale ) { + if ( vector === undefined ) vector = new THREE.Vector3(); + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + var te = this.elements; var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); @@ -5355,7 +5529,9 @@ THREE.Matrix4.prototype = { // if determine is negative, we need to invert one scale var det = this.determinant(); if ( det < 0 ) { + sx = - sx; + } position.x = te[ 12 ]; @@ -5445,6 +5621,21 @@ THREE.Matrix4.prototype = { }, + equals: function ( matrix ) { + + var te = this.elements; + var me = matrix.elements; + + for ( var i = 0; i < 16; i ++ ) { + + if ( te[ i ] !== me[ i ] ) return false; + + } + + return true; + + }, + fromArray: function ( array ) { this.elements.set( array ); @@ -5464,12 +5655,6 @@ THREE.Matrix4.prototype = { te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ] ]; - }, - - clone: function () { - - return new THREE.Matrix4().fromArray( this.elements ); - } }; @@ -5477,7 +5662,7 @@ THREE.Matrix4.prototype = { // File:src/math/Ray.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Ray = function ( origin, direction ) { @@ -5500,6 +5685,12 @@ THREE.Ray.prototype = { }, + clone: function () { + + return new this.constructor().copy( this ); + + }, + copy: function ( ray ) { this.origin.copy( ray.origin ); @@ -5517,6 +5708,12 @@ THREE.Ray.prototype = { }, + lookAt: function ( v ) { + + this.direction.copy( v ).sub( this.origin ).normalize(); + + }, + recast: function () { var v1 = new THREE.Vector3(); @@ -5547,7 +5744,13 @@ THREE.Ray.prototype = { }, - distanceToPoint: function () { + distanceToPoint: function ( point ) { + + return Math.sqrt( this.distanceSqToPoint( point ) ); + + }, + + distanceSqToPoint: function () { var v1 = new THREE.Vector3(); @@ -5699,18 +5902,13 @@ THREE.Ray.prototype = { intersectSphere: function () { - // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/ - var v1 = new THREE.Vector3(); return function ( sphere, optionalTarget ) { v1.subVectors( sphere.center, this.origin ); - var tca = v1.dot( this.direction ); - var d2 = v1.dot( v1 ) - tca * tca; - var radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; @@ -5731,46 +5929,27 @@ THREE.Ray.prototype = { // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, optionalTarget ); - // else t0 is in front of the ray, so return the first collision point scaled by t0 + // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, optionalTarget ); } }(), - isIntersectionPlane: function ( plane ) { + intersectsSphere: function ( sphere ) { - // check if the ray lies on the plane first - - var distToPoint = plane.distanceToPoint( this.origin ); - - if ( distToPoint === 0 ) { - - return true; - - } - - var denominator = plane.normal.dot( this.direction ); - - if ( denominator * distToPoint < 0 ) { - - return true; - - } - - // ray origin is behind the plane (and is pointing behind it) - - return false; + return this.distanceToPoint( sphere.center ) <= sphere.radius; }, distanceToPlane: function ( plane ) { var denominator = plane.normal.dot( this.direction ); - if ( denominator == 0 ) { + + if ( denominator === 0 ) { // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) == 0 ) { + if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; @@ -5797,29 +5976,44 @@ THREE.Ray.prototype = { if ( t === null ) { return null; + } return this.at( t, optionalTarget ); }, - isIntersectionBox: function () { - var v = new THREE.Vector3(); - return function ( box ) { + intersectsPlane: function ( plane ) { - return this.intersectBox( box, v ) !== null; + // check if the ray lies on the plane first - }; + var distToPoint = plane.distanceToPoint( this.origin ); - }(), + if ( distToPoint === 0 ) { + + return true; + + } + + var denominator = plane.normal.dot( this.direction ); + + if ( denominator * distToPoint < 0 ) { + + return true; + + } + + // ray origin is behind the plane (and is pointing behind it) + + return false; + + }, intersectBox: function ( box, optionalTarget ) { - // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ - - var tmin,tmax,tymin,tymax,tzmin,tzmax; + var tmin, tmax, tymin, tymax, tzmin, tzmax; var invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, @@ -5836,6 +6030,7 @@ THREE.Ray.prototype = { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; + } if ( invdiry >= 0 ) { @@ -5847,6 +6042,7 @@ THREE.Ray.prototype = { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; + } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; @@ -5867,6 +6063,7 @@ THREE.Ray.prototype = { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; + } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; @@ -5883,6 +6080,18 @@ THREE.Ray.prototype = { }, + intersectsBox: ( function () { + + var v = new THREE.Vector3(); + + return function ( box ) { + + return this.intersectBox( box, v ) !== null; + + }; + + } )(), + intersectTriangle: function () { // Compute the offset origin, edges, and normal. @@ -5974,18 +6183,13 @@ THREE.Ray.prototype = { this.direction.normalize(); return this; + }, equals: function ( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - }, - - clone: function () { - - return new THREE.Ray().copy( this ); - } }; @@ -5993,7 +6197,7 @@ THREE.Ray.prototype = { // File:src/math/Sphere.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ @@ -6014,6 +6218,7 @@ THREE.Sphere.prototype = { this.radius = radius; return this; + }, setFromPoints: function () { @@ -6050,6 +6255,12 @@ THREE.Sphere.prototype = { }(), + clone: function () { + + return new this.constructor().copy( this ); + + }, + copy: function ( sphere ) { this.center.copy( sphere.center ); @@ -6085,11 +6296,32 @@ THREE.Sphere.prototype = { }, + intersectsBox: function ( box ) { + + return box.intersectsSphere( this ); + + }, + + intersectsPlane: function ( plane ) { + + // We use the following equation to compute the signed distance from + // the center of the sphere to the plane. + // + // distance = q * n - d + // + // If this distance is greater than the radius of the sphere, + // then there is no intersection. + + return Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius; + + }, + clampPoint: function ( point, optionalTarget ) { var deltaLengthSq = this.center.distanceToSquared( point ); var result = optionalTarget || new THREE.Vector3(); + result.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { @@ -6135,12 +6367,6 @@ THREE.Sphere.prototype = { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - }, - - clone: function () { - - return new THREE.Sphere().copy( this ); - } }; @@ -6150,7 +6376,7 @@ THREE.Sphere.prototype = { /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) { @@ -6187,6 +6413,12 @@ THREE.Frustum.prototype = { }, + clone: function () { + + return new this.constructor().copy( this ); + + }, + copy: function ( frustum ) { var planes = this.planes; @@ -6292,9 +6524,11 @@ THREE.Frustum.prototype = { return false; } + } return true; + }; }(), @@ -6316,12 +6550,6 @@ THREE.Frustum.prototype = { return true; - }, - - clone: function () { - - return new THREE.Frustum().copy( this ); - } }; @@ -6329,7 +6557,7 @@ THREE.Frustum.prototype = { // File:src/math/Plane.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Plane = function ( normal, constant ) { @@ -6389,6 +6617,11 @@ THREE.Plane.prototype = { }(), + clone: function () { + + return new this.constructor().copy( this ); + + }, copy: function ( plane ) { @@ -6447,17 +6680,6 @@ THREE.Plane.prototype = { }, - isIntersectionLine: function ( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - var startSign = this.distanceToPoint( line.start ); - var endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - }, - intersectLine: function () { var v1 = new THREE.Vector3(); @@ -6470,10 +6692,10 @@ THREE.Plane.prototype = { var denominator = this.normal.dot( direction ); - if ( denominator == 0 ) { + if ( denominator === 0 ) { // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) == 0 ) { + if ( this.distanceToPoint( line.start ) === 0 ) { return result.copy( line.start ); @@ -6498,6 +6720,28 @@ THREE.Plane.prototype = { }(), + intersectsLine: function ( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + var startSign = this.distanceToPoint( line.start ); + var endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + }, + + intersectsBox: function ( box ) { + + return box.intersectsPlane( this ); + + }, + + intersectsSphere: function ( sphere ) { + + return sphere.intersectsPlane( this ); + + }, coplanarPoint: function ( optionalTarget ) { @@ -6540,13 +6784,7 @@ THREE.Plane.prototype = { equals: function ( plane ) { - return plane.normal.equals( this.normal ) && ( plane.constant == this.constant ); - - }, - - clone: function () { - - return new THREE.Plane().copy( this ); + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } @@ -6573,11 +6811,11 @@ THREE.Math = { for ( var i = 0; i < 36; i ++ ) { - if ( i == 8 || i == 13 || i == 18 || i == 23 ) { + if ( i === 8 || i === 13 || i === 18 || i === 23 ) { uuid[ i ] = '-'; - } else if ( i == 14 ) { + } else if ( i === 14 ) { uuid[ i ] = '4'; @@ -6586,9 +6824,10 @@ THREE.Math = { if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; r = rnd & 0xf; rnd = rnd >> 4; - uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ]; } + } return uuid.join( '' ); @@ -6597,19 +6836,18 @@ THREE.Math = { }(), - // Clamp value to range + clamp: function ( value, min, max ) { - clamp: function ( x, a, b ) { - - return ( x < a ) ? a : ( ( x > b ) ? b : x ); + return Math.max( min, Math.min( max, value ) ); }, - // Clamp value to range with 16 bits of randomness - // (standard Math.random() creates repetitive patterns when applied over larger space) - random16: function () { - return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + console.warn( 'THREE.Math.random16() has been deprecated. Use Math.random() instead.' ); + return Math.random(); }, @@ -6658,7 +6894,7 @@ THREE.Math = { randInt: function ( low, high ) { - return Math.floor( this.randFloat( low, high ) ); + return low + Math.floor( Math.random() * ( high - low + 1 ) ); }, @@ -6708,6 +6944,12 @@ THREE.Math = { }, + nearestPowerOfTwo: function ( value ) { + + return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); + + }, + nextPowerOfTwo: function ( value ) { value --; @@ -6832,7 +7074,7 @@ THREE.Spline = function ( points ) { point = ( this.points.length - 1 ) * index; intPoint = Math.floor( point ); - if ( intPoint != oldIntPoint ) { + if ( intPoint !== oldIntPoint ) { chunkLengths[ intPoint ] = totalLength; oldIntPoint = intPoint; @@ -6899,14 +7141,14 @@ THREE.Spline = function ( points ) { return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; - }; + } }; // File:src/math/Triangle.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ @@ -6943,7 +7185,7 @@ THREE.Triangle.normal = function () { }(); -// static/instance method to calculate barycoordinates +// static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html THREE.Triangle.barycoordFromPoint = function () { @@ -6967,18 +7209,20 @@ THREE.Triangle.barycoordFromPoint = function () { var result = optionalTarget || new THREE.Vector3(); - // colinear or singular triangle - if ( denom == 0 ) { + // collinear or singular triangle + if ( denom === 0 ) { + // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return result.set( - 2, - 1, - 1 ); + } var invDenom = 1 / denom; var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - // barycoordinates must always sum to 1 + // barycentric coordinates must always sum to 1 return result.set( 1 - u - v, v, u ); }; @@ -7023,6 +7267,12 @@ THREE.Triangle.prototype = { }, + clone: function () { + + return new this.constructor().copy( this ); + + }, + copy: function ( triangle ) { this.a.copy( triangle.a ); @@ -7086,16 +7336,545 @@ THREE.Triangle.prototype = { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + } + +}; + +// File:src/math/Interpolant.js + +/** + * Abstract base class of interpolants over parametric samples. + * + * The parameter domain is one dimensional, typically the time or a path + * along a curve defined by the data. + * + * The sample values can have any dimensionality and derived classes may + * apply special interpretations to the data. + * + * This class provides the interval seek in a Template Method, deferring + * the actual interpolation to derived classes. + * + * Time complexity is O(1) for linear access crossing at most two points + * and O(log N) for random access, where N is the number of positions. + * + * References: + * + * http://www.oodesign.com/template-method-pattern.html + * + * @author tschw + */ + +THREE.Interpolant = function( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; + + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor( sampleSize ); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; + +}; + +THREE.Interpolant.prototype = { + + constructor: THREE.Interpolant, + + evaluate: function( t ) { + + var pp = this.parameterPositions, + i1 = this._cachedIndex, + + t1 = pp[ i1 ], + t0 = pp[ i1 - 1 ]; + + validate_interval: { + + seek: { + + var right; + + linear_scan: { +//- See http://jsperf.com/comparison-to-undefined/3 +//- slower code: +//- +//- if ( t >= t1 || t1 === undefined ) { + forward_scan: if ( ! ( t < t1 ) ) { + + for ( var giveUpAt = i1 + 2; ;) { + + if ( t1 === undefined ) { + + if ( t < t0 ) break forward_scan; + + // after end + + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t, t0 ); + + } + + if ( i1 === giveUpAt ) break; // this loop + + t0 = t1; + t1 = pp[ ++ i1 ]; + + if ( t < t1 ) { + + // we have arrived at the sought interval + break seek; + + } + + } + + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; + + } + +//- slower code: +//- if ( t < t0 || t0 === undefined ) { + if ( ! ( t >= t0 ) ) { + + // looping? + + var t1global = pp[ 1 ]; + + if ( t < t1global ) { + + i1 = 2; // + 1, using the scan for the details + t0 = t1global; + + } + + // linear reverse scan + + for ( var giveUpAt = i1 - 2; ;) { + + if ( t0 === undefined ) { + + // before start + + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); + + } + + if ( i1 === giveUpAt ) break; // this loop + + t1 = t0; + t0 = pp[ -- i1 - 1 ]; + + if ( t >= t0 ) { + + // we have arrived at the sought interval + break seek; + + } + + } + + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; + + } + + // the interval is valid + + break validate_interval; + + } // linear scan + + // binary search + + while ( i1 < right ) { + + var mid = ( i1 + right ) >>> 1; + + if ( t < pp[ mid ] ) { + + right = mid; + + } else { + + i1 = mid + 1; + + } + + } + + t1 = pp[ i1 ]; + t0 = pp[ i1 - 1 ]; + + // check boundary cases, again + + if ( t0 === undefined ) { + + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); + + } + + if ( t1 === undefined ) { + + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t0, t ); + + } + + } // seek + + this._cachedIndex = i1; + + this.intervalChanged_( i1, t0, t1 ); + + } // validate_interval + + return this.interpolate_( i1, t0, t, t1 ); + }, - clone: function () { + settings: null, // optional, subclass-specific settings structure + // Note: The indirection allows central control of many interpolants. - return new THREE.Triangle().copy( this ); + // --- Protected interface + + DefaultSettings_: {}, + + getSettings_: function() { + + return this.settings || this.DefaultSettings_; + + }, + + copySampleValue_: function( index ) { + + // copies a sample value to the result buffer + + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; + + for ( var i = 0; i !== stride; ++ i ) { + + result[ i ] = values[ offset + i ]; + + } + + return result; + + }, + + // Template methods for derived classes: + + interpolate_: function( i1, t0, t, t1 ) { + + throw new Error( "call to abstract method" ); + // implementations shall return this.resultBuffer + + }, + + intervalChanged_: function( i1, t0, t1 ) { + + // empty } }; +Object.assign( THREE.Interpolant.prototype, { + + beforeStart_: //( 0, t, t0 ), returns this.resultBuffer + THREE.Interpolant.prototype.copySampleValue_, + + afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer + THREE.Interpolant.prototype.copySampleValue_ + +} ); + +// File:src/math/interpolants/CubicInterpolant.js + +/** + * Fast and simple cubic spline interpolant. + * + * It was derived from a Hermitian construction setting the first derivative + * at each sample position to the linear slope between neighboring positions + * over their parameter interval. + * + * @author tschw + */ + +THREE.CubicInterpolant = function( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + THREE.Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + + this._weightPrev = -0; + this._offsetPrev = -0; + this._weightNext = -0; + this._offsetNext = -0; + +}; + +THREE.CubicInterpolant.prototype = + Object.assign( Object.create( THREE.Interpolant.prototype ), { + + constructor: THREE.CubicInterpolant, + + DefaultSettings_: { + + endingStart: THREE.ZeroCurvatureEnding, + endingEnd: THREE.ZeroCurvatureEnding + + }, + + intervalChanged_: function( i1, t0, t1 ) { + + var pp = this.parameterPositions, + iPrev = i1 - 2, + iNext = i1 + 1, + + tPrev = pp[ iPrev ], + tNext = pp[ iNext ]; + + if ( tPrev === undefined ) { + + switch ( this.getSettings_().endingStart ) { + + case THREE.ZeroSlopeEnding: + + // f'(t0) = 0 + iPrev = i1; + tPrev = 2 * t0 - t1; + + break; + + case THREE.WrapAroundEnding: + + // use the other end of the curve + iPrev = pp.length - 2; + tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; + + break; + + default: // ZeroCurvatureEnding + + // f''(t0) = 0 a.k.a. Natural Spline + iPrev = i1; + tPrev = t1; + + } + + } + + if ( tNext === undefined ) { + + switch ( this.getSettings_().endingEnd ) { + + case THREE.ZeroSlopeEnding: + + // f'(tN) = 0 + iNext = i1; + tNext = 2 * t1 - t0; + + break; + + case THREE.WrapAroundEnding: + + // use the other end of the curve + iNext = 1; + tNext = t1 + pp[ 1 ] - pp[ 0 ]; + + break; + + default: // ZeroCurvatureEnding + + // f''(tN) = 0, a.k.a. Natural Spline + iNext = i1 - 1; + tNext = t0; + + } + + } + + var halfDt = ( t1 - t0 ) * 0.5, + stride = this.valueSize; + + this._weightPrev = halfDt / ( t0 - tPrev ); + this._weightNext = halfDt / ( tNext - t1 ); + this._offsetPrev = iPrev * stride; + this._offsetNext = iNext * stride; + + }, + + interpolate_: function( i1, t0, t, t1 ) { + + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + + o1 = i1 * stride, o0 = o1 - stride, + oP = this._offsetPrev, oN = this._offsetNext, + wP = this._weightPrev, wN = this._weightNext, + + p = ( t - t0 ) / ( t1 - t0 ), + pp = p * p, + ppp = pp * p; + + // evaluate polynomials + + var sP = - wP * ppp + 2 * wP * pp - wP * p; + var s0 = ( 1 + wP ) * ppp + (-1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1; + var s1 = (-1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; + var sN = wN * ppp - wN * pp; + + // combine data linearly + + for ( var i = 0; i !== stride; ++ i ) { + + result[ i ] = + sP * values[ oP + i ] + + s0 * values[ o0 + i ] + + s1 * values[ o1 + i ] + + sN * values[ oN + i ]; + + } + + return result; + + } + +} ); + +// File:src/math/interpolants/DiscreteInterpolant.js + +/** + * + * Interpolant that evaluates to the sample value at the position preceeding + * the parameter. + * + * @author tschw + */ + +THREE.DiscreteInterpolant = function( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + THREE.Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + +}; + +THREE.DiscreteInterpolant.prototype = + Object.assign( Object.create( THREE.Interpolant.prototype ), { + + constructor: THREE.DiscreteInterpolant, + + interpolate_: function( i1, t0, t, t1 ) { + + return this.copySampleValue_( i1 - 1 ); + + } + +} ); + +// File:src/math/interpolants/LinearInterpolant.js + +/** + * @author tschw + */ + +THREE.LinearInterpolant = function( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + THREE.Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + +}; + +THREE.LinearInterpolant.prototype = + Object.assign( Object.create( THREE.Interpolant.prototype ), { + + constructor: THREE.LinearInterpolant, + + interpolate_: function( i1, t0, t, t1 ) { + + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + + offset1 = i1 * stride, + offset0 = offset1 - stride, + + weight1 = ( t - t0 ) / ( t1 - t0 ), + weight0 = 1 - weight1; + + for ( var i = 0; i !== stride; ++ i ) { + + result[ i ] = + values[ offset0 + i ] * weight0 + + values[ offset1 + i ] * weight1; + + } + + return result; + + } + +} ); + +// File:src/math/interpolants/QuaternionLinearInterpolant.js + +/** + * Spherical linear unit quaternion interpolant. + * + * @author tschw + */ + +THREE.QuaternionLinearInterpolant = function( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + THREE.Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + +}; + +THREE.QuaternionLinearInterpolant.prototype = + Object.assign( Object.create( THREE.Interpolant.prototype ), { + + constructor: THREE.QuaternionLinearInterpolant, + + interpolate_: function( i1, t0, t, t1 ) { + + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + + offset = i1 * stride, + + alpha = ( t - t0 ) / ( t1 - t0 ); + + for ( var end = offset + stride; offset !== end; offset += 4 ) { + + THREE.Quaternion.slerpFlat( result, 0, + values, offset - stride, values, offset, alpha ); + + } + + return result; + + } + +} ); + // File:src/core/Clock.js /** @@ -7120,12 +7899,15 @@ THREE.Clock.prototype = { start: function () { - this.startTime = self.performance !== undefined && self.performance.now !== undefined - ? self.performance.now() - : Date.now(); + this.startTime = ( Qt === undefined && + self.performance !== undefined && + self.performance.now !== undefined ) + ? self.performance.now() + : Date.now(); this.oldTime = this.startTime; this.running = true; + }, stop: function () { @@ -7154,9 +7936,11 @@ THREE.Clock.prototype = { if ( this.running ) { - var newTime = self.performance !== undefined && self.performance.now !== undefined - ? self.performance.now() - : Date.now(); + var newTime = ( Qt === undefined && + self.performance !== undefined && + self.performance.now !== undefined ) + ? self.performance.now() + : Date.now(); diff = 0.001 * ( newTime - this.oldTime ); this.oldTime = newTime; @@ -7177,7 +7961,7 @@ THREE.Clock.prototype = { * https://github.com/mrdoob/eventdispatcher.js/ */ -THREE.EventDispatcher = function () {} +THREE.EventDispatcher = function () {}; THREE.EventDispatcher.prototype = { @@ -7281,11 +8065,59 @@ THREE.EventDispatcher.prototype = { }; +// File:src/core/Layers.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Layers = function () { + + this.mask = 1; + +}; + +THREE.Layers.prototype = { + + constructor: THREE.Layers, + + set: function ( channel ) { + + this.mask = 1 << channel; + + }, + + enable: function ( channel ) { + + this.mask |= 1 << channel; + + }, + + toggle: function ( channel ) { + + this.mask ^= 1 << channel; + + }, + + disable: function ( channel ) { + + this.mask &= ~ ( 1 << channel ); + + }, + + test: function ( layers ) { + + return ( this.mask & layers.mask ) !== 0; + + } + +}; + // File:src/core/Raycaster.js /** * @author mrdoob / http://mrdoob.com/ - * @author bhouston / http://exocortex.com/ + * @author bhouston / http://clara.io/ * @author stephomi / http://stephaneginier.com/ */ @@ -7300,22 +8132,33 @@ THREE.EventDispatcher.prototype = { this.far = far || Infinity; this.params = { - Sprite: {}, Mesh: {}, - PointCloud: { threshold: 1 }, + Line: {}, LOD: {}, - Line: {} + Points: { threshold: 1 }, + Sprite: {} }; + Object.defineProperties( this.params, { + PointCloud: { + get: function () { + console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); + return this.Points; + } + } + } ); + }; - var descSort = function ( a, b ) { + function ascSort( a, b ) { return a.distance - b.distance; - }; + } - var intersectObject = function ( object, raycaster, intersects, recursive ) { + function intersectObject( object, raycaster, intersects, recursive ) { + + if ( object.visible === false ) return; object.raycast( raycaster, intersects ); @@ -7331,7 +8174,7 @@ THREE.EventDispatcher.prototype = { } - }; + } // @@ -7339,7 +8182,6 @@ THREE.EventDispatcher.prototype = { constructor: THREE.Raycaster, - precision: 0.0001, linePrecision: 1, set: function ( origin, direction ) { @@ -7352,12 +8194,10 @@ THREE.EventDispatcher.prototype = { setFromCamera: function ( coords, camera ) { - // camera is assumed _not_ to be a child of a transformed object - if ( camera instanceof THREE.PerspectiveCamera ) { - this.ray.origin.copy( camera.position ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( camera.position ).normalize(); + this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); } else if ( camera instanceof THREE.OrthographicCamera ) { @@ -7366,7 +8206,7 @@ THREE.EventDispatcher.prototype = { } else { - THREE.error( 'THREE.Raycaster: Unsupported camera type.' ); + console.error( 'THREE.Raycaster: Unsupported camera type.' ); } @@ -7378,7 +8218,7 @@ THREE.EventDispatcher.prototype = { intersectObject( object, this, intersects, recursive ); - intersects.sort( descSort ); + intersects.sort( ascSort ); return intersects; @@ -7388,9 +8228,9 @@ THREE.EventDispatcher.prototype = { var intersects = []; - if ( objects instanceof Array === false ) { + if ( Array.isArray( objects ) === false ) { - THREE.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); + console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); return intersects; } @@ -7401,7 +8241,7 @@ THREE.EventDispatcher.prototype = { } - intersects.sort( descSort ); + intersects.sort( ascSort ); return intersects; @@ -7418,6 +8258,7 @@ THREE.EventDispatcher.prototype = { * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley + * @author elephantatwork / www.elephantatwork.ch */ THREE.Object3D = function () { @@ -7429,7 +8270,7 @@ THREE.Object3D = function () { this.name = ''; this.type = 'Object3D'; - this.parent = undefined; + this.parent = null; this.children = []; this.up = THREE.Object3D.DefaultUp.clone(); @@ -7439,13 +8280,17 @@ THREE.Object3D = function () { var quaternion = new THREE.Quaternion(); var scale = new THREE.Vector3( 1, 1, 1 ); - var onRotationChange = function () { - quaternion.setFromEuler( rotation, false ); - }; + function onRotationChange() { + + quaternion.setFromEuler( rotation, false ); + + } + + function onQuaternionChange() { - var onQuaternionChange = function () { rotation.setFromQuaternion( quaternion, undefined, false ); - }; + + } rotation.onChange( onRotationChange ); quaternion.onChange( onQuaternionChange ); @@ -7466,6 +8311,12 @@ THREE.Object3D = function () { scale: { enumerable: true, value: scale + }, + modelViewMatrix: { + value: new THREE.Matrix4() + }, + normalMatrix: { + value: new THREE.Matrix3() } } ); @@ -7474,9 +8325,10 @@ THREE.Object3D = function () { this.matrix = new THREE.Matrix4(); this.matrixWorld = new THREE.Matrix4(); - this.matrixAutoUpdate = true; + this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; + this.layers = new THREE.Layers(); this.visible = true; this.castShadow = false; @@ -7490,39 +8342,12 @@ THREE.Object3D = function () { }; THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 ); +THREE.Object3D.DefaultMatrixAutoUpdate = true; THREE.Object3D.prototype = { constructor: THREE.Object3D, - get eulerOrder () { - - THREE.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' ); - - return this.rotation.order; - - }, - - set eulerOrder ( value ) { - - THREE.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' ); - - this.rotation.order = value; - - }, - - get useQuaternion () { - - THREE.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - }, - - set useQuaternion ( value ) { - - THREE.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - }, - applyMatrix: function ( matrix ) { this.matrix.multiplyMatrices( matrix, this.matrix ); @@ -7576,7 +8401,7 @@ THREE.Object3D.prototype = { return this; - } + }; }(), @@ -7631,17 +8456,10 @@ THREE.Object3D.prototype = { return this; - } + }; }(), - translate: function ( distance, axis ) { - - THREE.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); - - }, - translateX: function () { var v1 = new THREE.Vector3( 1, 0, 0 ); @@ -7724,18 +8542,18 @@ THREE.Object3D.prototype = { return this; - }; + } if ( object === this ) { - THREE.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); + console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); return this; } if ( object instanceof THREE.Object3D ) { - if ( object.parent !== undefined ) { + if ( object.parent !== null ) { object.parent.remove( object ); @@ -7748,7 +8566,7 @@ THREE.Object3D.prototype = { } else { - THREE.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); + console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } @@ -7766,13 +8584,13 @@ THREE.Object3D.prototype = { } - }; + } var index = this.children.indexOf( object ); if ( index !== - 1 ) { - object.parent = undefined; + object.parent = null; object.dispatchEvent( { type: 'removed' } ); @@ -7782,13 +8600,6 @@ THREE.Object3D.prototype = { }, - getChildByName: function ( name ) { - - THREE.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); - - }, - getObjectById: function ( id ) { return this.getObjectByProperty( 'id', id ); @@ -7847,7 +8658,7 @@ THREE.Object3D.prototype = { return result; - } + }; }(), @@ -7863,7 +8674,7 @@ THREE.Object3D.prototype = { return result.setFromQuaternion( quaternion, this.rotation.order, false ); - } + }; }(), @@ -7882,7 +8693,7 @@ THREE.Object3D.prototype = { return result; - } + }; }(), @@ -7898,7 +8709,7 @@ THREE.Object3D.prototype = { return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); - } + }; }(), @@ -7908,9 +8719,11 @@ THREE.Object3D.prototype = { callback( this ); - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + var children = this.children; - this.children[ i ].traverse( callback ); + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].traverse( callback ); } @@ -7922,9 +8735,11 @@ THREE.Object3D.prototype = { callback( this ); - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + var children = this.children; - this.children[ i ].traverseVisible( callback ); + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].traverseVisible( callback ); } @@ -7932,11 +8747,13 @@ THREE.Object3D.prototype = { traverseAncestors: function ( callback ) { - if ( this.parent ) { + var parent = this.parent; - callback( this.parent ); + if ( parent !== null ) { - this.parent.traverseAncestors( callback ); + callback( parent ); + + parent.traverseAncestors( callback ); } @@ -7956,7 +8773,7 @@ THREE.Object3D.prototype = { if ( this.matrixWorldNeedsUpdate === true || force === true ) { - if ( this.parent === undefined ) { + if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); @@ -7982,210 +8799,173 @@ THREE.Object3D.prototype = { }, - toJSON: function () { + toJSON: function ( meta ) { - var output = { - metadata: { - version: 4.3, + var isRootObject = ( meta === undefined ); + + var output = {}; + + // meta is a hash used to collect geometries, materials. + // not providing it implies that this is the root object + // being serialized. + if ( isRootObject ) { + + // initialize meta obj + meta = { + geometries: {}, + materials: {}, + textures: {}, + images: {} + }; + + output.metadata = { + version: 4.4, type: 'Object', - generator: 'ObjectExporter' - } - }; - - // - - var geometries = {}; - - var parseGeometry = function ( geometry ) { - - if ( output.geometries === undefined ) { - - output.geometries = []; - - } - - if ( geometries[ geometry.uuid ] === undefined ) { - - var json = geometry.toJSON(); - - delete json.metadata; - - geometries[ geometry.uuid ] = json; - - output.geometries.push( json ); - - } - - return geometry.uuid; - - }; - - // - - var materials = {}; - - var parseMaterial = function ( material ) { - - if ( output.materials === undefined ) { - - output.materials = []; - - } - - if ( materials[ material.uuid ] === undefined ) { - - var json = material.toJSON(); - - delete json.metadata; - - materials[ material.uuid ] = json; - - output.materials.push( json ); - - } - - return material.uuid; - - }; - - // - - var parseObject = function ( object ) { - - var data = {}; - - data.uuid = object.uuid; - data.type = object.type; - - if ( object.name !== '' ) data.name = object.name; - if ( JSON.stringify( object.userData ) !== '{}' ) data.userData = object.userData; - if ( object.visible !== true ) data.visible = object.visible; - - if ( object instanceof THREE.PerspectiveCamera ) { - - data.fov = object.fov; - data.aspect = object.aspect; - data.near = object.near; - data.far = object.far; - - } else if ( object instanceof THREE.OrthographicCamera ) { - - data.left = object.left; - data.right = object.right; - data.top = object.top; - data.bottom = object.bottom; - data.near = object.near; - data.far = object.far; - - } else if ( object instanceof THREE.AmbientLight ) { - - data.color = object.color.getHex(); - - } else if ( object instanceof THREE.DirectionalLight ) { - - data.color = object.color.getHex(); - data.intensity = object.intensity; - - } else if ( object instanceof THREE.PointLight ) { - - data.color = object.color.getHex(); - data.intensity = object.intensity; - data.distance = object.distance; - data.decay = object.decay; - - } else if ( object instanceof THREE.SpotLight ) { - - data.color = object.color.getHex(); - data.intensity = object.intensity; - data.distance = object.distance; - data.angle = object.angle; - data.exponent = object.exponent; - data.decay = object.decay; - - } else if ( object instanceof THREE.HemisphereLight ) { - - data.color = object.color.getHex(); - data.groundColor = object.groundColor.getHex(); - - } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.PointCloud ) { - - data.geometry = parseGeometry( object.geometry ); - data.material = parseMaterial( object.material ); - - if ( object instanceof THREE.Line ) data.mode = object.mode; - - } else if ( object instanceof THREE.Sprite ) { - - data.material = parseMaterial( object.material ); - - } - - data.matrix = object.matrix.toArray(); - - if ( object.children.length > 0 ) { - - data.children = []; - - for ( var i = 0; i < object.children.length; i ++ ) { - - data.children.push( parseObject( object.children[ i ] ) ); - - } - - } - - return data; + generator: 'Object3D.toJSON' + }; } - output.object = parseObject( this ); + // standard Object3D serialization - return output; + var object = {}; - }, + object.uuid = this.uuid; + object.type = this.type; - clone: function ( object, recursive ) { + if ( this.name !== '' ) object.name = this.name; + if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; + if ( this.castShadow === true ) object.castShadow = true; + if ( this.receiveShadow === true ) object.receiveShadow = true; + if ( this.visible === false ) object.visible = false; - if ( object === undefined ) object = new THREE.Object3D(); - if ( recursive === undefined ) recursive = true; + object.matrix = this.matrix.toArray(); - object.name = this.name; + // - object.up.copy( this.up ); + if ( this.geometry !== undefined ) { - object.position.copy( this.position ); - object.quaternion.copy( this.quaternion ); - object.scale.copy( this.scale ); + if ( meta.geometries[ this.geometry.uuid ] === undefined ) { - object.rotationAutoUpdate = this.rotationAutoUpdate; + meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); - object.matrix.copy( this.matrix ); - object.matrixWorld.copy( this.matrixWorld ); + } - object.matrixAutoUpdate = this.matrixAutoUpdate; - object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate; + object.geometry = this.geometry.uuid; - object.visible = this.visible; + } - object.castShadow = this.castShadow; - object.receiveShadow = this.receiveShadow; + if ( this.material !== undefined ) { - object.frustumCulled = this.frustumCulled; + if ( meta.materials[ this.material.uuid ] === undefined ) { - object.userData = JSON.parse( JSON.stringify( this.userData ) ); + meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); - if ( recursive === true ) { + } + + object.material = this.material.uuid; + + } + + // + + if ( this.children.length > 0 ) { + + object.children = []; for ( var i = 0; i < this.children.length; i ++ ) { - var child = this.children[ i ]; - object.add( child.clone() ); + object.children.push( this.children[ i ].toJSON( meta ).object ); } } - return object; + if ( isRootObject ) { + + var geometries = extractFromCache( meta.geometries ); + var materials = extractFromCache( meta.materials ); + var textures = extractFromCache( meta.textures ); + var images = extractFromCache( meta.images ); + + if ( geometries.length > 0 ) output.geometries = geometries; + if ( materials.length > 0 ) output.materials = materials; + if ( textures.length > 0 ) output.textures = textures; + if ( images.length > 0 ) output.images = images; + + } + + output.object = object; + + return output; + + // extract data from the cache hash + // remove metadata on each item + // and return as array + function extractFromCache ( cache ) { + + var values = []; + for ( var key in cache ) { + + var data = cache[ key ]; + delete data.metadata; + values.push( data ); + + } + return values; + + } + + }, + + clone: function ( recursive ) { + + return new this.constructor().copy( this, recursive ); + + }, + + copy: function ( source, recursive ) { + + if ( recursive === undefined ) recursive = true; + + this.name = source.name; + + this.up.copy( source.up ); + + this.position.copy( source.position ); + this.quaternion.copy( source.quaternion ); + this.scale.copy( source.scale ); + + this.rotationAutoUpdate = source.rotationAutoUpdate; + + this.matrix.copy( source.matrix ); + this.matrixWorld.copy( source.matrixWorld ); + + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + + this.visible = source.visible; + + this.castShadow = source.castShadow; + this.receiveShadow = source.receiveShadow; + + this.frustumCulled = source.frustumCulled; + this.renderOrder = source.renderOrder; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + if ( recursive === true ) { + + for ( var i = 0; i < source.children.length; i ++ ) { + + var child = source.children[ i ]; + this.add( child.clone() ); + + } + + } + + return this; } @@ -8209,12 +8989,10 @@ THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { this.c = c; this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); - this.vertexNormals = normal instanceof Array ? normal : []; + this.vertexNormals = Array.isArray( normal ) ? normal : []; this.color = color instanceof THREE.Color ? color : new THREE.Color(); - this.vertexColors = color instanceof Array ? color : []; - - this.vertexTangents = []; + this.vertexColors = Array.isArray( color ) ? color : []; this.materialIndex = materialIndex !== undefined ? materialIndex : 0; @@ -8226,50 +9004,39 @@ THREE.Face3.prototype = { clone: function () { - var face = new THREE.Face3( this.a, this.b, this.c ); + return new this.constructor().copy( this ); - face.normal.copy( this.normal ); - face.color.copy( this.color ); + }, - face.materialIndex = this.materialIndex; + copy: function ( source ) { - for ( var i = 0, il = this.vertexNormals.length; i < il; i ++ ) { + this.a = source.a; + this.b = source.b; + this.c = source.c; - face.vertexNormals[ i ] = this.vertexNormals[ i ].clone(); + this.normal.copy( source.normal ); + this.color.copy( source.color ); + + this.materialIndex = source.materialIndex; + + for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { + + this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); } - for ( var i = 0, il = this.vertexColors.length; i < il; i ++ ) { + for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { - face.vertexColors[ i ] = this.vertexColors[ i ].clone(); + this.vertexColors[ i ] = source.vertexColors[ i ].clone(); } - for ( var i = 0, il = this.vertexTangents.length; i < il; i ++ ) { - - face.vertexTangents[ i ] = this.vertexTangents[ i ].clone(); - - } - - return face; + return this; } }; -// File:src/core/Face4.js - -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { - - THREE.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ) - return new THREE.Face3( a, b, c, normal, color, materialIndex ); - -}; - // File:src/core/BufferAttribute.js /** @@ -8278,10 +9045,15 @@ THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { THREE.BufferAttribute = function ( array, itemSize ) { + this.uuid = THREE.Math.generateUUID(); + this.array = array; this.itemSize = itemSize; - this.needsUpdate = false; + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; }; @@ -8289,9 +9061,34 @@ THREE.BufferAttribute.prototype = { constructor: THREE.BufferAttribute, - get length () { + get count() { - return this.array.length; + return this.array.length / this.itemSize; + + }, + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + }, + + setDynamic: function ( value ) { + + this.dynamic = value; + + return this; + + }, + + copy: function ( source ) { + + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + + this.dynamic = source.dynamic; + + return this; }, @@ -8310,6 +9107,410 @@ THREE.BufferAttribute.prototype = { }, + copyArray: function ( array ) { + + this.array.set( array ); + + return this; + + }, + + copyColorsArray: function ( colors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = colors.length; i < l; i ++ ) { + + var color = colors[ i ]; + + if ( color === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); + color = new THREE.Color(); + + } + + array[ offset ++ ] = color.r; + array[ offset ++ ] = color.g; + array[ offset ++ ] = color.b; + + } + + return this; + + }, + + copyIndicesArray: function ( indices ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = indices.length; i < l; i ++ ) { + + var index = indices[ i ]; + + array[ offset ++ ] = index.a; + array[ offset ++ ] = index.b; + array[ offset ++ ] = index.c; + + } + + return this; + + }, + + copyVector2sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); + vector = new THREE.Vector2(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + + } + + return this; + + }, + + copyVector3sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); + vector = new THREE.Vector3(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + + } + + return this; + + }, + + copyVector4sArray: function ( vectors ) { + + var array = this.array, offset = 0; + + for ( var i = 0, l = vectors.length; i < l; i ++ ) { + + var vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); + vector = new THREE.Vector4(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + array[ offset ++ ] = vector.w; + + } + + return this; + + }, + + set: function ( value, offset ) { + + if ( offset === undefined ) offset = 0; + + this.array.set( value, offset ); + + return this; + + }, + + getX: function ( index ) { + + return this.array[ index * this.itemSize ]; + + }, + + setX: function ( index, x ) { + + this.array[ index * this.itemSize ] = x; + + return this; + + }, + + getY: function ( index ) { + + return this.array[ index * this.itemSize + 1 ]; + + }, + + setY: function ( index, y ) { + + this.array[ index * this.itemSize + 1 ] = y; + + return this; + + }, + + getZ: function ( index ) { + + return this.array[ index * this.itemSize + 2 ]; + + }, + + setZ: function ( index, z ) { + + this.array[ index * this.itemSize + 2 ] = z; + + return this; + + }, + + getW: function ( index ) { + + return this.array[ index * this.itemSize + 3 ]; + + }, + + setW: function ( index, w ) { + + this.array[ index * this.itemSize + 3 ] = w; + + return this; + + }, + + setXY: function ( index, x, y ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + index *= this.itemSize; + + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// + +THREE.Int8Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int8Array( array ), itemSize ); + +}; + +THREE.Uint8Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint8Array( array ), itemSize ); + +}; + +THREE.Uint8ClampedAttribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint8ClampedArray( array ), itemSize ); + +}; + +THREE.Int16Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int16Array( array ), itemSize ); + +}; + +THREE.Uint16Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint16Array( array ), itemSize ); + +}; + +THREE.Int32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Int32Array( array ), itemSize ); + +}; + +THREE.Uint32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Uint32Array( array ), itemSize ); + +}; + +THREE.Float32Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Float32Array( array ), itemSize ); + +}; + +THREE.Float64Attribute = function ( array, itemSize ) { + + return new THREE.BufferAttribute( new Float64Array( array ), itemSize ); + +}; + + +// Deprecated + +THREE.DynamicBufferAttribute = function ( array, itemSize ) { + + console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); + return new THREE.BufferAttribute( array, itemSize ).setDynamic( true ); + +}; + +// File:src/core/InstancedBufferAttribute.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedBufferAttribute = function ( array, itemSize, meshPerAttribute ) { + + THREE.BufferAttribute.call( this, array, itemSize ); + + this.meshPerAttribute = meshPerAttribute || 1; + +}; + +THREE.InstancedBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype ); +THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute; + +THREE.InstancedBufferAttribute.prototype.copy = function ( source ) { + + THREE.BufferAttribute.prototype.copy.call( this, source ); + + this.meshPerAttribute = source.meshPerAttribute; + + return this; + +}; + +// File:src/core/InterleavedBuffer.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InterleavedBuffer = function ( array, stride ) { + + this.uuid = THREE.Math.generateUUID(); + + this.array = array; + this.stride = stride; + + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + +}; + +THREE.InterleavedBuffer.prototype = { + + constructor: THREE.InterleavedBuffer, + + get length () { + + return this.array.length; + + }, + + get count () { + + return this.array.length / this.stride; + + }, + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + }, + + setDynamic: function ( value ) { + + this.dynamic = value; + + return this; + + }, + + copy: function ( source ) { + + this.array = new source.array.constructor( source.array ); + this.stride = source.stride; + this.dynamic = source.dynamic; + + return this; + + }, + + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.stride; + index2 *= attribute.stride; + + for ( var i = 0, l = this.stride; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + }, + set: function ( value, offset ) { if ( offset === undefined ) offset = 0; @@ -8320,9 +9521,78 @@ THREE.BufferAttribute.prototype = { }, + clone: function () { + + return new this.constructor().copy( this ); + + } + +}; + +// File:src/core/InstancedInterleavedBuffer.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedInterleavedBuffer = function ( array, stride, meshPerAttribute ) { + + THREE.InterleavedBuffer.call( this, array, stride ); + + this.meshPerAttribute = meshPerAttribute || 1; + +}; + +THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype ); +THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer; + +THREE.InstancedInterleavedBuffer.prototype.copy = function ( source ) { + + THREE.InterleavedBuffer.prototype.copy.call( this, source ); + + this.meshPerAttribute = source.meshPerAttribute; + + return this; + +}; + +// File:src/core/InterleavedBufferAttribute.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) { + + this.uuid = THREE.Math.generateUUID(); + + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; + +}; + + +THREE.InterleavedBufferAttribute.prototype = { + + constructor: THREE.InterleavedBufferAttribute, + + get length() { + + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); + return this.array.length; + + }, + + get count() { + + return this.data.count; + + }, + setX: function ( index, x ) { - this.array[ index * this.itemSize ] = x; + this.data.array[ index * this.data.stride + this.offset ] = x; return this; @@ -8330,7 +9600,7 @@ THREE.BufferAttribute.prototype = { setY: function ( index, y ) { - this.array[ index * this.itemSize + 1 ] = y; + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; @@ -8338,18 +9608,50 @@ THREE.BufferAttribute.prototype = { setZ: function ( index, z ) { - this.array[ index * this.itemSize + 2 ] = z; + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; }, + setW: function ( index, w ) { + + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + + return this; + + }, + + getX: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset ]; + + }, + + getY: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 1 ]; + + }, + + getZ: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 2 ]; + + }, + + getW: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 3 ]; + + }, + setXY: function ( index, x, y ) { - index *= this.itemSize; + index = index * this.data.stride + this.offset; - this.array[ index ] = x; - this.array[ index + 1 ] = y; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; return this; @@ -8357,11 +9659,11 @@ THREE.BufferAttribute.prototype = { setXYZ: function ( index, x, y, z ) { - index *= this.itemSize; + index = index * this.data.stride + this.offset; - this.array[ index ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; return this; @@ -8369,1084 +9671,19 @@ THREE.BufferAttribute.prototype = { setXYZW: function ( index, x, y, z, w ) { - index *= this.itemSize; + index = index * this.data.stride + this.offset; - this.array[ index ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + this.data.array[ index + 3 ] = w; return this; - }, - - clone: function () { - - return new THREE.BufferAttribute( new this.array.constructor( this.array ), this.itemSize ); - } }; -// - -THREE.Int8Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -THREE.Uint8Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -THREE.Uint8ClampedAttribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - - -}; - -THREE.Int16Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -THREE.Uint16Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -THREE.Int32Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -THREE.Uint32Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -THREE.Float32Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -THREE.Float64Attribute = function ( data, itemSize ) { - - THREE.warn( 'THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); - -}; - -// File:src/core/DynamicBufferAttribute.js - -/** - * @author benaadams / https://twitter.com/ben_a_adams - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.DynamicBufferAttribute = function ( array, itemSize ) { - - THREE.BufferAttribute.call( this, array, itemSize ); - - this.updateRange = { offset: 0, count: -1 }; - -}; - -THREE.DynamicBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype ); -THREE.DynamicBufferAttribute.prototype.constructor = THREE.DynamicBufferAttribute; - -THREE.DynamicBufferAttribute.prototype.clone = function () { - - return new THREE.DynamicBufferAttribute( new this.array.constructor( this.array ), this.itemSize ); - -}; - -// File:src/core/BufferGeometry.js - -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.BufferGeometry = function () { - - Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); - - this.uuid = THREE.Math.generateUUID(); - - this.name = ''; - this.type = 'BufferGeometry'; - - this.attributes = {}; - this.attributesKeys = []; - - this.drawcalls = []; - this.offsets = this.drawcalls; // backwards compatibility - - this.boundingBox = null; - this.boundingSphere = null; - -}; - -THREE.BufferGeometry.prototype = { - - constructor: THREE.BufferGeometry, - - addAttribute: function ( name, attribute ) { - - if ( attribute instanceof THREE.BufferAttribute === false ) { - - THREE.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - - this.attributes[ name ] = { array: arguments[ 1 ], itemSize: arguments[ 2 ] }; - - return; - - } - - this.attributes[ name ] = attribute; - this.attributesKeys = Object.keys( this.attributes ); - - }, - - getAttribute: function ( name ) { - - return this.attributes[ name ]; - - }, - - addDrawCall: function ( start, count, indexOffset ) { - - this.drawcalls.push( { - - start: start, - count: count, - index: indexOffset !== undefined ? indexOffset : 0 - - } ); - - }, - - applyMatrix: function ( matrix ) { - - var position = this.attributes.position; - - if ( position !== undefined ) { - - matrix.applyToVector3Array( position.array ); - position.needsUpdate = true; - - } - - var normal = this.attributes.normal; - - if ( normal !== undefined ) { - - var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); - - normalMatrix.applyToVector3Array( normal.array ); - normal.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - }, - - center: function () { - - this.computeBoundingBox(); - - var offset = this.boundingBox.center().negate(); - - this.applyMatrix( new THREE.Matrix4().setPosition( offset ) ); - - return offset; - - }, - - fromGeometry: function ( geometry, settings ) { - - settings = settings || { 'vertexColors': THREE.NoColors }; - - var vertices = geometry.vertices; - var faces = geometry.faces; - var faceVertexUvs = geometry.faceVertexUvs; - var vertexColors = settings.vertexColors; - var hasFaceVertexUv = faceVertexUvs[ 0 ].length > 0; - var hasFaceVertexNormals = faces[ 0 ].vertexNormals.length == 3; - - var positions = new Float32Array( faces.length * 3 * 3 ); - this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); - - var normals = new Float32Array( faces.length * 3 * 3 ); - this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); - - if ( vertexColors !== THREE.NoColors ) { - - var colors = new Float32Array( faces.length * 3 * 3 ); - this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); - - } - - if ( hasFaceVertexUv === true ) { - - var uvs = new Float32Array( faces.length * 3 * 2 ); - this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); - - } - - for ( var i = 0, i2 = 0, i3 = 0; i < faces.length; i ++, i2 += 6, i3 += 9 ) { - - var face = faces[ i ]; - - var a = vertices[ face.a ]; - var b = vertices[ face.b ]; - var c = vertices[ face.c ]; - - positions[ i3 ] = a.x; - positions[ i3 + 1 ] = a.y; - positions[ i3 + 2 ] = a.z; - - positions[ i3 + 3 ] = b.x; - positions[ i3 + 4 ] = b.y; - positions[ i3 + 5 ] = b.z; - - positions[ i3 + 6 ] = c.x; - positions[ i3 + 7 ] = c.y; - positions[ i3 + 8 ] = c.z; - - if ( hasFaceVertexNormals === true ) { - - var na = face.vertexNormals[ 0 ]; - var nb = face.vertexNormals[ 1 ]; - var nc = face.vertexNormals[ 2 ]; - - normals[ i3 ] = na.x; - normals[ i3 + 1 ] = na.y; - normals[ i3 + 2 ] = na.z; - - normals[ i3 + 3 ] = nb.x; - normals[ i3 + 4 ] = nb.y; - normals[ i3 + 5 ] = nb.z; - - normals[ i3 + 6 ] = nc.x; - normals[ i3 + 7 ] = nc.y; - normals[ i3 + 8 ] = nc.z; - - } else { - - var n = face.normal; - - normals[ i3 ] = n.x; - normals[ i3 + 1 ] = n.y; - normals[ i3 + 2 ] = n.z; - - normals[ i3 + 3 ] = n.x; - normals[ i3 + 4 ] = n.y; - normals[ i3 + 5 ] = n.z; - - normals[ i3 + 6 ] = n.x; - normals[ i3 + 7 ] = n.y; - normals[ i3 + 8 ] = n.z; - - } - - if ( vertexColors === THREE.FaceColors ) { - - var fc = face.color; - - colors[ i3 ] = fc.r; - colors[ i3 + 1 ] = fc.g; - colors[ i3 + 2 ] = fc.b; - - colors[ i3 + 3 ] = fc.r; - colors[ i3 + 4 ] = fc.g; - colors[ i3 + 5 ] = fc.b; - - colors[ i3 + 6 ] = fc.r; - colors[ i3 + 7 ] = fc.g; - colors[ i3 + 8 ] = fc.b; - - } else if ( vertexColors === THREE.VertexColors ) { - - var vca = face.vertexColors[ 0 ]; - var vcb = face.vertexColors[ 1 ]; - var vcc = face.vertexColors[ 2 ]; - - colors[ i3 ] = vca.r; - colors[ i3 + 1 ] = vca.g; - colors[ i3 + 2 ] = vca.b; - - colors[ i3 + 3 ] = vcb.r; - colors[ i3 + 4 ] = vcb.g; - colors[ i3 + 5 ] = vcb.b; - - colors[ i3 + 6 ] = vcc.r; - colors[ i3 + 7 ] = vcc.g; - colors[ i3 + 8 ] = vcc.b; - - } - - if ( hasFaceVertexUv === true ) { - - var uva = faceVertexUvs[ 0 ][ i ][ 0 ]; - var uvb = faceVertexUvs[ 0 ][ i ][ 1 ]; - var uvc = faceVertexUvs[ 0 ][ i ][ 2 ]; - - uvs[ i2 ] = uva.x; - uvs[ i2 + 1 ] = uva.y; - - uvs[ i2 + 2 ] = uvb.x; - uvs[ i2 + 3 ] = uvb.y; - - uvs[ i2 + 4 ] = uvc.x; - uvs[ i2 + 5 ] = uvc.y; - - } - - } - - this.computeBoundingSphere() - - return this; - - }, - - computeBoundingBox: function () { - - var vector = new THREE.Vector3(); - - return function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new THREE.Box3(); - - } - - var positions = this.attributes.position.array; - - if ( positions ) { - - var bb = this.boundingBox; - bb.makeEmpty(); - - for ( var i = 0, il = positions.length; i < il; i += 3 ) { - - vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - bb.expandByPoint( vector ); - - } - - } - - if ( positions === undefined || positions.length === 0 ) { - - this.boundingBox.min.set( 0, 0, 0 ); - this.boundingBox.max.set( 0, 0, 0 ); - - } - - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - - THREE.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' ); - - } - - } - - }(), - - computeBoundingSphere: function () { - - var box = new THREE.Box3(); - var vector = new THREE.Vector3(); - - return function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new THREE.Sphere(); - - } - - var positions = this.attributes.position.array; - - if ( positions ) { - - box.makeEmpty(); - - var center = this.boundingSphere.center; - - for ( var i = 0, il = positions.length; i < il; i += 3 ) { - - vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - box.expandByPoint( vector ); - - } - - box.center( center ); - - // hoping to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - - var maxRadiusSq = 0; - - for ( var i = 0, il = positions.length; i < il; i += 3 ) { - - vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - THREE.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.' ); - - } - - } - - } - - }(), - - computeFaceNormals: function () { - - // backwards compatibility - - }, - - computeVertexNormals: function () { - - var attributes = this.attributes; - - if ( attributes.position ) { - - var positions = attributes.position.array; - - if ( attributes.normal === undefined ) { - - this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); - - } else { - - // reset existing normals to zero - - var normals = attributes.normal.array; - - for ( var i = 0, il = normals.length; i < il; i ++ ) { - - normals[ i ] = 0; - - } - - } - - var normals = attributes.normal.array; - - var vA, vB, vC, - - pA = new THREE.Vector3(), - pB = new THREE.Vector3(), - pC = new THREE.Vector3(), - - cb = new THREE.Vector3(), - ab = new THREE.Vector3(); - - // indexed elements - - if ( attributes.index ) { - - var indices = attributes.index.array; - - var offsets = ( this.offsets.length > 0 ? this.offsets : [ { start: 0, count: indices.length, index: 0 } ] ); - - for ( var j = 0, jl = offsets.length; j < jl; ++ j ) { - - var start = offsets[ j ].start; - var count = offsets[ j ].count; - var index = offsets[ j ].index; - - for ( var i = start, il = start + count; i < il; i += 3 ) { - - vA = ( index + indices[ i ] ) * 3; - vB = ( index + indices[ i + 1 ] ) * 3; - vC = ( index + indices[ i + 2 ] ) * 3; - - pA.fromArray( positions, vA ); - pB.fromArray( positions, vB ); - pC.fromArray( positions, vC ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normals[ vA ] += cb.x; - normals[ vA + 1 ] += cb.y; - normals[ vA + 2 ] += cb.z; - - normals[ vB ] += cb.x; - normals[ vB + 1 ] += cb.y; - normals[ vB + 2 ] += cb.z; - - normals[ vC ] += cb.x; - normals[ vC + 1 ] += cb.y; - normals[ vC + 2 ] += cb.z; - - } - - } - - } else { - - // non-indexed elements (unconnected triangle soup) - - for ( var i = 0, il = positions.length; i < il; i += 9 ) { - - pA.fromArray( positions, i ); - pB.fromArray( positions, i + 3 ); - pC.fromArray( positions, i + 6 ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normals[ i ] = cb.x; - normals[ i + 1 ] = cb.y; - normals[ i + 2 ] = cb.z; - - normals[ i + 3 ] = cb.x; - normals[ i + 4 ] = cb.y; - normals[ i + 5 ] = cb.z; - - normals[ i + 6 ] = cb.x; - normals[ i + 7 ] = cb.y; - normals[ i + 8 ] = cb.z; - - } - - } - - this.normalizeNormals(); - - attributes.normal.needsUpdate = true; - - } - - }, - - computeTangents: function () { - - // based on http://www.terathon.com/code/tangent.html - // (per vertex tangents) - - if ( this.attributes.index === undefined || - this.attributes.position === undefined || - this.attributes.normal === undefined || - this.attributes.uv === undefined ) { - - THREE.warn( 'THREE.BufferGeometry: Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); - return; - - } - - var indices = this.attributes.index.array; - var positions = this.attributes.position.array; - var normals = this.attributes.normal.array; - var uvs = this.attributes.uv.array; - - var nVertices = positions.length / 3; - - if ( this.attributes.tangent === undefined ) { - - this.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); - - } - - var tangents = this.attributes.tangent.array; - - var tan1 = [], tan2 = []; - - for ( var k = 0; k < nVertices; k ++ ) { - - tan1[ k ] = new THREE.Vector3(); - tan2[ k ] = new THREE.Vector3(); - - } - - var vA = new THREE.Vector3(), - vB = new THREE.Vector3(), - vC = new THREE.Vector3(), - - uvA = new THREE.Vector2(), - uvB = new THREE.Vector2(), - uvC = new THREE.Vector2(), - - x1, x2, y1, y2, z1, z2, - s1, s2, t1, t2, r; - - var sdir = new THREE.Vector3(), tdir = new THREE.Vector3(); - - function handleTriangle( a, b, c ) { - - vA.fromArray( positions, a * 3 ); - vB.fromArray( positions, b * 3 ); - vC.fromArray( positions, c * 3 ); - - uvA.fromArray( uvs, a * 2 ); - uvB.fromArray( uvs, b * 2 ); - uvC.fromArray( uvs, c * 2 ); - - x1 = vB.x - vA.x; - x2 = vC.x - vA.x; - - y1 = vB.y - vA.y; - y2 = vC.y - vA.y; - - z1 = vB.z - vA.z; - z2 = vC.z - vA.z; - - s1 = uvB.x - uvA.x; - s2 = uvC.x - uvA.x; - - t1 = uvB.y - uvA.y; - t2 = uvC.y - uvA.y; - - r = 1.0 / ( s1 * t2 - s2 * t1 ); - - sdir.set( - ( t2 * x1 - t1 * x2 ) * r, - ( t2 * y1 - t1 * y2 ) * r, - ( t2 * z1 - t1 * z2 ) * r - ); - - tdir.set( - ( s1 * x2 - s2 * x1 ) * r, - ( s1 * y2 - s2 * y1 ) * r, - ( s1 * z2 - s2 * z1 ) * r - ); - - tan1[ a ].add( sdir ); - tan1[ b ].add( sdir ); - tan1[ c ].add( sdir ); - - tan2[ a ].add( tdir ); - tan2[ b ].add( tdir ); - tan2[ c ].add( tdir ); - - } - - var i, il; - var j, jl; - var iA, iB, iC; - - if ( this.drawcalls.length === 0 ) { - - this.addDrawCall( 0, indices.length, 0 ); - - } - - var drawcalls = this.drawcalls; - - for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { - - var start = drawcalls[ j ].start; - var count = drawcalls[ j ].count; - var index = drawcalls[ j ].index; - - for ( i = start, il = start + count; i < il; i += 3 ) { - - iA = index + indices[ i ]; - iB = index + indices[ i + 1 ]; - iC = index + indices[ i + 2 ]; - - handleTriangle( iA, iB, iC ); - - } - - } - - var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); - var n = new THREE.Vector3(), n2 = new THREE.Vector3(); - var w, t, test; - - function handleVertex( v ) { - - n.fromArray( normals, v * 3 ); - n2.copy( n ); - - t = tan1[ v ]; - - // Gram-Schmidt orthogonalize - - tmp.copy( t ); - tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); - - // Calculate handedness - - tmp2.crossVectors( n2, t ); - test = tmp2.dot( tan2[ v ] ); - w = ( test < 0.0 ) ? - 1.0 : 1.0; - - tangents[ v * 4 ] = tmp.x; - tangents[ v * 4 + 1 ] = tmp.y; - tangents[ v * 4 + 2 ] = tmp.z; - tangents[ v * 4 + 3 ] = w; - - } - - for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { - - var start = drawcalls[ j ].start; - var count = drawcalls[ j ].count; - var index = drawcalls[ j ].index; - - for ( i = start, il = start + count; i < il; i += 3 ) { - - iA = index + indices[ i ]; - iB = index + indices[ i + 1 ]; - iC = index + indices[ i + 2 ]; - - handleVertex( iA ); - handleVertex( iB ); - handleVertex( iC ); - - } - - } - - }, - - /* - Compute the draw offset for large models by chunking the index buffer into chunks of 65k addressable vertices. - This method will effectively rewrite the index buffer and remap all attributes to match the new indices. - WARNING: This method will also expand the vertex count to prevent sprawled triangles across draw offsets. - size - Defaults to 65535, but allows for larger or smaller chunks. - */ - computeOffsets: function ( size ) { - - if ( size === undefined ) size = 65535; // WebGL limits type of index buffer values to 16-bit. - - var indices = this.attributes.index.array; - var vertices = this.attributes.position.array; - - var facesCount = ( indices.length / 3 ); - - /* - console.log("Computing buffers in offsets of "+size+" -> indices:"+indices.length+" vertices:"+vertices.length); - console.log("Faces to process: "+(indices.length/3)); - console.log("Reordering "+verticesCount+" vertices."); - */ - - var sortedIndices = new Uint16Array( indices.length ); //16-bit buffers - var indexPtr = 0; - var vertexPtr = 0; - - var offsets = [ { start:0, count:0, index:0 } ]; - var offset = offsets[ 0 ]; - - var duplicatedVertices = 0; - var newVerticeMaps = 0; - var faceVertices = new Int32Array( 6 ); - var vertexMap = new Int32Array( vertices.length ); - var revVertexMap = new Int32Array( vertices.length ); - for ( var j = 0; j < vertices.length; j ++ ) { vertexMap[ j ] = - 1; revVertexMap[ j ] = - 1; } - - /* - Traverse every face and reorder vertices in the proper offsets of 65k. - We can have more than 65k entries in the index buffer per offset, but only reference 65k values. - */ - for ( var findex = 0; findex < facesCount; findex ++ ) { - newVerticeMaps = 0; - - for ( var vo = 0; vo < 3; vo ++ ) { - var vid = indices[ findex * 3 + vo ]; - if ( vertexMap[ vid ] == - 1 ) { - //Unmapped vertice - faceVertices[ vo * 2 ] = vid; - faceVertices[ vo * 2 + 1 ] = - 1; - newVerticeMaps ++; - } else if ( vertexMap[ vid ] < offset.index ) { - //Reused vertices from previous block (duplicate) - faceVertices[ vo * 2 ] = vid; - faceVertices[ vo * 2 + 1 ] = - 1; - duplicatedVertices ++; - } else { - //Reused vertice in the current block - faceVertices[ vo * 2 ] = vid; - faceVertices[ vo * 2 + 1 ] = vertexMap[ vid ]; - } - } - - var faceMax = vertexPtr + newVerticeMaps; - if ( faceMax > ( offset.index + size ) ) { - var new_offset = { start:indexPtr, count:0, index:vertexPtr }; - offsets.push( new_offset ); - offset = new_offset; - - //Re-evaluate reused vertices in light of new offset. - for ( var v = 0; v < 6; v += 2 ) { - var new_vid = faceVertices[ v + 1 ]; - if ( new_vid > - 1 && new_vid < offset.index ) - faceVertices[ v + 1 ] = - 1; - } - } - - //Reindex the face. - for ( var v = 0; v < 6; v += 2 ) { - var vid = faceVertices[ v ]; - var new_vid = faceVertices[ v + 1 ]; - - if ( new_vid === - 1 ) - new_vid = vertexPtr ++; - - vertexMap[ vid ] = new_vid; - revVertexMap[ new_vid ] = vid; - sortedIndices[ indexPtr ++ ] = new_vid - offset.index; //XXX overflows at 16bit - offset.count ++; - } - } - - /* Move all attribute values to map to the new computed indices , also expand the vertice stack to match our new vertexPtr. */ - this.reorderBuffers( sortedIndices, revVertexMap, vertexPtr ); - this.offsets = offsets; // TODO: Deprecate - this.drawcalls = offsets; - - /* - var orderTime = Date.now(); - console.log("Reorder time: "+(orderTime-s)+"ms"); - console.log("Duplicated "+duplicatedVertices+" vertices."); - console.log("Compute Buffers time: "+(Date.now()-s)+"ms"); - console.log("Draw offsets: "+offsets.length); - */ - - return offsets; - - }, - - merge: function ( geometry, offset ) { - - if ( geometry instanceof THREE.BufferGeometry === false ) { - - THREE.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; - - } - - if ( offset === undefined ) offset = 0; - - var attributes = this.attributes; - - for ( var key in attributes ) { - - if ( geometry.attributes[ key ] === undefined ) continue; - - var attribute1 = attributes[ key ]; - var attributeArray1 = attribute1.array; - - var attribute2 = geometry.attributes[ key ]; - var attributeArray2 = attribute2.array; - - var attributeSize = attribute2.itemSize; - - for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { - - attributeArray1[ j ] = attributeArray2[ i ]; - - } - - } - - return this; - - }, - - normalizeNormals: function () { - - var normals = this.attributes.normal.array; - - var x, y, z, n; - - for ( var i = 0, il = normals.length; i < il; i += 3 ) { - - x = normals[ i ]; - y = normals[ i + 1 ]; - z = normals[ i + 2 ]; - - n = 1.0 / Math.sqrt( x * x + y * y + z * z ); - - normals[ i ] *= n; - normals[ i + 1 ] *= n; - normals[ i + 2 ] *= n; - - } - - }, - - /* - reoderBuffers: - Reorder attributes based on a new indexBuffer and indexMap. - indexBuffer - Uint16Array of the new ordered indices. - indexMap - Int32Array where the position is the new vertex ID and the value the old vertex ID for each vertex. - vertexCount - Amount of total vertices considered in this reordering (in case you want to grow the vertice stack). - */ - reorderBuffers: function ( indexBuffer, indexMap, vertexCount ) { - - /* Create a copy of all attributes for reordering. */ - var sortedAttributes = {}; - for ( var attr in this.attributes ) { - if ( attr == 'index' ) - continue; - var sourceArray = this.attributes[ attr ].array; - sortedAttributes[ attr ] = new sourceArray.constructor( this.attributes[ attr ].itemSize * vertexCount ); - } - - /* Move attribute positions based on the new index map */ - for ( var new_vid = 0; new_vid < vertexCount; new_vid ++ ) { - var vid = indexMap[ new_vid ]; - for ( var attr in this.attributes ) { - if ( attr == 'index' ) - continue; - var attrArray = this.attributes[ attr ].array; - var attrSize = this.attributes[ attr ].itemSize; - var sortedAttr = sortedAttributes[ attr ]; - for ( var k = 0; k < attrSize; k ++ ) - sortedAttr[ new_vid * attrSize + k ] = attrArray[ vid * attrSize + k ]; - } - } - - /* Carry the new sorted buffers locally */ - this.attributes[ 'index' ].array = indexBuffer; - for ( var attr in this.attributes ) { - if ( attr == 'index' ) - continue; - this.attributes[ attr ].array = sortedAttributes[ attr ]; - this.attributes[ attr ].numItems = this.attributes[ attr ].itemSize * vertexCount; - } - }, - - toJSON: function () { - - var output = { - metadata: { - version: 4.0, - type: 'BufferGeometry', - generator: 'BufferGeometryExporter' - }, - uuid: this.uuid, - type: this.type, - data: { - attributes: {} - } - }; - - var attributes = this.attributes; - var offsets = this.offsets; - var boundingSphere = this.boundingSphere; - - for ( var key in attributes ) { - - var attribute = attributes[ key ]; - - var array = Array.prototype.slice.call( attribute.array ); - - output.data.attributes[ key ] = { - itemSize: attribute.itemSize, - type: attribute.array.constructor.name, - array: array - } - - } - - if ( offsets.length > 0 ) { - - output.data.offsets = JSON.parse( JSON.stringify( offsets ) ); - - } - - if ( boundingSphere !== null ) { - - output.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - } - - } - - return output; - - }, - - clone: function () { - - var geometry = new THREE.BufferGeometry(); - - for ( var attr in this.attributes ) { - - var sourceAttr = this.attributes[ attr ]; - geometry.addAttribute( attr, sourceAttr.clone() ); - - } - - for ( var i = 0, il = this.offsets.length; i < il; i ++ ) { - - var offset = this.offsets[ i ]; - - geometry.offsets.push( { - - start: offset.start, - index: offset.index, - count: offset.count - - } ); - - } - - return geometry; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - -}; - -THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); - // File:src/core/Geometry.js /** @@ -9455,7 +9692,7 @@ THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author zz85 / http://www.lab4games.net/zz85/blog - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Geometry = function () { @@ -9468,14 +9705,11 @@ THREE.Geometry = function () { this.type = 'Geometry'; this.vertices = []; - this.colors = []; // one-to-one vertex colors, used in Points and Line - + this.colors = []; this.faces = []; - this.faceVertexUvs = [ [] ]; this.morphTargets = []; - this.morphColors = []; this.morphNormals = []; this.skinWeights = []; @@ -9486,20 +9720,14 @@ THREE.Geometry = function () { this.boundingBox = null; this.boundingSphere = null; - this.hasTangents = false; - - this.dynamic = true; // the intermediate typed arrays will be deleted when set to false - // update flags this.verticesNeedUpdate = false; this.elementsNeedUpdate = false; this.uvsNeedUpdate = false; this.normalsNeedUpdate = false; - this.tangentsNeedUpdate = false; this.colorsNeedUpdate = false; this.lineDistancesNeedUpdate = false; - this.groupsNeedUpdate = false; }; @@ -9549,24 +9777,146 @@ THREE.Geometry.prototype = { }, + rotateX: function () { + + // rotate geometry around world x-axis + + var m1; + + return function rotateX( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationX( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateY: function () { + + // rotate geometry around world y-axis + + var m1; + + return function rotateY( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationY( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateZ: function () { + + // rotate geometry around world z-axis + + var m1; + + return function rotateZ( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationZ( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + translate: function () { + + // translate geometry + + var m1; + + return function translate( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeTranslation( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + scale: function () { + + // scale geometry + + var m1; + + return function scale( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeScale( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + lookAt: function () { + + var obj; + + return function lookAt( vector ) { + + if ( obj === undefined ) obj = new THREE.Object3D(); + + obj.lookAt( vector ); + + obj.updateMatrix(); + + this.applyMatrix( obj.matrix ); + + }; + + }(), + fromBufferGeometry: function ( geometry ) { var scope = this; + var indices = geometry.index !== null ? geometry.index.array : undefined; var attributes = geometry.attributes; - var vertices = attributes.position.array; - var indices = attributes.index !== undefined ? attributes.index.array : undefined; + var positions = attributes.position.array; var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; var colors = attributes.color !== undefined ? attributes.color.array : undefined; var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; + var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; + + if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; var tempNormals = []; var tempUVs = []; + var tempUVs2 = []; - for ( var i = 0, j = 0; i < vertices.length; i += 3, j += 2 ) { + for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { - scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + scope.vertices.push( new THREE.Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); if ( normals !== undefined ) { @@ -9586,14 +9936,22 @@ THREE.Geometry.prototype = { } + if ( uvs2 !== undefined ) { + + tempUVs2.push( new THREE.Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); + + } + } - var addFace = function ( a, b, c ) { + function addFace( a, b, c ) { var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; - scope.faces.push( new THREE.Face3( a, b, c, vertexNormals, vertexColors ) ); + var face = new THREE.Face3( a, b, c, vertexNormals, vertexColors ); + + scope.faces.push( face ); if ( uvs !== undefined ) { @@ -9601,25 +9959,30 @@ THREE.Geometry.prototype = { } - }; + if ( uvs2 !== undefined ) { + + scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); + + } + + } if ( indices !== undefined ) { - var drawcalls = geometry.drawcalls; + var groups = geometry.groups; - if ( drawcalls.length > 0 ) { + if ( groups.length > 0 ) { - for ( var i = 0; i < drawcalls.length; i ++ ) { + for ( var i = 0; i < groups.length; i ++ ) { - var drawcall = drawcalls[ i ]; + var group = groups[ i ]; - var start = drawcall.start; - var count = drawcall.count; - var index = drawcall.index; + var start = group.start; + var count = group.count; for ( var j = start, jl = start + count; j < jl; j += 3 ) { - addFace( index + indices[ j ], index + indices[ j + 1 ], index + indices[ j + 2 ] ); + addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ] ); } @@ -9637,7 +10000,7 @@ THREE.Geometry.prototype = { } else { - for ( var i = 0; i < vertices.length / 3; i += 3 ) { + for ( var i = 0; i < positions.length / 3; i += 3 ) { addFace( i, i + 1, i + 2 ); @@ -9669,12 +10032,35 @@ THREE.Geometry.prototype = { var offset = this.boundingBox.center().negate(); - this.applyMatrix( new THREE.Matrix4().setPosition( offset ) ); + this.translate( offset.x, offset.y, offset.z ); return offset; }, + normalize: function () { + + this.computeBoundingSphere(); + + var center = this.boundingSphere.center; + var radius = this.boundingSphere.radius; + + var s = radius === 0 ? 1 : 1.0 / radius; + + var matrix = new THREE.Matrix4(); + matrix.set( + s, 0, 0, - s * center.x, + 0, s, 0, - s * center.y, + 0, 0, s, - s * center.z, + 0, 0, 0, 1 + ); + + this.applyMatrix( matrix ); + + return this; + + }, + computeFaceNormals: function () { var cb = new THREE.Vector3(), ab = new THREE.Vector3(); @@ -9701,6 +10087,8 @@ THREE.Geometry.prototype = { computeVertexNormals: function ( areaWeighted ) { + if ( areaWeighted === undefined ) areaWeighted = true; + var v, vl, f, fl, face, vertices; vertices = new Array( this.vertices.length ); @@ -9761,9 +10149,27 @@ THREE.Geometry.prototype = { face = this.faces[ f ]; - face.vertexNormals[ 0 ] = vertices[ face.a ].clone(); - face.vertexNormals[ 1 ] = vertices[ face.b ].clone(); - face.vertexNormals[ 2 ] = vertices[ face.c ].clone(); + var vertexNormals = face.vertexNormals; + + if ( vertexNormals.length === 3 ) { + + vertexNormals[ 0 ].copy( vertices[ face.a ] ); + vertexNormals[ 1 ].copy( vertices[ face.b ] ); + vertexNormals[ 2 ].copy( vertices[ face.c ] ); + + } else { + + vertexNormals[ 0 ] = vertices[ face.a ].clone(); + vertexNormals[ 1 ] = vertices[ face.b ].clone(); + vertexNormals[ 2 ] = vertices[ face.c ].clone(); + + } + + } + + if ( this.faces.length > 0 ) { + + this.normalsNeedUpdate = true; } @@ -9888,106 +10294,7 @@ THREE.Geometry.prototype = { computeTangents: function () { - // based on http://www.terathon.com/code/tangent.html - // tangents go to vertices - - var f, fl, v, vl, i, vertexIndex, - face, uv, vA, vB, vC, uvA, uvB, uvC, - x1, x2, y1, y2, z1, z2, - s1, s2, t1, t2, r, t, test, - tan1 = [], tan2 = [], - sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), - tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), - n = new THREE.Vector3(), w; - - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - tan1[ v ] = new THREE.Vector3(); - tan2[ v ] = new THREE.Vector3(); - - } - - function handleTriangle( context, a, b, c, ua, ub, uc ) { - - vA = context.vertices[ a ]; - vB = context.vertices[ b ]; - vC = context.vertices[ c ]; - - uvA = uv[ ua ]; - uvB = uv[ ub ]; - uvC = uv[ uc ]; - - x1 = vB.x - vA.x; - x2 = vC.x - vA.x; - y1 = vB.y - vA.y; - y2 = vC.y - vA.y; - z1 = vB.z - vA.z; - z2 = vC.z - vA.z; - - s1 = uvB.x - uvA.x; - s2 = uvC.x - uvA.x; - t1 = uvB.y - uvA.y; - t2 = uvC.y - uvA.y; - - r = 1.0 / ( s1 * t2 - s2 * t1 ); - sdir.set( ( t2 * x1 - t1 * x2 ) * r, - ( t2 * y1 - t1 * y2 ) * r, - ( t2 * z1 - t1 * z2 ) * r ); - tdir.set( ( s1 * x2 - s2 * x1 ) * r, - ( s1 * y2 - s2 * y1 ) * r, - ( s1 * z2 - s2 * z1 ) * r ); - - tan1[ a ].add( sdir ); - tan1[ b ].add( sdir ); - tan1[ c ].add( sdir ); - - tan2[ a ].add( tdir ); - tan2[ b ].add( tdir ); - tan2[ c ].add( tdir ); - - } - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents - - handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); - - } - - var faceIndex = [ 'a', 'b', 'c', 'd' ]; - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); i ++ ) { - - n.copy( face.vertexNormals[ i ] ); - - vertexIndex = face[ faceIndex[ i ] ]; - - t = tan1[ vertexIndex ]; - - // Gram-Schmidt orthogonalize - - tmp.copy( t ); - tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); - - // Calculate handedness - - tmp2.crossVectors( face.vertexNormals[ i ], t ); - test = tmp2.dot( tan2[ vertexIndex ] ); - w = ( test < 0.0 ) ? - 1.0 : 1.0; - - face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w ); - - } - - } - - this.hasTangents = true; + console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); }, @@ -10038,7 +10345,7 @@ THREE.Geometry.prototype = { if ( geometry instanceof THREE.Geometry === false ) { - THREE.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); + console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); return; } @@ -10148,7 +10455,7 @@ THREE.Geometry.prototype = { if ( mesh instanceof THREE.Mesh === false ) { - THREE.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); + console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); return; } @@ -10167,11 +10474,11 @@ THREE.Geometry.prototype = { mergeVertices: function () { - var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique) + var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) var unique = [], changes = []; var v, key; - var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001 + var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 var precision = Math.pow( 10, precisionPoints ); var i, il, face; var indices, j, jl; @@ -10194,7 +10501,7 @@ THREE.Geometry.prototype = { } - }; + } // if faces are completely degenerate after merging vertices, we @@ -10216,18 +10523,21 @@ THREE.Geometry.prototype = { // if any duplicate vertices are found in a Face3 // we have to remove the face as nothing can be saved for ( var n = 0; n < 3; n ++ ) { - if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) { + + if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { dupIndex = n; faceIndicesToRemove.push( i ); break; } + } } for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + var idx = faceIndicesToRemove[ i ]; this.faces.splice( idx, 1 ); @@ -10248,19 +10558,68 @@ THREE.Geometry.prototype = { }, + sortFacesByMaterialIndex: function () { + + var faces = this.faces; + var length = faces.length; + + // tag faces + + for ( var i = 0; i < length; i ++ ) { + + faces[ i ]._id = i; + + } + + // sort faces + + function materialIndexSort( a, b ) { + + return a.materialIndex - b.materialIndex; + + } + + faces.sort( materialIndexSort ); + + // sort uvs + + var uvs1 = this.faceVertexUvs[ 0 ]; + var uvs2 = this.faceVertexUvs[ 1 ]; + + var newUvs1, newUvs2; + + if ( uvs1 && uvs1.length === length ) newUvs1 = []; + if ( uvs2 && uvs2.length === length ) newUvs2 = []; + + for ( var i = 0; i < length; i ++ ) { + + var id = faces[ i ]._id; + + if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); + if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); + + } + + if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; + if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; + + }, + toJSON: function () { - var output = { + var data = { metadata: { - version: 4.0, - type: 'BufferGeometry', - generator: 'BufferGeometryExporter' - }, - uuid: this.uuid, - type: this.type + version: 4.4, + type: 'Geometry', + generator: 'Geometry.toJSON' + } }; - if ( this.name !== "" ) output.name = this.name; + // standard Geometry serialization + + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; if ( this.parameters !== undefined ) { @@ -10268,11 +10627,11 @@ THREE.Geometry.prototype = { for ( var key in parameters ) { - if ( parameters[ key ] !== undefined ) output[ key ] = parameters[ key ]; + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } - return output; + return data; } @@ -10297,7 +10656,7 @@ THREE.Geometry.prototype = { var face = this.faces[ i ]; - var hasMaterial = false; // face.materialIndex !== undefined; + var hasMaterial = true; var hasFaceUv = false; // deprecated var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; var hasFaceNormal = face.normal.length() > 0; @@ -10307,7 +10666,7 @@ THREE.Geometry.prototype = { var faceType = 0; - faceType = setBit( faceType, 0, 0 ); + faceType = setBit( faceType, 0, 0 ); // isQuad faceType = setBit( faceType, 1, hasMaterial ); faceType = setBit( faceType, 2, hasFaceUv ); faceType = setBit( faceType, 3, hasFaceVertexUv ); @@ -10318,15 +10677,7 @@ THREE.Geometry.prototype = { faces.push( faceType ); faces.push( face.a, face.b, face.c ); - - - /* - if ( hasMaterial ) { - - faces.push( face.materialIndex ); - - } - */ + faces.push( face.materialIndex ); if ( hasFaceVertexUv ) { @@ -10380,7 +10731,7 @@ THREE.Geometry.prototype = { function setBit( value, position, enabled ) { - return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position) ); + return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); } @@ -10435,47 +10786,77 @@ THREE.Geometry.prototype = { } - output.data = {}; + data.data = {}; - output.data.vertices = vertices; - output.data.normals = normals; - if ( colors.length > 0 ) output.data.colors = colors; - if ( uvs.length > 0 ) output.data.uvs = [ uvs ]; // temporal backward compatibility - output.data.faces = faces; + data.data.vertices = vertices; + data.data.normals = normals; + if ( colors.length > 0 ) data.data.colors = colors; + if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility + data.data.faces = faces; - // - - return output; + return data; }, clone: function () { - var geometry = new THREE.Geometry(); + /* + // Handle primitives - var vertices = this.vertices; + var parameters = this.parameters; + + if ( parameters !== undefined ) { + + var values = []; + + for ( var key in parameters ) { + + values.push( parameters[ key ] ); + + } + + var geometry = Object.create( this.constructor.prototype ); + this.constructor.apply( geometry, values ); + return geometry; + + } + + return new this.constructor().copy( this ); + */ + + return new THREE.Geometry().copy( this ); + + }, + + copy: function ( source ) { + + this.vertices = []; + this.faces = []; + this.faceVertexUvs = [ [] ]; + + var vertices = source.vertices; for ( var i = 0, il = vertices.length; i < il; i ++ ) { - geometry.vertices.push( vertices[ i ].clone() ); + this.vertices.push( vertices[ i ].clone() ); } - var faces = this.faces; + var faces = source.faces; for ( var i = 0, il = faces.length; i < il; i ++ ) { - geometry.faces.push( faces[ i ].clone() ); + this.faces.push( faces[ i ].clone() ); } - for ( var i = 0, il = this.faceVertexUvs.length; i < il; i ++ ) { + for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { - var faceVertexUvs = this.faceVertexUvs[ i ]; + var faceVertexUvs = source.faceVertexUvs[ i ]; - if ( geometry.faceVertexUvs[ i ] === undefined ) { + if ( this.faceVertexUvs[ i ] === undefined ) { - geometry.faceVertexUvs[ i ] = []; + this.faceVertexUvs[ i ] = []; } @@ -10491,13 +10872,13 @@ THREE.Geometry.prototype = { } - geometry.faceVertexUvs[ i ].push( uvsCopy ); + this.faceVertexUvs[ i ].push( uvsCopy ); } } - return geometry; + return this; }, @@ -10513,6 +10894,5785 @@ THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); THREE.GeometryIdCount = 0; +// File:src/core/DirectGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.DirectGeometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'DirectGeometry'; + + this.indices = []; + this.vertices = []; + this.normals = []; + this.colors = []; + this.uvs = []; + this.uvs2 = []; + + this.groups = []; + + this.morphTargets = {}; + + this.skinWeights = []; + this.skinIndices = []; + + // this.lineDistances = []; + + this.boundingBox = null; + this.boundingSphere = null; + + // update flags + + this.verticesNeedUpdate = false; + this.normalsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.groupsNeedUpdate = false; + +}; + +THREE.DirectGeometry.prototype = { + + constructor: THREE.DirectGeometry, + + computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox, + computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere, + + computeFaceNormals: function () { + + console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' ); + + }, + + computeVertexNormals: function () { + + console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' ); + + }, + + computeGroups: function ( geometry ) { + + var group; + var groups = []; + var materialIndex; + + var faces = geometry.faces; + + for ( var i = 0; i < faces.length; i ++ ) { + + var face = faces[ i ]; + + // materials + + if ( face.materialIndex !== materialIndex ) { + + materialIndex = face.materialIndex; + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + group = { + start: i * 3, + materialIndex: materialIndex + }; + + } + + } + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); + + } + + this.groups = groups; + + }, + + fromGeometry: function ( geometry ) { + + var faces = geometry.faces; + var vertices = geometry.vertices; + var faceVertexUvs = geometry.faceVertexUvs; + + var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; + var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; + + // morphs + + var morphTargets = geometry.morphTargets; + var morphTargetsLength = morphTargets.length; + + var morphTargetsPosition; + + if ( morphTargetsLength > 0 ) { + + morphTargetsPosition = []; + + for ( var i = 0; i < morphTargetsLength; i ++ ) { + + morphTargetsPosition[ i ] = []; + + } + + this.morphTargets.position = morphTargetsPosition; + + } + + var morphNormals = geometry.morphNormals; + var morphNormalsLength = morphNormals.length; + + var morphTargetsNormal; + + if ( morphNormalsLength > 0 ) { + + morphTargetsNormal = []; + + for ( var i = 0; i < morphNormalsLength; i ++ ) { + + morphTargetsNormal[ i ] = []; + + } + + this.morphTargets.normal = morphTargetsNormal; + + } + + // skins + + var skinIndices = geometry.skinIndices; + var skinWeights = geometry.skinWeights; + + var hasSkinIndices = skinIndices.length === vertices.length; + var hasSkinWeights = skinWeights.length === vertices.length; + + // + + for ( var i = 0; i < faces.length; i ++ ) { + + var face = faces[ i ]; + + this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); + + var vertexNormals = face.vertexNormals; + + if ( vertexNormals.length === 3 ) { + + this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); + + } else { + + var normal = face.normal; + + this.normals.push( normal, normal, normal ); + + } + + var vertexColors = face.vertexColors; + + if ( vertexColors.length === 3 ) { + + this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); + + } else { + + var color = face.color; + + this.colors.push( color, color, color ); + + } + + if ( hasFaceVertexUv === true ) { + + var vertexUvs = faceVertexUvs[ 0 ][ i ]; + + if ( vertexUvs !== undefined ) { + + this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + + } else { + + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); + + this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); + + } + + } + + if ( hasFaceVertexUv2 === true ) { + + var vertexUvs = faceVertexUvs[ 1 ][ i ]; + + if ( vertexUvs !== undefined ) { + + this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + + } else { + + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); + + this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); + + } + + } + + // morphs + + for ( var j = 0; j < morphTargetsLength; j ++ ) { + + var morphTarget = morphTargets[ j ].vertices; + + morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); + + } + + for ( var j = 0; j < morphNormalsLength; j ++ ) { + + var morphNormal = morphNormals[ j ].vertexNormals[ i ]; + + morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); + + } + + // skins + + if ( hasSkinIndices ) { + + this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); + + } + + if ( hasSkinWeights ) { + + this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); + + } + + } + + this.computeGroups( geometry ); + + this.verticesNeedUpdate = geometry.verticesNeedUpdate; + this.normalsNeedUpdate = geometry.normalsNeedUpdate; + this.colorsNeedUpdate = geometry.colorsNeedUpdate; + this.uvsNeedUpdate = geometry.uvsNeedUpdate; + this.groupsNeedUpdate = geometry.groupsNeedUpdate; + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype ); + +// File:src/core/BufferGeometry.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'BufferGeometry'; + + this.index = null; + this.attributes = {}; + + this.morphAttributes = {}; + + this.groups = []; + + this.boundingBox = null; + this.boundingSphere = null; + + this.drawRange = { start: 0, count: Infinity }; + +}; + +THREE.BufferGeometry.prototype = { + + constructor: THREE.BufferGeometry, + + getIndex: function () { + + return this.index; + + }, + + setIndex: function ( index ) { + + this.index = index; + + }, + + addAttribute: function ( name, attribute ) { + + if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) { + + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + + this.addAttribute( name, new THREE.BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + + return; + + } + + if ( name === 'index' ) { + + console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); + this.setIndex( attribute ); + + return; + + } + + this.attributes[ name ] = attribute; + + return this; + + }, + + getAttribute: function ( name ) { + + return this.attributes[ name ]; + + }, + + removeAttribute: function ( name ) { + + delete this.attributes[ name ]; + + return this; + + }, + + addGroup: function ( start, count, materialIndex ) { + + this.groups.push( { + + start: start, + count: count, + materialIndex: materialIndex !== undefined ? materialIndex : 0 + + } ); + + }, + + clearGroups: function () { + + this.groups = []; + + }, + + setDrawRange: function ( start, count ) { + + this.drawRange.start = start; + this.drawRange.count = count; + + }, + + applyMatrix: function ( matrix ) { + + var position = this.attributes.position; + + if ( position !== undefined ) { + + matrix.applyToVector3Array( position.array ); + position.needsUpdate = true; + + } + + var normal = this.attributes.normal; + + if ( normal !== undefined ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + normalMatrix.applyToVector3Array( normal.array ); + normal.needsUpdate = true; + + } + + if ( this.boundingBox !== null ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere !== null ) { + + this.computeBoundingSphere(); + + } + + }, + + rotateX: function () { + + // rotate geometry around world x-axis + + var m1; + + return function rotateX( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationX( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateY: function () { + + // rotate geometry around world y-axis + + var m1; + + return function rotateY( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationY( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + rotateZ: function () { + + // rotate geometry around world z-axis + + var m1; + + return function rotateZ( angle ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeRotationZ( angle ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + translate: function () { + + // translate geometry + + var m1; + + return function translate( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeTranslation( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + scale: function () { + + // scale geometry + + var m1; + + return function scale( x, y, z ) { + + if ( m1 === undefined ) m1 = new THREE.Matrix4(); + + m1.makeScale( x, y, z ); + + this.applyMatrix( m1 ); + + return this; + + }; + + }(), + + lookAt: function () { + + var obj; + + return function lookAt( vector ) { + + if ( obj === undefined ) obj = new THREE.Object3D(); + + obj.lookAt( vector ); + + obj.updateMatrix(); + + this.applyMatrix( obj.matrix ); + + }; + + }(), + + center: function () { + + this.computeBoundingBox(); + + var offset = this.boundingBox.center().negate(); + + this.translate( offset.x, offset.y, offset.z ); + + return offset; + + }, + + setFromObject: function ( object ) { + + // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); + + var geometry = object.geometry; + + if ( object instanceof THREE.Points || object instanceof THREE.Line ) { + + var positions = new THREE.Float32Attribute( geometry.vertices.length * 3, 3 ); + var colors = new THREE.Float32Attribute( geometry.colors.length * 3, 3 ); + + this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); + this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); + + if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { + + var lineDistances = new THREE.Float32Attribute( geometry.lineDistances.length, 1 ); + + this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); + + } + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + } else if ( object instanceof THREE.Mesh ) { + + if ( geometry instanceof THREE.Geometry ) { + + this.fromGeometry( geometry ); + + } + + } + + return this; + + }, + + updateFromObject: function ( object ) { + + var geometry = object.geometry; + + if ( object instanceof THREE.Mesh ) { + + var direct = geometry.__directGeometry; + + if ( direct === undefined ) { + + return this.fromGeometry( geometry ); + + } + + direct.verticesNeedUpdate = geometry.verticesNeedUpdate; + direct.normalsNeedUpdate = geometry.normalsNeedUpdate; + direct.colorsNeedUpdate = geometry.colorsNeedUpdate; + direct.uvsNeedUpdate = geometry.uvsNeedUpdate; + direct.groupsNeedUpdate = geometry.groupsNeedUpdate; + + geometry.verticesNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.groupsNeedUpdate = false; + + geometry = direct; + + } + + if ( geometry.verticesNeedUpdate === true ) { + + var attribute = this.attributes.position; + + if ( attribute !== undefined ) { + + attribute.copyVector3sArray( geometry.vertices ); + attribute.needsUpdate = true; + + } + + geometry.verticesNeedUpdate = false; + + } + + if ( geometry.normalsNeedUpdate === true ) { + + var attribute = this.attributes.normal; + + if ( attribute !== undefined ) { + + attribute.copyVector3sArray( geometry.normals ); + attribute.needsUpdate = true; + + } + + geometry.normalsNeedUpdate = false; + + } + + if ( geometry.colorsNeedUpdate === true ) { + + var attribute = this.attributes.color; + + if ( attribute !== undefined ) { + + attribute.copyColorsArray( geometry.colors ); + attribute.needsUpdate = true; + + } + + geometry.colorsNeedUpdate = false; + + } + + if ( geometry.uvsNeedUpdate ) { + + var attribute = this.attributes.uv; + + if ( attribute !== undefined ) { + + attribute.copyVector2sArray( geometry.uvs ); + attribute.needsUpdate = true; + + } + + geometry.uvsNeedUpdate = false; + + } + + if ( geometry.lineDistancesNeedUpdate ) { + + var attribute = this.attributes.lineDistance; + + if ( attribute !== undefined ) { + + attribute.copyArray( geometry.lineDistances ); + attribute.needsUpdate = true; + + } + + geometry.lineDistancesNeedUpdate = false; + + } + + if ( geometry.groupsNeedUpdate ) { + + geometry.computeGroups( object.geometry ); + this.groups = geometry.groups; + + geometry.groupsNeedUpdate = false; + + } + + return this; + + }, + + fromGeometry: function ( geometry ) { + + geometry.__directGeometry = new THREE.DirectGeometry().fromGeometry( geometry ); + + return this.fromDirectGeometry( geometry.__directGeometry ); + + }, + + fromDirectGeometry: function ( geometry ) { + + var positions = new Float32Array( geometry.vertices.length * 3 ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); + + if ( geometry.normals.length > 0 ) { + + var normals = new Float32Array( geometry.normals.length * 3 ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); + + } + + if ( geometry.colors.length > 0 ) { + + var colors = new Float32Array( geometry.colors.length * 3 ); + this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); + + } + + if ( geometry.uvs.length > 0 ) { + + var uvs = new Float32Array( geometry.uvs.length * 2 ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); + + } + + if ( geometry.uvs2.length > 0 ) { + + var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); + this.addAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); + + } + + if ( geometry.indices.length > 0 ) { + + var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; + var indices = new TypeArray( geometry.indices.length * 3 ); + this.setIndex( new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); + + } + + // groups + + this.groups = geometry.groups; + + // morphs + + for ( var name in geometry.morphTargets ) { + + var array = []; + var morphTargets = geometry.morphTargets[ name ]; + + for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { + + var morphTarget = morphTargets[ i ]; + + var attribute = new THREE.Float32Attribute( morphTarget.length * 3, 3 ); + + array.push( attribute.copyVector3sArray( morphTarget ) ); + + } + + this.morphAttributes[ name ] = array; + + } + + // skinning + + if ( geometry.skinIndices.length > 0 ) { + + var skinIndices = new THREE.Float32Attribute( geometry.skinIndices.length * 4, 4 ); + this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); + + } + + if ( geometry.skinWeights.length > 0 ) { + + var skinWeights = new THREE.Float32Attribute( geometry.skinWeights.length * 4, 4 ); + this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); + + } + + // + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + return this; + + }, + + computeBoundingBox: function () { + + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + this.boundingBox.setFromArray( positions ); + + } + + if ( positions === undefined || positions.length === 0 ) { + + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); + + } + + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + + } + + }; + + }(), + + computeBoundingSphere: function () { + + var box = new THREE.Box3(); + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + var center = this.boundingSphere.center; + + box.setFromArray( positions ); + box.center( center ); + + // hoping to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + + var maxRadiusSq = 0; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.fromArray( positions, i ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + + } + + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + + if ( isNaN( this.boundingSphere.radius ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); + + } + + } + + }; + + }(), + + computeFaceNormals: function () { + + // backwards compatibility + + }, + + computeVertexNormals: function () { + + var index = this.index; + var attributes = this.attributes; + var groups = this.groups; + + if ( attributes.position ) { + + var positions = attributes.position.array; + + if ( attributes.normal === undefined ) { + + this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); + + } else { + + // reset existing normals to zero + + var array = attributes.normal.array; + + for ( var i = 0, il = array.length; i < il; i ++ ) { + + array[ i ] = 0; + + } + + } + + var normals = attributes.normal.array; + + var vA, vB, vC, + + pA = new THREE.Vector3(), + pB = new THREE.Vector3(), + pC = new THREE.Vector3(), + + cb = new THREE.Vector3(), + ab = new THREE.Vector3(); + + // indexed elements + + if ( index ) { + + var indices = index.array; + + if ( groups.length === 0 ) { + + this.addGroup( 0, indices.length ); + + } + + for ( var j = 0, jl = groups.length; j < jl; ++ j ) { + + var group = groups[ j ]; + + var start = group.start; + var count = group.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + vA = indices[ i + 0 ] * 3; + vB = indices[ i + 1 ] * 3; + vC = indices[ i + 2 ] * 3; + + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; + + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; + + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; + + } + + } + + } else { + + // non-indexed elements (unconnected triangle soup) + + for ( var i = 0, il = positions.length; i < il; i += 9 ) { + + pA.fromArray( positions, i ); + pB.fromArray( positions, i + 3 ); + pC.fromArray( positions, i + 6 ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ i ] = cb.x; + normals[ i + 1 ] = cb.y; + normals[ i + 2 ] = cb.z; + + normals[ i + 3 ] = cb.x; + normals[ i + 4 ] = cb.y; + normals[ i + 5 ] = cb.z; + + normals[ i + 6 ] = cb.x; + normals[ i + 7 ] = cb.y; + normals[ i + 8 ] = cb.z; + + } + + } + + this.normalizeNormals(); + + attributes.normal.needsUpdate = true; + + } + + }, + + merge: function ( geometry, offset ) { + + if ( geometry instanceof THREE.BufferGeometry === false ) { + + console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); + return; + + } + + if ( offset === undefined ) offset = 0; + + var attributes = this.attributes; + + for ( var key in attributes ) { + + if ( geometry.attributes[ key ] === undefined ) continue; + + var attribute1 = attributes[ key ]; + var attributeArray1 = attribute1.array; + + var attribute2 = geometry.attributes[ key ]; + var attributeArray2 = attribute2.array; + + var attributeSize = attribute2.itemSize; + + for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { + + attributeArray1[ j ] = attributeArray2[ i ]; + + } + + } + + return this; + + }, + + normalizeNormals: function () { + + var normals = this.attributes.normal.array; + + var x, y, z, n; + + for ( var i = 0, il = normals.length; i < il; i += 3 ) { + + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; + + n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; + + } + + }, + + toNonIndexed: function () { + + if ( this.index === null ) { + + console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); + return this; + + } + + var geometry2 = new THREE.BufferGeometry(); + + var indices = this.index.array; + var attributes = this.attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + + var array = attribute.array; + var itemSize = attribute.itemSize; + + var array2 = new array.constructor( indices.length * itemSize ); + + var index = 0, index2 = 0; + + for ( var i = 0, l = indices.length; i < l; i ++ ) { + + index = indices[ i ] * itemSize; + + for ( var j = 0; j < itemSize; j ++ ) { + + array2[ index2 ++ ] = array[ index ++ ]; + + } + + } + + geometry2.addAttribute( name, new THREE.BufferAttribute( array2, itemSize ) ); + + } + + return geometry2; + + }, + + toJSON: function () { + + var data = { + metadata: { + version: 4.4, + type: 'BufferGeometry', + generator: 'BufferGeometry.toJSON' + } + }; + + // standard BufferGeometry serialization + + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.parameters !== undefined ) { + + var parameters = this.parameters; + + for ( var key in parameters ) { + + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + + } + + return data; + + } + + data.data = { attributes: {} }; + + var index = this.index; + + if ( index !== null ) { + + var array = Array.prototype.slice.call( index.array ); + + data.data.index = { + type: index.array.constructor.name, + array: array + }; + + } + + var attributes = this.attributes; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + + var array = Array.prototype.slice.call( attribute.array ); + + data.data.attributes[ key ] = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: array + }; + + } + + var groups = this.groups; + + if ( groups.length > 0 ) { + + data.data.groups = JSON.parse( JSON.stringify( groups ) ); + + } + + var boundingSphere = this.boundingSphere; + + if ( boundingSphere !== null ) { + + data.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + }; + + } + + return data; + + }, + + clone: function () { + + /* + // Handle primitives + + var parameters = this.parameters; + + if ( parameters !== undefined ) { + + var values = []; + + for ( var key in parameters ) { + + values.push( parameters[ key ] ); + + } + + var geometry = Object.create( this.constructor.prototype ); + this.constructor.apply( geometry, values ); + return geometry; + + } + + return new this.constructor().copy( this ); + */ + + return new THREE.BufferGeometry().copy( this ); + + }, + + copy: function ( source ) { + + var index = source.index; + + if ( index !== null ) { + + this.setIndex( index.clone() ); + + } + + var attributes = source.attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + this.addAttribute( name, attribute.clone() ); + + } + + var groups = source.groups; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + this.addGroup( group.start, group.count ); + + } + + return this; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); + +THREE.BufferGeometry.MaxIndex = 65535; + +// File:src/core/InstancedBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ + +THREE.InstancedBufferGeometry = function () { + + THREE.BufferGeometry.call( this ); + + this.type = 'InstancedBufferGeometry'; + this.maxInstancedCount = undefined; + +}; + +THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry; + +THREE.InstancedBufferGeometry.prototype.addGroup = function ( start, count, instances ) { + + this.groups.push( { + + start: start, + count: count, + instances: instances + + } ); + +}; + +THREE.InstancedBufferGeometry.prototype.copy = function ( source ) { + + var index = source.index; + + if ( index !== null ) { + + this.setIndex( index.clone() ); + + } + + var attributes = source.attributes; + + for ( var name in attributes ) { + + var attribute = attributes[ name ]; + this.addAttribute( name, attribute.clone() ); + + } + + var groups = source.groups; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + this.addGroup( group.start, group.count, group.instances ); + + } + + return this; + +}; + +THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype ); + +// File:src/core/Uniform.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Uniform = function ( type, value ) { + + this.type = type; + this.value = value; + + this.dynamic = false; + +}; + +THREE.Uniform.prototype = { + + constructor: THREE.Uniform, + + onUpdate: function ( callback ) { + + this.dynamic = true; + this.onUpdateCallback = callback; + + return this; + + } + +}; + +// File:src/animation/AnimationClip.js + +/** + * + * Reusable set of Tracks that represent an animation. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationClip = function ( name, duration, tracks ) { + + this.name = name || THREE.Math.generateUUID(); + this.tracks = tracks; + this.duration = ( duration !== undefined ) ? duration : -1; + + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { + + this.resetDuration(); + + } + + // maybe only do these on demand, as doing them here could potentially slow down loading + // but leaving these here during development as this ensures a lot of testing of these functions + this.trim(); + this.optimize(); + +}; + +THREE.AnimationClip.prototype = { + + constructor: THREE.AnimationClip, + + resetDuration: function() { + + var tracks = this.tracks, + duration = 0; + + for ( var i = 0, n = tracks.length; i !== n; ++ i ) { + + var track = this.tracks[ i ]; + + duration = Math.max( + duration, track.times[ track.times.length - 1 ] ); + + } + + this.duration = duration; + + }, + + trim: function() { + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].trim( 0, this.duration ); + + } + + return this; + + }, + + optimize: function() { + + for ( var i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].optimize(); + + } + + return this; + + } + +}; + +// Static methods: + +Object.assign( THREE.AnimationClip, { + + parse: function( json ) { + + var tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / ( json.fps || 1.0 ); + + for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { + + tracks.push( THREE.KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); + + } + + return new THREE.AnimationClip( json.name, json.duration, tracks ); + + }, + + + toJSON: function( clip ) { + + var tracks = [], + clipTracks = clip.tracks; + + var json = { + + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks + + }; + + for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { + + tracks.push( THREE.KeyframeTrack.toJSON( clipTracks[ i ] ) ); + + } + + return json; + + }, + + + CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps ) { + + var numMorphTargets = morphTargetSequence.length; + var tracks = []; + + for ( var i = 0; i < numMorphTargets; i ++ ) { + + var times = []; + var values = []; + + times.push( + ( i + numMorphTargets - 1 ) % numMorphTargets, + i, + ( i + 1 ) % numMorphTargets ); + + values.push( 0, 1, 0 ); + + var order = THREE.AnimationUtils.getKeyframeOrder( times ); + times = THREE.AnimationUtils.sortedArray( times, 1, order ); + values = THREE.AnimationUtils.sortedArray( values, 1, order ); + + // if there is a key at the first frame, duplicate it as the + // last frame as well for perfect loop. + if ( times[ 0 ] === 0 ) { + + times.push( numMorphTargets ); + values.push( values[ 0 ] ); + + } + + tracks.push( + new THREE.NumberKeyframeTrack( + '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', + times, values + ).scale( 1.0 / fps ) ); + } + + return new THREE.AnimationClip( name, -1, tracks ); + + }, + + findByName: function( clipArray, name ) { + + for ( var i = 0; i < clipArray.length; i ++ ) { + + if ( clipArray[ i ].name === name ) { + + return clipArray[ i ]; + + } + } + + return null; + + }, + + CreateClipsFromMorphTargetSequences: function( morphTargets, fps ) { + + var animationToMorphTargets = {}; + + // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + var pattern = /^([\w-]*?)([\d]+)$/; + + // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { + + var morphTarget = morphTargets[ i ]; + var parts = morphTarget.name.match( pattern ); + + if ( parts && parts.length > 1 ) { + + var name = parts[ 1 ]; + + var animationMorphTargets = animationToMorphTargets[ name ]; + if ( ! animationMorphTargets ) { + + animationToMorphTargets[ name ] = animationMorphTargets = []; + + } + + animationMorphTargets.push( morphTarget ); + + } + + } + + var clips = []; + + for ( var name in animationToMorphTargets ) { + + clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) ); + + } + + return clips; + + }, + + // parse the animation.hierarchy format + parseAnimation: function( animation, bones, nodeName ) { + + if ( ! animation ) { + + console.error( " no animation in JSONLoader data" ); + return null; + + } + + var addNonemptyTrack = function( + trackType, trackName, animationKeys, propertyName, destTracks ) { + + // only return track if there are actually keys. + if ( animationKeys.length !== 0 ) { + + var times = []; + var values = []; + + THREE.AnimationUtils.flattenJSON( + animationKeys, times, values, propertyName ); + + // empty keys are filtered out, so check again + if ( times.length !== 0 ) { + + destTracks.push( new trackType( trackName, times, values ) ); + + } + + } + + }; + + var tracks = []; + + var clipName = animation.name || 'default'; + // automatic length determination in AnimationClip. + var duration = animation.length || -1; + var fps = animation.fps || 30; + + var hierarchyTracks = animation.hierarchy || []; + + for ( var h = 0; h < hierarchyTracks.length; h ++ ) { + + var animationKeys = hierarchyTracks[ h ].keys; + + // skip empty tracks + if ( ! animationKeys || animationKeys.length == 0 ) continue; + + // process morph targets in a way exactly compatible + // with AnimationHandler.init( animation ) + if ( animationKeys[0].morphTargets ) { + + // figure out all morph targets used in this track + var morphTargetNames = {}; + for ( var k = 0; k < animationKeys.length; k ++ ) { + + if ( animationKeys[k].morphTargets ) { + + for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { + + morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; + } + + } + + } + + // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + for ( var morphTargetName in morphTargetNames ) { + + var times = []; + var values = []; + + for ( var m = 0; + m !== animationKeys[k].morphTargets.length; ++ m ) { + + var animationKey = animationKeys[k]; + + times.push( animationKey.time ); + values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ) + + } + + tracks.push( new THREE.NumberKeyframeTrack( + '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); + + } + + duration = morphTargetNames.length * ( fps || 1.0 ); + + } else { + // ...assume skeletal animation + + var boneName = '.bones[' + bones[ h ].name + ']'; + + addNonemptyTrack( + THREE.VectorKeyframeTrack, boneName + '.position', + animationKeys, 'pos', tracks ); + + addNonemptyTrack( + THREE.QuaternionKeyframeTrack, boneName + '.quaternion', + animationKeys, 'rot', tracks ); + + addNonemptyTrack( + THREE.VectorKeyframeTrack, boneName + '.scale', + animationKeys, 'scl', tracks ); + + } + + } + + if ( tracks.length === 0 ) { + + return null; + + } + + var clip = new THREE.AnimationClip( clipName, duration, tracks ); + + return clip; + + } + +} ); + + +// File:src/animation/AnimationMixer.js + +/** + * + * Player for AnimationClips. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.AnimationMixer = function( root ) { + + this._root = root; + this._initMemoryManager(); + this._accuIndex = 0; + + this.time = 0; + + this.timeScale = 1.0; + +}; + +THREE.AnimationMixer.prototype = { + + constructor: THREE.AnimationMixer, + + // return an action for a clip optionally using a custom root target + // object (this method allocates a lot of dynamic memory in case a + // previously unknown clip/root combination is specified) + clipAction: function( clip, optionalRoot ) { + + var root = optionalRoot || this._root, + rootUuid = root.uuid, + clipName = ( typeof clip === 'string' ) ? clip : clip.name, + clipObject = ( clip !== clipName ) ? clip : null, + + actionsForClip = this._actionsByClip[ clipName ], + prototypeAction; + + if ( actionsForClip !== undefined ) { + + var existingAction = + actionsForClip.actionByRoot[ rootUuid ]; + + if ( existingAction !== undefined ) { + + return existingAction; + + } + + // we know the clip, so we don't have to parse all + // the bindings again but can just copy + prototypeAction = actionsForClip.knownActions[ 0 ]; + + // also, take the clip from the prototype action + clipObject = prototypeAction._clip; + + if ( clip !== clipName && clip !== clipObject ) { + + throw new Error( + "Different clips with the same name detected!" ); + + } + + } + + // clip must be known when specified via string + if ( clipObject === null ) return null; + + // allocate all resources required to run it + var newAction = new THREE. + AnimationMixer._Action( this, clipObject, optionalRoot ); + + this._bindAction( newAction, prototypeAction ); + + // and make the action known to the memory manager + this._addInactiveAction( newAction, clipName, rootUuid ); + + return newAction; + + }, + + // get an existing action + existingAction: function( clip, optionalRoot ) { + + var root = optionalRoot || this._root, + rootUuid = root.uuid, + clipName = ( typeof clip === 'string' ) ? clip : clip.name, + actionsForClip = this._actionsByClip[ clipName ]; + + if ( actionsForClip !== undefined ) { + + return actionsForClip.actionByRoot[ rootUuid ] || null; + + } + + return null; + + }, + + // deactivates all previously scheduled actions + stopAllAction: function() { + + var actions = this._actions, + nActions = this._nActiveActions, + bindings = this._bindings, + nBindings = this._nActiveBindings; + + this._nActiveActions = 0; + this._nActiveBindings = 0; + + for ( var i = 0; i !== nActions; ++ i ) { + + actions[ i ].reset(); + + } + + for ( var i = 0; i !== nBindings; ++ i ) { + + bindings[ i ].useCount = 0; + + } + + return this; + + }, + + // advance the time and update apply the animation + update: function( deltaTime ) { + + deltaTime *= this.timeScale; + + var actions = this._actions, + nActions = this._nActiveActions, + + time = this.time += deltaTime, + timeDirection = Math.sign( deltaTime ), + + accuIndex = this._accuIndex ^= 1; + + // run active actions + + for ( var i = 0; i !== nActions; ++ i ) { + + var action = actions[ i ]; + + if ( action.enabled ) { + + action._update( time, deltaTime, timeDirection, accuIndex ); + + } + + } + + // update scene graph + + var bindings = this._bindings, + nBindings = this._nActiveBindings; + + for ( var i = 0; i !== nBindings; ++ i ) { + + bindings[ i ].apply( accuIndex ); + + } + + return this; + + }, + + // return this mixer's root target object + getRoot: function() { + + return this._root; + + }, + + // free all resources specific to a particular clip + uncacheClip: function( clip ) { + + var actions = this._actions, + clipName = clip.name, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipName ]; + + if ( actionsForClip !== undefined ) { + + // note: just calling _removeInactiveAction would mess up the + // iteration state and also require updating the state we can + // just throw away + + var actionsToRemove = actionsForClip.knownActions; + + for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { + + var action = actionsToRemove[ i ]; + + this._deactivateAction( action ); + + var cacheIndex = action._cacheIndex, + lastInactiveAction = actions[ actions.length - 1 ]; + + action._cacheIndex = null; + action._byClipCacheIndex = null; + + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); + + this._removeInactiveBindingsForAction( action ); + + } + + delete actionsByClip[ clipName ]; + + } + + }, + + // free all resources specific to a particular root target object + uncacheRoot: function( root ) { + + var rootUuid = root.uuid, + actionsByClip = this._actionsByClip; + + for ( var clipName in actionsByClip ) { + + var actionByRoot = actionsByClip[ clipName ].actionByRoot, + action = actionByRoot[ rootUuid ]; + + if ( action !== undefined ) { + + this._deactivateAction( action ); + this._removeInactiveAction( action ); + + } + + } + + var bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ]; + + if ( bindingByName !== undefined ) { + + for ( var trackName in bindingByName ) { + + var binding = bindingByName[ trackName ]; + binding.restoreOriginalState(); + this._removeInactiveBinding( binding ); + + } + + } + + }, + + // remove a targeted clip from the cache + uncacheAction: function( clip, optionalRoot ) { + + var action = this.existingAction( clip, optionalRoot ); + + if ( action !== null ) { + + this._deactivateAction( action ); + this._removeInactiveAction( action ); + + } + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype ); + +THREE.AnimationMixer._Action = + function( mixer, clip, localRoot ) { + + this._mixer = mixer; + this._clip = clip; + this._localRoot = localRoot || null; + + var tracks = clip.tracks, + nTracks = tracks.length, + interpolants = new Array( nTracks ); + + var interpolantSettings = { + endingStart: THREE.ZeroCurvatureEnding, + endingEnd: THREE.ZeroCurvatureEnding + }; + + for ( var i = 0; i !== nTracks; ++ i ) { + + var interpolant = tracks[ i ].createInterpolant( null ); + interpolants[ i ] = interpolant; + interpolant.settings = interpolantSettings + + } + + this._interpolantSettings = interpolantSettings; + + this._interpolants = interpolants; // bound by the mixer + + // inside: PropertyMixer (managed by the mixer) + this._propertyBindings = new Array( nTracks ); + + this._cacheIndex = null; // for the memory manager + this._byClipCacheIndex = null; // for the memory manager + + this._timeScaleInterpolant = null; + this._weightInterpolant = null; + + this.loop = THREE.LoopRepeat; + this._loopCount = -1; + + // global mixer time when the action is to be started + // it's set back to 'null' upon start of the action + this._startTime = null; + + // scaled local time of the action + // gets clamped or wrapped to 0..clip.duration according to loop + this.time = 0; + + this.timeScale = 1; + this._effectiveTimeScale = 1; + + this.weight = 1; + this._effectiveWeight = 1; + + this.repetitions = Infinity; // no. of repetitions when looping + + this.paused = false; // false -> zero effective time scale + this.enabled = true; // true -> zero effective weight + + this.clampWhenFinished = false; // keep feeding the last frame? + + this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate + this.zeroSlopeAtEnd = true; // clips for start, loop and end + +}; + +THREE.AnimationMixer._Action.prototype = { + + constructor: THREE.AnimationMixer._Action, + + // State & Scheduling + + play: function() { + + this._mixer._activateAction( this ); + + return this; + + }, + + stop: function() { + + this._mixer._deactivateAction( this ); + + return this.reset(); + + }, + + reset: function() { + + this.paused = false; + this.enabled = true; + + this.time = 0; // restart clip + this._loopCount = -1; // forget previous loops + this._startTime = null; // forget scheduling + + return this.stopFading().stopWarping(); + + }, + + isRunning: function() { + + var start = this._startTime; + + return this.enabled && ! this.paused && this.timeScale !== 0 && + this._startTime === null && this._mixer._isActiveAction( this ) + + }, + + // return true when play has been called + isScheduled: function() { + + return this._mixer._isActiveAction( this ); + + }, + + startAt: function( time ) { + + this._startTime = time; + + return this; + + }, + + setLoop: function( mode, repetitions ) { + + this.loop = mode; + this.repetitions = repetitions; + + return this; + + }, + + // Weight + + // set the weight stopping any scheduled fading + // although .enabled = false yields an effective weight of zero, this + // method does *not* change .enabled, because it would be confusing + setEffectiveWeight: function( weight ) { + + this.weight = weight; + + // note: same logic as when updated at runtime + this._effectiveWeight = this.enabled ? weight : 0; + + return this.stopFading(); + + }, + + // return the weight considering fading and .enabled + getEffectiveWeight: function() { + + return this._effectiveWeight; + + }, + + fadeIn: function( duration ) { + + return this._scheduleFading( duration, 0, 1 ); + + }, + + fadeOut: function( duration ) { + + return this._scheduleFading( duration, 1, 0 ); + + }, + + crossFadeFrom: function( fadeOutAction, duration, warp ) { + + var mixer = this._mixer; + + fadeOutAction.fadeOut( duration ); + this.fadeIn( duration ); + + if( warp ) { + + var fadeInDuration = this._clip.duration, + fadeOutDuration = fadeOutAction._clip.duration, + + startEndRatio = fadeOutDuration / fadeInDuration, + endStartRatio = fadeInDuration / fadeOutDuration; + + fadeOutAction.warp( 1.0, startEndRatio, duration ); + this.warp( endStartRatio, 1.0, duration ); + + } + + return this; + + }, + + crossFadeTo: function( fadeInAction, duration, warp ) { + + return fadeInAction.crossFadeFrom( this, duration, warp ); + + }, + + stopFading: function() { + + var weightInterpolant = this._weightInterpolant; + + if ( weightInterpolant !== null ) { + + this._weightInterpolant = null; + this._mixer._takeBackControlInterpolant( weightInterpolant ); + + } + + return this; + + }, + + // Time Scale Control + + // set the weight stopping any scheduled warping + // although .paused = true yields an effective time scale of zero, this + // method does *not* change .paused, because it would be confusing + setEffectiveTimeScale: function( timeScale ) { + + this.timeScale = timeScale; + this._effectiveTimeScale = this.paused ? 0 :timeScale; + + return this.stopWarping(); + + }, + + // return the time scale considering warping and .paused + getEffectiveTimeScale: function() { + + return this._effectiveTimeScale; + + }, + + setDuration: function( duration ) { + + this.timeScale = this._clip.duration / duration; + + return this.stopWarping(); + + }, + + syncWith: function( action ) { + + this.time = action.time; + this.timeScale = action.timeScale; + + return this.stopWarping(); + + }, + + halt: function( duration ) { + + return this.warp( this._currentTimeScale, 0, duration ); + + }, + + warp: function( startTimeScale, endTimeScale, duration ) { + + var mixer = this._mixer, now = mixer.time, + interpolant = this._timeScaleInterpolant, + + timeScale = this.timeScale; + + if ( interpolant === null ) { + + interpolant = mixer._lendControlInterpolant(), + this._timeScaleInterpolant = interpolant; + + } + + var times = interpolant.parameterPositions, + values = interpolant.sampleValues; + + times[ 0 ] = now; + times[ 1 ] = now + duration; + + values[ 0 ] = startTimeScale / timeScale; + values[ 1 ] = endTimeScale / timeScale; + + return this; + + }, + + stopWarping: function() { + + var timeScaleInterpolant = this._timeScaleInterpolant; + + if ( timeScaleInterpolant !== null ) { + + this._timeScaleInterpolant = null; + this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); + + } + + return this; + + }, + + // Object Accessors + + getMixer: function() { + + return this._mixer; + + }, + + getClip: function() { + + return this._clip; + + }, + + getRoot: function() { + + return this._localRoot || this._mixer._root; + + }, + + // Interna + + _update: function( time, deltaTime, timeDirection, accuIndex ) { + // called by the mixer + + var startTime = this._startTime; + + if ( startTime !== null ) { + + // check for scheduled start of action + + var timeRunning = ( time - startTime ) * timeDirection; + if ( timeRunning < 0 || timeDirection === 0 ) { + + return; // yet to come / don't decide when delta = 0 + + } + + // start + + this._startTime = null; // unschedule + deltaTime = timeDirection * timeRunning; + + } + + // apply time scale and advance time + + deltaTime *= this._updateTimeScale( time ); + var clipTime = this._updateTime( deltaTime ); + + // note: _updateTime may disable the action resulting in + // an effective weight of 0 + + var weight = this._updateWeight( time ); + + if ( weight > 0 ) { + + var interpolants = this._interpolants; + var propertyMixers = this._propertyBindings; + + for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { + + interpolants[ j ].evaluate( clipTime ); + propertyMixers[ j ].accumulate( accuIndex, weight ); + + } + + } + + }, + + _updateWeight: function( time ) { + + var weight = 0; + + if ( this.enabled ) { + + weight = this.weight; + var interpolant = this._weightInterpolant; + + if ( interpolant !== null ) { + + var interpolantValue = interpolant.evaluate( time )[ 0 ]; + + weight *= interpolantValue; + + if ( time > interpolant.parameterPositions[ 1 ] ) { + + this.stopFading(); + + if ( interpolantValue === 0 ) { + + // faded out, disable + this.enabled = false; + + } + + } + + } + + } + + this._effectiveWeight = weight; + return weight; + + }, + + _updateTimeScale: function( time ) { + + var timeScale = 0; + + if ( ! this.paused ) { + + timeScale = this.timeScale; + + var interpolant = this._timeScaleInterpolant; + + if ( interpolant !== null ) { + + var interpolantValue = interpolant.evaluate( time )[ 0 ]; + + timeScale *= interpolantValue; + + if ( time > interpolant.parameterPositions[ 1 ] ) { + + this.stopWarping(); + + if ( timeScale === 0 ) { + + // motion has halted, pause + this.pause = true; + + } else { + + // warp done - apply final time scale + this.timeScale = timeScale; + + } + + } + + } + + } + + this._effectiveTimeScale = timeScale; + return timeScale; + + }, + + _updateTime: function( deltaTime ) { + + var time = this.time + deltaTime; + + if ( deltaTime === 0 ) return time; + + var duration = this._clip.duration, + + loop = this.loop, + loopCount = this._loopCount, + + pingPong = false; + + switch ( loop ) { + + case THREE.LoopOnce: + + if ( loopCount === -1 ) { + + // just started + + this.loopCount = 0; + this._setEndings( true, true, false ); + + } + + if ( time >= duration ) { + + time = duration; + + } else if ( time < 0 ) { + + time = 0; + + } else break; + + // reached the end + + if ( this.clampWhenFinished ) this.pause = true; + else this.enabled = false; + + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime < 0 ? -1 : 1 + } ); + + break; + + case THREE.LoopPingPong: + + pingPong = true; + + case THREE.LoopRepeat: + + if ( loopCount === -1 ) { + + // just started + + if ( deltaTime > 0 ) { + + loopCount = 0; + + this._setEndings( + true, this.repetitions === 0, pingPong ); + + } else { + + // when looping in reverse direction, the initial + // transition through zero counts as a repetition, + // so leave loopCount at -1 + + this._setEndings( + this.repetitions === 0, true, pingPong ); + + } + + } + + if ( time >= duration || time < 0 ) { + + // wrap around + + var loopDelta = Math.floor( time / duration ); // signed + time -= duration * loopDelta; + + loopCount += Math.abs( loopDelta ); + + var pending = this.repetitions - loopCount; + + if ( pending < 0 ) { + + // stop (switch state, clamp time, fire event) + + if ( this.clampWhenFinished ) this.paused = true; + else this.enabled = false; + + time = deltaTime > 0 ? duration : 0; + + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime > 0 ? 1 : -1 + } ); + + break; + + } else if ( pending === 0 ) { + + // transition to last round + + var atStart = deltaTime < 0; + this._setEndings( atStart, ! atStart, pingPong ); + + } else { + + this._setEndings( false, false, pingPong ); + + } + + this._loopCount = loopCount; + + this._mixer.dispatchEvent( { + type: 'loop', action: this, loopDelta: loopDelta + } ); + + } + + if ( loop === THREE.LoopPingPong && ( loopCount & 1 ) === 1 ) { + + // invert time for the "pong round" + + this.time = time; + + return duration - time; + + } + + break; + + } + + this.time = time; + + return time; + + }, + + _setEndings: function( atStart, atEnd, pingPong ) { + + var settings = this._interpolantSettings; + + if ( pingPong ) { + + settings.endingStart = THREE.ZeroSlopeEnding; + settings.endingEnd = THREE.ZeroSlopeEnding; + + } else { + + // assuming for LoopOnce atStart == atEnd == true + + if ( atStart ) { + + settings.endingStart = this.zeroSlopeAtStart ? + THREE.ZeroSlopeEnding : THREE.ZeroCurvatureEnding; + + } else { + + settings.endingStart = THREE.WrapAroundEnding; + + } + + if ( atEnd ) { + + settings.endingEnd = this.zeroSlopeAtEnd ? + THREE.ZeroSlopeEnding : THREE.ZeroCurvatureEnding; + + } else { + + settings.endingEnd = THREE.WrapAroundEnding; + + } + + } + + }, + + _scheduleFading: function( duration, weightNow, weightThen ) { + + var mixer = this._mixer, now = mixer.time, + interpolant = this._weightInterpolant; + + if ( interpolant === null ) { + + interpolant = mixer._lendControlInterpolant(), + this._weightInterpolant = interpolant; + + } + + var times = interpolant.parameterPositions, + values = interpolant.sampleValues; + + times[ 0 ] = now; values[ 0 ] = weightNow; + times[ 1 ] = now + duration; values[ 1 ] = weightThen; + + return this; + + } + +}; + +// Implementation details: + +Object.assign( THREE.AnimationMixer.prototype, { + + _bindAction: function( action, prototypeAction ) { + + var root = action._localRoot || this._root, + tracks = action._clip.tracks, + nTracks = tracks.length, + bindings = action._propertyBindings, + interpolants = action._interpolants, + rootUuid = root.uuid, + bindingsByRoot = this._bindingsByRootAndName, + bindingsByName = bindingsByRoot[ rootUuid ]; + + if ( bindingsByName === undefined ) { + + bindingsByName = {}; + bindingsByRoot[ rootUuid ] = bindingsByName; + + } + + for ( var i = 0; i !== nTracks; ++ i ) { + + var track = tracks[ i ], + trackName = track.name, + binding = bindingsByName[ trackName ]; + + if ( binding !== undefined ) { + + bindings[ i ] = binding; + + } else { + + binding = bindings[ i ]; + + if ( binding !== undefined ) { + + // existing binding, make sure the cache knows + + if ( binding._cacheIndex === null ) { + + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); + + } + + continue; + + } + + var path = prototypeAction && prototypeAction. + _propertyBindings[ i ].binding.parsedPath; + + binding = new THREE.PropertyMixer( + THREE.PropertyBinding.create( root, trackName, path ), + track.ValueTypeName, track.getValueSize() ); + + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); + + bindings[ i ] = binding; + + } + + interpolants[ i ].resultBuffer = binding.buffer; + + } + + }, + + _activateAction: function( action ) { + + if ( ! this._isActiveAction( action ) ) { + + if ( action._cacheIndex === null ) { + + // this action has been forgotten by the cache, but the user + // appears to be still using it -> rebind + + var rootUuid = ( action._localRoot || this._root ).uuid, + clipName = action._clip.name, + actionsForClip = this._actionsByClip[ clipName ]; + + this._bindAction( action, + actionsForClip && actionsForClip.knownActions[ 0 ] ); + + this._addInactiveAction( action, clipName, rootUuid ); + + } + + var bindings = action._propertyBindings; + + // increment reference counts / sort out state + for ( var i = 0, n = bindings.length; i !== n; ++ i ) { + + var binding = bindings[ i ]; + + if ( binding.useCount ++ === 0 ) { + + this._lendBinding( binding ); + binding.saveOriginalState(); + + } + + } + + this._lendAction( action ); + + } + + }, + + _deactivateAction: function( action ) { + + if ( this._isActiveAction( action ) ) { + + var bindings = action._propertyBindings; + + // decrement reference counts / sort out state + for ( var i = 0, n = bindings.length; i !== n; ++ i ) { + + var binding = bindings[ i ]; + + if ( -- binding.useCount === 0 ) { + + binding.restoreOriginalState(); + this._takeBackBinding( binding ); + + } + + } + + this._takeBackAction( action ); + + } + + }, + + // Memory manager + + _initMemoryManager: function() { + + this._actions = []; // 'nActiveActions' followed by inactive ones + this._nActiveActions = 0; + + this._actionsByClip = {}; + // inside: + // { + // knownActions: Array< _Action > - used as prototypes + // actionByRoot: _Action - lookup + // } + + + this._bindings = []; // 'nActiveBindings' followed by inactive ones + this._nActiveBindings = 0; + + this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > + + + this._controlInterpolants = []; // same game as above + this._nActiveControlInterpolants = 0; + + var scope = this; + + this.stats = { + + actions: { + get total() { return scope._actions.length; }, + get inUse() { return scope._nActiveActions; } + }, + bindings: { + get total() { return scope._bindings.length; }, + get inUse() { return scope._nActiveBindings; } + }, + controlInterpolants: { + get total() { return scope._controlInterpolants.length; }, + get inUse() { return scope._nActiveControlInterpolants; } + } + + }; + + }, + + // Memory management for _Action objects + + _isActiveAction: function( action ) { + + var index = action._cacheIndex; + return index !== null && index < this._nActiveActions; + + }, + + _addInactiveAction: function( action, clipName, rootUuid ) { + + var actions = this._actions, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipName ]; + + if ( actionsForClip === undefined ) { + + actionsForClip = { + + knownActions: [ action ], + actionByRoot: {} + + }; + + action._byClipCacheIndex = 0; + + actionsByClip[ clipName ] = actionsForClip; + + } else { + + var knownActions = actionsForClip.knownActions; + + action._byClipCacheIndex = knownActions.length; + knownActions.push( action ); + + } + + action._cacheIndex = actions.length; + actions.push( action ); + + actionsForClip.actionByRoot[ rootUuid ] = action; + + }, + + _removeInactiveAction: function( action ) { + + var actions = this._actions, + lastInactiveAction = actions[ actions.length - 1 ], + cacheIndex = action._cacheIndex; + + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); + + action._cacheIndex = null; + + + var clipName = action._clip.name, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipName ], + knownActionsForClip = actionsForClip.knownActions, + + lastKnownAction = + knownActionsForClip[ knownActionsForClip.length - 1 ], + + byClipCacheIndex = action._byClipCacheIndex; + + lastKnownAction._byClipCacheIndex = byClipCacheIndex; + knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; + knownActionsForClip.pop(); + + action._byClipCacheIndex = null; + + + var actionByRoot = actionsForClip.actionByRoot, + rootUuid = ( actions._localRoot || this._root ).uuid; + + delete actionByRoot[ rootUuid ]; + + if ( knownActionsForClip.length === 0 ) { + + delete actionsByClip[ clipName ]; + + } + + this._removeInactiveBindingsForAction( action ); + + }, + + _removeInactiveBindingsForAction: function( action ) { + + var bindings = action._propertyBindings; + for ( var i = 0, n = bindings.length; i !== n; ++ i ) { + + var binding = bindings[ i ]; + + if ( -- binding.referenceCount === 0 ) { + + this._removeInactiveBinding( binding ); + + } + + } + + }, + + _lendAction: function( action ) { + + // [ active actions | inactive actions ] + // [ active actions >| inactive actions ] + // s a + // <-swap-> + // a s + + var actions = this._actions, + prevIndex = action._cacheIndex, + + lastActiveIndex = this._nActiveActions ++, + + firstInactiveAction = actions[ lastActiveIndex ]; + + action._cacheIndex = lastActiveIndex; + actions[ lastActiveIndex ] = action; + + firstInactiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = firstInactiveAction; + + }, + + _takeBackAction: function( action ) { + + // [ active actions | inactive actions ] + // [ active actions |< inactive actions ] + // a s + // <-swap-> + // s a + + var actions = this._actions, + prevIndex = action._cacheIndex, + + firstInactiveIndex = -- this._nActiveActions, + + lastActiveAction = actions[ firstInactiveIndex ]; + + action._cacheIndex = firstInactiveIndex; + actions[ firstInactiveIndex ] = action; + + lastActiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = lastActiveAction; + + }, + + // Memory management for PropertyMixer objects + + _addInactiveBinding: function( binding, rootUuid, trackName ) { + + var bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ], + + bindings = this._bindings; + + if ( bindingByName === undefined ) { + + bindingByName = {}; + bindingsByRoot[ rootUuid ] = bindingByName; + + } + + bindingByName[ trackName ] = binding; + + binding._cacheIndex = bindings.length; + bindings.push( binding ); + + }, + + _removeInactiveBinding: function( binding ) { + + var bindings = this._bindings, + propBinding = binding.binding, + rootUuid = propBinding.rootNode.uuid, + trackName = propBinding.path, + bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ], + + lastInactiveBinding = bindings[ bindings.length - 1 ], + cacheIndex = binding._cacheIndex; + + lastInactiveBinding._cacheIndex = cacheIndex; + bindings[ cacheIndex ] = lastInactiveBinding; + bindings.pop(); + + delete bindingByName[ trackName ]; + + remove_empty_map: { + + for ( var _ in bindingByName ) break remove_empty_map; + + delete bindingsByRoot[ rootUuid ]; + + } + + }, + + _lendBinding: function( binding ) { + + var bindings = this._bindings, + prevIndex = binding._cacheIndex, + + lastActiveIndex = this._nActiveBindings ++, + + firstInactiveBinding = bindings[ lastActiveIndex ]; + + binding._cacheIndex = lastActiveIndex; + bindings[ lastActiveIndex ] = binding; + + firstInactiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = firstInactiveBinding; + + }, + + _takeBackBinding: function( binding ) { + + var bindings = this._bindings, + prevIndex = binding._cacheIndex, + + firstInactiveIndex = -- this._nActiveBindings, + + lastActiveBinding = bindings[ firstInactiveIndex ]; + + binding._cacheIndex = firstInactiveIndex; + bindings[ firstInactiveIndex ] = binding; + + lastActiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = lastActiveBinding; + + }, + + + // Memory management of Interpolants for weight and time scale + + _lendControlInterpolant: function() { + + var interpolants = this._controlInterpolants, + lastActiveIndex = this._nActiveControlInterpolants ++, + interpolant = interpolants[ lastActiveIndex ]; + + if ( interpolant === undefined ) { + + interpolant = new THREE.LinearInterpolant( + new Float32Array( 2 ), new Float32Array( 2 ), + 1, this._controlInterpolantsResultBuffer ); + + interpolant.__cacheIndex = lastActiveIndex; + interpolants[ lastActiveIndex ] = interpolant; + + } + + return interpolant; + + }, + + _takeBackControlInterpolant: function( interpolant ) { + + var interpolants = this._controlInterpolants, + prevIndex = interpolant.__cacheIndex, + + firstInactiveIndex = -- this._nActiveControlInterpolants, + + lastActiveInterpolant = interpolants[ firstInactiveIndex ]; + + interpolant.__cacheIndex = firstInactiveIndex; + interpolants[ firstInactiveIndex ] = interpolant; + + lastActiveInterpolant.__cacheIndex = prevIndex; + interpolants[ prevIndex ] = lastActiveInterpolant; + + }, + + _controlInterpolantsResultBuffer: new Float32Array( 1 ) + +} ); + + +// File:src/animation/AnimationObjectGroup.js + +/** + * + * A group of objects that receives a shared animation state. + * + * Usage: + * + * - Add objects you would otherwise pass as 'root' to the + * constructor or the .clipAction method of AnimationMixer. + * + * - Instead pass this object as 'root'. + * + * - You can also add and remove objects later when the mixer + * is running. + * + * Note: + * + * Objects of this class appear as one object to the mixer, + * so cache control of the individual objects must be done + * on the group. + * + * Limitation: + * + * - The animated properties must be compatible among the + * all objects in the group. + * + * - A single property can either be controlled through a + * target group or directly, but not both. + * + * @author tschw + */ + +THREE.AnimationObjectGroup = function( var_args ) { + + this.uuid = THREE.Math.generateUUID(); + + // cached objects followed by the active ones + this._objects = Array.prototype.slice.call( arguments ); + + this.nCachedObjects_ = 0; // threshold + // note: read by PropertyBinding.Composite + + var indices = {}; + this._indicesByUUID = indices; // for bookkeeping + + for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + + indices[ arguments[ i ].uuid ] = i; + + } + + this._paths = []; // inside: string + this._parsedPaths = []; // inside: { we don't care, here } + this._bindings = []; // inside: Array< PropertyBinding > + this._bindingsIndicesByPath = {}; // inside: indices in these arrays + + var scope = this; + + this.stats = { + + objects: { + get total() { return scope._objects.length; }, + get inUse() { return this.total - scope.nCachedObjects_; } + }, + + get bindingsPerObject() { return scope._bindings.length; } + + }; + +}; + +THREE.AnimationObjectGroup.prototype = { + + constructor: THREE.AnimationObjectGroup, + + add: function( var_args ) { + + var objects = this._objects, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_, + indicesByUUID = this._indicesByUUID, + paths = this._paths, + parsedPaths = this._parsedPaths, + bindings = this._bindings, + nBindings = bindings.length; + + for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + + var object = arguments[ i ], + uuid = object.uuid, + index = indicesByUUID[ uuid ]; + + if ( index === undefined ) { + + // unknown object -> add it to the ACTIVE region + + index = nObjects ++; + indicesByUUID[ uuid ] = index; + objects.push( object ); + + // accounting is done, now do the same for all bindings + + for ( var j = 0, m = nBindings; j !== m; ++ j ) { + + bindings[ j ].push( + new THREE.PropertyBinding( + object, paths[ j ], parsedPaths[ j ] ) ); + + } + + } else if ( index < nCachedObjects ) { + + var knownObject = objects[ index ]; + + // move existing object to the ACTIVE region + + var firstActiveIndex = -- nCachedObjects, + lastCachedObject = objects[ firstActiveIndex ]; + + indicesByUUID[ lastCachedObject.uuid ] = index; + objects[ index ] = lastCachedObject; + + indicesByUUID[ uuid ] = firstActiveIndex; + objects[ firstActiveIndex ] = object; + + // accounting is done, now do the same for all bindings + + for ( var j = 0, m = nBindings; j !== m; ++ j ) { + + var bindingsForPath = bindings[ j ], + lastCached = bindingsForPath[ firstActiveIndex ], + binding = bindingsForPath[ index ]; + + bindingsForPath[ index ] = lastCached; + + if ( binding === undefined ) { + + // since we do not bother to create new bindings + // for objects that are cached, the binding may + // or may not exist + + binding = new THREE.PropertyBinding( + object, paths[ j ], parsedPaths[ j ] ); + + } + + bindingsForPath[ firstActiveIndex ] = binding; + + } + + } else if ( objects[ index ] !== knownObject) { + + console.error( "Different objects with the same UUID " + + "detected. Clean the caches or recreate your " + + "infrastructure when reloading scenes..." ); + + } // else the object is already where we want it to be + + } // for arguments + + this.nCachedObjects_ = nCachedObjects; + + }, + + remove: function( var_args ) { + + var objects = this._objects, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_, + indicesByUUID = this._indicesByUUID, + bindings = this._bindings, + nBindings = bindings.length; + + for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + + var object = arguments[ i ], + uuid = object.uuid, + index = indicesByUUID[ uuid ]; + + if ( index !== undefined && index >= nCachedObjects ) { + + // move existing object into the CACHED region + + var lastCachedIndex = nCachedObjects ++, + firstActiveObject = objects[ lastCachedIndex ]; + + indicesByUUID[ firstActiveObject.uuid ] = index; + objects[ index ] = firstActiveObject; + + indicesByUUID[ uuid ] = lastCachedIndex; + objects[ lastCachedIndex ] = object; + + // accounting is done, now do the same for all bindings + + for ( var j = 0, m = nBindings; j !== m; ++ j ) { + + var bindingsForPath = bindings[ j ], + firstActive = bindingsForPath[ lastCachedIndex ], + binding = bindingsForPath[ index ]; + + bindingsForPath[ index ] = firstActive; + bindingsForPath[ lastCachedIndex ] = binding; + + } + + } + + } // for arguments + + this.nCachedObjects_ = nCachedObjects; + + }, + + // remove & forget + uncache: function( var_args ) { + + var objects = this._objects, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_, + indicesByUUID = this._indicesByUUID, + bindings = this._bindings, + nBindings = bindings.length; + + for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + + var object = arguments[ i ], + uuid = object.uuid, + index = indicesByUUID[ uuid ]; + + if ( index !== undefined ) { + + delete indicesByUUID[ uuid ]; + + if ( index < nCachedObjects ) { + + // object is cached, shrink the CACHED region + + var firstActiveIndex = -- nCachedObjects, + lastCachedObject = objects[ firstActiveIndex ], + lastIndex = -- nObjects, + lastObject = objects[ lastIndex ]; + + // last cached object takes this object's place + indicesByUUID[ lastCachedObject.uuid ] = index; + objects[ index ] = lastCachedObject; + + // last object goes to the activated slot and pop + indicesByUUID[ lastObject.uuid ] = firstActiveIndex; + objects[ firstActiveIndex ] = lastObject; + objects.pop(); + + // accounting is done, now do the same for all bindings + + for ( var j = 0, m = nBindings; j !== m; ++ j ) { + + var bindingsForPath = bindings[ j ], + lastCached = bindingsForPath[ firstActiveIndex ], + last = bindingsForPath[ lastIndex ]; + + bindingsForPath[ index ] = lastCached; + bindingsForPath[ firstActiveIndex ] = last; + bindingsForPath.pop(); + + } + + } else { + + // object is active, just swap with the last and pop + + var lastIndex = -- nObjects, + lastObject = objects[ lastIndex ]; + + indicesByUUID[ lastObject.uuid ] = index; + objects[ index ] = lastObject; + objects.pop(); + + // accounting is done, now do the same for all bindings + + for ( var j = 0, m = nBindings; j !== m; ++ j ) { + + var bindingsForPath = bindings[ j ]; + + bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; + bindingsForPath.pop(); + + } + + } // cached or active + + } // if object is known + + } // for arguments + + this.nCachedObjects_ = nCachedObjects; + + }, + + // Internal interface used by befriended PropertyBinding.Composite: + + subscribe_: function( path, parsedPath ) { + // returns an array of bindings for the given path that is changed + // according to the contained objects in the group + + var indicesByPath = this._bindingsIndicesByPath, + index = indicesByPath[ path ], + bindings = this._bindings; + + if ( index !== undefined ) return bindings[ index ]; + + var paths = this._paths, + parsedPaths = this._parsedPaths, + objects = this._objects, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_, + bindingsForPath = new Array( nObjects ); + + index = bindings.length; + + indicesByPath[ path ] = index; + + paths.push( path ); + parsedPaths.push( parsedPath ); + bindings.push( bindingsForPath ); + + for ( var i = nCachedObjects, + n = objects.length; i !== n; ++ i ) { + + var object = objects[ i ]; + + bindingsForPath[ i ] = + new THREE.PropertyBinding( object, path, parsedPath ); + + } + + return bindingsForPath; + + }, + + unsubscribe_: function( path ) { + // tells the group to forget about a property path and no longer + // update the array previously obtained with 'subscribe_' + + var indicesByPath = this._bindingsIndicesByPath, + index = indicesByPath[ path ]; + + if ( index !== undefined ) { + + var paths = this._paths, + parsedPaths = this._parsedPaths, + bindings = this._bindings, + lastBindingsIndex = bindings.length - 1, + lastBindings = bindings[ lastBindingsIndex ], + lastBindingsPath = path[ lastBindingsIndex ]; + + indicesByPath[ lastBindingsPath ] = index; + + bindings[ index ] = lastBindings; + bindings.pop(); + + parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; + parsedPaths.pop(); + + paths[ index ] = paths[ lastBindingsIndex ]; + paths.pop(); + + } + + } + +}; + + +// File:src/animation/AnimationUtils.js + +/** + * @author tschw + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ + +THREE.AnimationUtils = { + + // same as Array.prototype.slice, but also works on typed arrays + arraySlice: function( array, from, to ) { + + if ( THREE.AnimationUtils.isTypedArray( array ) ) { + + return new array.constructor( array.subarray( from, to ) ); + + } + + return array.slice( from, to ); + + }, + + // converts an array to a specific type + convertArray: function( array, type, forceClone ) { + + if ( ! array || // let 'undefined' and 'null' pass + ! forceClone && array.constructor === type ) return array; + + if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + + return new type( array ); // create typed array + + } + + return Array.prototype.slice.call( array ); // create Array + + }, + + isTypedArray: function( object ) { + + return ArrayBuffer.isView( object ) && + ! ( object instanceof DataView ); + + }, + + // returns an array by which times and values can be sorted + getKeyframeOrder: function( times ) { + + function compareTime( i, j ) { + + return times[ i ] - times[ j ]; + + } + + var n = times.length; + var result = new Array( n ); + for ( var i = 0; i !== n; ++ i ) result[ i ] = i; + + result.sort( compareTime ); + + return result; + + }, + + // uses the array previously returned by 'getKeyframeOrder' to sort data + sortedArray: function( values, stride, order ) { + + var nValues = values.length; + var result = new values.constructor( nValues ); + + for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { + + var srcOffset = order[ i ] * stride; + + for ( var j = 0; j !== stride; ++ j ) { + + result[ dstOffset ++ ] = values[ srcOffset + j ]; + + } + + } + + return result; + + }, + + // function for parsing AOS keyframe formats + flattenJSON: function( jsonKeys, times, values, valuePropertyName ) { + + var i = 1, key = jsonKeys[ 0 ]; + + while ( key !== undefined && key[ valuePropertyName ] === undefined ) { + + key = jsonKeys[ i ++ ]; + + } + + if ( key === undefined ) return; // no data + + var value = key[ valuePropertyName ]; + if ( value === undefined ) return; // no data + + if ( Array.isArray( value ) ) { + + do { + + value = key[ valuePropertyName ]; + + if ( value !== undefined ) { + + times.push( key.time ); + values.push.apply( values, value ); // push all elements + + } + + key = jsonKeys[ i ++ ]; + + } while ( key !== undefined ); + + } else if ( value.toArray !== undefined ) { + // ...assume THREE.Math-ish + + do { + + value = key[ valuePropertyName ]; + + if ( value !== undefined ) { + + times.push( key.time ); + value.toArray( values, values.length ); + + } + + key = jsonKeys[ i ++ ]; + + } while ( key !== undefined ); + + } else { + // otherwise push as-is + + do { + + value = key[ valuePropertyName ]; + + if ( value !== undefined ) { + + times.push( key.time ); + values.push( value ); + + } + + key = jsonKeys[ i ++ ]; + + } while ( key !== undefined ); + + } + + } + +}; + +// File:src/animation/KeyframeTrack.js + +/** + * + * A timed sequence of keyframes for a specific property. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.KeyframeTrack = function ( name, times, values, interpolation ) { + + if( name === undefined ) throw new Error( "track name is undefined" ); + + if( times === undefined || times.length === 0 ) { + + throw new Error( "no keyframes in track named " + name ); + + } + + this.name = name; + + this.times = THREE.AnimationUtils.convertArray( times, this.TimeBufferType ); + this.values = THREE.AnimationUtils.convertArray( values, this.ValueBufferType ); + + this.setInterpolation( interpolation || this.DefaultInterpolation ); + + this.validate(); + this.optimize(); + +}; + +THREE.KeyframeTrack.prototype = { + + constructor: THREE.KeyframeTrack, + + TimeBufferType: Float32Array, + ValueBufferType: Float32Array, + + DefaultInterpolation: THREE.InterpolateLinear, + + InterpolantFactoryMethodDiscrete: function( result ) { + + return new THREE.DiscreteInterpolant( + this.times, this.values, this.getValueSize(), result ); + + }, + + InterpolantFactoryMethodLinear: function( result ) { + + return new THREE.LinearInterpolant( + this.times, this.values, this.getValueSize(), result ); + + }, + + InterpolantFactoryMethodSmooth: function( result ) { + + return new THREE.CubicInterpolant( + this.times, this.values, this.getValueSize(), result ); + + }, + + setInterpolation: function( interpolation ) { + + var factoryMethod = undefined; + + switch ( interpolation ) { + + case THREE.InterpolateDiscrete: + + factoryMethod = this.InterpolantFactoryMethodDiscrete; + + break; + + case THREE.InterpolateLinear: + + factoryMethod = this.InterpolantFactoryMethodLinear; + + break; + + case THREE.InterpolateSmooth: + + factoryMethod = this.InterpolantFactoryMethodSmooth; + + break; + + } + + if ( factoryMethod === undefined ) { + + var message = "unsupported interpolation for " + + this.ValueTypeName + " keyframe track named " + this.name; + + if ( this.createInterpolant === undefined ) { + + // fall back to default, unless the default itself is messed up + if ( interpolation !== this.DefaultInterpolation ) { + + this.setInterpolation( this.DefaultInterpolation ); + + } else { + + throw new Error( message ); // fatal, in this case + + } + + } + + console.warn( message ); + return; + + } + + this.createInterpolant = factoryMethod; + + }, + + getInterpolation: function() { + + switch ( this.createInterpolant ) { + + case this.InterpolantFactoryMethodDiscrete: + + return THREE.InterpolateDiscrete; + + case this.InterpolantFactoryMethodLinear: + + return THREE.InterpolateLinear; + + case this.InterpolantFactoryMethodSmooth: + + return THREE.InterpolateSmooth; + + } + + }, + + getValueSize: function() { + + return this.values.length / this.times.length; + + }, + + // move all keyframes either forwards or backwards in time + shift: function( timeOffset ) { + + if( timeOffset !== 0.0 ) { + + var times = this.times; + + for( var i = 0, n = times.length; i !== n; ++ i ) { + + times[ i ] += timeOffset; + + } + + } + + return this; + + }, + + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale: function( timeScale ) { + + if( timeScale !== 1.0 ) { + + var times = this.times; + + for( var i = 0, n = times.length; i !== n; ++ i ) { + + times[ i ] *= timeScale; + + } + + } + + return this; + + }, + + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim: function( startTime, endTime ) { + + var times = this.times, + nKeys = times.length, + from = 0, + to = nKeys - 1; + + while ( from !== nKeys && times[ from ] < startTime ) ++ from; + while ( to !== -1 && times[ to ] > endTime ) -- to; + + ++ to; // inclusive -> exclusive bound + + if( from !== 0 || to !== nKeys ) { + + // empty tracks are forbidden, so keep at least one keyframe + if ( from >= to ) to = Math.max( to , 1 ), from = to - 1; + + var stride = this.getValueSize(); + this.times = THREE.AnimationUtils.arraySlice( times, from, to ); + this.values = THREE.AnimationUtils. + arraySlice( this.values, from * stride, to * stride ); + + } + + return this; + + }, + + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + validate: function() { + + var valid = true; + + var valueSize = this.getValueSize(); + if ( valueSize - Math.floor( valueSize ) !== 0 ) { + + console.error( "invalid value size in track", this ); + valid = false; + + } + + var times = this.times, + values = this.values, + + nKeys = times.length; + + if( nKeys === 0 ) { + + console.error( "track is empty", this ); + valid = false; + + } + + var prevTime = null; + + for( var i = 0; i !== nKeys; i ++ ) { + + var currTime = times[ i ]; + + if ( typeof currTime === 'number' && isNaN( currTime ) ) { + + console.error( "time is not a valid number", this, i, currTime ); + valid = false; + break; + + } + + if( prevTime !== null && prevTime > currTime ) { + + console.error( "out of order keys", this, i, currTime, prevTime ); + valid = false; + break; + + } + + prevTime = currTime; + + } + + if ( values !== undefined ) { + + if ( THREE.AnimationUtils.isTypedArray( values ) ) { + + for ( var i = 0, n = values.length; i !== n; ++ i ) { + + var value = values[ i ]; + + if ( isNaN( value ) ) { + + console.error( "value is not a valid number", this, i, value ); + valid = false; + break; + + } + + } + + } + + } + + return valid; + + }, + + // removes equivalent sequential keys as common in morph target sequences + // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) + optimize: function() { + + var times = this.times, + values = this.values, + stride = this.getValueSize(), + + writeIndex = 1; + + for( var i = 1, n = times.length - 1; i <= n; ++ i ) { + + var keep = false; + + var time = times[ i ]; + var timeNext = times[ i + 1 ]; + + // remove adjacent keyframes scheduled at the same time + + if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { + + // remove unnecessary keyframes same as their neighbors + var offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; + + for ( var j = 0; j !== stride; ++ j ) { + + var value = values[ offset + j ]; + + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { + + keep = true; + break; + + } + + } + + } + + // in-place compaction + + if ( keep ) { + + if ( i !== writeIndex ) { + + times[ writeIndex ] = times[ i ]; + + var readOffset = i * stride, + writeOffset = writeIndex * stride; + + for ( var j = 0; j !== stride; ++ j ) { + + values[ writeOffset + j ] = values[ readOffset + j ]; + + } + + + } + + ++ writeIndex; + + } + + } + + if ( writeIndex !== times.length ) { + + this.times = THREE.AnimationUtils.arraySlice( times, 0, writeIndex ); + this.values = THREE.AnimationUtils.arraySlice( values, 0, writeIndex * stride ); + + } + + return this; + + } + +}; + +// Static methods: + +Object.assign( THREE.KeyframeTrack, { + + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): + + parse: function( json ) { + + if( json.type === undefined ) { + + throw new Error( "track type undefined, can not parse" ); + + } + + var trackType = THREE.KeyframeTrack._getTrackTypeForValueTypeName( json.type ); + + if ( json.times === undefined ) { + + console.warn( "legacy JSON format detected, converting" ); + + var times = [], values = []; + + THREE.AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); + + json.times = times; + json.values = values; + + } + + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { + + return trackType.parse( json ); + + } else { + + // by default, we asssume a constructor compatible with the base + return new trackType( + json.name, json.times, json.values, json.interpolation ); + + } + + }, + + toJSON: function( track ) { + + var trackType = track.constructor; + + var json; + + // derived classes can define a static toJSON method + if ( trackType.toJSON !== undefined ) { + + json = trackType.toJSON( track ); + + } else { + + // by default, we assume the data can be serialized as-is + json = { + + 'name': track.name, + 'times': THREE.AnimationUtils.convertArray( track.times, Array ), + 'values': THREE.AnimationUtils.convertArray( track.values, Array ) + + }; + + var interpolation = track.getInterpolation(); + + if ( interpolation !== track.DefaultInterpolation ) { + + json.interpolation = interpolation; + + } + + } + + json.type = track.ValueTypeName; // mandatory + + return json; + + }, + + _getTrackTypeForValueTypeName: function( typeName ) { + + switch( typeName.toLowerCase() ) { + + case "scalar": + case "double": + case "float": + case "number": + case "integer": + + return THREE.NumberKeyframeTrack; + + case "vector": + case "vector2": + case "vector3": + case "vector4": + + return THREE.VectorKeyframeTrack; + + case "color": + + return THREE.ColorKeyframeTrack; + + case "quaternion": + + return THREE.QuaternionKeyframeTrack; + + case "bool": + case "boolean": + + return THREE.BooleanKeyframeTrack; + + case "string": + + return THREE.StringKeyframeTrack; + + }; + + throw new Error( "Unsupported typeName: " + typeName ); + + } + +} ); + +// File:src/animation/PropertyBinding.js + +/** + * + * A reference to a real property in the scene graph. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.PropertyBinding = function ( rootNode, path, parsedPath ) { + + this.path = path; + this.parsedPath = parsedPath || + THREE.PropertyBinding.parseTrackName( path ); + + this.node = THREE.PropertyBinding.findNode( + rootNode, this.parsedPath.nodeName ) || rootNode; + + this.rootNode = rootNode; + +}; + +THREE.PropertyBinding.prototype = { + + constructor: THREE.PropertyBinding, + + getValue: function getValue_unbound( targetArray, offset ) { + + this.bind(); + this.getValue( targetArray, offset ); + + // Note: This class uses a State pattern on a per-method basis: + // 'bind' sets 'this.getValue' / 'setValue' and shadows the + // prototype version of these methods with one that represents + // the bound state. When the property is not found, the methods + // become no-ops. + + }, + + setValue: function getValue_unbound( sourceArray, offset ) { + + this.bind(); + this.setValue( sourceArray, offset ); + + }, + + // create getter / setter pair for a property in the scene graph + bind: function() { + + var targetObject = this.node, + parsedPath = this.parsedPath, + + objectName = parsedPath.objectName, + propertyName = parsedPath.propertyName, + propertyIndex = parsedPath.propertyIndex; + + if ( ! targetObject ) { + + targetObject = THREE.PropertyBinding.findNode( + this.rootNode, parsedPath.nodeName ) || this.rootNode; + + this.node = targetObject; + + } + + // set fail state so we can just 'return' on error + this.getValue = this._getValue_unavailable; + this.setValue = this._setValue_unavailable; + + // ensure there is a value node + if ( ! targetObject ) { + + console.error( " trying to update node for track: " + this.path + " but it wasn't found." ); + return; + + } + + if( objectName ) { + + var objectIndex = parsedPath.objectIndex; + + // special cases were we need to reach deeper into the hierarchy to get the face materials.... + switch ( objectName ) { + + case 'materials': + + if( ! targetObject.material ) { + + console.error( ' can not bind to material as node does not have a material', this ); + return; + + } + + if( ! targetObject.material.materials ) { + + console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); + return; + + } + + targetObject = targetObject.material.materials; + + break; + + case 'bones': + + if( ! targetObject.skeleton ) { + + console.error( ' can not bind to bones as node does not have a skeleton', this ); + return; + + } + + // potential future optimization: skip this if propertyIndex is already an integer + // and convert the integer string to a true integer. + + targetObject = targetObject.skeleton.bones; + + // support resolving morphTarget names into indices. + for ( var i = 0; i < targetObject.length; i ++ ) { + + if ( targetObject[i].name === objectIndex ) { + + objectIndex = i; + break; + + } + + } + + break; + + default: + + if ( targetObject[ objectName ] === undefined ) { + + console.error( ' can not bind to objectName of node, undefined', this ); + return; + + } + + targetObject = targetObject[ objectName ]; + + } + + + if ( objectIndex !== undefined ) { + + if( targetObject[ objectIndex ] === undefined ) { + + console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); + return; + + } + + targetObject = targetObject[ objectIndex ]; + + } + + } + + // resolve property + var nodeProperty = targetObject[ propertyName ]; + + if ( ! nodeProperty ) { + + var nodeName = parsedPath.nodeName; + + console.error( " trying to update property for track: " + nodeName + + '.' + propertyName + " but it wasn't found.", targetObject ); + return; + + } + + // determine versioning scheme + var versioning = this.Versioning.None; + + if ( targetObject.needsUpdate !== undefined ) { // material + + versioning = this.Versioning.NeedsUpdate; + this.targetObject = targetObject; + + } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform + + versioning = this.Versioning.MatrixWorldNeedsUpdate; + this.targetObject = targetObject; + + } + + // determine how the property gets bound + var bindingType = this.BindingType.Direct; + + if ( propertyIndex !== undefined ) { + // access a sub element of the property array (only primitives are supported right now) + + if ( propertyName === "morphTargetInfluences" ) { + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + + // support resolving morphTarget names into indices. + if ( ! targetObject.geometry ) { + + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); + return; + + } + + if ( ! targetObject.geometry.morphTargets ) { + + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); + return; + + } + + for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { + + if ( targetObject.geometry.morphTargets[i].name === propertyIndex ) { + + propertyIndex = i; + break; + + } + + } + + } + + bindingType = this.BindingType.ArrayElement; + + this.resolvedProperty = nodeProperty; + this.propertyIndex = propertyIndex; + + } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { + // must use copy for Object3D.Euler/Quaternion + + bindingType = this.BindingType.HasFromToArray; + + this.resolvedProperty = nodeProperty; + + } else if ( nodeProperty.length !== undefined ) { + + bindingType = this.BindingType.EntireArray; + + this.resolvedProperty = nodeProperty; + + } else { + + this.propertyName = propertyName; + + } + + // select getter / setter + this.getValue = this.GetterByBindingType[ bindingType ]; + this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; + + }, + + unbind: function() { + + this.node = null; + + // back to the prototype version of getValue / setValue + // note: avoiding to mutate the shape of 'this' via 'delete' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; + + } + +}; + +Object.assign( THREE.PropertyBinding.prototype, { // prototype, continued + + // these are used to "bind" a nonexistent property + _getValue_unavailable: function() {}, + _setValue_unavailable: function() {}, + + // initial state of these methods that calls 'bind' + _getValue_unbound: THREE.PropertyBinding.prototype.getValue, + _setValue_unbound: THREE.PropertyBinding.prototype.setValue, + + BindingType: { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 + }, + + Versioning: { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 + }, + + GetterByBindingType: [ + + function getValue_direct( buffer, offset ) { + + buffer[ offset ] = this.node[ this.propertyName ]; + + }, + + function getValue_array( buffer, offset ) { + + var source = this.resolvedProperty; + + for ( var i = 0, n = source.length; i !== n; ++ i ) { + + buffer[ offset ++ ] = source[ i ]; + + } + + }, + + function getValue_arrayElement( buffer, offset ) { + + buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; + + }, + + function getValue_toArray( buffer, offset ) { + + this.resolvedProperty.toArray( buffer, offset ); + + } + + ], + + SetterByBindingTypeAndVersioning: [ + + [ + // Direct + + function setValue_direct( buffer, offset ) { + + this.node[ this.propertyName ] = buffer[ offset ]; + + }, + + function setValue_direct_setNeedsUpdate( buffer, offset ) { + + this.node[ this.propertyName ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; + + }, + + function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { + + this.node[ this.propertyName ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; + + } + + ], [ + + // EntireArray + + function setValue_array( buffer, offset ) { + + var dest = this.resolvedProperty; + + for ( var i = 0, n = dest.length; i !== n; ++ i ) { + + dest[ i ] = buffer[ offset ++ ]; + + } + + }, + + function setValue_array_setNeedsUpdate( buffer, offset ) { + + var dest = this.resolvedProperty; + + for ( var i = 0, n = dest.length; i !== n; ++ i ) { + + dest[ i ] = buffer[ offset ++ ]; + + } + + this.targetObject.needsUpdate = true; + + }, + + function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { + + var dest = this.resolvedProperty; + + for ( var i = 0, n = dest.length; i !== n; ++ i ) { + + dest[ i ] = buffer[ offset ++ ]; + + } + + this.targetObject.matrixWorldNeedsUpdate = true; + + } + + ], [ + + // ArrayElement + + function setValue_arrayElement( buffer, offset ) { + + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + + }, + + function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { + + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; + + }, + + function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { + + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; + + } + + ], [ + + // HasToFromArray + + function setValue_fromArray( buffer, offset ) { + + this.resolvedProperty.fromArray( buffer, offset ); + + }, + + function setValue_fromArray_setNeedsUpdate( buffer, offset ) { + + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.needsUpdate = true; + + }, + + function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { + + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.matrixWorldNeedsUpdate = true; + + } + + ] + + ] + +} ); + +THREE.PropertyBinding.Composite = + function( targetGroup, path, optionalParsedPath ) { + + var parsedPath = optionalParsedPath || + THREE.PropertyBinding.parseTrackName( path ); + + this._targetGroup = targetGroup; + this._bindings = targetGroup.subscribe_( path, parsedPath ); + +}; + +THREE.PropertyBinding.Composite.prototype = { + + constructor: THREE.PropertyBinding.Composite, + + getValue: function( array, offset ) { + + this.bind(); // bind all binding + + var firstValidIndex = this._targetGroup.nCachedObjects_, + binding = this._bindings[ firstValidIndex ]; + + // and only call .getValue on the first + if ( binding !== undefined ) binding.getValue( array, offset ); + + }, + + setValue: function( array, offset ) { + + var bindings = this._bindings; + + for ( var i = this._targetGroup.nCachedObjects_, + n = bindings.length; i !== n; ++ i ) { + + bindings[ i ].setValue( array, offset ); + + } + + }, + + bind: function() { + + var bindings = this._bindings; + + for ( var i = this._targetGroup.nCachedObjects_, + n = bindings.length; i !== n; ++ i ) { + + bindings[ i ].bind(); + + } + + }, + + unbind: function() { + + var bindings = this._bindings; + + for ( var i = this._targetGroup.nCachedObjects_, + n = bindings.length; i !== n; ++ i ) { + + bindings[ i ].unbind(); + + } + + } + +}; + +THREE.PropertyBinding.create = function( root, path, parsedPath ) { + + if ( ! ( root instanceof THREE.AnimationObjectGroup ) ) { + + return new THREE.PropertyBinding( root, path, parsedPath ); + + } else { + + return new THREE.PropertyBinding.Composite( root, path, parsedPath ); + + } + +}; + +THREE.PropertyBinding.parseTrackName = function( trackName ) { + + // matches strings in the form of: + // nodeName.property + // nodeName.property[accessor] + // nodeName.material.property[accessor] + // uuid.property[accessor] + // uuid.objectName[objectIndex].propertyName[propertyIndex] + // parentName/nodeName.property + // parentName/parentName/nodeName.property[index] + // .bone[Armature.DEF_cog].position + // created and tested via https://regex101.com/#javascript + + var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/; + var matches = re.exec(trackName); + + if( ! matches ) { + throw new Error( "cannot parse trackName at all: " + trackName ); + } + + if (matches.index === re.lastIndex) { + re.lastIndex++; + } + + var results = { + // directoryName: matches[1], // (tschw) currently unused + nodeName: matches[3], // allowed to be null, specified root node. + objectName: matches[5], + objectIndex: matches[7], + propertyName: matches[9], + propertyIndex: matches[11] // allowed to be null, specifies that the whole property is set. + }; + + if( results.propertyName === null || results.propertyName.length === 0 ) { + throw new Error( "can not parse propertyName from trackName: " + trackName ); + } + + return results; + +}; + +THREE.PropertyBinding.findNode = function( root, nodeName ) { + + if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { + + return root; + + } + + // search into skeleton bones. + if( root.skeleton ) { + + var searchSkeleton = function( skeleton ) { + + for( var i = 0; i < skeleton.bones.length; i ++ ) { + + var bone = skeleton.bones[i]; + + if( bone.name === nodeName ) { + + return bone; + + } + } + + return null; + + }; + + var bone = searchSkeleton( root.skeleton ); + + if( bone ) { + + return bone; + + } + } + + // search into node subtree. + if( root.children ) { + + var searchNodeSubtree = function( children ) { + + for( var i = 0; i < children.length; i ++ ) { + + var childNode = children[i]; + + if( childNode.name === nodeName || childNode.uuid === nodeName ) { + + return childNode; + + } + + var result = searchNodeSubtree( childNode.children ); + + if( result ) return result; + + } + + return null; + + }; + + var subTreeNode = searchNodeSubtree( root.children ); + + if( subTreeNode ) { + + return subTreeNode; + + } + + } + + return null; + +} + +// File:src/animation/PropertyMixer.js + +/** + * + * Buffered scene graph property that allows weighted accumulation. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.PropertyMixer = function ( binding, typeName, valueSize ) { + + this.binding = binding; + this.valueSize = valueSize; + + var bufferType = Float64Array, + mixFunction; + + switch ( typeName ) { + + case 'quaternion': mixFunction = this._slerp; break; + + case 'string': + case 'bool': + + bufferType = Array, mixFunction = this._select; break; + + default: mixFunction = this._lerp; + + } + + this.buffer = new bufferType( valueSize * 4 ); + // layout: [ incoming | accu0 | accu1 | orig ] + // + // interpolators can use .buffer as their .result + // the data then goes to 'incoming' + // + // 'accu0' and 'accu1' are used frame-interleaved for + // the cumulative result and are compared to detect + // changes + // + // 'orig' stores the original state of the property + + this._mixBufferRegion = mixFunction; + + this.cumulativeWeight = 0; + + this.useCount = 0; + this.referenceCount = 0; + +}; + +THREE.PropertyMixer.prototype = { + + constructor: THREE.PropertyMixer, + + // accumulate data in the 'incoming' region into 'accu' + accumulate: function( accuIndex, weight ) { + + // note: happily accumulating nothing when weight = 0, the caller knows + // the weight and shouldn't have made the call in the first place + + var buffer = this.buffer, + stride = this.valueSize, + offset = accuIndex * stride + stride, + + currentWeight = this.cumulativeWeight; + + if ( currentWeight === 0 ) { + + // accuN := incoming * weight + + for ( var i = 0; i !== stride; ++ i ) { + + buffer[ offset + i ] = buffer[ i ]; + + } + + currentWeight = weight; + + } else { + + // accuN := accuN + incoming * weight + + currentWeight += weight; + var mix = weight / currentWeight; + this._mixBufferRegion( buffer, offset, 0, mix, stride ); + + } + + this.cumulativeWeight = currentWeight; + + }, + + // apply the state of 'accu' to the binding when accus differ + apply: function( accuIndex ) { + + var stride = this.valueSize, + buffer = this.buffer, + offset = accuIndex * stride + stride, + + weight = this.cumulativeWeight, + + binding = this.binding; + + this.cumulativeWeight = 0; + + if ( weight < 1 ) { + + // accuN := accuN + original * ( 1 - cumulativeWeight ) + + var originalValueOffset = stride * 3; + + this._mixBufferRegion( + buffer, offset, originalValueOffset, 1 - weight, stride ); + + } + + for ( var i = stride, e = stride + stride; i !== e; ++ i ) { + + if ( buffer[ i ] !== buffer[ i + stride ] ) { + + // value has changed -> update scene graph + + binding.setValue( buffer, offset ); + break; + + } + + } + + }, + + // remember the state of the bound property and copy it to both accus + saveOriginalState: function() { + + var binding = this.binding; + + var buffer = this.buffer, + stride = this.valueSize, + + originalValueOffset = stride * 3; + + binding.getValue( buffer, originalValueOffset ); + + // accu[0..1] := orig -- initially detect changes against the original + for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { + + buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; + + } + + this.cumulativeWeight = 0; + + }, + + // apply the state previously taken via 'saveOriginalState' to the binding + restoreOriginalState: function() { + + var originalValueOffset = this.valueSize * 3; + this.binding.setValue( this.buffer, originalValueOffset ); + + }, + + + // mix functions + + _select: function( buffer, dstOffset, srcOffset, t, stride ) { + + if ( t >= 0.5 ) { + + for ( var i = 0; i !== stride; ++ i ) { + + buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; + + } + + } + + }, + + _slerp: function( buffer, dstOffset, srcOffset, t, stride ) { + + THREE.Quaternion.slerpFlat( buffer, dstOffset, + buffer, dstOffset, buffer, srcOffset, t ); + + }, + + _lerp: function( buffer, dstOffset, srcOffset, t, stride ) { + + var s = 1 - t; + + for ( var i = 0; i !== stride; ++ i ) { + + var j = dstOffset + i; + + buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; + + } + + } + +}; + +// File:src/animation/tracks/BooleanKeyframeTrack.js + +/** + * + * A Track of Boolean keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.BooleanKeyframeTrack = function ( name, times, values ) { + + THREE.KeyframeTrack.call( this, name, times, values ); + +}; + +THREE.BooleanKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { + + constructor: THREE.BooleanKeyframeTrack, + + ValueTypeName: 'bool', + ValueBufferType: Array, + + DefaultInterpolation: THREE.IntepolateDiscrete, + + InterpolantFactoryMethodLinear: undefined, + InterpolantFactoryMethodSmooth: undefined + + // Note: Actually this track could have a optimized / compressed + // representation of a single value and a custom interpolant that + // computes "firstValue ^ isOdd( index )". + +} ); + +// File:src/animation/tracks/NumberKeyframeTrack.js + +/** + * + * A Track of numeric keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.NumberKeyframeTrack = function ( name, times, values, interpolation ) { + + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); + +}; + +THREE.NumberKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { + + constructor: THREE.NumberKeyframeTrack, + + ValueTypeName: 'number', + + // ValueBufferType is inherited + + // DefaultInterpolation is inherited + +} ); + +// File:src/animation/tracks/QuaternionKeyframeTrack.js + +/** + * + * A Track of quaternion keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.QuaternionKeyframeTrack = function ( name, times, values, interpolation ) { + + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); + +}; + +THREE.QuaternionKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { + + constructor: THREE.QuaternionKeyframeTrack, + + ValueTypeName: 'quaternion', + + // ValueBufferType is inherited + + DefaultInterpolation: THREE.InterpolateLinear, + + InterpolantFactoryMethodLinear: function( result ) { + + return new THREE.QuaternionLinearInterpolant( + this.times, this.values, this.getValueSize(), result ); + + }, + + InterpolantFactoryMethodSmooth: undefined // not yet implemented + +} ); + +// File:src/animation/tracks/StringKeyframeTrack.js + +/** + * + * A Track that interpolates Strings + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.StringKeyframeTrack = function ( name, times, values, interpolation ) { + + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); + +}; + +THREE.StringKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { + + constructor: THREE.StringKeyframeTrack, + + ValueTypeName: 'string', + ValueBufferType: Array, + + DefaultInterpolation: THREE.IntepolateDiscrete, + + InterpolantFactoryMethodLinear: undefined, + + InterpolantFactoryMethodSmooth: undefined + +} ); + +// File:src/animation/tracks/VectorKeyframeTrack.js + +/** + * + * A Track of vectored keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + +THREE.VectorKeyframeTrack = function ( name, times, values, interpolation ) { + + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); + +}; + +THREE.VectorKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { + + constructor: THREE.VectorKeyframeTrack, + + ValueTypeName: 'vector' + + // ValueBufferType is inherited + + // DefaultInterpolation is inherited + +} ); + +// File:src/audio/Audio.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Audio = function ( listener ) { + + THREE.Object3D.call( this ); + + this.type = 'Audio'; + + this.context = listener.context; + this.source = this.context.createBufferSource(); + this.source.onended = this.onEnded.bind( this ); + + this.gain = this.context.createGain(); + this.gain.connect( listener.getInput() ); + + this.autoplay = false; + + this.startTime = 0; + this.playbackRate = 1; + this.isPlaying = false; + this.hasPlaybackControl = true; + this.sourceType = 'empty'; + + this.filter = null; + +}; + +THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Audio.prototype.constructor = THREE.Audio; + +THREE.Audio.prototype.getOutput = function () { + + return this.gain; + +}; + +THREE.Audio.prototype.load = function ( file ) { + + var buffer = new THREE.AudioBuffer( this.context ); + buffer.load( file ); + + this.setBuffer( buffer ); + + return this; + +}; + +THREE.Audio.prototype.setNodeSource = function ( audioNode ) { + + this.hasPlaybackControl = false; + this.sourceType = 'audioNode'; + this.source = audioNode; + this.connect(); + + return this; + +}; + +THREE.Audio.prototype.setBuffer = function ( audioBuffer ) { + + var scope = this; + + audioBuffer.onReady( function( buffer ) { + + scope.source.buffer = buffer; + scope.sourceType = 'buffer'; + if ( scope.autoplay ) scope.play(); + + } ); + + return this; + +}; + +THREE.Audio.prototype.play = function () { + + if ( this.isPlaying === true ) { + + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; + + } + + if ( this.hasPlaybackControl === false ) { + + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; + + } + + var source = this.context.createBufferSource(); + + source.buffer = this.source.buffer; + source.loop = this.source.loop; + source.onended = this.source.onended; + source.start( 0, this.startTime ); + source.playbackRate.value = this.playbackRate; + + this.isPlaying = true; + + this.source = source; + + this.connect(); + +}; + +THREE.Audio.prototype.pause = function () { + + if ( this.hasPlaybackControl === false ) { + + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; + + } + + this.source.stop(); + this.startTime = this.context.currentTime; + +}; + +THREE.Audio.prototype.stop = function () { + + if ( this.hasPlaybackControl === false ) { + + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; + + } + + this.source.stop(); + this.startTime = 0; + +}; + +THREE.Audio.prototype.connect = function () { + + if ( this.filter !== null ) { + + this.source.connect( this.filter ); + this.filter.connect( this.getOutput() ); + + } else { + + this.source.connect( this.getOutput() ); + + } + +}; + +THREE.Audio.prototype.disconnect = function () { + + if ( this.filter !== null ) { + + this.source.disconnect( this.filter ); + this.filter.disconnect( this.getOutput() ); + + } else { + + this.source.disconnect( this.getOutput() ); + + } + +}; + +THREE.Audio.prototype.getFilter = function () { + + return this.filter; + +}; + +THREE.Audio.prototype.setFilter = function ( value ) { + + if ( value === undefined ) value = null; + + if ( this.isPlaying === true ) { + + this.disconnect(); + this.filter = value; + this.connect(); + + } else { + + this.filter = value; + + } + +}; + +THREE.Audio.prototype.setPlaybackRate = function ( value ) { + + if ( this.hasPlaybackControl === false ) { + + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; + + } + + this.playbackRate = value; + + if ( this.isPlaying === true ) { + + this.source.playbackRate.value = this.playbackRate; + + } + +}; + +THREE.Audio.prototype.getPlaybackRate = function () { + + return this.playbackRate; + +}; + +THREE.Audio.prototype.onEnded = function() { + + this.isPlaying = false; + +}; + +THREE.Audio.prototype.setLoop = function ( value ) { + + if ( this.hasPlaybackControl === false ) { + + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; + + } + + this.source.loop = value; + +}; + +THREE.Audio.prototype.getLoop = function () { + + if ( this.hasPlaybackControl === false ) { + + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return false; + + } + + return this.source.loop; + +}; + + +THREE.Audio.prototype.setVolume = function ( value ) { + + this.gain.gain.value = value; + +}; + +THREE.Audio.prototype.getVolume = function () { + + return this.gain.gain.value; + +}; + +// File:src/audio/AudioAnalyser.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AudioAnalyser = function ( audio, fftSize ) { + + this.analyser = audio.context.createAnalyser(); + this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; + + this.data = new Uint8Array( this.analyser.frequencyBinCount ); + + audio.getOutput().connect( this.analyser ); + +}; + +THREE.AudioAnalyser.prototype = { + + constructor: THREE.AudioAnalyser, + + getData: function () { + + this.analyser.getByteFrequencyData( this.data ); + return this.data; + + } + +}; + +// File:src/audio/AudioBuffer.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AudioBuffer = function ( context ) { + + this.context = context; + this.ready = false; + this.readyCallbacks = []; + +}; + +THREE.AudioBuffer.prototype.load = function ( file ) { + + var scope = this; + + var request = new XMLHttpRequest(); + request.open( 'GET', file, true ); + request.responseType = 'arraybuffer'; + request.onload = function ( e ) { + + scope.context.decodeAudioData( this.response, function ( buffer ) { + + scope.buffer = buffer; + scope.ready = true; + + for ( var i = 0; i < scope.readyCallbacks.length; i ++ ) { + + scope.readyCallbacks[ i ]( scope.buffer ); + + } + + scope.readyCallbacks = []; + + } ); + + }; + request.send(); + + return this; + +}; + +THREE.AudioBuffer.prototype.onReady = function ( callback ) { + + if ( this.ready ) { + + callback( this.buffer ); + + } else { + + this.readyCallbacks.push( callback ); + + } + +}; + +// File:src/audio/PositionalAudio.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PositionalAudio = function ( listener ) { + + THREE.Audio.call( this, listener ); + + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); + +}; + +THREE.PositionalAudio.prototype = Object.create( THREE.Audio.prototype ); +THREE.PositionalAudio.prototype.constructor = THREE.PositionalAudio; + +THREE.PositionalAudio.prototype.getOutput = function () { + + return this.panner; + +}; + +THREE.PositionalAudio.prototype.setRefDistance = function ( value ) { + + this.panner.refDistance = value; + +}; + +THREE.PositionalAudio.prototype.getRefDistance = function () { + + return this.panner.refDistance; + +}; + +THREE.PositionalAudio.prototype.setRolloffFactor = function ( value ) { + + this.panner.rolloffFactor = value; + +}; + +THREE.PositionalAudio.prototype.getRolloffFactor = function () { + + return this.panner.rolloffFactor; + +}; + +THREE.PositionalAudio.prototype.setDistanceModel = function ( value ) { + + this.panner.distanceModel = value; + +}; + +THREE.PositionalAudio.prototype.getDistanceModel = function () { + + return this.panner.distanceModel; + +}; + +THREE.PositionalAudio.prototype.setMaxDistance = function ( value ) { + + this.panner.maxDistance = value; + +}; + +THREE.PositionalAudio.prototype.getMaxDistance = function () { + + return this.panner.maxDistance; + +}; + +THREE.PositionalAudio.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + + return function updateMatrixWorld( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + position.setFromMatrixPosition( this.matrixWorld ); + + this.panner.setPosition( position.x, position.y, position.z ); + + }; + +} )(); + +// File:src/audio/AudioListener.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AudioListener = function () { + + THREE.Object3D.call( this ); + + this.type = 'AudioListener'; + + this.context = new ( window.AudioContext || window.webkitAudioContext )(); + + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); + + this.filter = null; + +}; + +THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); +THREE.AudioListener.prototype.constructor = THREE.AudioListener; + +THREE.AudioListener.prototype.getInput = function () { + + return this.gain; + +}; + +THREE.AudioListener.prototype.removeFilter = function ( ) { + + if ( this.filter !== null ) { + + this.gain.disconnect( this.filter ); + this.filter.disconnect( this.context.destination ); + this.gain.connect( this.context.destination ); + this.filter = null; + + } + +}; + +THREE.AudioListener.prototype.setFilter = function ( value ) { + + if ( this.filter !== null ) { + + this.gain.disconnect( this.filter ); + this.filter.disconnect( this.context.destination ); + + } else { + + this.gain.disconnect( this.context.destination ); + + } + + this.filter = value; + this.gain.connect( this.filter ); + this.filter.connect( this.context.destination ); + +}; + +THREE.AudioListener.prototype.getFilter = function () { + + return this.filter; + +}; + +THREE.AudioListener.prototype.setMasterVolume = function ( value ) { + + this.gain.gain.value = value; + +}; + +THREE.AudioListener.prototype.getMasterVolume = function () { + + return this.gain.gain.value; + +}; + + +THREE.AudioListener.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); + + var orientation = new THREE.Vector3(); + + return function updateMatrixWorld( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + var listener = this.context.listener; + var up = this.up; + + this.matrixWorld.decompose( position, quaternion, scale ); + + orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + + listener.setPosition( position.x, position.y, position.z ); + listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); + + }; + +} )(); + // File:src/cameras/Camera.js /** @@ -10547,7 +16707,7 @@ THREE.Camera.prototype.getWorldDirection = function () { return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - } + }; }(); @@ -10567,16 +16727,21 @@ THREE.Camera.prototype.lookAt = function () { }(); -THREE.Camera.prototype.clone = function ( camera ) { +THREE.Camera.prototype.clone = function () { - if ( camera === undefined ) camera = new THREE.Camera(); + return new this.constructor().copy( this ); - THREE.Object3D.prototype.clone.call( this, camera ); +}; - camera.matrixWorldInverse.copy( this.matrixWorldInverse ); - camera.projectionMatrix.copy( this.projectionMatrix ); +THREE.Camera.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.matrixWorldInverse.copy( source.matrixWorldInverse ); + this.projectionMatrix.copy( source.projectionMatrix ); + + return this; - return camera; }; // File:src/cameras/CubeCamera.js @@ -10626,14 +16791,18 @@ THREE.CubeCamera = function ( near, far, cubeResolution ) { cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); this.add( cameraNZ ); - this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + var options = { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter }; + + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); this.updateCubeMap = function ( renderer, scene ) { - var renderTarget = this.renderTarget; - var generateMipmaps = renderTarget.generateMipmaps; + if ( this.parent === null ) this.updateMatrixWorld(); - renderTarget.generateMipmaps = false; + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.texture.generateMipmaps; + + renderTarget.texture.generateMipmaps = false; renderTarget.activeCubeFace = 0; renderer.render( scene, cameraPX, renderTarget ); @@ -10650,11 +16819,13 @@ THREE.CubeCamera = function ( near, far, cubeResolution ) { renderTarget.activeCubeFace = 4; renderer.render( scene, cameraPZ, renderTarget ); - renderTarget.generateMipmaps = generateMipmaps; + renderTarget.texture.generateMipmaps = generateMipmaps; renderTarget.activeCubeFace = 5; renderer.render( scene, cameraNZ, renderTarget ); + renderer.setRenderTarget( null ); + }; }; @@ -10702,25 +16873,37 @@ THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { }; -THREE.OrthographicCamera.prototype.clone = function () { +THREE.OrthographicCamera.prototype.copy = function ( source ) { - var camera = new THREE.OrthographicCamera(); + THREE.Camera.prototype.copy.call( this, source ); - THREE.Camera.prototype.clone.call( this, camera ); + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; - camera.zoom = this.zoom; + this.zoom = source.zoom; - camera.left = this.left; - camera.right = this.right; - camera.top = this.top; - camera.bottom = this.bottom; + return this; - camera.near = this.near; - camera.far = this.far; +}; - camera.projectionMatrix.copy( this.projectionMatrix ); +THREE.OrthographicCamera.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; + + return data; - return camera; }; // File:src/cameras/PerspectiveCamera.js @@ -10737,6 +16920,7 @@ THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { this.type = 'PerspectiveCamera'; + this.focalLength = 10; this.zoom = 1; this.fov = fov !== undefined ? fov : 50; @@ -10754,7 +16938,7 @@ THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; /** * Uses Focal Length (in mm) to estimate and set FOV - * 35mm (fullframe) camera is used if frame size is not specified; + * 35mm (full-frame) camera is used if frame size is not specified; * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html */ @@ -10765,7 +16949,7 @@ THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); this.updateProjectionMatrix(); -} +}; /** @@ -10849,22 +17033,127 @@ THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { }; -THREE.PerspectiveCamera.prototype.clone = function () { +THREE.PerspectiveCamera.prototype.copy = function ( source ) { - var camera = new THREE.PerspectiveCamera(); + THREE.Camera.prototype.copy.call( this, source ); - THREE.Camera.prototype.clone.call( this, camera ); + this.focalLength = source.focalLength; + this.zoom = source.zoom; - camera.zoom = this.zoom; + this.fov = source.fov; + this.aspect = source.aspect; + this.near = source.near; + this.far = source.far; - camera.fov = this.fov; - camera.aspect = this.aspect; - camera.near = this.near; - camera.far = this.far; + return this; - camera.projectionMatrix.copy( this.projectionMatrix ); +}; - return camera; +THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.focalLength = this.focalLength; + data.object.zoom = this.zoom; + + data.object.fov = this.fov; + data.object.aspect = this.aspect; + data.object.near = this.near; + data.object.far = this.far; + + return data; + +}; + +// File:src/cameras/StereoCamera.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.StereoCamera = function () { + + this.type = 'StereoCamera'; + + this.aspect = 1; + + this.cameraL = new THREE.PerspectiveCamera(); + this.cameraL.layers.enable( 1 ); + this.cameraL.matrixAutoUpdate = false; + + this.cameraR = new THREE.PerspectiveCamera(); + this.cameraR.layers.enable( 2 ); + this.cameraR.matrixAutoUpdate = false; + +}; + +THREE.StereoCamera.prototype = { + + constructor: THREE.StereoCamera, + + update: ( function () { + + var focalLength, fov, aspect, near, far; + + var eyeRight = new THREE.Matrix4(); + var eyeLeft = new THREE.Matrix4(); + + return function update ( camera ) { + + var needsUpdate = focalLength !== camera.focalLength || fov !== camera.fov || + aspect !== camera.aspect * this.aspect || near !== camera.near || + far !== camera.far; + + if ( needsUpdate ) { + + focalLength = camera.focalLength; + fov = camera.fov; + aspect = camera.aspect * this.aspect; + near = camera.near; + far = camera.far; + + // Off-axis stereoscopic effect based on + // http://paulbourke.net/stereographics/stereorender/ + + var projectionMatrix = camera.projectionMatrix.clone(); + var eyeSep = 0.064 / 2; + var eyeSepOnProjection = eyeSep * near / focalLength; + var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) ); + var xmin, xmax; + + // translate xOffset + + eyeLeft.elements[ 12 ] = - eyeSep; + eyeRight.elements[ 12 ] = eyeSep; + + // for left eye + + xmin = - ymax * aspect + eyeSepOnProjection; + xmax = ymax * aspect + eyeSepOnProjection; + + projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + + this.cameraL.projectionMatrix.copy( projectionMatrix ); + + // for right eye + + xmin = - ymax * aspect - eyeSepOnProjection; + xmax = ymax * aspect - eyeSepOnProjection; + + projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + + this.cameraR.projectionMatrix.copy( projectionMatrix ); + + } + + this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); + this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); + + }; + + } )() }; @@ -10875,28 +17164,93 @@ THREE.PerspectiveCamera.prototype.clone = function () { * @author alteredq / http://alteredqualia.com/ */ -THREE.Light = function ( color ) { +THREE.Light = function ( color, intensity ) { THREE.Object3D.call( this ); this.type = 'Light'; - + this.color = new THREE.Color( color ); + this.intensity = intensity !== undefined ? intensity : 1; + + this.receiveShadow = undefined; }; THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); THREE.Light.prototype.constructor = THREE.Light; -THREE.Light.prototype.clone = function ( light ) { +THREE.Light.prototype.copy = function ( source ) { - if ( light === undefined ) light = new THREE.Light(); + THREE.Object3D.prototype.copy.call( this, source ); - THREE.Object3D.prototype.clone.call( this, light ); + this.color.copy( source.color ); + this.intensity = source.intensity; - light.color.copy( this.color ); + return this; - return light; +}; + +THREE.Light.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; + + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.exponent !== undefined ) data.object.exponent = this.exponent; + + return data; + +}; + +// File:src/lights/LightShadow.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LightShadow = function ( camera ) { + + this.camera = camera; + + this.bias = 0; + this.radius = 1; + + this.mapSize = new THREE.Vector2( 512, 512 ); + + this.map = null; + this.matrix = new THREE.Matrix4(); + +}; + +THREE.LightShadow.prototype = { + + constructor: THREE.LightShadow, + + copy: function ( source ) { + + this.camera = source.camera.clone(); + + this.bias = source.bias; + this.radius = source.radius; + + this.mapSize.copy( source.mapSize ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } }; @@ -10906,58 +17260,19 @@ THREE.Light.prototype.clone = function ( light ) { * @author mrdoob / http://mrdoob.com/ */ -THREE.AmbientLight = function ( color ) { +THREE.AmbientLight = function ( color, intensity ) { - THREE.Light.call( this, color ); + THREE.Light.call( this, color, intensity ); this.type = 'AmbientLight'; + this.castShadow = undefined; + }; THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; -THREE.AmbientLight.prototype.clone = function () { - - var light = new THREE.AmbientLight(); - - THREE.Light.prototype.clone.call( this, light ); - - return light; - -}; - -// File:src/lights/AreaLight.js - -/** - * @author MPanknin / http://www.redplant.de/ - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.AreaLight = function ( color, intensity ) { - - THREE.Light.call( this, color ); - - this.type = 'AreaLight'; - - this.normal = new THREE.Vector3( 0, - 1, 0 ); - this.right = new THREE.Vector3( 1, 0, 0 ); - - this.intensity = ( intensity !== undefined ) ? intensity : 1; - - this.width = 1.0; - this.height = 1.0; - - this.constantAttenuation = 1.5; - this.linearAttenuation = 0.5; - this.quadraticAttenuation = 0.1; - -}; - -THREE.AreaLight.prototype = Object.create( THREE.Light.prototype ); -THREE.AreaLight.prototype.constructor = THREE.AreaLight; - - // File:src/lights/DirectionalLight.js /** @@ -10967,110 +17282,31 @@ THREE.AreaLight.prototype.constructor = THREE.AreaLight; THREE.DirectionalLight = function ( color, intensity ) { - THREE.Light.call( this, color ); + THREE.Light.call( this, color, intensity ); this.type = 'DirectionalLight'; this.position.set( 0, 1, 0 ); + this.updateMatrix(); + this.target = new THREE.Object3D(); - this.intensity = ( intensity !== undefined ) ? intensity : 1; - - this.castShadow = false; - this.onlyShadow = false; - - // - - this.shadowCameraNear = 50; - this.shadowCameraFar = 5000; - - this.shadowCameraLeft = - 500; - this.shadowCameraRight = 500; - this.shadowCameraTop = 500; - this.shadowCameraBottom = - 500; - - this.shadowCameraVisible = false; - - this.shadowBias = 0; - this.shadowDarkness = 0.5; - - this.shadowMapWidth = 512; - this.shadowMapHeight = 512; - - // - - this.shadowCascade = false; - - this.shadowCascadeOffset = new THREE.Vector3( 0, 0, - 1000 ); - this.shadowCascadeCount = 2; - - this.shadowCascadeBias = [ 0, 0, 0 ]; - this.shadowCascadeWidth = [ 512, 512, 512 ]; - this.shadowCascadeHeight = [ 512, 512, 512 ]; - - this.shadowCascadeNearZ = [ - 1.000, 0.990, 0.998 ]; - this.shadowCascadeFarZ = [ 0.990, 0.998, 1.000 ]; - - this.shadowCascadeArray = []; - - // - - this.shadowMap = null; - this.shadowMapSize = null; - this.shadowCamera = null; - this.shadowMatrix = null; + this.shadow = new THREE.LightShadow( new THREE.OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); }; THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; -THREE.DirectionalLight.prototype.clone = function () { +THREE.DirectionalLight.prototype.copy = function ( source ) { - var light = new THREE.DirectionalLight(); + THREE.Light.prototype.copy.call( this, source ); - THREE.Light.prototype.clone.call( this, light ); + this.target = source.target.clone(); - light.target = this.target.clone(); + this.shadow = source.shadow.clone(); - light.intensity = this.intensity; - - light.castShadow = this.castShadow; - light.onlyShadow = this.onlyShadow; - - // - - light.shadowCameraNear = this.shadowCameraNear; - light.shadowCameraFar = this.shadowCameraFar; - - light.shadowCameraLeft = this.shadowCameraLeft; - light.shadowCameraRight = this.shadowCameraRight; - light.shadowCameraTop = this.shadowCameraTop; - light.shadowCameraBottom = this.shadowCameraBottom; - - light.shadowCameraVisible = this.shadowCameraVisible; - - light.shadowBias = this.shadowBias; - light.shadowDarkness = this.shadowDarkness; - - light.shadowMapWidth = this.shadowMapWidth; - light.shadowMapHeight = this.shadowMapHeight; - - // - - light.shadowCascade = this.shadowCascade; - - light.shadowCascadeOffset.copy( this.shadowCascadeOffset ); - light.shadowCascadeCount = this.shadowCascadeCount; - - light.shadowCascadeBias = this.shadowCascadeBias.slice( 0 ); - light.shadowCascadeWidth = this.shadowCascadeWidth.slice( 0 ); - light.shadowCascadeHeight = this.shadowCascadeHeight.slice( 0 ); - - light.shadowCascadeNearZ = this.shadowCascadeNearZ.slice( 0 ); - light.shadowCascadeFarZ = this.shadowCascadeFarZ.slice( 0 ); - - return light; + return this; }; @@ -11082,30 +17318,29 @@ THREE.DirectionalLight.prototype.clone = function () { THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { - THREE.Light.call( this, skyColor ); + THREE.Light.call( this, skyColor, intensity ); this.type = 'HemisphereLight'; - this.position.set( 0, 100, 0 ); + this.castShadow = undefined; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); this.groundColor = new THREE.Color( groundColor ); - this.intensity = ( intensity !== undefined ) ? intensity : 1; }; THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight; -THREE.HemisphereLight.prototype.clone = function () { +THREE.HemisphereLight.prototype.copy = function ( source ) { - var light = new THREE.HemisphereLight(); + THREE.Light.prototype.copy.call( this, source ); - THREE.Light.prototype.clone.call( this, light ); + this.groundColor.copy( source.groundColor ); - light.groundColor.copy( this.groundColor ); - light.intensity = this.intensity; - - return light; + return this; }; @@ -11115,32 +17350,33 @@ THREE.HemisphereLight.prototype.clone = function () { * @author mrdoob / http://mrdoob.com/ */ + THREE.PointLight = function ( color, intensity, distance, decay ) { - THREE.Light.call( this, color ); + THREE.Light.call( this, color, intensity ); this.type = 'PointLight'; - this.intensity = ( intensity !== undefined ) ? intensity : 1; this.distance = ( distance !== undefined ) ? distance : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 0.5, 500 ) ); + }; THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); THREE.PointLight.prototype.constructor = THREE.PointLight; -THREE.PointLight.prototype.clone = function () { +THREE.PointLight.prototype.copy = function ( source ) { - var light = new THREE.PointLight(); + THREE.Light.prototype.copy.call( this, source ); - THREE.Light.prototype.clone.call( this, light ); + this.distance = source.distance; + this.decay = source.decay; - light.intensity = this.intensity; - light.distance = this.distance; - light.decay = this.decay; + this.shadow = source.shadow.clone(); - return light; + return this; }; @@ -11152,80 +17388,41 @@ THREE.PointLight.prototype.clone = function () { THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) { - THREE.Light.call( this, color ); + THREE.Light.call( this, color, intensity ); this.type = 'SpotLight'; this.position.set( 0, 1, 0 ); + this.updateMatrix(); + this.target = new THREE.Object3D(); - this.intensity = ( intensity !== undefined ) ? intensity : 1; this.distance = ( distance !== undefined ) ? distance : 0; this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; this.exponent = ( exponent !== undefined ) ? exponent : 10; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - this.castShadow = false; - this.onlyShadow = false; - - // - - this.shadowCameraNear = 50; - this.shadowCameraFar = 5000; - this.shadowCameraFov = 50; - - this.shadowCameraVisible = false; - - this.shadowBias = 0; - this.shadowDarkness = 0.5; - - this.shadowMapWidth = 512; - this.shadowMapHeight = 512; - - // - - this.shadowMap = null; - this.shadowMapSize = null; - this.shadowCamera = null; - this.shadowMatrix = null; + this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 0.5, 500 ) ); }; THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); THREE.SpotLight.prototype.constructor = THREE.SpotLight; -THREE.SpotLight.prototype.clone = function () { +THREE.SpotLight.prototype.copy = function ( source ) { - var light = new THREE.SpotLight(); + THREE.Light.prototype.copy.call( this, source ); - THREE.Light.prototype.clone.call( this, light ); + this.distance = source.distance; + this.angle = source.angle; + this.exponent = source.exponent; + this.decay = source.decay; - light.target = this.target.clone(); + this.target = source.target.clone(); - light.intensity = this.intensity; - light.distance = this.distance; - light.angle = this.angle; - light.exponent = this.exponent; - light.decay = this.decay; + this.shadow = source.shadow.clone(); - light.castShadow = this.castShadow; - light.onlyShadow = this.onlyShadow; - - // - - light.shadowCameraNear = this.shadowCameraNear; - light.shadowCameraFar = this.shadowCameraFar; - light.shadowCameraFov = this.shadowCameraFov; - - light.shadowCameraVisible = this.shadowCameraVisible; - - light.shadowBias = this.shadowBias; - light.shadowDarkness = this.shadowDarkness; - - light.shadowMapWidth = this.shadowMapWidth; - light.shadowMapHeight = this.shadowMapHeight; - - return light; + return this; }; @@ -11237,10 +17434,14 @@ THREE.SpotLight.prototype.clone = function () { THREE.Cache = { + enabled: false, + files: {}, add: function ( key, file ) { + if ( this.enabled === false ) return; + // console.log( 'THREE.Cache', 'Adding key:', key ); this.files[ key ] = file; @@ -11249,6 +17450,8 @@ THREE.Cache = { get: function ( key ) { + if ( this.enabled === false ) return; + // console.log( 'THREE.Cache', 'Checking key:', key ); return this.files[ key ]; @@ -11263,7 +17466,7 @@ THREE.Cache = { clear: function () { - this.files = {} + this.files = {}; } @@ -11275,12 +17478,7 @@ THREE.Cache = { * @author alteredq / http://alteredqualia.com/ */ -THREE.Loader = function ( showStatus ) { - - this.showStatus = showStatus; - this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null; - - this.imageLoader = new THREE.ImageLoader(); +THREE.Loader = function () { this.onLoadStart = function () {}; this.onLoadProgress = function () {}; @@ -11295,46 +17493,6 @@ THREE.Loader.prototype = { crossOrigin: undefined, - addStatusElement: function () { - - var e = document.createElement( 'div' ); - - e.style.position = 'absolute'; - e.style.right = '0px'; - e.style.top = '0px'; - e.style.fontSize = '0.8em'; - e.style.textAlign = 'left'; - e.style.background = 'rgba(0,0,0,0.25)'; - e.style.color = '#fff'; - e.style.width = '120px'; - e.style.padding = '0.5em 0.5em 0.5em 0.5em'; - e.style.zIndex = 1000; - - e.innerHTML = 'Loading ...'; - - return e; - - }, - - updateProgress: function ( progress ) { - - var message = 'Loaded '; - - if ( progress.total ) { - - message += ( 100 * progress.loaded / progress.total ).toFixed( 0 ) + '%'; - - - } else { - - message += ( progress.loaded / 1024 ).toFixed( 2 ) + ' KB'; - - } - - this.statusDomElement.innerHTML = message; - - }, - extractUrlBase: function ( url ) { var parts = url.split( '/' ); @@ -11347,13 +17505,13 @@ THREE.Loader.prototype = { }, - initMaterials: function ( materials, texturePath ) { + initMaterials: function ( materials, texturePath, crossOrigin ) { var array = []; for ( var i = 0; i < materials.length; ++ i ) { - array[ i ] = this.createMaterial( materials[ i ], texturePath ); + array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); } @@ -11361,304 +17519,224 @@ THREE.Loader.prototype = { }, - needsTangents: function ( materials ) { + createMaterial: ( function () { - for ( var i = 0, il = materials.length; i < il; i ++ ) { + var color, textureLoader, materialLoader; - var m = materials[ i ]; + return function ( m, texturePath, crossOrigin ) { - if ( m instanceof THREE.ShaderMaterial ) return true; + if ( color === undefined ) color = new THREE.Color(); + if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader(); + if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader(); - } + // convert from old material format - return false; + var textures = {}; - }, + function loadTexture( path, repeat, offset, wrap, anisotropy ) { - createMaterial: function ( m, texturePath ) { + var fullPath = texturePath + path; + var loader = THREE.Loader.Handlers.get( fullPath ); - var scope = this; + var texture; - function nearest_pow2( n ) { + if ( loader !== null ) { - var l = Math.log( n ) / Math.LN2; - return Math.pow( 2, Math.round( l ) ); + texture = loader.load( fullPath ); - } + } else { - function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { + textureLoader.setCrossOrigin( crossOrigin ); + texture = textureLoader.load( fullPath ); - var fullPath = texturePath + sourceFile; - - var texture; - - var loader = THREE.Loader.Handlers.get( fullPath ); - - if ( loader !== null ) { - - texture = loader.load( fullPath ); - - } else { - - texture = new THREE.Texture(); - - loader = scope.imageLoader; - loader.crossOrigin = scope.crossOrigin; - loader.load( fullPath, function ( image ) { - - if ( THREE.Math.isPowerOfTwo( image.width ) === false || - THREE.Math.isPowerOfTwo( image.height ) === false ) { - - var width = nearest_pow2( image.width ); - var height = nearest_pow2( image.height ); - - texture.image = image.resize( width, height ); - - } else { - - texture.image = image; - - } - - texture.needsUpdate = true; - - } ); - - } - - texture.sourceFile = sourceFile; - - if ( repeat ) { - - texture.repeat.set( repeat[ 0 ], repeat[ 1 ] ); - - if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; - if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; - - } - - if ( offset ) { - - texture.offset.set( offset[ 0 ], offset[ 1 ] ); - - } - - if ( wrap ) { - - var wrapMap = { - 'repeat': THREE.RepeatWrapping, - 'mirror': THREE.MirroredRepeatWrapping } - if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ]; - if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ]; + if ( repeat !== undefined ) { + + texture.repeat.fromArray( repeat ); + + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + + } + + if ( offset !== undefined ) { + + texture.offset.fromArray( offset ); + + } + + if ( wrap !== undefined ) { + + if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping; + if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping; + + if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping; + if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping; + + } + + if ( anisotropy !== undefined ) { + + texture.anisotropy = anisotropy; + + } + + var uuid = THREE.Math.generateUUID(); + + textures[ uuid ] = texture; + + return uuid; } - if ( anisotropy ) { + // - texture.anisotropy = anisotropy; + var json = { + uuid: THREE.Math.generateUUID(), + type: 'MeshLambertMaterial' + }; + + for ( var name in m ) { + + var value = m[ name ]; + + switch ( name ) { + case 'DbgColor': + case 'DbgIndex': + case 'opticalDensity': + case 'illumination': + break; + case 'DbgName': + json.name = value; + break; + case 'blending': + json.blending = THREE[ value ]; + break; + case 'colorAmbient': + console.warn( 'THREE.Loader.createMaterial: colorAmbient is no longer supported' ); + break; + case 'colorDiffuse': + json.color = color.fromArray( value ).getHex(); + break; + case 'colorSpecular': + json.specular = color.fromArray( value ).getHex(); + break; + case 'colorEmissive': + json.emissive = color.fromArray( value ).getHex(); + break; + case 'specularCoef': + json.shininess = value; + break; + case 'shading': + if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; + if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; + break; + case 'mapDiffuse': + json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + break; + case 'mapDiffuseRepeat': + case 'mapDiffuseOffset': + case 'mapDiffuseWrap': + case 'mapDiffuseAnisotropy': + break; + case 'mapLight': + json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + break; + case 'mapLightRepeat': + case 'mapLightOffset': + case 'mapLightWrap': + case 'mapLightAnisotropy': + break; + case 'mapAO': + json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); + break; + case 'mapAORepeat': + case 'mapAOOffset': + case 'mapAOWrap': + case 'mapAOAnisotropy': + break; + case 'mapBump': + json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + break; + case 'mapBumpScale': + json.bumpScale = value; + break; + case 'mapBumpRepeat': + case 'mapBumpOffset': + case 'mapBumpWrap': + case 'mapBumpAnisotropy': + break; + case 'mapNormal': + json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + break; + case 'mapNormalFactor': + json.normalScale = [ value, value ]; + break; + case 'mapNormalRepeat': + case 'mapNormalOffset': + case 'mapNormalWrap': + case 'mapNormalAnisotropy': + break; + case 'mapSpecular': + json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + break; + case 'mapSpecularRepeat': + case 'mapSpecularOffset': + case 'mapSpecularWrap': + case 'mapSpecularAnisotropy': + break; + case 'mapAlpha': + json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + break; + case 'mapAlphaRepeat': + case 'mapAlphaOffset': + case 'mapAlphaWrap': + case 'mapAlphaAnisotropy': + break; + case 'flipSided': + json.side = THREE.BackSide; + break; + case 'doubleSided': + json.side = THREE.DoubleSide; + break; + case 'transparency': + console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); + json.opacity = value; + break; + case 'depthTest': + case 'depthWrite': + case 'colorWrite': + case 'opacity': + case 'reflectivity': + case 'transparent': + case 'visible': + case 'wireframe': + json[ name ] = value; + break; + case 'vertexColors': + if ( value === true ) json.vertexColors = THREE.VertexColors; + if ( value === 'face' ) json.vertexColors = THREE.FaceColors; + break; + default: + console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); + break; + } } - where[ name ] = texture; + if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; + if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; - } + if ( json.opacity < 1 ) json.transparent = true; - function rgb2hex( rgb ) { + materialLoader.setTextures( textures ); - return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; + return materialLoader.parse( json ); - } + }; - // defaults - - var mtype = 'MeshLambertMaterial'; - var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false }; - - // parameters from model file - - if ( m.shading ) { - - var shading = m.shading.toLowerCase(); - - if ( shading === 'phong' ) mtype = 'MeshPhongMaterial'; - else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial'; - - } - - if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { - - mpars.blending = THREE[ m.blending ]; - - } - - if ( m.transparent !== undefined ) { - - mpars.transparent = m.transparent; - - } - - if ( m.opacity !== undefined && m.opacity < 1.0 ) { - - mpars.transparent = true; - - } - - if ( m.depthTest !== undefined ) { - - mpars.depthTest = m.depthTest; - - } - - if ( m.depthWrite !== undefined ) { - - mpars.depthWrite = m.depthWrite; - - } - - if ( m.visible !== undefined ) { - - mpars.visible = m.visible; - - } - - if ( m.flipSided !== undefined ) { - - mpars.side = THREE.BackSide; - - } - - if ( m.doubleSided !== undefined ) { - - mpars.side = THREE.DoubleSide; - - } - - if ( m.wireframe !== undefined ) { - - mpars.wireframe = m.wireframe; - - } - - if ( m.vertexColors !== undefined ) { - - if ( m.vertexColors === 'face' ) { - - mpars.vertexColors = THREE.FaceColors; - - } else if ( m.vertexColors ) { - - mpars.vertexColors = THREE.VertexColors; - - } - - } - - // colors - - if ( m.colorDiffuse ) { - - mpars.color = rgb2hex( m.colorDiffuse ); - - } else if ( m.DbgColor ) { - - mpars.color = m.DbgColor; - - } - - if ( m.colorSpecular ) { - - mpars.specular = rgb2hex( m.colorSpecular ); - - } - - if ( m.colorEmissive ) { - - mpars.emissive = rgb2hex( m.colorEmissive ); - - } - - // modifiers - - if ( m.transparency !== undefined ) { - - console.warn( 'THREE.Loader: transparency has been renamed to opacity' ); - m.opacity = m.transparency; - - } - - if ( m.opacity !== undefined ) { - - mpars.opacity = m.opacity; - - } - - if ( m.specularCoef ) { - - mpars.shininess = m.specularCoef; - - } - - // textures - - if ( m.mapDiffuse && texturePath ) { - - create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); - - } - - if ( m.mapLight && texturePath ) { - - create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); - - } - - if ( m.mapBump && texturePath ) { - - create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); - - } - - if ( m.mapNormal && texturePath ) { - - create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); - - } - - if ( m.mapSpecular && texturePath ) { - - create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); - - } - - if ( m.mapAlpha && texturePath ) { - - create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); - - } - - // - - if ( m.mapBumpScale ) { - - mpars.bumpScale = m.mapBumpScale; - - } - - if ( m.mapNormalFactor ) { - - mpars.normalScale = new THREE.Vector2( m.mapNormalFactor, m.mapNormalFactor ); - - } - - var material = new THREE[ mtype ]( mpars ); - - if ( m.DbgName !== undefined ) material.name = m.DbgName; - - return material; - - } + } )() }; @@ -11674,10 +17752,12 @@ THREE.Loader.Handlers = { get: function ( file ) { - for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) { + var handlers = this.handlers; - var regex = this.handlers[ i ]; - var loader = this.handlers[ i + 1 ]; + for ( var i = 0, l = handlers.length; i < l; i += 2 ) { + + var regex = handlers[ i ]; + var loader = handlers[ i + 1 ]; if ( regex.test( file ) ) { @@ -11711,53 +17791,70 @@ THREE.XHRLoader.prototype = { load: function ( url, onLoad, onProgress, onError ) { + if ( this.path !== undefined ) url = this.path + url; + var scope = this; var cached = THREE.Cache.get( url ); if ( cached !== undefined ) { - if ( onLoad ) onLoad( cached ); - return; + if ( onLoad ) { + + // setTimeout doesn't work in QML. + // There should be no need to do this asynchronously anyway, + // as we add the url to cache after the loading is done. + //setTimeout( function () { + + onLoad( cached ); + + //}, 0 ); + + } + + return cached; } var request = new XMLHttpRequest(); + //request.overrideMimeType( 'text/plain' ); // Not supported in QML request.onreadystatechange = function() { - if (request.readyState === XMLHttpRequest.DONE) { -// TODO: Re-visit https://bugreports.qt.io/browse/QTBUG-45581 is solved in Qt - if (request.status == 200 || request.status == 0) { - var response; -// TODO: Remove once https://bugreports.qt.io/browse/QTBUG-45862 is fixed in Qt - if ( scope.responseType == 'arraybuffer' ) - response = request.response; - else - response = request.responseText; - - THREE.Cache.add( url, response ); - if ( onLoad ) onLoad( response ); - scope.manager.itemEnd( url ); - } else { - if ( onError !== undefined ) { - onError(); - } - } - } else if (request.readyState === XMLHttpRequest.HEADERS_RECEIVED) { - if ( onProgress !== undefined ) { - onProgress(); + if (request.readyState === XMLHttpRequest.DONE) { + if (request.status == 200 || request.status == 0) { + var response; + response = request.response; + if ( onLoad ) onLoad( response ); + THREE.Cache.add( url, response ); + scope.manager.itemEnd( url ); + } else { + if ( onError !== undefined ) { + onError(); } } - }; + } else if (request.readyState === XMLHttpRequest.HEADERS_RECEIVED) { + if ( onProgress !== undefined ) { + onProgress(); + } + } + }; - request.open( 'GET', url, true ); + request.open( 'GET', url, true ); - if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; - if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; request.send( null ); scope.manager.itemStart( url ); + return request; + + }, + + setPath: function ( value ) { + + this.path = value; + }, setResponseType: function ( value ) { @@ -11766,9 +17863,38 @@ THREE.XHRLoader.prototype = { }, - setCrossOrigin: function ( value ) { + setWithCredentials: function ( value ) { - this.crossOrigin = value; + this.withCredentials = value; + + } + +}; + +// File:src/loaders/FontLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.FontLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.FontLoader.prototype = { + + constructor: THREE.FontLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var loader = new THREE.XHRLoader( this.manager ); + loader.load( url, function ( text ) { + + onLoad( new THREE.Font( JSON.parse( text.substring( 65, text.length - 2 ) ) ) ); + + }, onProgress, onError ); } @@ -11792,14 +17918,33 @@ THREE.ImageLoader.prototype = { load: function ( url, onLoad, onProgress, onError ) { + if ( this.path !== undefined ) url = this.path + url; + var scope = this; var cached = THREE.Cache.get( url ); if ( cached !== undefined ) { - onLoad( cached ); - return; + scope.manager.itemStart( url ); + + if ( onLoad ) { + + setTimeout( function () { + + onLoad( cached ); + + scope.manager.itemEnd( url ); + + }, 0 ); + + } else { + + scope.manager.itemEnd( url ); + + } + + return cached; } @@ -11810,7 +17955,7 @@ THREE.ImageLoader.prototype = { THREE.Cache.add( url, image ); if ( onLoad ) onLoad( image ); - + scope.manager.itemEnd( url ); }, false ); @@ -11825,22 +17970,20 @@ THREE.ImageLoader.prototype = { } - if ( onError !== undefined ) { + image.addEventListener( 'error', function ( event ) { - image.addEventListener( 'error', function ( event ) { + if ( onError ) onError( event ); - onError( event ); + scope.manager.itemError( url ); - }, false ); - - } + }, false ); if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - image.src = url; - scope.manager.itemStart( url ); + image.src = url; + return image; }, @@ -11849,9 +17992,15 @@ THREE.ImageLoader.prototype = { this.crossOrigin = value; + }, + + setPath: function ( value ) { + + this.path = value; + } -} +}; // File:src/loaders/JSONLoader.js @@ -11860,502 +18009,487 @@ THREE.ImageLoader.prototype = { * @author alteredq / http://alteredqualia.com/ */ -THREE.JSONLoader = function ( showStatus ) { +THREE.JSONLoader = function ( manager ) { - THREE.Loader.call( this, showStatus ); + if ( typeof manager === 'boolean' ) { + + console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; + + } + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; this.withCredentials = false; }; -THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype ); -THREE.JSONLoader.prototype.constructor = THREE.JSONLoader; +THREE.JSONLoader.prototype = { -THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) { + constructor: THREE.JSONLoader, - // todo: unify load API to for easier SceneLoader use + // Deprecated - texturePath = texturePath && ( typeof texturePath === 'string' ) ? texturePath : this.extractUrlBase( url ); + get statusDomElement () { - this.onLoadStart(); - this.loadAjaxJSON( this, url, callback, texturePath ); + if ( this._statusDomElement === undefined ) { -}; + this._statusDomElement = document.createElement( 'div' ); -THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) { + } - var xhr = new XMLHttpRequest(); + console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' ); + return this._statusDomElement; - var length = 0; + }, - xhr.onreadystatechange = function () { + load: function( url, onLoad, onProgress, onError ) { - if ( xhr.readyState === xhr.DONE ) { + var scope = this; - if ( xhr.status === 200 || xhr.status === 0 ) { + var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); - if ( xhr.responseText ) { + var loader = new THREE.XHRLoader( this.manager ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { - var json = JSON.parse( xhr.responseText ); - var metadata = json.metadata; + var json = JSON.parse( text ); + var metadata = json.metadata; - if ( metadata !== undefined ) { + if ( metadata !== undefined ) { - if ( metadata.type === 'object' ) { + var type = metadata.type; - THREE.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); - return; + if ( type !== undefined ) { - } + if ( type.toLowerCase() === 'object' ) { - if ( metadata.type === 'scene' ) { - - THREE.error( 'THREE.JSONLoader: ' + url + ' seems to be a Scene. Use THREE.SceneLoader instead.' ); - return; - - } + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); + return; } - var result = context.parse( json, texturePath ); - callback( result.geometry, result.materials ); + if ( type.toLowerCase() === 'scene' ) { - } else { + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); + return; - THREE.error( 'THREE.JSONLoader: ' + url + ' seems to be unreachable or the file is empty.' ); + } } - // in context of more complex asset initialization - // do not block on single failed file - // maybe should go even one more level up + } - context.onLoadComplete(); + var object = scope.parse( json, texturePath ); + onLoad( object.geometry, object.materials ); - } else { + }, onProgress, onError ); - THREE.error( 'THREE.JSONLoader: Couldn\'t load ' + url + ' (' + xhr.status + ')' ); + }, + + setTexturePath: function ( value ) { + + this.texturePath = value; + + }, + + parse: function ( json, texturePath ) { + + var geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + + parseModel( scale ); + + parseSkin(); + parseMorphing( scale ); + parseAnimations(); + + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); + + function parseModel( scale ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); } - } else if ( xhr.readyState === xhr.LOADING ) { + var i, j, fi, - if ( callbackProgress ) { - - if ( length === 0 ) { - - length = xhr.getResponseHeader( 'Content-Length' ); - - } - - callbackProgress( { total: length, loaded: xhr.responseText.length } ); - - } - - } else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) { - - if ( callbackProgress !== undefined ) { - - length = xhr.getResponseHeader( 'Content-Length' ); - - } - - } - - }; - - xhr.open( 'GET', url, true ); - xhr.withCredentials = this.withCredentials; - xhr.send( null ); - -}; - -THREE.JSONLoader.prototype.parse = function ( json, texturePath ) { - - var geometry = new THREE.Geometry(), - scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; - - parseModel( scale ); - - parseSkin(); - parseMorphing( scale ); - - geometry.computeFaceNormals(); - geometry.computeBoundingSphere(); - - function parseModel( scale ) { - - function isBitSet( value, position ) { - - return value & ( 1 << position ); - - } - - var i, j, fi, - - offset, zLength, + offset, zLength, colorIndex, normalIndex, uvIndex, materialIndex, - type, - isQuad, - hasMaterial, - hasFaceVertexUv, - hasFaceNormal, hasFaceVertexNormal, - hasFaceColor, hasFaceVertexColor, + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, vertex, face, faceA, faceB, hex, normal, - uvLayer, uv, u, v, + uvLayer, uv, u, v, - faces = json.faces, - vertices = json.vertices, - normals = json.normals, - colors = json.colors, + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, - nUvLayers = 0; + nUvLayers = 0; - if ( json.uvs !== undefined ) { + if ( json.uvs !== undefined ) { - // disregard empty arrays + // disregard empty arrays - for ( i = 0; i < json.uvs.length; i ++ ) { + for ( i = 0; i < json.uvs.length; i ++ ) { - if ( json.uvs[ i ].length ) nUvLayers ++; - - } - - for ( i = 0; i < nUvLayers; i ++ ) { - - geometry.faceVertexUvs[ i ] = []; - - } - - } - - offset = 0; - zLength = vertices.length; - - while ( offset < zLength ) { - - vertex = new THREE.Vector3(); - - vertex.x = vertices[ offset ++ ] * scale; - vertex.y = vertices[ offset ++ ] * scale; - vertex.z = vertices[ offset ++ ] * scale; - - geometry.vertices.push( vertex ); - - } - - offset = 0; - zLength = faces.length; - - while ( offset < zLength ) { - - type = faces[ offset ++ ]; - - - isQuad = isBitSet( type, 0 ); - hasMaterial = isBitSet( type, 1 ); - hasFaceVertexUv = isBitSet( type, 3 ); - hasFaceNormal = isBitSet( type, 4 ); - hasFaceVertexNormal = isBitSet( type, 5 ); - hasFaceColor = isBitSet( type, 6 ); - hasFaceVertexColor = isBitSet( type, 7 ); - - // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - - if ( isQuad ) { - - faceA = new THREE.Face3(); - faceA.a = faces[ offset ]; - faceA.b = faces[ offset + 1 ]; - faceA.c = faces[ offset + 3 ]; - - faceB = new THREE.Face3(); - faceB.a = faces[ offset + 1 ]; - faceB.b = faces[ offset + 2 ]; - faceB.c = faces[ offset + 3 ]; - - offset += 4; - - if ( hasMaterial ) { - - materialIndex = faces[ offset ++ ]; - faceA.materialIndex = materialIndex; - faceB.materialIndex = materialIndex; + if ( json.uvs[ i ].length ) nUvLayers ++; } - // to get face <=> uv index correspondence + for ( i = 0; i < nUvLayers; i ++ ) { - fi = geometry.faces.length; + geometry.faceVertexUvs[ i ] = []; - if ( hasFaceVertexUv ) { + } - for ( i = 0; i < nUvLayers; i ++ ) { + } - uvLayer = json.uvs[ i ]; + offset = 0; + zLength = vertices.length; - geometry.faceVertexUvs[ i ][ fi ] = []; - geometry.faceVertexUvs[ i ][ fi + 1 ] = [] + while ( offset < zLength ) { - for ( j = 0; j < 4; j ++ ) { + vertex = new THREE.Vector3(); - uvIndex = faces[ offset ++ ]; + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; + geometry.vertices.push( vertex ); - uv = new THREE.Vector2( u, v ); + } - if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); - if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; + + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; + + offset += 4; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = []; + + for ( j = 0; j < 4; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } } } - } - - if ( hasFaceNormal ) { - - normalIndex = faces[ offset ++ ] * 3; - - faceA.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - faceB.normal.copy( faceA.normal ); - - } - - if ( hasFaceVertexNormal ) { - - for ( i = 0; i < 4; i ++ ) { + if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; - normal = new THREE.Vector3( + faceA.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); - - if ( i !== 2 ) faceA.vertexNormals.push( normal ); - if ( i !== 0 ) faceB.vertexNormals.push( normal ); + faceB.normal.copy( faceA.normal ); } - } + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 4; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - if ( hasFaceColor ) { + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; + } - faceA.color.setHex( hex ); - faceB.color.setHex( hex ); - - } + } - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 4; i ++ ) { + if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; - if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); - if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); } - } - geometry.faces.push( faceA ); - geometry.faces.push( faceB ); + if ( hasFaceVertexColor ) { - } else { + for ( i = 0; i < 4; i ++ ) { - face = new THREE.Face3(); - face.a = faces[ offset ++ ]; - face.b = faces[ offset ++ ]; - face.c = faces[ offset ++ ]; + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - if ( hasMaterial ) { - - materialIndex = faces[ offset ++ ]; - face.materialIndex = materialIndex; - - } - - // to get face <=> uv index correspondence - - fi = geometry.faces.length; - - if ( hasFaceVertexUv ) { - - for ( i = 0; i < nUvLayers; i ++ ) { - - uvLayer = json.uvs[ i ]; - - geometry.faceVertexUvs[ i ][ fi ] = []; - - for ( j = 0; j < 3; j ++ ) { - - uvIndex = faces[ offset ++ ]; - - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; - - uv = new THREE.Vector2( u, v ); - - geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); } } - } + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); - if ( hasFaceNormal ) { + } else { - normalIndex = faces[ offset ++ ] * 3; + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; - face.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + if ( hasMaterial ) { - } + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; - if ( hasFaceVertexNormal ) { + } - for ( i = 0; i < 3; i ++ ) { + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + + for ( j = 0; j < 3; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + geometry.faceVertexUvs[ i ][ fi ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; - normal = new THREE.Vector3( + face.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); - face.vertexNormals.push( normal ); + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 3; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + face.vertexNormals.push( normal ); + + } } - } - - if ( hasFaceColor ) { - - colorIndex = faces[ offset ++ ]; - face.color.setHex( colors[ colorIndex ] ); - - } - - - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 3; i ++ ) { + if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; - face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + face.color.setHex( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 3; i ++ ) { + + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + + } + + } + + geometry.faces.push( face ); + + } + + } + + }; + + function parseSkin() { + + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + + if ( json.skinWeights ) { + + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + + } + + }; + + function parseMorphing( scale ) { + + if ( json.morphTargets !== undefined ) { + + for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + var dstVertices = geometry.morphTargets[ i ].vertices; + var srcVertices = json.morphTargets[ i ].vertices; + + for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); } } - geometry.faces.push( face ); - } - } + if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { - }; + console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); - function parseSkin() { - var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + var faces = geometry.faces; + var morphColors = json.morphColors[ 0 ].colors; - if ( json.skinWeights ) { + for ( var i = 0, l = faces.length; i < l; i ++ ) { - for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { - - var x = json.skinWeights[ i ]; - var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; - var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; - var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; - - geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); - - } - - } - - if ( json.skinIndices ) { - - for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { - - var a = json.skinIndices[ i ]; - var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; - var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; - var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; - - geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); - - } - - } - - geometry.bones = json.bones; - - if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { - - THREE.warn( 'THREE.JSONLoader: When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + - geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); - - } - - - // could change this to json.animations[0] or remove completely - - geometry.animation = json.animation; - geometry.animations = json.animations; - - }; - - function parseMorphing( scale ) { - - if ( json.morphTargets !== undefined ) { - - var i, l, v, vl, dstVertices, srcVertices; - - for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { - - geometry.morphTargets[ i ] = {}; - geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; - geometry.morphTargets[ i ].vertices = []; - - dstVertices = geometry.morphTargets[ i ].vertices; - srcVertices = json.morphTargets [ i ].vertices; - - for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { - - var vertex = new THREE.Vector3(); - vertex.x = srcVertices[ v ] * scale; - vertex.y = srcVertices[ v + 1 ] * scale; - vertex.z = srcVertices[ v + 2 ] * scale; - - dstVertices.push( vertex ); + faces[ i ].color.fromArray( morphColors, i * 3 ); } @@ -12363,49 +18497,65 @@ THREE.JSONLoader.prototype.parse = function ( json, texturePath ) { } - if ( json.morphColors !== undefined ) { + function parseAnimations() { - var i, l, c, cl, dstColors, srcColors, color; + var outputAnimations = []; - for ( i = 0, l = json.morphColors.length; i < l; i ++ ) { + // parse old style Bone/Hierarchy animations + var animations = []; - geometry.morphColors[ i ] = {}; - geometry.morphColors[ i ].name = json.morphColors[ i ].name; - geometry.morphColors[ i ].colors = []; + if ( json.animation !== undefined ) { - dstColors = geometry.morphColors[ i ].colors; - srcColors = json.morphColors [ i ].colors; + animations.push( json.animation ); - for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { + } - color = new THREE.Color( 0xffaa00 ); - color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); - dstColors.push( color ); + if ( json.animations !== undefined ) { + + if ( json.animations.length ) { + + animations = animations.concat( json.animations ); + + } else { + + animations.push( json.animations ); } } - } + for ( var i = 0; i < animations.length; i ++ ) { - }; + var clip = THREE.AnimationClip.parseAnimation( animations[ i ], geometry.bones ); + if ( clip ) outputAnimations.push( clip ); - if ( json.materials === undefined || json.materials.length === 0 ) { + } - return { geometry: geometry }; + // parse implicit morph animations + if ( geometry.morphTargets ) { - } else { + // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. + var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); + outputAnimations = outputAnimations.concat( morphAnimationClips ); - var materials = this.initMaterials( json.materials, texturePath ); + } - if ( this.needsTangents( materials ) ) { + if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; - geometry.computeTangents(); + }; + + if ( json.materials === undefined || json.materials.length === 0 ) { + + return { geometry: geometry }; + + } else { + + var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); + + return { geometry: geometry, materials: materials }; } - return { geometry: geometry, materials: materials }; - } }; @@ -12420,31 +18570,60 @@ THREE.LoadingManager = function ( onLoad, onProgress, onError ) { var scope = this; - var loaded = 0, total = 0; + var isLoading = false, itemsLoaded = 0, itemsTotal = 0; + this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function ( url ) { - total ++; + itemsTotal ++; + + if ( isLoading === false ) { + + if ( scope.onStart !== undefined ) { + + scope.onStart( url, itemsLoaded, itemsTotal ); + + } + + } + + isLoading = true; }; this.itemEnd = function ( url ) { - loaded ++; + itemsLoaded ++; if ( scope.onProgress !== undefined ) { - scope.onProgress( url, loaded, total ); + scope.onProgress( url, itemsLoaded, itemsTotal ); } - if ( loaded === total && scope.onLoad !== undefined ) { + if ( itemsLoaded === itemsTotal ) { - scope.onLoad(); + isLoading = false; + + if ( scope.onLoad !== undefined ) { + + scope.onLoad(); + + } + + } + + }; + + this.itemError = function ( url ) { + + if ( scope.onError !== undefined ) { + + scope.onError( url ); } @@ -12475,7 +18654,6 @@ THREE.BufferGeometryLoader.prototype = { var scope = this; var loader = new THREE.XHRLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); @@ -12484,32 +18662,53 @@ THREE.BufferGeometryLoader.prototype = { }, - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - - }, - parse: function ( json ) { var geometry = new THREE.BufferGeometry(); + var index = json.data.index; + + var TYPED_ARRAYS = { + 'Int8Array': Int8Array, + 'Uint8Array': Uint8Array, + 'Uint8ClampedArray': Uint8ClampedArray, + 'Int16Array': Int16Array, + 'Uint16Array': Uint16Array, + 'Int32Array': Int32Array, + 'Uint32Array': Uint32Array, + 'Float32Array': Float32Array, + 'Float64Array': Float64Array + }; + + if ( index !== undefined ) { + + var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); + geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) ); + + } + var attributes = json.data.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; - var typedArray = new self[ attribute.type ]( attribute.array ); + var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); } - var offsets = json.data.offsets; + var groups = json.data.groups || json.data.drawcalls || json.data.offsets; - if ( offsets !== undefined ) { + if ( groups !== undefined ) { - geometry.offsets = JSON.parse( JSON.stringify( offsets ) ); + for ( var i = 0, n = groups.length; i !== n; ++ i ) { + + var group = groups[ i ]; + + geometry.addGroup( group.start, group.count, group.materialIndex ); + + } } @@ -12544,6 +18743,7 @@ THREE.BufferGeometryLoader.prototype = { THREE.MaterialLoader = function ( manager ) { this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.textures = {}; }; @@ -12556,7 +18756,6 @@ THREE.MaterialLoader.prototype = { var scope = this; var loader = new THREE.XHRLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); @@ -12565,9 +18764,23 @@ THREE.MaterialLoader.prototype = { }, - setCrossOrigin: function ( value ) { + setTextures: function ( value ) { - this.crossOrigin = value; + this.textures = value; + + }, + + getTexture: function ( name ) { + + var textures = this.textures; + + if ( textures[ name ] === undefined ) { + + console.warn( 'THREE.MaterialLoader: Undefined texture', name ); + + } + + return textures[ name ]; }, @@ -12575,7 +18788,11 @@ THREE.MaterialLoader.prototype = { var material = new THREE[ json.type ]; + if ( json.uuid !== undefined ) material.uuid = json.uuid; + if ( json.name !== undefined ) material.name = json.name; if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.roughness !== undefined ) material.roughness = json.roughness; + if ( json.metalness !== undefined ) material.metalness = json.metalness; if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); if ( json.specular !== undefined ) material.specular.setHex( json.specular ); if ( json.shininess !== undefined ) material.shininess = json.shininess; @@ -12588,12 +18805,77 @@ THREE.MaterialLoader.prototype = { if ( json.side !== undefined ) material.side = json.side; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; + if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; + if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; + if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - // for PointCloudMaterial + // for PointsMaterial if ( json.size !== undefined ) material.size = json.size; if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; + // maps + + if ( json.map !== undefined ) material.map = this.getTexture( json.map ); + + if ( json.alphaMap !== undefined ) { + + material.alphaMap = this.getTexture( json.alphaMap ); + material.transparent = true; + + } + + if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap ); + if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; + + if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap ); + if ( json.normalScale !== undefined ) { + + var normalScale = json.normalScale; + + if ( Array.isArray( normalScale ) === false ) { + + // Blender exporter used to export a scalar. See #7459 + + normalScale = [ normalScale, normalScale ]; + + } + + material.normalScale = new THREE.Vector2().fromArray( normalScale ); + + } + + if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap ); + if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; + if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; + + if ( json.roughnessMap !== undefined ) material.roughnessMap = this.getTexture( json.roughnessMap ); + if ( json.metalnessMap !== undefined ) material.metalnessMap = this.getTexture( json.metalnessMap ); + + if ( json.emissiveMap !== undefined ) material.emissiveMap = this.getTexture( json.emissiveMap ); + if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; + + if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap ); + + if ( json.envMap !== undefined ) { + + material.envMap = this.getTexture( json.envMap ); + material.combine = THREE.MultiplyOperation; + + } + + if ( json.reflectivity ) material.reflectivity = json.reflectivity; + + if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap ); + if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; + + if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap ); + if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; + + // MultiMaterial + if ( json.materials !== undefined ) { for ( var i = 0, l = json.materials.length; i < l; i ++ ) { @@ -12638,7 +18920,6 @@ THREE.ObjectLoader.prototype = { var scope = this; var loader = new THREE.XHRLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); loader.load( url, function ( text ) { scope.parse( JSON.parse( text ), onLoad ); @@ -12668,10 +18949,18 @@ THREE.ObjectLoader.prototype = { if ( onLoad !== undefined ) onLoad( object ); } ); + var textures = this.parseTextures( json.textures, images ); var materials = this.parseMaterials( json.materials, textures ); + var object = this.parseObject( json.object, geometries, materials ); + if ( json.animations ) { + + object.animations = this.parseAnimations( json.animations ); + + } + if ( json.images === undefined || json.images.length === 0 ) { if ( onLoad !== undefined ) onLoad( object ); @@ -12724,11 +19013,24 @@ THREE.ObjectLoader.prototype = { break; + case 'CircleBufferGeometry': + + geometry = new THREE.CircleBufferGeometry( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); + + break; + case 'CircleGeometry': geometry = new THREE.CircleGeometry( data.radius, - data.segments + data.segments, + data.thetaStart, + data.thetaLength ); break; @@ -12741,7 +19043,9 @@ THREE.ObjectLoader.prototype = { data.height, data.radialSegments, data.heightSegments, - data.openEnded + data.openEnded, + data.thetaStart, + data.thetaLength ); break; @@ -12760,6 +19064,29 @@ THREE.ObjectLoader.prototype = { break; + case 'SphereBufferGeometry': + + geometry = new THREE.SphereBufferGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'DodecahedronGeometry': + + geometry = new THREE.DodecahedronGeometry( + data.radius, + data.detail + ); + + break; + case 'IcosahedronGeometry': geometry = new THREE.IcosahedronGeometry( @@ -12769,6 +19096,37 @@ THREE.ObjectLoader.prototype = { break; + case 'OctahedronGeometry': + + geometry = new THREE.OctahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'TetrahedronGeometry': + + geometry = new THREE.TetrahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'RingGeometry': + + geometry = new THREE.RingGeometry( + data.innerRadius, + data.outerRadius, + data.thetaSegments, + data.phiSegments, + data.thetaStart, + data.thetaLength + ); + + break; + case 'TorusGeometry': geometry = new THREE.TorusGeometry( @@ -12795,6 +19153,17 @@ THREE.ObjectLoader.prototype = { break; + case 'LatheGeometry': + + geometry = new THREE.LatheGeometry( + data.points, + data.segments, + data.phiStart, + data.phiLength + ); + + break; + case 'BufferGeometry': geometry = bufferGeometryLoader.parse( data ); @@ -12803,10 +19172,16 @@ THREE.ObjectLoader.prototype = { case 'Geometry': - geometry = geometryLoader.parse( data.data ).geometry; + geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; break; + default: + + console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); + + continue; + } geometry.uuid = data.uuid; @@ -12829,78 +19204,13 @@ THREE.ObjectLoader.prototype = { if ( json !== undefined ) { - var getTexture = function ( name ) { - - if ( textures[ name ] === undefined ) { - - THREE.warn( 'THREE.ObjectLoader: Undefined texture', name ); - - } - - return textures[ name ]; - - }; - var loader = new THREE.MaterialLoader(); + loader.setTextures( textures ); for ( var i = 0, l = json.length; i < l; i ++ ) { - var data = json[ i ]; - var material = loader.parse( data ); - - material.uuid = data.uuid; - - if ( data.name !== undefined ) material.name = data.name; - - if ( data.map !== undefined && data.map !== null ) { - - material.map = getTexture( data.map ); - - } - - if ( data.bumpMap !== undefined ) { - - material.bumpMap = getTexture( data.bumpMap ); - if ( data.bumpScale ) { - material.bumpScale = new THREE.Vector2( data.bumpScale, data.bumpScale ); - } - - } - - if ( data.alphaMap !== undefined ) { - - material.alphaMap = getTexture( data.alphaMap ); - - } - - if ( data.envMap !== undefined ) { - - material.envMap = getTexture( data.envMap ); - - } - - if ( data.normalMap !== undefined ) { - - material.normalMap = getTexture( data.normalMap ); - if ( data.normalScale ) { - material.normalScale = new THREE.Vector2( data.normalScale, data.normalScale ); - } - - } - - if ( data.lightMap !== undefined ) { - - material.lightMap = getTexture( data.lightMap ); - - } - - if ( data.specularMap !== undefined ) { - - material.specularMap = getTexture( data.specularMap ); - - } - - materials[ data.uuid ] = material; + var material = loader.parse( json[ i ] ); + materials[ material.uuid ] = material; } @@ -12910,11 +19220,39 @@ THREE.ObjectLoader.prototype = { }, + parseAnimations: function ( json ) { + + var animations = []; + + for ( var i = 0; i < json.length; i ++ ) { + + var clip = THREE.AnimationClip.parse( json[ i ] ); + + animations.push( clip ); + + } + + return animations; + + }, + parseImages: function ( json, onLoad ) { var scope = this; var images = {}; + function loadImage( url ) { + + scope.manager.itemStart( url ); + + return loader.load( url, function () { + + scope.manager.itemEnd( url ); + + } ); + + } + if ( json !== undefined && json.length > 0 ) { var manager = new THREE.LoadingManager( onLoad ); @@ -12922,18 +19260,6 @@ THREE.ObjectLoader.prototype = { var loader = new THREE.ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); - var loadImage = function ( url ) { - - scope.manager.itemStart( url ); - - return loader.load( url, function () { - - scope.manager.itemEnd( url ); - - } ); - - }; - for ( var i = 0, l = json.length; i < l; i ++ ) { var image = json[ i ]; @@ -12951,6 +19277,16 @@ THREE.ObjectLoader.prototype = { parseTextures: function ( json, images ) { + function parseConstant( value ) { + + if ( typeof( value ) === 'number' ) return value; + + console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); + + return THREE[ value ]; + + } + var textures = {}; if ( json !== undefined ) { @@ -12961,13 +19297,13 @@ THREE.ObjectLoader.prototype = { if ( data.image === undefined ) { - THREE.warn( 'THREE.ObjectLoader: No "image" speficied for', data.uuid ); + console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); } if ( images[ data.image ] === undefined ) { - THREE.warn( 'THREE.ObjectLoader: Undefined image', data.image ); + console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); } @@ -12977,14 +19313,16 @@ THREE.ObjectLoader.prototype = { texture.uuid = data.uuid; if ( data.name !== undefined ) texture.name = data.name; + if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); + if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); - if ( data.minFilter !== undefined ) texture.minFilter = THREE[ data.minFilter ]; - if ( data.magFilter !== undefined ) texture.magFilter = THREE[ data.magFilter ]; + if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); + if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; - if ( data.wrap instanceof Array ) { + if ( Array.isArray( data.wrap ) ) { - texture.wrapS = THREE[ data.wrap[ 0 ] ]; - texture.wrapT = THREE[ data.wrap[ 1 ] ]; + texture.wrapS = parseConstant( data.wrap[ 0 ] ); + texture.wrapT = parseConstant( data.wrap[ 1 ] ); } @@ -13006,29 +19344,31 @@ THREE.ObjectLoader.prototype = { var object; - var getGeometry = function ( name ) { + function getGeometry( name ) { if ( geometries[ name ] === undefined ) { - THREE.warn( 'THREE.ObjectLoader: Undefined geometry', name ); + console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); } return geometries[ name ]; - }; + } - var getMaterial = function ( name ) { + function getMaterial( name ) { + + if ( name === undefined ) return undefined; if ( materials[ name ] === undefined ) { - THREE.warn( 'THREE.ObjectLoader: Undefined material', name ); + console.warn( 'THREE.ObjectLoader: Undefined material', name ); } return materials[ name ]; - }; + } switch ( data.type ) { @@ -13052,7 +19392,7 @@ THREE.ObjectLoader.prototype = { case 'AmbientLight': - object = new THREE.AmbientLight( data.color ); + object = new THREE.AmbientLight( data.color, data.intensity ); break; @@ -13082,7 +19422,24 @@ THREE.ObjectLoader.prototype = { case 'Mesh': - object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) ); + var geometry = getGeometry( data.geometry ); + var material = getMaterial( data.material ); + + if ( geometry.bones && geometry.bones.length > 0 ) { + + object = new THREE.SkinnedMesh( geometry, material ); + + } else { + + object = new THREE.Mesh( geometry, material ); + + } + + break; + + case 'LOD': + + object = new THREE.LOD(); break; @@ -13093,8 +19450,9 @@ THREE.ObjectLoader.prototype = { break; case 'PointCloud': + case 'Points': - object = new THREE.PointCloud( getGeometry( data.geometry ), getMaterial( data.material ) ); + object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); break; @@ -13132,6 +19490,9 @@ THREE.ObjectLoader.prototype = { } + if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; + if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; + if ( data.visible !== undefined ) object.visible = data.visible; if ( data.userData !== undefined ) object.userData = data.userData; @@ -13145,6 +19506,25 @@ THREE.ObjectLoader.prototype = { } + if ( data.type === 'LOD' ) { + + var levels = data.levels; + + for ( var l = 0; l < levels.length; l ++ ) { + + var level = levels[ l ]; + var child = object.getObjectByProperty( 'uuid', level.object ); + + if ( child !== undefined ) { + + object.addLevel( child, level.distance ); + + } + + } + + } + return object; } @@ -13171,13 +19551,14 @@ THREE.TextureLoader.prototype = { load: function ( url, onLoad, onProgress, onError ) { - var scope = this; + var texture = new THREE.Texture(); - var loader = new THREE.ImageLoader( scope.manager ); + var loader = new THREE.ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); loader.load( url, function ( image ) { - var texture = new THREE.Texture( image ); + texture.image = image; texture.needsUpdate = true; if ( onLoad !== undefined ) { @@ -13188,12 +19569,90 @@ THREE.TextureLoader.prototype = { }, onProgress, onError ); + return texture; + }, setCrossOrigin: function ( value ) { this.crossOrigin = value; + }, + + setPath: function ( value ) { + + this.path = value; + + } + +}; + +// File:src/loaders/CubeTextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CubeTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.CubeTextureLoader.prototype = { + + constructor: THREE.CubeTextureLoader, + + load: function ( urls, onLoad, onProgress, onError ) { + + var texture = new THREE.CubeTexture( [] ); + + var loader = new THREE.ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); + + var loaded = 0; + + function loadTexture( i ) { + + loader.load( urls[ i ], function ( image ) { + + texture.images[ i ] = image; + + loaded ++; + + if ( loaded === 6 ) { + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + }, undefined, onError ); + + } + + for ( var i = 0; i < urls.length; ++ i ) { + + loadTexture( i ); + + } + + return texture; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + setPath: function ( value ) { + + this.path = value; + } }; @@ -13206,7 +19665,9 @@ THREE.TextureLoader.prototype = { * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) */ -THREE.DataTextureLoader = THREE.BinaryTextureLoader = function () { +THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; // override in sub classes this._parser = null; @@ -13221,16 +19682,16 @@ THREE.BinaryTextureLoader.prototype = { var scope = this; - var texture = new THREE.DataTexture( ); + var texture = new THREE.DataTexture(); - var loader = new THREE.XHRLoader(); + var loader = new THREE.XHRLoader( this.manager ); loader.setResponseType( 'arraybuffer' ); loader.load( url, function ( buffer ) { var texData = scope._parser( buffer ); - if ( !texData ) return; + if ( ! texData ) return; if ( undefined !== texData.image ) { @@ -13296,7 +19757,9 @@ THREE.BinaryTextureLoader.prototype = { * Abstract Base class to block based textures loader (dds, pvr, ...) */ -THREE.CompressedTextureLoader = function () { +THREE.CompressedTextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; // override in sub classes this._parser = null; @@ -13308,7 +19771,7 @@ THREE.CompressedTextureLoader.prototype = { constructor: THREE.CompressedTextureLoader, - load: function ( url, onLoad, onError ) { + load: function ( url, onLoad, onProgress, onError ) { var scope = this; @@ -13317,44 +19780,45 @@ THREE.CompressedTextureLoader.prototype = { var texture = new THREE.CompressedTexture(); texture.image = images; - var loader = new THREE.XHRLoader(); + var loader = new THREE.XHRLoader( this.manager ); + loader.setPath( this.path ); loader.setResponseType( 'arraybuffer' ); - if ( url instanceof Array ) { + function loadTexture( i ) { + + loader.load( url[ i ], function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; + + loaded += 1; + + if ( loaded === 6 ) { + + if ( texDatas.mipmapCount === 1 ) + texture.minFilter = THREE.LinearFilter; + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + }, onProgress, onError ); + + } + + if ( Array.isArray( url ) ) { var loaded = 0; - var loadTexture = function ( i ) { - - loader.load( url[ i ], function ( buffer ) { - - var texDatas = scope._parser( buffer, true ); - - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; - - loaded += 1; - - if ( loaded === 6 ) { - - if (texDatas.mipmapCount == 1) - texture.minFilter = THREE.LinearFilter; - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - } ); - - }; - for ( var i = 0, il = url.length; i < il; ++ i ) { loadTexture( i ); @@ -13407,12 +19871,18 @@ THREE.CompressedTextureLoader.prototype = { if ( onLoad ) onLoad( texture ); - } ); + }, onProgress, onError ); } return texture; + }, + + setPath: function ( value ) { + + this.path = value; + } }; @@ -13447,11 +19917,14 @@ THREE.Material = function () { this.blendDstAlpha = null; this.blendEquationAlpha = null; + this.depthFunc = THREE.LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.colorWrite = true; + this.precision = null; // override the renderer's default precision for this material + this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; @@ -13494,33 +19967,36 @@ THREE.Material.prototype = { if ( newValue === undefined ) { - THREE.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); continue; } - if ( key in this ) { + var currentValue = this[ key ]; - var currentValue = this[ key ]; + if ( currentValue === undefined ) { - if ( currentValue instanceof THREE.Color ) { + console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); + continue; - currentValue.set( newValue ); + } - } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + if ( currentValue instanceof THREE.Color ) { - currentValue.copy( newValue ); + currentValue.set( newValue ); - } else if ( key == 'overdraw' ) { + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { - // ensure overdraw is backwards-compatable with legacy boolean type - this[ key ] = Number( newValue ); + currentValue.copy( newValue ); - } else { + } else if ( key === 'overdraw' ) { - this[ key ] = newValue; + // ensure overdraw is backwards-compatible with legacy boolean type + this[ key ] = Number( newValue ); - } + } else { + + this[ key ] = newValue; } @@ -13528,120 +20004,165 @@ THREE.Material.prototype = { }, - toJSON: function () { + toJSON: function ( meta ) { - var output = { - metadata: { - version: 4.2, - type: 'material', - generator: 'MaterialExporter' - }, - uuid: this.uuid, - type: this.type - }; + var isRoot = meta === undefined; - if ( this.name !== "" ) output.name = this.name; + if ( isRoot ) { - if ( this instanceof THREE.MeshBasicMaterial ) { - - output.color = this.color.getHex(); - if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; - if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; - if ( this.side !== THREE.FrontSide ) output.side = this.side; - - } else if ( this instanceof THREE.MeshLambertMaterial ) { - - output.color = this.color.getHex(); - output.emissive = this.emissive.getHex(); - if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; - if ( this.shading !== THREE.SmoothShading ) output.shading = this.shading; - if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; - if ( this.side !== THREE.FrontSide ) output.side = this.side; - - } else if ( this instanceof THREE.MeshPhongMaterial ) { - - output.color = this.color.getHex(); - output.emissive = this.emissive.getHex(); - output.specular = this.specular.getHex(); - output.shininess = this.shininess; - if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; - if ( this.shading !== THREE.SmoothShading ) output.shading = this.shading; - if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; - if ( this.side !== THREE.FrontSide ) output.side = this.side; - - } else if ( this instanceof THREE.MeshNormalMaterial ) { - - if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; - if ( this.side !== THREE.FrontSide ) output.side = this.side; - - } else if ( this instanceof THREE.MeshDepthMaterial ) { - - if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; - if ( this.side !== THREE.FrontSide ) output.side = this.side; - - } else if ( this instanceof THREE.PointCloudMaterial ) { - - output.size = this.size; - output.sizeAttenuation = this.sizeAttenuation; - output.color = this.color.getHex(); - - if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; - if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; - - } else if ( this instanceof THREE.ShaderMaterial ) { - - output.uniforms = this.uniforms; - output.vertexShader = this.vertexShader; - output.fragmentShader = this.fragmentShader; - - } else if ( this instanceof THREE.SpriteMaterial ) { - - output.color = this.color.getHex(); + meta = { + textures: {}, + images: {} + }; } - if ( this.opacity < 1 ) output.opacity = this.opacity; - if ( this.transparent !== false ) output.transparent = this.transparent; - if ( this.wireframe !== false ) output.wireframe = this.wireframe; + var data = { + metadata: { + version: 4.4, + type: 'Material', + generator: 'Material.toJSON' + } + }; - return output; + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + + if ( this.color instanceof THREE.Color ) data.color = this.color.getHex(); + + if ( this.roughness !== 0.5 ) data.roughness = this.roughness; + if ( this.metalness !== 0.5 ) data.metalness = this.metalness; + + if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex(); + if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex(); + if ( this.shininess !== undefined ) data.shininess = this.shininess; + + if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; + if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; + if ( this.bumpMap instanceof THREE.Texture ) { + + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; + + } + if ( this.normalMap instanceof THREE.Texture ) { + + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalScale = this.normalScale.toArray(); + + } + if ( this.displacementMap instanceof THREE.Texture ) { + + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; + + } + if ( this.roughnessMap instanceof THREE.Texture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; + if ( this.metalnessMap instanceof THREE.Texture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; + + if ( this.emissiveMap instanceof THREE.Texture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; + if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; + + if ( this.envMap instanceof THREE.Texture ) { + + data.envMap = this.envMap.toJSON( meta ).uuid; + data.reflectivity = this.reflectivity; // Scale behind envMap + + } + + if ( this.size !== undefined ) data.size = this.size; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + + if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors; + if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading; + if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending; + if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side; + + if ( this.opacity < 1 ) data.opacity = this.opacity; + if ( this.transparent === true ) data.transparent = this.transparent; + if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; + if ( this.wireframe === true ) data.wireframe = this.wireframe; + if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; + + // TODO: Copied from Object3D.toJSON + + function extractFromCache ( cache ) { + + var values = []; + + for ( var key in cache ) { + + var data = cache[ key ]; + delete data.metadata; + values.push( data ); + + } + + return values; + + } + + if ( isRoot ) { + + var textures = extractFromCache( meta.textures ); + var images = extractFromCache( meta.images ); + + if ( textures.length > 0 ) data.textures = textures; + if ( images.length > 0 ) data.images = images; + + } + + return data; }, - clone: function ( material ) { + clone: function () { - if ( material === undefined ) material = new THREE.Material(); + return new this.constructor().copy( this ); - material.name = this.name; + }, - material.side = this.side; + copy: function ( source ) { - material.opacity = this.opacity; - material.transparent = this.transparent; + this.name = source.name; - material.blending = this.blending; + this.side = source.side; - material.blendSrc = this.blendSrc; - material.blendDst = this.blendDst; - material.blendEquation = this.blendEquation; - material.blendSrcAlpha = this.blendSrcAlpha; - material.blendDstAlpha = this.blendDstAlpha; - material.blendEquationAlpha = this.blendEquationAlpha; + this.opacity = source.opacity; + this.transparent = source.transparent; - material.depthTest = this.depthTest; - material.depthWrite = this.depthWrite; + this.blending = source.blending; - material.polygonOffset = this.polygonOffset; - material.polygonOffsetFactor = this.polygonOffsetFactor; - material.polygonOffsetUnits = this.polygonOffsetUnits; + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; - material.alphaTest = this.alphaTest; + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; - material.overdraw = this.overdraw; + this.colorWrite = source.colorWrite; - material.visible = this.visible; + this.precision = source.precision; - return material; + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; + + this.alphaTest = source.alphaTest; + + this.overdraw = source.overdraw; + + this.visible = source.visible; + + return this; }, @@ -13710,23 +20231,21 @@ THREE.LineBasicMaterial = function ( parameters ) { THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; -THREE.LineBasicMaterial.prototype.clone = function () { +THREE.LineBasicMaterial.prototype.copy = function ( source ) { - var material = new THREE.LineBasicMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.color.copy( source.color ); - material.color.copy( this.color ); + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; - material.linewidth = this.linewidth; - material.linecap = this.linecap; - material.linejoin = this.linejoin; + this.vertexColors = source.vertexColors; - material.vertexColors = this.vertexColors; + this.fog = source.fog; - material.fog = this.fog; - - return material; + return this; }; @@ -13749,7 +20268,7 @@ THREE.LineBasicMaterial.prototype.clone = function () { * dashSize: , * gapSize: , * - * vertexColors: + * vertexColors: THREE.NoColors / THREE.FaceColors / THREE.VertexColors * * fog: * } @@ -13769,7 +20288,7 @@ THREE.LineDashedMaterial = function ( parameters ) { this.dashSize = 3; this.gapSize = 1; - this.vertexColors = false; + this.vertexColors = THREE.NoColors; this.fog = true; @@ -13780,25 +20299,23 @@ THREE.LineDashedMaterial = function ( parameters ) { THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; -THREE.LineDashedMaterial.prototype.clone = function () { +THREE.LineDashedMaterial.prototype.copy = function ( source ) { - var material = new THREE.LineDashedMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.color.copy( source.color ); + + this.linewidth = source.linewidth; - material.color.copy( this.color ); + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; - material.linewidth = this.linewidth; + this.vertexColors = source.vertexColors; - material.scale = this.scale; - material.dashSize = this.dashSize; - material.gapSize = this.gapSize; + this.fog = source.fog; - material.vertexColors = this.vertexColors; - - material.fog = this.fog; - - return material; + return this; }; @@ -13813,7 +20330,8 @@ THREE.LineDashedMaterial.prototype.clone = function () { * opacity: , * map: new THREE.Texture( ), * - * lightMap: new THREE.Texture( ), + * aoMap: new THREE.Texture( ), + * aoMapIntensity: * * specularMap: new THREE.Texture( ), * @@ -13851,7 +20369,8 @@ THREE.MeshBasicMaterial = function ( parameters ) { this.map = null; - this.lightMap = null; + this.aoMap = null; + this.aoMapIntensity = 1.0; this.specularMap = null; @@ -13883,42 +20402,41 @@ THREE.MeshBasicMaterial = function ( parameters ) { THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; -THREE.MeshBasicMaterial.prototype.clone = function () { +THREE.MeshBasicMaterial.prototype.copy = function ( source ) { - var material = new THREE.MeshBasicMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.color.copy( source.color ); - material.color.copy( this.color ); + this.map = source.map; - material.map = this.map; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - material.lightMap = this.lightMap; + this.specularMap = source.specularMap; - material.specularMap = this.specularMap; + this.alphaMap = source.alphaMap; - material.alphaMap = this.alphaMap; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - material.envMap = this.envMap; - material.combine = this.combine; - material.reflectivity = this.reflectivity; - material.refractionRatio = this.refractionRatio; + this.fog = source.fog; - material.fog = this.fog; + this.shading = source.shading; - material.shading = this.shading; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - material.wireframeLinecap = this.wireframeLinecap; - material.wireframeLinejoin = this.wireframeLinejoin; + this.vertexColors = source.vertexColors; - material.vertexColors = this.vertexColors; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; - material.skinning = this.skinning; - material.morphTargets = this.morphTargets; - - return material; + return this; }; @@ -13930,12 +20448,19 @@ THREE.MeshBasicMaterial.prototype.clone = function () { * * parameters = { * color: , - * emissive: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * @@ -13946,7 +20471,6 @@ THREE.MeshBasicMaterial.prototype.clone = function () { * reflectivity: , * refractionRatio: , * - * shading: THREE.SmoothShading, * blending: THREE.NormalBlending, * depthTest: , * depthWrite: , @@ -13971,14 +20495,18 @@ THREE.MeshLambertMaterial = function ( parameters ) { this.type = 'MeshLambertMaterial'; this.color = new THREE.Color( 0xffffff ); // diffuse - this.emissive = new THREE.Color( 0x000000 ); - - this.wrapAround = false; - this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); this.map = null; this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissive = new THREE.Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; this.specularMap = null; @@ -13991,8 +20519,6 @@ THREE.MeshLambertMaterial = function ( parameters ) { this.fog = true; - this.shading = THREE.SmoothShading; - this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; @@ -14011,47 +20537,47 @@ THREE.MeshLambertMaterial = function ( parameters ) { THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; -THREE.MeshLambertMaterial.prototype.clone = function () { +THREE.MeshLambertMaterial.prototype.copy = function ( source ) { - var material = new THREE.MeshLambertMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.color.copy( source.color ); - material.color.copy( this.color ); - material.emissive.copy( this.emissive ); + this.map = source.map; - material.wrapAround = this.wrapAround; - material.wrapRGB.copy( this.wrapRGB ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - material.map = this.map; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - material.lightMap = this.lightMap; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - material.specularMap = this.specularMap; + this.specularMap = source.specularMap; - material.alphaMap = this.alphaMap; + this.alphaMap = source.alphaMap; - material.envMap = this.envMap; - material.combine = this.combine; - material.reflectivity = this.reflectivity; - material.refractionRatio = this.refractionRatio; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - material.fog = this.fog; + this.fog = source.fog; - material.shading = this.shading; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - material.wireframeLinecap = this.wireframeLinecap; - material.wireframeLinejoin = this.wireframeLinejoin; + this.vertexColors = source.vertexColors; - material.vertexColors = this.vertexColors; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - material.skinning = this.skinning; - material.morphTargets = this.morphTargets; - material.morphNormals = this.morphNormals; - - return material; + return this; }; @@ -14063,7 +20589,6 @@ THREE.MeshLambertMaterial.prototype.clone = function () { * * parameters = { * color: , - * emissive: , * specular: , * shininess: , * opacity: , @@ -14071,6 +20596,14 @@ THREE.MeshLambertMaterial.prototype.clone = function () { * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , @@ -14078,6 +20611,10 @@ THREE.MeshLambertMaterial.prototype.clone = function () { * normalMap: new THREE.Texture( ), * normalScale: , * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), @@ -14112,18 +20649,20 @@ THREE.MeshPhongMaterial = function ( parameters ) { this.type = 'MeshPhongMaterial'; this.color = new THREE.Color( 0xffffff ); // diffuse - this.emissive = new THREE.Color( 0x000000 ); this.specular = new THREE.Color( 0x111111 ); this.shininess = 30; - this.metal = false; - - this.wrapAround = false; - this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); - this.map = null; this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissive = new THREE.Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; @@ -14131,6 +20670,10 @@ THREE.MeshPhongMaterial = function ( parameters ) { this.normalMap = null; this.normalScale = new THREE.Vector2( 1, 1 ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + this.specularMap = null; this.alphaMap = null; @@ -14162,57 +20705,248 @@ THREE.MeshPhongMaterial = function ( parameters ) { THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; -THREE.MeshPhongMaterial.prototype.clone = function () { +THREE.MeshPhongMaterial.prototype.copy = function ( source ) { - var material = new THREE.MeshPhongMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.color.copy( source.color ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; - material.color.copy( this.color ); - material.emissive.copy( this.emissive ); - material.specular.copy( this.specular ); - material.shininess = this.shininess; + this.map = source.map; - material.metal = this.metal; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - material.wrapAround = this.wrapAround; - material.wrapRGB.copy( this.wrapRGB ); + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - material.map = this.map; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - material.lightMap = this.lightMap; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - material.bumpMap = this.bumpMap; - material.bumpScale = this.bumpScale; + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); - material.normalMap = this.normalMap; - material.normalScale.copy( this.normalScale ); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - material.specularMap = this.specularMap; + this.specularMap = source.specularMap; - material.alphaMap = this.alphaMap; + this.alphaMap = source.alphaMap; - material.envMap = this.envMap; - material.combine = this.combine; - material.reflectivity = this.reflectivity; - material.refractionRatio = this.refractionRatio; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - material.fog = this.fog; + this.fog = source.fog; - material.shading = this.shading; + this.shading = source.shading; - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - material.wireframeLinecap = this.wireframeLinecap; - material.wireframeLinejoin = this.wireframeLinejoin; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - material.vertexColors = this.vertexColors; + this.vertexColors = source.vertexColors; - material.skinning = this.skinning; - material.morphTargets = this.morphTargets; - material.morphNormals = this.morphNormals; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - return material; + return this; + +}; + +// File:src/materials/MeshStandardMaterial.js + +/** + * @author WestLangley / http://github.com/WestLangley + * + * parameters = { + * color: , + * roughness: , + * metalness: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * roughnessMap: new THREE.Texture( ), + * + * metalnessMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * envMapIntensity: + * + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshStandardMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshStandardMaterial'; + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.roughness = 0.5; + this.metalness = 0.5; + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissive = new THREE.Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.roughnessMap = null; + + this.metalnessMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.envMapIntensity = 1.0; + + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshStandardMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshStandardMaterial.prototype.constructor = THREE.MeshStandardMaterial; + +THREE.MeshStandardMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + this.roughness = source.roughness; + this.metalness = source.metalness; + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.roughnessMap = source.roughnessMap; + + this.metalnessMap = source.metalnessMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.envMapIntensity = source.envMapIntensity; + + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + return this; }; @@ -14251,16 +20985,14 @@ THREE.MeshDepthMaterial = function ( parameters ) { THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; -THREE.MeshDepthMaterial.prototype.clone = function () { +THREE.MeshDepthMaterial.prototype.copy = function ( source ) { - var material = new THREE.MeshDepthMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - - return material; + return this; }; @@ -14300,40 +21032,40 @@ THREE.MeshNormalMaterial = function ( parameters ) { THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; -THREE.MeshNormalMaterial.prototype.clone = function () { +THREE.MeshNormalMaterial.prototype.copy = function ( source ) { - var material = new THREE.MeshNormalMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - - return material; + return this; }; -// File:src/materials/MeshFaceMaterial.js +// File:src/materials/MultiMaterial.js /** * @author mrdoob / http://mrdoob.com/ */ -THREE.MeshFaceMaterial = function ( materials ) { +THREE.MultiMaterial = function ( materials ) { this.uuid = THREE.Math.generateUUID(); - this.type = 'MeshFaceMaterial'; - + this.type = 'MultiMaterial'; + this.materials = materials instanceof Array ? materials : []; + this.visible = true; + }; -THREE.MeshFaceMaterial.prototype = { +THREE.MultiMaterial.prototype = { - constructor: THREE.MeshFaceMaterial, + constructor: THREE.MultiMaterial, - toJSON: function () { + toJSON: function ( meta ) { var output = { metadata: { @@ -14346,19 +21078,26 @@ THREE.MeshFaceMaterial.prototype = { materials: [] }; - for ( var i = 0, l = this.materials.length; i < l; i ++ ) { + var materials = this.materials; - output.materials.push( this.materials[ i ].toJSON() ); + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + var material = materials[ i ].toJSON( meta ); + delete material.metadata; + + output.materials.push( material ); } + output.visible = this.visible; + return output; }, clone: function () { - var material = new THREE.MeshFaceMaterial(); + var material = new this.constructor(); for ( var i = 0; i < this.materials.length; i ++ ) { @@ -14366,13 +21105,15 @@ THREE.MeshFaceMaterial.prototype = { } + material.visible = this.visible; + return material; } }; -// File:src/materials/PointCloudMaterial.js +// File:src/materials/PointsMaterial.js /** * @author mrdoob / http://mrdoob.com/ @@ -14396,11 +21137,11 @@ THREE.MeshFaceMaterial.prototype = { * } */ -THREE.PointCloudMaterial = function ( parameters ) { +THREE.PointsMaterial = function ( parameters ) { THREE.Material.call( this ); - this.type = 'PointCloudMaterial'; + this.type = 'PointsMaterial'; this.color = new THREE.Color( 0xffffff ); @@ -14417,43 +21158,25 @@ THREE.PointCloudMaterial = function ( parameters ) { }; -THREE.PointCloudMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.PointCloudMaterial.prototype.constructor = THREE.PointCloudMaterial; +THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial; -THREE.PointCloudMaterial.prototype.clone = function () { +THREE.PointsMaterial.prototype.copy = function ( source ) { - var material = new THREE.PointCloudMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.color.copy( source.color ); - material.color.copy( this.color ); + this.map = source.map; - material.map = this.map; + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; - material.size = this.size; - material.sizeAttenuation = this.sizeAttenuation; + this.vertexColors = source.vertexColors; - material.vertexColors = this.vertexColors; + this.fog = source.fog; - material.fog = this.fog; - - return material; - -}; - -// backwards compatibility - -THREE.ParticleBasicMaterial = function ( parameters ) { - - THREE.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.' ); - return new THREE.PointCloudMaterial( parameters ); - -}; - -THREE.ParticleSystemMaterial = function ( parameters ) { - - THREE.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.' ); - return new THREE.PointCloudMaterial( parameters ); + return this; }; @@ -14497,7 +21220,6 @@ THREE.ShaderMaterial = function ( parameters ) { this.defines = {}; this.uniforms = {}; - this.attributes = null; this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; @@ -14520,6 +21242,13 @@ THREE.ShaderMaterial = function ( parameters ) { this.morphTargets = false; // set to use morph targets this.morphNormals = false; // set to use morph normals + this.extensions = { + derivatives: false, // set to use derivatives + fragDepth: false, // set to use fragment depth values + drawBuffers: false, // set to use draw buffers + shaderTextureLOD: false // set to use shader texture LOD + }; + // When rendered geometry doesn't include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { @@ -14530,44 +21259,65 @@ THREE.ShaderMaterial = function ( parameters ) { this.index0AttributeName = undefined; - this.setValues( parameters ); + if ( parameters !== undefined ) { + + if ( parameters.attributes !== undefined ) { + + console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); + + } + + this.setValues( parameters ); + + } }; THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; -THREE.ShaderMaterial.prototype.clone = function () { +THREE.ShaderMaterial.prototype.copy = function ( source ) { - var material = new THREE.ShaderMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; - material.fragmentShader = this.fragmentShader; - material.vertexShader = this.vertexShader; + this.uniforms = THREE.UniformsUtils.clone( source.uniforms ); - material.uniforms = THREE.UniformsUtils.clone( this.uniforms ); + this.defines = source.defines; - material.attributes = this.attributes; - material.defines = this.defines; + this.shading = source.shading; - material.shading = this.shading; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; + this.fog = source.fog; - material.fog = this.fog; + this.lights = source.lights; - material.lights = this.lights; + this.vertexColors = source.vertexColors; - material.vertexColors = this.vertexColors; + this.skinning = source.skinning; - material.skinning = this.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - material.morphTargets = this.morphTargets; - material.morphNormals = this.morphNormals; + this.extensions = source.extensions; - return material; + return this; + +}; + +THREE.ShaderMaterial.prototype.toJSON = function ( meta ) { + + var data = THREE.Material.prototype.toJSON.call( this, meta ); + + data.uniforms = this.uniforms; + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; + + return data; }; @@ -14588,16 +21338,6 @@ THREE.RawShaderMaterial = function ( parameters ) { THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; -THREE.RawShaderMaterial.prototype.clone = function () { - - var material = new THREE.RawShaderMaterial(); - - THREE.ShaderMaterial.prototype.clone.call( this, material ); - - return material; - -}; - // File:src/materials/SpriteMaterial.js /** @@ -14641,20 +21381,18 @@ THREE.SpriteMaterial = function ( parameters ) { THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; -THREE.SpriteMaterial.prototype.clone = function () { +THREE.SpriteMaterial.prototype.copy = function ( source ) { - var material = new THREE.SpriteMaterial(); + THREE.Material.prototype.copy.call( this, source ); - THREE.Material.prototype.clone.call( this, material ); + this.color.copy( source.color ); + this.map = source.map; - material.color.copy( this.color ); - material.map = this.map; + this.rotation = source.rotation; - material.rotation = this.rotation; + this.fog = source.fog; - material.fog = this.fog; - - return material; + return this; }; @@ -14699,18 +21437,14 @@ THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, f this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - this._needsUpdate = false; + this.version = 0; this.onUpdate = null; var _this = this; - this.__defineGetter__("needsUpdate", function(){ - return _this._needsUpdate; - }); this.__defineSetter__("needsUpdate", function(value){ - if ( value === true ) _this.update(); - _this._needsUpdate = value; + if ( value === true ) _this.version++; }); }; @@ -14721,41 +21455,129 @@ THREE.Texture.prototype = { constructor: THREE.Texture, - clone: function ( texture ) { + clone: function () { - if ( texture === undefined ) texture = new THREE.Texture(); - - texture.image = this.image; - texture.mipmaps = this.mipmaps.slice( 0 ); - - texture.mapping = this.mapping; - - texture.wrapS = this.wrapS; - texture.wrapT = this.wrapT; - - texture.magFilter = this.magFilter; - texture.minFilter = this.minFilter; - - texture.anisotropy = this.anisotropy; - - texture.format = this.format; - texture.type = this.type; - - texture.offset.copy( this.offset ); - texture.repeat.copy( this.repeat ); - - texture.generateMipmaps = this.generateMipmaps; - texture.premultiplyAlpha = this.premultiplyAlpha; - texture.flipY = this.flipY; - texture.unpackAlignment = this.unpackAlignment; - - return texture; + return new this.constructor().copy( this ); }, - update: function () { + copy: function ( source ) { - this.dispatchEvent( { type: 'update' } ); + this.image = source.image; + this.mipmaps = source.mipmaps.slice( 0 ); + + this.mapping = source.mapping; + + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; + + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; + + this.anisotropy = source.anisotropy; + + this.format = source.format; + this.type = source.type; + + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); + + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + + return this; + + }, + + toJSON: function ( meta ) { + + if ( meta.textures[ this.uuid ] !== undefined ) { + + return meta.textures[ this.uuid ]; + + } + + function getDataURL( image ) { + + var canvas; + + if ( image.toDataURL !== undefined ) { + + canvas = image; + + } else { + + canvas = document.createElement( 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; + + canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); + + } + + if ( canvas.width > 2048 || canvas.height > 2048 ) { + + return canvas.toDataURL( 'image/jpeg', 0.6 ); + + } else { + + return canvas.toDataURL( 'image/png' ); + + } + + } + + var output = { + metadata: { + version: 4.4, + type: 'Texture', + generator: 'Texture.toJSON' + }, + + uuid: this.uuid, + name: this.name, + + mapping: this.mapping, + + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + wrap: [ this.wrapS, this.wrapT ], + + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy + }; + + if ( this.image !== undefined ) { + + // TODO: Move to THREE.Image + + var image = this.image; + + if ( image.uuid === undefined ) { + + image.uuid = THREE.Math.generateUUID(); // UGH + + } + + if ( meta.images[ image.uuid ] === undefined ) { + + meta.images[ image.uuid ] = { + uuid: image.uuid, + url: getDataURL( image ) + }; + + } + + output.image = image.uuid; + + } + + meta.textures[ this.uuid ] = output; + + return output; }, @@ -14763,6 +21585,83 @@ THREE.Texture.prototype = { this.dispatchEvent( { type: 'dispose' } ); + }, + + transformUv: function ( uv ) { + + if ( this.mapping !== THREE.UVMapping ) return; + + uv.multiply( this.repeat ); + uv.add( this.offset ); + + if ( uv.x < 0 || uv.x > 1 ) { + + switch ( this.wrapS ) { + + case THREE.RepeatWrapping: + + uv.x = uv.x - Math.floor( uv.x ); + break; + + case THREE.ClampToEdgeWrapping: + + uv.x = uv.x < 0 ? 0 : 1; + break; + + case THREE.MirroredRepeatWrapping: + + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + + uv.x = Math.ceil( uv.x ) - uv.x; + + } else { + + uv.x = uv.x - Math.floor( uv.x ); + + } + break; + + } + + } + + if ( uv.y < 0 || uv.y > 1 ) { + + switch ( this.wrapT ) { + + case THREE.RepeatWrapping: + + uv.y = uv.y - Math.floor( uv.y ); + break; + + case THREE.ClampToEdgeWrapping: + + uv.y = uv.y < 0 ? 0 : 1; + break; + + case THREE.MirroredRepeatWrapping: + + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { + + uv.y = Math.ceil( uv.y ) - uv.y; + + } else { + + uv.y = uv.y - Math.floor( uv.y ); + + } + break; + + } + + } + + if ( this.flipY ) { + + uv.y = 1 - uv.y; + + } + } }; @@ -14771,6 +21670,23 @@ THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); THREE.TextureIdCount = 0; +// File:src/textures/CanvasTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.needsUpdate = true; + +}; + +THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture; + // File:src/textures/CubeTexture.js /** @@ -14780,25 +21696,24 @@ THREE.TextureIdCount = 0; THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; - + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.images = images; + this.flipY = false; }; THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; -THREE.CubeTexture.clone = function ( texture ) { +THREE.CubeTexture.prototype.copy = function ( source ) { - if ( texture === undefined ) texture = new THREE.CubeTexture(); + THREE.Texture.prototype.copy.call( this, source ); - THREE.Texture.prototype.clone.call( this, texture ); + this.images = source.images; - texture.images = this.images; - - return texture; + return this; }; @@ -14830,16 +21745,6 @@ THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mappi THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; -THREE.CompressedTexture.prototype.clone = function () { - - var texture = new THREE.CompressedTexture(); - - THREE.Texture.prototype.clone.call( this, texture ); - - return texture; - -}; - // File:src/textures/DataTexture.js /** @@ -14852,21 +21757,17 @@ THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS this.image = { data: data, width: width, height: height }; + this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; + + this.flipY = false; + this.generateMipmaps = false; + }; THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); THREE.DataTexture.prototype.constructor = THREE.DataTexture; -THREE.DataTexture.prototype.clone = function () { - - var texture = new THREE.DataTexture(); - - THREE.Texture.prototype.clone.call( this, texture ); - - return texture; - -}; - // File:src/textures/VideoTexture.js /** @@ -14881,7 +21782,7 @@ THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilt var scope = this; - var update = function () { + function update() { requestAnimationFrame( update ); @@ -14891,7 +21792,7 @@ THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilt } - }; + } update(); @@ -14916,69 +21817,74 @@ THREE.Group = function () { THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); THREE.Group.prototype.constructor = THREE.Group; - -// File:src/objects/PointCloud.js +// File:src/objects/Points.js /** * @author alteredq / http://alteredqualia.com/ */ -THREE.PointCloud = function ( geometry, material ) { +THREE.Points = function ( geometry, material ) { THREE.Object3D.call( this ); - this.type = 'PointCloud'; + this.type = 'Points'; this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); - this.material = material !== undefined ? material : new THREE.PointCloudMaterial( { color: Math.random() * 0xffffff } ); + this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } ); }; -THREE.PointCloud.prototype = Object.create( THREE.Object3D.prototype ); -THREE.PointCloud.prototype.constructor = THREE.PointCloud; +THREE.Points.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Points.prototype.constructor = THREE.Points; -THREE.PointCloud.prototype.raycast = ( function () { +THREE.Points.prototype.raycast = ( function () { var inverseMatrix = new THREE.Matrix4(); var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); - return function ( raycaster, intersects ) { + return function raycast( raycaster, intersects ) { var object = this; - var geometry = object.geometry; - var threshold = raycaster.params.PointCloud.threshold; + var geometry = this.geometry; + var matrixWorld = this.matrixWorld; + var threshold = raycaster.params.Points.threshold; - inverseMatrix.getInverse( this.matrixWorld ); + // Checking boundingSphere distance to ray + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( matrixWorld ); + + if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; + + // + + inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - if ( geometry.boundingBox !== null ) { - - if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { - - return; - - } - - } - var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var localThresholdSq = localThreshold * localThreshold; var position = new THREE.Vector3(); - var testPoint = function ( point, index ) { + function testPoint( point, index ) { - var rayPointDistance = ray.distanceToPoint( point ); + var rayPointDistanceSq = ray.distanceSqToPoint( point ); - if ( rayPointDistance < localThreshold ) { + if ( rayPointDistanceSq < localThresholdSq ) { var intersectPoint = ray.closestPointToPoint( point ); - intersectPoint.applyMatrix4( object.matrixWorld ); + intersectPoint.applyMatrix4( matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectPoint ); + if ( distance < raycaster.near || distance > raycaster.far ) return; + intersects.push( { distance: distance, - distanceToRay: rayPointDistance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint.clone(), index: index, face: null, @@ -14988,59 +21894,33 @@ THREE.PointCloud.prototype.raycast = ( function () { } - }; + } if ( geometry instanceof THREE.BufferGeometry ) { + var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; - if ( attributes.index !== undefined ) { + if ( index !== null ) { - var indices = attributes.index.array; - var offsets = geometry.offsets; + var indices = index.array; - if ( offsets.length === 0 ) { + for ( var i = 0, il = indices.length; i < il; i ++ ) { - var offset = { - start: 0, - count: indices.length, - index: 0 - }; + var a = indices[ i ]; - offsets = [ offset ]; + position.fromArray( positions, a * 3 ); - } - - for ( var oi = 0, ol = offsets.length; oi < ol; ++ oi ) { - - var start = offsets[ oi ].start; - var count = offsets[ oi ].count; - var index = offsets[ oi ].index; - - for ( var i = start, il = start + count; i < il; i ++ ) { - - var a = index + indices[ i ]; - - position.fromArray( positions, a * 3 ); - - testPoint( position, a ); - - } + testPoint( position, a ); } } else { - var pointCount = positions.length / 3; + for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { - for ( var i = 0; i < pointCount; i ++ ) { - - position.set( - positions[ 3 * i ], - positions[ 3 * i + 1 ], - positions[ 3 * i + 2 ] - ); + position.fromArray( positions, i * 3 ); testPoint( position, i ); @@ -15050,9 +21930,9 @@ THREE.PointCloud.prototype.raycast = ( function () { } else { - var vertices = this.geometry.vertices; + var vertices = geometry.vertices; - for ( var i = 0; i < vertices.length; i ++ ) { + for ( var i = 0, l = vertices.length; i < l; i ++ ) { testPoint( vertices[ i ], i ); @@ -15064,22 +21944,9 @@ THREE.PointCloud.prototype.raycast = ( function () { }() ); -THREE.PointCloud.prototype.clone = function ( object ) { +THREE.Points.prototype.clone = function () { - if ( object === undefined ) object = new THREE.PointCloud( this.geometry, this.material ); - - THREE.Object3D.prototype.clone.call( this, object ); - - return object; - -}; - -// Backwards compatibility - -THREE.ParticleSystem = function ( geometry, material ) { - - THREE.warn( 'THREE.ParticleSystem has been renamed to THREE.PointCloud.' ); - return new THREE.PointCloud( geometry, material ); + return new this.constructor( this.geometry, this.material ).copy( this ); }; @@ -15091,6 +21958,13 @@ THREE.ParticleSystem = function ( geometry, material ) { THREE.Line = function ( geometry, material, mode ) { + if ( mode === 1 ) { + + console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); + return new THREE.LineSegments( geometry, material ); + + } + THREE.Object3D.call( this ); this.type = 'Line'; @@ -15098,13 +21972,8 @@ THREE.Line = function ( geometry, material, mode ) { this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); - this.mode = mode !== undefined ? mode : THREE.LineStrip; - }; -THREE.LineStrip = 0; -THREE.LinePieces = 1; - THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); THREE.Line.prototype.constructor = THREE.Line; @@ -15114,96 +21983,80 @@ THREE.Line.prototype.raycast = ( function () { var ray = new THREE.Ray(); var sphere = new THREE.Sphere(); - return function ( raycaster, intersects ) { + return function raycast( raycaster, intersects ) { var precision = raycaster.linePrecision; var precisionSq = precision * precision; var geometry = this.geometry; - - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + var matrixWorld = this.matrixWorld; // Checking boundingSphere distance to ray + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( this.matrixWorld ); + sphere.applyMatrix4( matrixWorld ); - if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - return; + // - } - - inverseMatrix.getInverse( this.matrixWorld ); + inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var vStart = new THREE.Vector3(); var vEnd = new THREE.Vector3(); var interSegment = new THREE.Vector3(); var interRay = new THREE.Vector3(); - var step = this.mode === THREE.LineStrip ? 1 : 2; + var step = this instanceof THREE.LineSegments ? 2 : 1; if ( geometry instanceof THREE.BufferGeometry ) { + var index = geometry.index; var attributes = geometry.attributes; + var positions = attributes.position.array; - if ( attributes.index !== undefined ) { + if ( index !== null ) { - var indices = attributes.index.array; - var positions = attributes.position.array; - var offsets = geometry.offsets; + var indices = index.array; - if ( offsets.length === 0 ) { + for ( var i = 0, l = indices.length - 1; i < l; i += step ) { - offsets = [ { start: 0, count: indices.length, index: 0 } ]; + var a = indices[ i ]; + var b = indices[ i + 1 ]; - } + vStart.fromArray( positions, a * 3 ); + vEnd.fromArray( positions, b * 3 ); - for ( var oi = 0; oi < offsets.length; oi ++) { + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - var start = offsets[ oi ].start; - var count = offsets[ oi ].count; - var index = offsets[ oi ].index; + if ( distSq > precisionSq ) continue; - for ( var i = start; i < start + count - 1; i += step ) { + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - var a = index + indices[ i ]; - var b = index + indices[ i + 1 ]; + var distance = raycaster.ray.origin.distanceTo( interRay ); - vStart.fromArray( positions, a * 3 ); - vEnd.fromArray( positions, b * 3 ); + if ( distance < raycaster.near || distance > raycaster.far ) continue; - var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + intersects.push( { - if ( distSq > precisionSq ) continue; + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - var distance = ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - offsetIndex: oi, - face: null, - faceIndex: null, - object: this - - } ); - - } + } ); } } else { - var positions = attributes.position.array; - - for ( var i = 0; i < positions.length / 3 - 1; i += step ) { + for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { vStart.fromArray( positions, 3 * i ); vEnd.fromArray( positions, 3 * i + 3 ); @@ -15212,7 +22065,9 @@ THREE.Line.prototype.raycast = ( function () { if ( distSq > precisionSq ) continue; - var distance = ray.origin.distanceTo( interRay ); + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; @@ -15244,7 +22099,9 @@ THREE.Line.prototype.raycast = ( function () { if ( distSq > precisionSq ) continue; - var distance = ray.origin.distanceTo( interRay ); + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; @@ -15269,16 +22126,34 @@ THREE.Line.prototype.raycast = ( function () { }() ); -THREE.Line.prototype.clone = function ( object ) { +THREE.Line.prototype.clone = function () { - if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.mode ); - - THREE.Object3D.prototype.clone.call( this, object ); - - return object; + return new this.constructor( this.geometry, this.material ).copy( this ); }; +// DEPRECATED + +THREE.LineStrip = 0; +THREE.LinePieces = 1; + +// File:src/objects/LineSegments.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LineSegments = function ( geometry, material ) { + + THREE.Line.call( this, geometry, material ); + + this.type = 'LineSegments'; + +}; + +THREE.LineSegments.prototype = Object.create( THREE.Line.prototype ); +THREE.LineSegments.prototype.constructor = THREE.LineSegments; + // File:src/objects/Mesh.js /** @@ -15293,10 +22168,12 @@ THREE.Mesh = function ( geometry, material ) { THREE.Object3D.call( this ); this.type = 'Mesh'; - + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + this.drawMode = THREE.TrianglesDrawMode; + this.updateMorphTargets(); }; @@ -15304,12 +22181,17 @@ THREE.Mesh = function ( geometry, material ) { THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); THREE.Mesh.prototype.constructor = THREE.Mesh; +THREE.Mesh.prototype.setDrawMode = function ( value ) { + + this.drawMode = value; + +}; + THREE.Mesh.prototype.updateMorphTargets = function () { if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { this.morphTargetBase = - 1; - this.morphTargetForcedOrder = []; this.morphTargetInfluences = []; this.morphTargetDictionary = {}; @@ -15332,7 +22214,7 @@ THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { } - THREE.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); + console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); return 0; @@ -15349,104 +22231,155 @@ THREE.Mesh.prototype.raycast = ( function () { var vB = new THREE.Vector3(); var vC = new THREE.Vector3(); - return function ( raycaster, intersects ) { + var tempA = new THREE.Vector3(); + var tempB = new THREE.Vector3(); + var tempC = new THREE.Vector3(); + + var uvA = new THREE.Vector2(); + var uvB = new THREE.Vector2(); + var uvC = new THREE.Vector2(); + + var barycoord = new THREE.Vector3(); + + var intersectionPoint = new THREE.Vector3(); + var intersectionPointWorld = new THREE.Vector3(); + + function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { + + THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); + + uv1.multiplyScalar( barycoord.x ); + uv2.multiplyScalar( barycoord.y ); + uv3.multiplyScalar( barycoord.z ); + + uv1.add( uv2 ).add( uv3 ); + + return uv1.clone(); + + } + + function checkIntersection( object, raycaster, ray, pA, pB, pC, point ) { + + var intersect; + var material = object.material; + + if ( material.side === THREE.BackSide ) { + + intersect = ray.intersectTriangle( pC, pB, pA, true, point ); + + } else { + + intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point ); + + } + + if ( intersect === null ) return null; + + intersectionPointWorld.copy( point ); + intersectionPointWorld.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); + + if ( distance < raycaster.near || distance > raycaster.far ) return null; + + return { + distance: distance, + point: intersectionPointWorld.clone(), + object: object + }; + + } + + function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) { + + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); + + var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint ); + + if ( intersection ) { + + if ( uvs ) { + + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); + + intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); + + } + + intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ); + intersection.faceIndex = a; + + } + + return intersection; + + } + + return function raycast( raycaster, intersects ) { var geometry = this.geometry; + var material = this.material; + var matrixWorld = this.matrixWorld; + + if ( material === undefined ) return; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( this.matrixWorld ); + sphere.applyMatrix4( matrixWorld ); - if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - return; + // - } + inverseMatrix.getInverse( matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); // Check boundingBox before continuing - inverseMatrix.getInverse( this.matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - if ( geometry.boundingBox !== null ) { - if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { - - return; - - } + if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; } + var uvs, intersection; + if ( geometry instanceof THREE.BufferGeometry ) { - var material = this.material; - - if ( material === undefined ) return; - - var attributes = geometry.attributes; - var a, b, c; - var precision = raycaster.precision; + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; - if ( attributes.index !== undefined ) { + if ( attributes.uv !== undefined ) { - var indices = attributes.index.array; - var positions = attributes.position.array; - var offsets = geometry.offsets; + uvs = attributes.uv.array; - if ( offsets.length === 0 ) { + } - offsets = [ { start: 0, count: indices.length, index: 0 } ]; + if ( index !== null ) { - } + var indices = index.array; - for ( var oi = 0, ol = offsets.length; oi < ol; ++ oi ) { + for ( var i = 0, l = indices.length; i < l; i += 3 ) { - var start = offsets[ oi ].start; - var count = offsets[ oi ].count; - var index = offsets[ oi ].index; + a = indices[ i ]; + b = indices[ i + 1 ]; + c = indices[ i + 2 ]; - for ( var i = start, il = start + count; i < il; i += 3 ) { + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); - a = index + indices[ i ]; - b = index + indices[ i + 1 ]; - c = index + indices[ i + 2 ]; + if ( intersection ) { - vA.fromArray( positions, a * 3 ); - vB.fromArray( positions, b * 3 ); - vC.fromArray( positions, c * 3 ); - - if ( material.side === THREE.BackSide ) { - - var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); - - } else { - - var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); - - } - - if ( intersectionPoint === null ) continue; - - intersectionPoint.applyMatrix4( this.matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); - - if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - point: intersectionPoint, - face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), - faceIndex: null, - object: this - - } ); + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics + intersects.push( intersection ); } @@ -15454,73 +22387,49 @@ THREE.Mesh.prototype.raycast = ( function () { } else { - var positions = attributes.position.array; - for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) { + for ( var i = 0, l = positions.length; i < l; i += 9 ) { - a = i; - b = i + 1; - c = i + 2; + a = i / 3; + b = a + 1; + c = a + 2; - vA.fromArray( positions, j ); - vB.fromArray( positions, j + 3 ); - vC.fromArray( positions, j + 6 ); + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); - if ( material.side === THREE.BackSide ) { + if ( intersection ) { - var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); - - } else { - - var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + intersection.index = a; // triangle number in positions buffer semantics + intersects.push( intersection ); } - if ( intersectionPoint === null ) continue; - - intersectionPoint.applyMatrix4( this.matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); - - if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - point: intersectionPoint, - face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), - faceIndex: null, - object: this - - } ); - } } } else if ( geometry instanceof THREE.Geometry ) { - var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial; - var objectMaterials = isFaceMaterial === true ? this.material.materials : null; - - var a, b, c; - var precision = raycaster.precision; + var fvA, fvB, fvC; + var isFaceMaterial = material instanceof THREE.MultiMaterial; + var materials = isFaceMaterial === true ? material.materials : null; var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; + if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; - for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + for ( var f = 0, fl = faces.length; f < fl; f ++ ) { - var face = geometry.faces[ f ]; + var face = faces[ f ]; + var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; - var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material; + if ( faceMaterial === undefined ) continue; - if ( material === undefined ) continue; + fvA = vertices[ face.a ]; + fvB = vertices[ face.b ]; + fvC = vertices[ face.c ]; - a = vertices[ face.a ]; - b = vertices[ face.b ]; - c = vertices[ face.c ]; - - if ( material.morphTargets === true ) { + if ( faceMaterial.morphTargets === true ) { var morphTargets = geometry.morphTargets; var morphInfluences = this.morphTargetInfluences; @@ -15537,58 +22446,43 @@ THREE.Mesh.prototype.raycast = ( function () { var targets = morphTargets[ t ].vertices; - vA.x += ( targets[ face.a ].x - a.x ) * influence; - vA.y += ( targets[ face.a ].y - a.y ) * influence; - vA.z += ( targets[ face.a ].z - a.z ) * influence; - - vB.x += ( targets[ face.b ].x - b.x ) * influence; - vB.y += ( targets[ face.b ].y - b.y ) * influence; - vB.z += ( targets[ face.b ].z - b.z ) * influence; - - vC.x += ( targets[ face.c ].x - c.x ) * influence; - vC.y += ( targets[ face.c ].y - c.y ) * influence; - vC.z += ( targets[ face.c ].z - c.z ) * influence; + vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); + vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); + vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); } - vA.add( a ); - vB.add( b ); - vC.add( c ); + vA.add( fvA ); + vB.add( fvB ); + vC.add( fvC ); - a = vA; - b = vB; - c = vC; + fvA = vA; + fvB = vB; + fvC = vC; } - if ( material.side === THREE.BackSide ) { + intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); - var intersectionPoint = ray.intersectTriangle( c, b, a, true ); + if ( intersection ) { - } else { + if ( uvs ) { - var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide ); + var uvs_f = uvs[ f ]; + uvA.copy( uvs_f[ 0 ] ); + uvB.copy( uvs_f[ 1 ] ); + uvC.copy( uvs_f[ 2 ] ); + + intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); + + } + + intersection.face = face; + intersection.faceIndex = f; + intersects.push( intersection ); } - if ( intersectionPoint === null ) continue; - - intersectionPoint.applyMatrix4( this.matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); - - if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; - - intersects.push( { - - distance: distance, - point: intersectionPoint, - face: face, - faceIndex: f, - object: this - - } ); - } } @@ -15597,13 +22491,9 @@ THREE.Mesh.prototype.raycast = ( function () { }() ); -THREE.Mesh.prototype.clone = function ( object, recursive ) { +THREE.Mesh.prototype.clone = function () { - if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material ); - - THREE.Object3D.prototype.clone.call( this, object, recursive ); - - return object; + return new this.constructor( this.geometry, this.material ).copy( this ); }; @@ -15628,6 +22518,16 @@ THREE.Bone = function ( skin ) { THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); THREE.Bone.prototype.constructor = THREE.Bone; +THREE.Bone.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.skin = source.skin; + + return this; + +}; + // File:src/objects/Skeleton.js /** @@ -15655,31 +22555,21 @@ THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones (8 * 8 / 4) - // 16x16 pixel texture max 64 bones (16 * 16 / 4) - // 32x32 pixel texture max 256 bones (32 * 32 / 4) - // 64x64 pixel texture max 1024 bones (64 * 64 / 4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - var size; - - if ( this.bones.length > 256 ) - size = 64; - else if ( this.bones.length > 64 ) - size = 32; - else if ( this.bones.length > 16 ) - size = 16; - else - size = 8; + + var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix + size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) ); + size = Math.max( size, 4 ); this.boneTextureWidth = size; this.boneTextureHeight = size; this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); - this.boneTexture.minFilter = THREE.NearestFilter; - this.boneTexture.magFilter = THREE.NearestFilter; - this.boneTexture.generateMipmaps = false; - this.boneTexture.flipY = false; } else { @@ -15701,7 +22591,7 @@ THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { } else { - THREE.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); this.boneInverses = []; @@ -15785,8 +22675,8 @@ THREE.Skeleton.prototype.pose = function () { THREE.Skeleton.prototype.update = ( function () { var offsetMatrix = new THREE.Matrix4(); - - return function () { + + return function update() { // flatten bone matrices to array @@ -15806,11 +22696,16 @@ THREE.Skeleton.prototype.update = ( function () { this.boneTexture.needsUpdate = true; } - + }; } )(); +THREE.Skeleton.prototype.clone = function () { + + return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); + +}; // File:src/objects/SkinnedMesh.js @@ -15839,32 +22734,19 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { if ( this.geometry && this.geometry.bones !== undefined ) { - var bone, gbone, p, q, s; + var bone, gbone; for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { gbone = this.geometry.bones[ b ]; - p = gbone.pos; - q = gbone.rotq; - s = gbone.scl; - bone = new THREE.Bone( this ); bones.push( bone ); bone.name = gbone.name; - bone.position.set( p[ 0 ], p[ 1 ], p[ 2 ] ); - bone.quaternion.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] ); - - if ( s !== undefined ) { - - bone.scale.set( s[ 0 ], s[ 1 ], s[ 2 ] ); - - } else { - - bone.scale.set( 1, 1, 1 ); - - } + bone.position.fromArray( gbone.pos ); + bone.quaternion.fromArray( gbone.rotq ); + if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); } @@ -15872,7 +22754,7 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { gbone = this.geometry.bones[ b ]; - if ( gbone.parent !== - 1 ) { + if ( gbone.parent !== - 1 && gbone.parent !== null ) { bones[ gbone.parent ].add( bones[ b ] ); @@ -15889,7 +22771,7 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { this.normalizeSkinWeights(); this.updateMatrixWorld( true ); - this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ) ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); }; @@ -15905,6 +22787,8 @@ THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { this.updateMatrixWorld( true ); + this.skeleton.calculateInverses(); + bindMatrix = this.matrixWorld; } @@ -15924,7 +22808,7 @@ THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { if ( this.geometry instanceof THREE.Geometry ) { - for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + for ( var i = 0; i < this.geometry.skinWeights.length; i ++ ) { var sw = this.geometry.skinWeights[ i ]; @@ -15936,15 +22820,40 @@ THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { } else { - sw.set( 1 ); // this will be normalized by the shader anyway + sw.set( 1, 0, 0, 0 ); // do something reasonable } } - } else { + } else if ( this.geometry instanceof THREE.BufferGeometry ) { - // skinning weights assumed to be normalized for THREE.BufferGeometry + var vec = new THREE.Vector4(); + + var skinWeight = this.geometry.attributes.skinWeight; + + for ( var i = 0; i < skinWeight.count; i ++ ) { + + vec.x = skinWeight.getX( i ); + vec.y = skinWeight.getY( i ); + vec.z = skinWeight.getZ( i ); + vec.w = skinWeight.getW( i ); + + var scale = 1.0 / vec.lengthManhattan(); + + if ( scale !== Infinity ) { + + vec.multiplyScalar( scale ); + + } else { + + vec.set( 1, 0, 0, 0 ); // do something reasonable + + } + + skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); + + } } @@ -15964,240 +22873,15 @@ THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { } else { - THREE.warn( 'THREE.SkinnedMesh unreckognized bindMode: ' + this.bindMode ); + console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); } }; -THREE.SkinnedMesh.prototype.clone = function( object ) { +THREE.SkinnedMesh.prototype.clone = function() { - if ( object === undefined ) { - - object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ); - - } - - THREE.Mesh.prototype.clone.call( this, object ); - - return object; - -}; - - -// File:src/objects/MorphAnimMesh.js - -/** - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.MorphAnimMesh = function ( geometry, material ) { - - THREE.Mesh.call( this, geometry, material ); - - this.type = 'MorphAnimMesh'; - - // API - - this.duration = 1000; // milliseconds - this.mirroredLoop = false; - this.time = 0; - - // internals - - this.lastKeyframe = 0; - this.currentKeyframe = 0; - - this.direction = 1; - this.directionBackwards = false; - - this.setFrameRange( 0, this.geometry.morphTargets.length - 1 ); - -}; - -THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); -THREE.MorphAnimMesh.prototype.constructor = THREE.MorphAnimMesh; - -THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { - - this.startKeyframe = start; - this.endKeyframe = end; - - this.length = this.endKeyframe - this.startKeyframe + 1; - -}; - -THREE.MorphAnimMesh.prototype.setDirectionForward = function () { - - this.direction = 1; - this.directionBackwards = false; - -}; - -THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { - - this.direction = - 1; - this.directionBackwards = true; - -}; - -THREE.MorphAnimMesh.prototype.parseAnimations = function () { - - var geometry = this.geometry; - - if ( ! geometry.animations ) geometry.animations = {}; - - var firstAnimation, animations = geometry.animations; - - var pattern = /([a-z]+)_?(\d+)/; - - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { - - var morph = geometry.morphTargets[ i ]; - var parts = morph.name.match( pattern ); - - if ( parts && parts.length > 1 ) { - - var label = parts[ 1 ]; - - if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: - Infinity }; - - var animation = animations[ label ]; - - if ( i < animation.start ) animation.start = i; - if ( i > animation.end ) animation.end = i; - - if ( ! firstAnimation ) firstAnimation = label; - - } - - } - - geometry.firstAnimation = firstAnimation; - -}; - -THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { - - if ( ! this.geometry.animations ) this.geometry.animations = {}; - - this.geometry.animations[ label ] = { start: start, end: end }; - -}; - -THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { - - var animation = this.geometry.animations[ label ]; - - if ( animation ) { - - this.setFrameRange( animation.start, animation.end ); - this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); - this.time = 0; - - } else { - - THREE.warn( 'THREE.MorphAnimMesh: animation[' + label + '] undefined in .playAnimation()' ); - - } - -}; - -THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { - - var frameTime = this.duration / this.length; - - this.time += this.direction * delta; - - if ( this.mirroredLoop ) { - - if ( this.time > this.duration || this.time < 0 ) { - - this.direction *= - 1; - - if ( this.time > this.duration ) { - - this.time = this.duration; - this.directionBackwards = true; - - } - - if ( this.time < 0 ) { - - this.time = 0; - this.directionBackwards = false; - - } - - } - - } else { - - this.time = this.time % this.duration; - - if ( this.time < 0 ) this.time += this.duration; - - } - - var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); - - if ( keyframe !== this.currentKeyframe ) { - - this.morphTargetInfluences[ this.lastKeyframe ] = 0; - this.morphTargetInfluences[ this.currentKeyframe ] = 1; - - this.morphTargetInfluences[ keyframe ] = 0; - - this.lastKeyframe = this.currentKeyframe; - this.currentKeyframe = keyframe; - - } - - var mix = ( this.time % frameTime ) / frameTime; - - if ( this.directionBackwards ) { - - mix = 1 - mix; - - } - - this.morphTargetInfluences[ this.currentKeyframe ] = mix; - this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix; - -}; - -THREE.MorphAnimMesh.prototype.interpolateTargets = function ( a, b, t ) { - - var influences = this.morphTargetInfluences; - - for ( var i = 0, l = influences.length; i < l; i ++ ) { - - influences[ i ] = 0; - - } - - if ( a > -1 ) influences[ a ] = 1 - t; - if ( b > -1 ) influences[ b ] = t; - -}; - -THREE.MorphAnimMesh.prototype.clone = function ( object ) { - - if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material ); - - object.duration = this.duration; - object.mirroredLoop = this.mirroredLoop; - object.time = this.time; - - object.lastKeyframe = this.lastKeyframe; - object.currentKeyframe = this.currentKeyframe; - - object.direction = this.direction; - object.directionBackwards = this.directionBackwards; - - THREE.Mesh.prototype.clone.call( this, object ); - - return object; + return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this ); }; @@ -16213,7 +22897,22 @@ THREE.LOD = function () { THREE.Object3D.call( this ); - this.objects = []; + this.type = 'LOD'; + + Object.defineProperties( this, { + levels: { + enumerable: true, + value: [] + }, + objects: { + get: function () { + + console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); + return this.levels; + + } + } + } ); }; @@ -16227,9 +22926,11 @@ THREE.LOD.prototype.addLevel = function ( object, distance ) { distance = Math.abs( distance ); - for ( var l = 0; l < this.objects.length; l ++ ) { + var levels = this.levels; - if ( distance < this.objects[ l ].distance ) { + for ( var l = 0; l < levels.length; l ++ ) { + + if ( distance < levels[ l ].distance ) { break; @@ -16237,16 +22938,19 @@ THREE.LOD.prototype.addLevel = function ( object, distance ) { } - this.objects.splice( l, 0, { distance: distance, object: object } ); + levels.splice( l, 0, { distance: distance, object: object } ); + this.add( object ); }; THREE.LOD.prototype.getObjectForDistance = function ( distance ) { - for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + var levels = this.levels; - if ( distance < this.objects[ i ].distance ) { + for ( var i = 1, l = levels.length; i < l; i ++ ) { + + if ( distance < levels[ i ].distance ) { break; @@ -16254,7 +22958,7 @@ THREE.LOD.prototype.getObjectForDistance = function ( distance ) { } - return this.objects[ i - 1 ].object; + return levels[ i - 1 ].object; }; @@ -16262,7 +22966,7 @@ THREE.LOD.prototype.raycast = ( function () { var matrixPosition = new THREE.Vector3(); - return function ( raycaster, intersects ) { + return function raycast( raycaster, intersects ) { matrixPosition.setFromMatrixPosition( this.matrixWorld ); @@ -16279,23 +22983,25 @@ THREE.LOD.prototype.update = function () { var v1 = new THREE.Vector3(); var v2 = new THREE.Vector3(); - return function ( camera ) { + return function update( camera ) { - if ( this.objects.length > 1 ) { + var levels = this.levels; + + if ( levels.length > 1 ) { v1.setFromMatrixPosition( camera.matrixWorld ); v2.setFromMatrixPosition( this.matrixWorld ); var distance = v1.distanceTo( v2 ); - this.objects[ 0 ].object.visible = true; + levels[ 0 ].object.visible = true; - for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + for ( var i = 1, l = levels.length; i < l; i ++ ) { - if ( distance >= this.objects[ i ].distance ) { + if ( distance >= levels[ i ].distance ) { - this.objects[ i - 1 ].object.visible = false; - this.objects[ i ].object.visible = true; + levels[ i - 1 ].object.visible = false; + levels[ i ].object.visible = true; } else { @@ -16307,7 +23013,7 @@ THREE.LOD.prototype.update = function () { for ( ; i < l; i ++ ) { - this.objects[ i ].object.visible = false; + levels[ i ].object.visible = false; } @@ -16317,19 +23023,44 @@ THREE.LOD.prototype.update = function () { }(); -THREE.LOD.prototype.clone = function ( object ) { +THREE.LOD.prototype.copy = function ( source ) { - if ( object === undefined ) object = new THREE.LOD(); + THREE.Object3D.prototype.copy.call( this, source, false ); - THREE.Object3D.prototype.clone.call( this, object ); + var levels = source.levels; + + for ( var i = 0, l = levels.length; i < l; i ++ ) { + + var level = levels[ i ]; + + this.addLevel( level.object.clone(), level.distance ); - for ( var i = 0, l = this.objects.length; i < l; i ++ ) { - var x = this.objects[ i ].object.clone(); - x.visible = i === 0; - object.addLevel( x, this.objects[ i ].distance ); } - return object; + return this; + +}; + +THREE.LOD.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.levels = []; + + var levels = this.levels; + + for ( var i = 0, l = levels.length; i < l; i ++ ) { + + var level = levels[ i ]; + + data.object.levels.push( { + object: level.object.uuid, + distance: level.distance + } ); + + } + + return data; }; @@ -16347,11 +23078,11 @@ THREE.Sprite = function (material) { var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] ); var geometry = new THREE.BufferGeometry(); - geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) ); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); -// return function ( material ) { +// return function Sprite( material ) { THREE.Object3D.call( this ); @@ -16371,13 +23102,14 @@ THREE.Sprite.prototype.raycast = ( function () { var matrixPosition = new THREE.Vector3(); - return function ( raycaster, intersects ) { + return function raycast( raycaster, intersects ) { matrixPosition.setFromMatrixPosition( this.matrixWorld ); - var distance = raycaster.ray.distanceToPoint( matrixPosition ); + var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); + var guessSizeSq = this.scale.x * this.scale.y; - if ( distance > this.scale.x ) { + if ( distanceSq > guessSizeSq ) { return; @@ -16385,7 +23117,7 @@ THREE.Sprite.prototype.raycast = ( function () { intersects.push( { - distance: distance, + distance: Math.sqrt( distanceSq ), point: this.position, face: null, object: this @@ -16396,13 +23128,9 @@ THREE.Sprite.prototype.raycast = ( function () { }() ); -THREE.Sprite.prototype.clone = function ( object ) { +THREE.Sprite.prototype.clone = function () { - if ( object === undefined ) object = new THREE.Sprite( this.material ); - - THREE.Object3D.prototype.clone.call( this, object ); - - return object; + return new this.constructor( this.material ).copy( this ); }; @@ -16453,15 +23181,15 @@ THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, co distance = Math.min( distance, Math.max( 0, distance ) ); this.lensFlares.push( { - texture: texture, // THREE.Texture - size: size, // size in pixels (-1 = use texture.width) - distance: distance, // distance (0-1) from light source (0=at light source) - x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back - scale: 1, // scale - rotation: 1, // rotation - opacity: opacity, // opacity - color: color, // color - blending: blending // blending + texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back + scale: 1, // scale + rotation: 0, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending // blending } ); }; @@ -16492,6 +23220,22 @@ THREE.LensFlare.prototype.updateLensFlares = function () { }; +THREE.LensFlare.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.positionScreen.copy( source.positionScreen ); + this.customUpdateCallback = source.customUpdateCallback; + + for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { + + this.lensFlares.push( source.lensFlares[ i ] ); + + } + + return this; + +}; // File:src/scenes/Scene.js @@ -16515,19 +23259,17 @@ THREE.Scene = function () { THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); THREE.Scene.prototype.constructor = THREE.Scene; -THREE.Scene.prototype.clone = function ( object ) { +THREE.Scene.prototype.copy = function ( source ) { - if ( object === undefined ) object = new THREE.Scene(); + THREE.Object3D.prototype.copy.call( this, source ); - THREE.Object3D.prototype.clone.call( this, object ); + if ( source.fog !== null ) this.fog = source.fog.clone(); + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - if ( this.fog !== null ) object.fog = this.fog.clone(); - if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone(); + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; - object.autoUpdate = this.autoUpdate; - object.matrixAutoUpdate = this.matrixAutoUpdate; - - return object; + return this; }; @@ -16581,217 +23323,297 @@ THREE.FogExp2.prototype.clone = function () { THREE.ShaderChunk = {}; -// File:src/renderers/shaders/ShaderChunk/common.glsl - -THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\nfloat square( in float a ) { return a*a; }\nvec2 square( in vec2 a ) { return vec2( a.x*a.x, a.y*a.y ); }\nvec3 square( in vec3 a ) { return vec3( a.x*a.x, a.y*a.y, a.z*a.z ); }\nvec4 square( in vec4 a ) { return vec4( a.x*a.x, a.y*a.y, a.z*a.z, a.w*a.w ); }\nfloat saturate( in float a ) { return clamp( a, 0.0, 1.0 ); }\nvec2 saturate( in vec2 a ) { return clamp( a, 0.0, 1.0 ); }\nvec3 saturate( in vec3 a ) { return clamp( a, 0.0, 1.0 ); }\nvec4 saturate( in vec4 a ) { return clamp( a, 0.0, 1.0 ); }\nfloat average( in float a ) { return a; }\nfloat average( in vec2 a ) { return ( a.x + a.y) * 0.5; }\nfloat average( in vec3 a ) { return ( a.x + a.y + a.z) / 3.0; }\nfloat average( in vec4 a ) { return ( a.x + a.y + a.z + a.w) * 0.25; }\nfloat whiteCompliment( in float a ) { return saturate( 1.0 - a ); }\nvec2 whiteCompliment( in vec2 a ) { return saturate( vec2(1.0) - a ); }\nvec3 whiteCompliment( in vec3 a ) { return saturate( vec3(1.0) - a ); }\nvec4 whiteCompliment( in vec4 a ) { return saturate( vec4(1.0) - a ); }\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n}\n// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal) {\n float distance = dot( planeNormal, point-pointOnPlane );\n return point - distance * planeNormal;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return pointOnLine + lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) );\n}\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n if ( decayExponent > 0.0 ) {\n return pow( saturate( 1.0 - lightDistance / cutoffDistance ), decayExponent );\n }\n return 1.0;\n}\n\nvec3 inputToLinear( in vec3 a ) {\n#ifdef GAMMA_INPUT\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n#else\n return a;\n#endif\n}\nvec3 linearToOutput( in vec3 a ) {\n#ifdef GAMMA_OUTPUT\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n#else\n return a;\n#endif\n}\n"; - -// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl - -THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl - -THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 dirVector = transformDirection( directionalLightDirection[ i ], viewMatrix );\n\n float dotProduct = dot( transformedNormal, dirVector );\n vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n #endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n lVector = normalize( lVector );\n float dotProduct = dot( transformedNormal, lVector );\n\n vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += pointLightColor[ i ] * pointLightWeighting * attenuation;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += pointLightColor[ i ] * pointLightWeightingBack * attenuation;\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n lVector = normalize( lVector );\n\n float dotProduct = dot( transformedNormal, lVector );\n vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += spotLightColor[ i ] * spotLightWeighting * attenuation * spotEffect;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += spotLightColor[ i ] * spotLightWeightingBack * attenuation * spotEffect;\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lVector = transformDirection( hemisphereLightDirection[ i ], viewMatrix );\n\n float dotProduct = dot( transformedNormal, lVector );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n\nvLightFront += ambientLightColor;\n\n#ifdef DOUBLE_SIDED\n\n vLightBack += ambientLightColor;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl - -THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/default_vertex.glsl - -THREE.ShaderChunk[ 'default_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#elif defined( USE_MORPHTARGETS )\n\n vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n"; - -// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl - -THREE.ShaderChunk[ 'map_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl - -THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n #ifdef USE_MORPHNORMALS\n\n vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n #else\n\n vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n #endif\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl - -THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/lightmap_pars_vertex.glsl - -THREE.ShaderChunk[ 'lightmap_pars_vertex'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl - -THREE.ShaderChunk[ 'lights_phong_fragment'] = "#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n lVector = normalize( lVector );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float pointDiffuseWeightFull = max( dotProduct, 0.0 );\n float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float pointDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n totalDiffuseLight += pointLightColor[ i ] * pointDiffuseWeight * attenuation;\n\n // specular\n\n vec3 pointHalfVector = normalize( lVector + viewPosition );\n float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n totalSpecularLight += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * attenuation * specularNormalization;\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n lVector = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float spotDiffuseWeightFull = max( dotProduct, 0.0 );\n float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float spotDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n totalDiffuseLight += spotLightColor[ i ] * spotDiffuseWeight * attenuation * spotEffect;\n\n // specular\n\n vec3 spotHalfVector = normalize( lVector + viewPosition );\n float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n totalSpecularLight += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * attenuation * specularNormalization * spotEffect;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 dirVector = transformDirection( directionalLightDirection[ i ], viewMatrix );\n\n // diffuse\n\n float dotProduct = dot( normal, dirVector );\n\n #ifdef WRAP_AROUND\n\n float dirDiffuseWeightFull = max( dotProduct, 0.0 );\n float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float dirDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n totalDiffuseLight += directionalLightColor[ i ] * dirDiffuseWeight;\n\n // specular\n\n vec3 dirHalfVector = normalize( dirVector + viewPosition );\n float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n /*\n // fresnel term from skin shader\n const float F0 = 0.128;\n\n float base = 1.0 - dot( viewPosition, dirHalfVector );\n float exponential = pow( base, 5.0 );\n\n float fresnel = exponential + F0 * ( 1.0 - exponential );\n */\n\n /*\n // fresnel term from fresnel shader\n const float mFresnelBias = 0.08;\n const float mFresnelScale = 0.3;\n const float mFresnelPower = 5.0;\n\n float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n */\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n // dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n totalSpecularLight += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lVector = transformDirection( hemisphereLightDirection[ i ], viewMatrix );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalDiffuseLight += hemiColor;\n\n // specular (sky light)\n\n vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n float hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n // specular (ground light)\n\n vec3 lVectorGround = -lVector;\n\n vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n float hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n float dotProductGround = dot( normal, lVectorGround );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n totalSpecularLight += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n }\n\n#endif\n\n#ifdef METAL\n\n outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + ambientLightColor ) * specular + totalSpecularLight + emissive;\n\n#else\n\n outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + ambientLightColor ) + totalSpecularLight + emissive;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl - -THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl - -THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n vec3 morphedNormal = vec3( 0.0 );\n\n morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n morphedNormal += normal;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl - -THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl - -THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl - -THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n // Per-Pixel Tangent Space Normal Mapping\n // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl - -THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl - -THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n uniform sampler2D lightMap;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl - -THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl - -THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl - -THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/lightmap_vertex.glsl - -THREE.ShaderChunk[ 'lightmap_vertex'] = "#ifdef USE_LIGHTMAP\n\n vUv2 = uv2;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl - -THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl - -THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl - -THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n vColor.xyz = inputToLinear( color.xyz );\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl - -THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n #ifdef USE_MORPHTARGETS\n\n vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n #else\n\n vec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n #endif\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl - -THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl - -THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n outgoingLight = linearToOutput( outgoingLight );\n"; - -// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl - -THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl - -THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/map_pars_vertex.glsl - -THREE.ShaderChunk[ 'map_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl - -THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n // Transforming Normal Vectors with the Inverse Transformation\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl - -THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl - -THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl - -THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl - -THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl - -THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n float fogFactor = exp2( - square( fogDensity ) * square( depth ) * LOG2 );\n fogFactor = whiteCompliment( fogFactor );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl - -THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n // http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm; // normalized\n\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl - -THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef USE_SKINNING\n\n vec3 objectNormal = skinnedNormal.xyz;\n\n#elif defined( USE_MORPHNORMALS )\n\n vec3 objectNormal = morphedNormal;\n\n#else\n\n vec3 objectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; - -// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl - -THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl - -THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/map_vertex.glsl - -THREE.ShaderChunk[ 'map_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl - -THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n outgoingLight *= diffuseColor.xyz * texture2D( lightMap, vUv2 ).xyz;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl - -THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl - -THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl - -THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n vec3 morphed = vec3( 0.0 );\n morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n morphed += position;\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl - -THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 worldNormal = transformDirection( objectNormal, modelMatrix );\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl - -THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n #ifdef SHADOWMAP_DEBUG\n\n vec3 frustumColors[3];\n frustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n frustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n frustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n #endif\n\n #ifdef SHADOWMAP_CASCADE\n\n int inFrustumCount = 0;\n\n #endif\n\n float fDepth;\n vec3 shadowColor = vec3( 1.0 );\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n // if ( something && something ) breaks ATI OpenGL shader compiler\n // if ( all( something, something ) ) using this instead\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n // don't shadow pixels outside of light frustum\n // use just first frustum (for cascades)\n // don't shadow pixels behind far plane of light frustum\n\n #ifdef SHADOWMAP_CASCADE\n\n inFrustumCount += int( inFrustum );\n bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n #else\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n #endif\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n shadowCoord.z += shadowBias[ i ];\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n /*\n // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n // must enroll loop manually\n\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n\n }\n\n shadow /= 9.0;\n\n */\n\n const float shadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.25 * xPixelOffset;\n float dy0 = -1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.0 * xPixelOffset;\n float dy0 = -1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n shadowKernel[0] *= vec3(0.25);\n\n shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n shadowKernel[1] *= vec3(0.25);\n\n shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n shadowKernel[2] *= vec3(0.25);\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) );\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #else\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n\n // spot with multiple shadows is darker\n\n shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n // spot with multiple shadows has the same color as single shadow spot\n\n // shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n #endif\n\n }\n\n\n #ifdef SHADOWMAP_DEBUG\n\n #ifdef SHADOWMAP_CASCADE\n\n if ( inFrustum && inFrustumCount == 1 ) outgoingLight *= frustumColors[ i ];\n\n #else\n\n if ( inFrustum ) outgoingLight *= frustumColors[ i ];\n\n #endif\n\n #endif\n\n }\n\n // NOTE: I am unsure if this is correct in linear space. -bhouston, Dec 29, 2014\n shadowColor = inputToLinear( shadowColor );\n\n outgoingLight = outgoingLight * shadowColor;\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl - -THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #elif defined( USE_MORPHTARGETS )\n\n vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n #endif\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl - -THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n#endif"; - -// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl - -THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n"; - -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl - -THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n #extension GL_EXT_frag_depth : enable\n varying float vFragDepth;\n\n #endif\n\n#endif"; - // File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl -THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; +THREE.ShaderChunk[ 'alphamap_fragment' ] = "#ifdef USE_ALPHAMAP\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n"; // File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl -THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; +THREE.ShaderChunk[ 'alphamap_pars_fragment' ] = "#ifdef USE_ALPHAMAP\n uniform sampler2D alphaMap;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl + +THREE.ShaderChunk[ 'alphatest_fragment' ] = "#ifdef ALPHATEST\n if ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/ambient_pars.glsl + +THREE.ShaderChunk[ 'ambient_pars' ] = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n return PI * ambientLightColor;\n}\n"; + +// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl + +THREE.ShaderChunk[ 'aomap_fragment' ] = "#ifdef USE_AOMAP\n reflectedLight.indirectDiffuse *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl + +THREE.ShaderChunk[ 'aomap_pars_fragment' ] = "#ifdef USE_AOMAP\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl + +THREE.ShaderChunk[ 'begin_vertex' ] = "\nvec3 transformed = vec3( position );\n"; + +// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl + +THREE.ShaderChunk[ 'beginnormal_vertex' ] = "\nvec3 objectNormal = vec3( normal );\n"; + +// File:src/renderers/shaders/ShaderChunk/bsdfs.glsl + +THREE.ShaderChunk[ 'bsdfs' ] = "float calcLightAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n if ( decayExponent > 0.0 ) {\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n }\n return 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n return RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n float a2 = alpha * alpha;\n float gl = dotNL + pow( a2 + ( 1.0 - a2 ) * dotNL * dotNL, 0.5 );\n float gv = dotNV + pow( a2 + ( 1.0 - a2 ) * dotNV * dotNV, 0.5 );\n return 1.0 / ( gl * gv );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n float a2 = alpha * alpha;\n float denom = dotNH * dotNH * ( a2 - 1.0 ) + 1.0;\n return RECIPROCAL_PI * a2 / ( denom * denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n float alpha = roughness * roughness;\n vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n float dotNH = saturate( dot( geometry.normal, halfDir ) );\n float dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n vec3 F = F_Schlick( specularColor, dotLH );\n float G = G_GGX_Smith( alpha, dotNL, dotNV );\n float D = D_GGX( alpha, dotNH );\n return F * ( G * D );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n vec4 r = roughness * c0 + c1;\n float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n return specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n return 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n float dotNH = saturate( dot( geometry.normal, halfDir ) );\n float dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n vec3 F = F_Schlick( specularColor, dotLH );\n float G = G_BlinnPhong_Implicit( );\n float D = D_BlinnPhong( shininess, dotNH );\n return F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n return ( 2.0 / square( ggxRoughness + 0.0001 ) - 2.0 );\n}"; + +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'bumpmap_pars_fragment' ] = "#ifdef USE_BUMPMAP\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n vec2 dHdxy_fwd() {\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n return vec2( dBx, dBy );\n }\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n float fDet = dot( vSigmaX, R1 );\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n }\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl + +THREE.ShaderChunk[ 'color_fragment' ] = "#ifdef USE_COLOR\n diffuseColor.rgb *= vColor;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl + +THREE.ShaderChunk[ 'color_pars_fragment' ] = "#ifdef USE_COLOR\n varying vec3 vColor;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl + +THREE.ShaderChunk[ 'color_pars_vertex' ] = "#ifdef USE_COLOR\n varying vec3 vColor;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl + +THREE.ShaderChunk[ 'color_vertex' ] = "#ifdef USE_COLOR\n vColor.xyz = color.xyz;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/common.glsl + +THREE.ShaderChunk[ 'common' ] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat square( const in float x ) { return x*x; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nstruct IncidentLight {\n vec3 color;\n vec3 direction;\n};\nstruct ReflectedLight {\n vec3 directDiffuse;\n vec3 directSpecular;\n vec3 indirectDiffuse;\n vec3 indirectSpecular;\n};\nstruct GeometricContext {\n vec3 position;\n vec3 normal;\n vec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n float distance = dot( planeNormal, point - pointOnPlane );\n return - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nvec3 inputToLinear( in vec3 a ) {\n #ifdef GAMMA_INPUT\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n #else\n return a;\n #endif\n}\nvec3 linearToOutput( in vec3 a ) {\n #ifdef GAMMA_OUTPUT\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n #else\n return a;\n #endif\n}\n"; + +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl + +THREE.ShaderChunk[ 'defaultnormal_vertex' ] = "#ifdef FLIP_SIDED\n objectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; + +// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl + +THREE.ShaderChunk[ 'displacementmap_vertex' ] = "#ifdef USE_DISPLACEMENTMAP\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'displacementmap_pars_vertex' ] = "#ifdef USE_DISPLACEMENTMAP\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl + +THREE.ShaderChunk[ 'emissivemap_fragment' ] = "#ifdef USE_EMISSIVEMAP\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n totalEmissiveLight *= emissiveColor.rgb;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl + +THREE.ShaderChunk[ 'emissivemap_pars_fragment' ] = "#ifdef USE_EMISSIVEMAP\n uniform sampler2D emissiveMap;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl + +THREE.ShaderChunk[ 'envmap_fragment' ] = "#ifdef USE_ENVMAP\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n #ifdef ENVMAP_MODE_REFLECTION\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n #else\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n #endif\n #else\n vec3 reflectVec = vReflect;\n #endif\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n envColor.xyz = inputToLinear( envColor.xyz );\n #ifdef ENVMAP_BLENDING_MULTIPLY\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n #elif defined( ENVMAP_BLENDING_MIX )\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n #elif defined( ENVMAP_BLENDING_ADD )\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'envmap_pars_fragment' ] = "#if defined( USE_ENVMAP ) || defined( STANDARD )\n uniform float reflectivity;\n uniform float envMapIntenstiy;\n#endif\n#ifdef USE_ENVMAP\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( STANDARD )\n uniform float refractionRatio;\n #else\n varying vec3 vReflect;\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'envmap_pars_vertex' ] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) && ! defined( STANDARD )\n varying vec3 vReflect;\n uniform float refractionRatio;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl + +THREE.ShaderChunk[ 'envmap_vertex' ] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) && ! defined( STANDARD )\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n #ifdef ENVMAP_MODE_REFLECTION\n vReflect = reflect( cameraToVertex, worldNormal );\n #else\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl + +THREE.ShaderChunk[ 'fog_fragment' ] = "#ifdef USE_FOG\n #ifdef USE_LOGDEPTHBUF_EXT\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n #else\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n #endif\n #ifdef FOG_EXP2\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n #else\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl + +THREE.ShaderChunk[ 'fog_pars_fragment' ] = "#ifdef USE_FOG\n uniform vec3 fogColor;\n #ifdef FOG_EXP2\n uniform float fogDensity;\n #else\n uniform float fogNear;\n uniform float fogFar;\n #endif\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_fragment' ] = "#ifdef USE_LIGHTMAP\n reflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_pars_fragment' ] = "#ifdef USE_LIGHTMAP\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_vertex' ] = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n vLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n directLight = getPointDirectLight( pointLights[ i ], geometry );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_SPOT_LIGHTS > 0\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n directLight = getSpotDirectLight( spotLights[ i ], geometry );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_DIR_LIGHTS > 0\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directLight = getDirectionalDirectLight( directionalLights[ i ], geometry );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_HEMI_LIGHTS > 0\n for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n vLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n #ifdef DOUBLE_SIDED\n vLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n #endif\n }\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_pars.glsl + +THREE.ShaderChunk[ 'lights_pars' ] = "#if NUM_DIR_LIGHTS > 0\n struct DirectionalLight {\n vec3 direction;\n vec3 color;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n IncidentLight getDirectionalDirectLight( const in DirectionalLight directionalLight, const in GeometricContext geometry ) {\n IncidentLight directLight;\n directLight.color = directionalLight.color;\n directLight.direction = directionalLight.direction;\n return directLight;\n }\n#endif\n#if NUM_POINT_LIGHTS > 0\n struct PointLight {\n vec3 position;\n vec3 color;\n float distance;\n float decay;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n IncidentLight getPointDirectLight( const in PointLight pointLight, const in GeometricContext geometry ) {\n IncidentLight directLight;\n vec3 lVector = pointLight.position - geometry.position;\n directLight.direction = normalize( lVector );\n directLight.color = pointLight.color;\n directLight.color *= calcLightAttenuation( length( lVector ), pointLight.distance, pointLight.decay );\n return directLight;\n }\n#endif\n#if NUM_SPOT_LIGHTS > 0\n struct SpotLight {\n vec3 position;\n vec3 direction;\n vec3 color;\n float distance;\n float decay;\n float angleCos;\n float exponent;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n IncidentLight getSpotDirectLight( const in SpotLight spotLight, const in GeometricContext geometry ) {\n IncidentLight directLight;\n vec3 lVector = spotLight.position - geometry.position;\n directLight.direction = normalize( lVector );\n float spotEffect = dot( directLight.direction, spotLight.direction );\n if ( spotEffect > spotLight.angleCos ) {\n float spotEffect = dot( spotLight.direction, directLight.direction );\n spotEffect = saturate( pow( saturate( spotEffect ), spotLight.exponent ) );\n directLight.color = spotLight.color;\n directLight.color *= ( spotEffect * calcLightAttenuation( length( lVector ), spotLight.distance, spotLight.decay ) );\n } else {\n directLight.color = vec3( 0.0 );\n }\n return directLight;\n }\n#endif\n#if NUM_HEMI_LIGHTS > 0\n struct HemisphereLight {\n vec3 direction;\n vec3 skyColor;\n vec3 groundColor;\n };\n uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n float dotNL = dot( geometry.normal, hemiLight.direction );\n float hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n return PI * mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n }\n#endif\n#if defined( USE_ENVMAP ) && defined( STANDARD )\n vec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n #ifdef ENVMAP_TYPE_CUBE\n vec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n #else\n vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n #endif\n #else\n vec3 envMapColor = vec3( 0.0 );\n #endif\n envMapColor.rgb = inputToLinear( envMapColor.rgb );\n return PI * envMapColor.rgb * envMapIntensity;\n }\n float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n float maxMIPLevelScalar = float( maxMIPLevel );\n float desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( square( blinnShininessExponent ) + 1.0 );\n return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n }\n vec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n #ifdef ENVMAP_MODE_REFLECTION\n vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n #else\n vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n #endif\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n reflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n float specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n #ifdef ENVMAP_TYPE_CUBE\n vec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n #else\n vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n #endif\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n #else\n vec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n #endif\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n #else\n vec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n #endif\n #endif\n envMapColor.rgb = inputToLinear( envMapColor.rgb );\n return envMapColor.rgb * envMapIntensity;\n }\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_fragment' ] = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_fragment' ] = "#ifdef USE_ENVMAP\n varying vec3 vWorldPosition;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n vec3 diffuseColor;\n vec3 specularColor;\n float specularShininess;\n float specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n float dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n vec3 irradiance = dotNL * PI * directLight.color;\n reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct RE_Direct_BlinnPhong\n#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material ) (0)\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_vertex' ] = "#ifdef USE_ENVMAP\n varying vec3 vWorldPosition;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_vertex' ] = "#ifdef USE_ENVMAP\n vWorldPosition = worldPosition.xyz;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_standard_fragment.glsl + +THREE.ShaderChunk[ 'lights_standard_fragment' ] = "StandardMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\nmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_standard_pars_fragment.glsl + +THREE.ShaderChunk[ 'lights_standard_pars_fragment' ] = "struct StandardMaterial {\n vec3 diffuseColor;\n float specularRoughness;\n vec3 specularColor;\n};\nvoid RE_Direct_Standard( const in IncidentLight directLight, const in GeometricContext geometry, const in StandardMaterial material, inout ReflectedLight reflectedLight ) {\n float dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n vec3 irradiance = dotNL * PI * directLight.color;\n reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n}\nvoid RE_IndirectDiffuse_Standard( const in vec3 irradiance, const in GeometricContext geometry, const in StandardMaterial material, inout ReflectedLight reflectedLight ) {\n reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Standard( const in vec3 radiance, const in GeometricContext geometry, const in StandardMaterial material, inout ReflectedLight reflectedLight ) {\n reflectedLight.indirectSpecular += radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n}\n#define RE_Direct RE_Direct_Standard\n#define RE_IndirectDiffuse RE_IndirectDiffuse_Standard\n#define RE_IndirectSpecular RE_IndirectSpecular_Standard\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_template.glsl + +THREE.ShaderChunk[ 'lights_template' ] = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n PointLight pointLight;\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n pointLight = pointLights[ i ];\n directLight = getPointDirectLight( pointLight, geometry );\n #ifdef USE_SHADOWMAP\n directLight.color *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n SpotLight spotLight;\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n spotLight = spotLights[ i ];\n directLight = getSpotDirectLight( spotLight, geometry );\n #ifdef USE_SHADOWMAP\n directLight.color *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n DirectionalLight directionalLight;\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directionalLight = directionalLights[ i ];\n directLight = getDirectionalDirectLight( directionalLight, geometry );\n #ifdef USE_SHADOWMAP\n directLight.color *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if defined( RE_IndirectDiffuse )\n vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n #ifdef USE_LIGHTMAP\n irradiance += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n #endif\n #if ( NUM_HEMI_LIGHTS > 0 )\n for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n }\n #endif\n RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n vec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n RE_IndirectSpecular( radiance, geometry, material, reflectedLight );\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl + +THREE.ShaderChunk[ 'linear_to_gamma_fragment' ] = "\n outgoingLight = linearToOutput( outgoingLight );\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_fragment' ] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment' ] = "#ifdef USE_LOGDEPTHBUF\n uniform float logDepthBufFC;\n #ifdef USE_LOGDEPTHBUF_EXT\n varying float vFragDepth;\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_vertex' ] = "#ifdef USE_LOGDEPTHBUF\n #ifdef USE_LOGDEPTHBUF_EXT\n varying float vFragDepth;\n #endif\n uniform float logDepthBufFC;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_vertex' ] = "#ifdef USE_LOGDEPTHBUF\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n #ifdef USE_LOGDEPTHBUF_EXT\n vFragDepth = 1.0 + gl_Position.w;\n #else\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl + +THREE.ShaderChunk[ 'map_fragment' ] = "#ifdef USE_MAP\n vec4 texelColor = texture2D( map, vUv );\n texelColor.xyz = inputToLinear( texelColor.xyz );\n diffuseColor *= texelColor;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_pars_fragment' ] = "#ifdef USE_MAP\n uniform sampler2D map;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_fragment' ] = "#ifdef USE_MAP\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_pars_fragment' ] = "#ifdef USE_MAP\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl + +THREE.ShaderChunk[ 'metalnessmap_fragment' ] = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n vec4 texelMetalness = texture2D( metalnessMap, vUv );\n metalnessFactor *= texelMetalness.r;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'metalnessmap_pars_fragment' ] = "#ifdef USE_METALNESSMAP\n uniform sampler2D metalnessMap;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl + +THREE.ShaderChunk[ 'morphnormal_vertex' ] = "#ifdef USE_MORPHNORMALS\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_pars_vertex' ] = "#ifdef USE_MORPHTARGETS\n #ifndef USE_MORPHNORMALS\n uniform float morphTargetInfluences[ 8 ];\n #else\n uniform float morphTargetInfluences[ 4 ];\n #endif\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_vertex' ] = "#ifdef USE_MORPHTARGETS\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n #ifndef USE_MORPHNORMALS\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/normal_fragment.glsl + +THREE.ShaderChunk[ 'normal_fragment' ] = "#ifdef FLAT_SHADED\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\n#else\n vec3 normal = normalize( vNormal );\n #ifdef DOUBLE_SIDED\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n #endif\n#endif\n#ifdef USE_NORMALMAP\n normal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'normalmap_pars_fragment' ] = "#ifdef USE_NORMALMAP\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n }\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl + +THREE.ShaderChunk[ 'project_vertex' ] = "#ifdef USE_SKINNING\n vec4 mvPosition = modelViewMatrix * skinned;\n#else\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n"; + +// File:src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl + +THREE.ShaderChunk[ 'roughnessmap_fragment' ] = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n vec4 texelRoughness = texture2D( roughnessMap, vUv );\n roughnessFactor *= texelRoughness.r;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'roughnessmap_pars_fragment' ] = "#ifdef USE_ROUGHNESSMAP\n uniform sampler2D roughnessMap;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_fragment' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n #endif\n #if NUM_SPOT_LIGHTS > 0\n uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n #endif\n #if NUM_POINT_LIGHTS > 0\n uniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n #endif\n float unpackDepth( const in vec4 rgba_depth ) {\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n return dot( rgba_depth, bit_shift );\n }\n float texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n return step( compare, unpackDepth( texture2D( depths, uv ) ) );\n }\n float texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n const vec2 offset = vec2( 0.0, 1.0 );\n vec2 texelSize = vec2( 1.0 ) / size;\n vec2 centroidUV = floor( uv * size + 0.5 ) / size;\n float lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n float lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n float rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n float rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n vec2 f = fract( uv * size + 0.5 );\n float a = mix( lb, lt, f.y );\n float b = mix( rb, rt, f.y );\n float c = mix( a, b, f.x );\n return c;\n }\n float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n shadowCoord.xyz /= shadowCoord.w;\n shadowCoord.z += shadowBias;\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n bool frustumTest = all( frustumTestVec );\n if ( frustumTest ) {\n #if defined( SHADOWMAP_TYPE_PCF )\n vec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n float dx0 = - texelSize.x * shadowRadius;\n float dy0 = - texelSize.y * shadowRadius;\n float dx1 = + texelSize.x * shadowRadius;\n float dy1 = + texelSize.y * shadowRadius;\n return (\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n ) * ( 1.0 / 9.0 );\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n vec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n float dx0 = - texelSize.x * shadowRadius;\n float dy0 = - texelSize.y * shadowRadius;\n float dx1 = + texelSize.x * shadowRadius;\n float dy1 = + texelSize.y * shadowRadius;\n return (\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n ) * ( 1.0 / 9.0 );\n #else\n return texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n #endif\n }\n return 1.0;\n }\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n vec3 absV = abs( v );\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n vec2 planar = v.xy;\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n if ( absV.z >= almostOne ) {\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n } else if ( absV.x >= almostOne ) {\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n } else if ( absV.y >= almostOne ) {\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n }\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n }\n float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n vec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n vec3 lightToPosition = shadowCoord.xyz;\n vec3 bd3D = normalize( lightToPosition );\n float dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n vec3 offset = vec3( - 1, 0, 1 ) * shadowRadius * 2.0 * texelSize.y;\n return (\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zzz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zxz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xzz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zzx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zxx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xzx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zzy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zxy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xzy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zyz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.zyx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yzz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxz, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yzx, texelSize.y ), dp )\n ) * ( 1.0 / 21.0 );\n #else\n return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n #endif\n }\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_vertex' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n #endif\n #if NUM_SPOT_LIGHTS > 0\n uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n #endif\n #if NUM_POINT_LIGHTS > 0\n uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_vertex' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n }\n #endif\n #if NUM_SPOT_LIGHTS > 0\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n }\n #endif\n #if NUM_POINT_LIGHTS > 0\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n }\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl + +THREE.ShaderChunk[ 'shadowmask_pars_fragment' ] = "float getShadowMask() {\n float shadow = 1.0;\n #ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n DirectionalLight directionalLight;\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directionalLight = directionalLights[ i ];\n shadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #if NUM_SPOT_LIGHTS > 0\n SpotLight spotLight;\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n spotLight = spotLights[ i ];\n shadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #if NUM_POINT_LIGHTS > 0\n PointLight pointLight;\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n pointLight = pointLights[ i ];\n shadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #endif\n return shadow;\n}\n"; + +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl + +THREE.ShaderChunk[ 'skinbase_vertex' ] = "#ifdef USE_SKINNING\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl + +THREE.ShaderChunk[ 'skinning_pars_vertex' ] = "#ifdef USE_SKINNING\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n #ifdef BONE_TEXTURE\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n mat4 getBoneMatrix( const in float i ) {\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n y = dy * ( y + 0.5 );\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n mat4 bone = mat4( v1, v2, v3, v4 );\n return bone;\n }\n #else\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n mat4 getBoneMatrix( const in float i ) {\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n }\n #endif\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl + +THREE.ShaderChunk[ 'skinning_vertex' ] = "#ifdef USE_SKINNING\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl + +THREE.ShaderChunk[ 'skinnormal_vertex' ] = "#ifdef USE_SKINNING\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_fragment' ] = "float specularStrength;\n#ifdef USE_SPECULARMAP\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n#else\n specularStrength = 1.0;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_pars_fragment' ] = "#ifdef USE_SPECULARMAP\n uniform sampler2D specularMap;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl + +THREE.ShaderChunk[ 'uv2_pars_fragment' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n varying vec2 vUv2;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl + +THREE.ShaderChunk[ 'uv2_pars_vertex' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n attribute vec2 uv2;\n varying vec2 vUv2;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl + +THREE.ShaderChunk[ 'uv2_vertex' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n vUv2 = uv2;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl + +THREE.ShaderChunk[ 'uv_pars_fragment' ] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n varying vec2 vUv;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl + +THREE.ShaderChunk[ 'uv_pars_vertex' ] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl + +THREE.ShaderChunk[ 'uv_vertex' ] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl + +THREE.ShaderChunk[ 'worldpos_vertex' ] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( STANDARD ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n #ifdef USE_SKINNING\n vec4 worldPosition = modelMatrix * skinned;\n #else\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n #endif\n#endif\n"; // File:src/renderers/shaders/UniformsUtils.js @@ -16837,12 +23659,13 @@ THREE.UniformsUtils = { parameter_src instanceof THREE.Vector2 || parameter_src instanceof THREE.Vector3 || parameter_src instanceof THREE.Vector4 || + parameter_src instanceof THREE.Matrix3 || parameter_src instanceof THREE.Matrix4 || parameter_src instanceof THREE.Texture ) { uniforms_dst[ u ][ p ] = parameter_src.clone(); - } else if ( parameter_src instanceof Array ) { + } else if ( Array.isArray( parameter_src ) ) { uniforms_dst[ u ][ p ] = parameter_src.slice(); @@ -16872,98 +23695,155 @@ THREE.UniformsLib = { common: { - "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, - "opacity" : { type: "f", value: 1.0 }, + "diffuse": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity": { type: "f", value: 1.0 }, - "map" : { type: "t", value: null }, - "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + "map": { type: "t", value: null }, + "offsetRepeat": { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, - "lightMap" : { type: "t", value: null }, - "specularMap" : { type: "t", value: null }, - "alphaMap" : { type: "t", value: null }, + "specularMap": { type: "t", value: null }, + "alphaMap": { type: "t", value: null }, - "envMap" : { type: "t", value: null }, - "flipEnvMap" : { type: "f", value: - 1 }, - "reflectivity" : { type: "f", value: 1.0 }, - "refractionRatio" : { type: "f", value: 0.98 }, - - "morphTargetInfluences" : { type: "f", value: 0 } + "envMap": { type: "t", value: null }, + "flipEnvMap": { type: "f", value: - 1 }, + "reflectivity": { type: "f", value: 1.0 }, + "refractionRatio": { type: "f", value: 0.98 } }, - bump: { + aomap: { - "bumpMap" : { type: "t", value: null }, - "bumpScale" : { type: "f", value: 1 } + "aoMap": { type: "t", value: null }, + "aoMapIntensity": { type: "f", value: 1 } + + }, + + lightmap: { + + "lightMap": { type: "t", value: null }, + "lightMapIntensity": { type: "f", value: 1 } + + }, + + emissivemap: { + + "emissiveMap": { type: "t", value: null } + + }, + + bumpmap: { + + "bumpMap": { type: "t", value: null }, + "bumpScale": { type: "f", value: 1 } }, normalmap: { - "normalMap" : { type: "t", value: null }, - "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + "normalMap": { type: "t", value: null }, + "normalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) } + }, - fog : { + displacementmap: { - "fogDensity" : { type: "f", value: 0.00025 }, - "fogNear" : { type: "f", value: 1 }, - "fogFar" : { type: "f", value: 2000 }, - "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + "displacementMap": { type: "t", value: null }, + "displacementScale": { type: "f", value: 1 }, + "displacementBias": { type: "f", value: 0 } + + }, + + roughnessmap: { + + "roughnessMap": { type: "t", value: null } + + }, + + metalnessmap: { + + "metalnessMap": { type: "t", value: null } + + }, + + fog: { + + "fogDensity": { type: "f", value: 0.00025 }, + "fogNear": { type: "f", value: 1 }, + "fogFar": { type: "f", value: 2000 }, + "fogColor": { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + ambient: { + + "ambientLightColor": { type: "fv", value: [] } }, lights: { - "ambientLightColor" : { type: "fv", value: [] }, + "directionalLights": { type: "sa", value: [], properties: { + "direction": { type: "v3" }, + "color": { type: "c" }, - "directionalLightDirection" : { type: "fv", value: [] }, - "directionalLightColor" : { type: "fv", value: [] }, + "shadow": { type: "i" }, + "shadowBias": { type: "f" }, + "shadowRadius": { type: "f" }, + "shadowMapSize": { type: "v2" } + } }, - "hemisphereLightDirection" : { type: "fv", value: [] }, - "hemisphereLightSkyColor" : { type: "fv", value: [] }, - "hemisphereLightGroundColor" : { type: "fv", value: [] }, + "directionalShadowMap": { type: "tv", value: [] }, + "directionalShadowMatrix": { type: "m4v", value: [] }, - "pointLightColor" : { type: "fv", value: [] }, - "pointLightPosition" : { type: "fv", value: [] }, - "pointLightDistance" : { type: "fv1", value: [] }, - "pointLightDecay" : { type: "fv1", value: [] }, + "spotLights": { type: "sa", value: [], properties: { + "color": { type: "c" }, + "position": { type: "v3" }, + "direction": { type: "v3" }, + "distance": { type: "f" }, + "angleCos": { type: "f" }, + "exponent": { type: "f" }, + "decay": { type: "f" }, - "spotLightColor" : { type: "fv", value: [] }, - "spotLightPosition" : { type: "fv", value: [] }, - "spotLightDirection" : { type: "fv", value: [] }, - "spotLightDistance" : { type: "fv1", value: [] }, - "spotLightAngleCos" : { type: "fv1", value: [] }, - "spotLightExponent" : { type: "fv1", value: [] }, - "spotLightDecay" : { type: "fv1", value: [] } + "shadow": { type: "i" }, + "shadowBias": { type: "f" }, + "shadowRadius": { type: "f" }, + "shadowMapSize": { type: "v2" } + } }, + + "spotShadowMap": { type: "tv", value: [] }, + "spotShadowMatrix": { type: "m4v", value: [] }, + + "pointLights": { type: "sa", value: [], properties: { + "color": { type: "c" }, + "position": { type: "v3" }, + "decay": { type: "f" }, + "distance": { type: "f" }, + + "shadow": { type: "i" }, + "shadowBias": { type: "f" }, + "shadowRadius": { type: "f" }, + "shadowMapSize": { type: "v2" } + } }, + + "pointShadowMap": { type: "tv", value: [] }, + "pointShadowMatrix": { type: "m4v", value: [] }, + + "hemisphereLights": { type: "sa", value: [], properties: { + "direction": { type: "v3" }, + "skyColor": { type: "c" }, + "groundColor": { type: "c" } + } } }, - particle: { + points: { - "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, - "opacity" : { type: "f", value: 1.0 }, - "size" : { type: "f", value: 1.0 }, - "scale" : { type: "f", value: 1.0 }, - "map" : { type: "t", value: null }, - "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, - - "fogDensity" : { type: "f", value: 0.00025 }, - "fogNear" : { type: "f", value: 1 }, - "fogFar" : { type: "f", value: 2000 }, - "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } - - }, - - shadowmap: { - - "shadowMap": { type: "tv", value: [] }, - "shadowMapSize": { type: "v2v", value: [] }, - - "shadowBias" : { type: "fv1", value: [] }, - "shadowDarkness": { type: "fv1", value: [] }, - - "shadowMatrix" : { type: "m4v", value: [] } + "diffuse": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity": { type: "f", value: 1.0 }, + "size": { type: "f", value: 1.0 }, + "scale": { type: "f", value: 1.0 }, + "map": { type: "t", value: null }, + "offsetRepeat": { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) } } @@ -16987,16 +23867,16 @@ THREE.ShaderLib = { uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "shadowmap" ] + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "fog" ] ] ), vertexShader: [ THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], THREE.ShaderChunk[ "envmap_pars_vertex" ], THREE.ShaderChunk[ "color_pars_vertex" ], THREE.ShaderChunk[ "morphtarget_pars_vertex" ], @@ -17006,22 +23886,24 @@ THREE.ShaderLib = { "void main() {", - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], THREE.ShaderChunk[ "color_vertex" ], THREE.ShaderChunk[ "skinbase_vertex" ], " #ifdef USE_ENVMAP", + THREE.ShaderChunk[ "beginnormal_vertex" ], THREE.ShaderChunk[ "morphnormal_vertex" ], THREE.ShaderChunk[ "skinnormal_vertex" ], THREE.ShaderChunk[ "defaultnormal_vertex" ], " #endif", + THREE.ShaderChunk[ "begin_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], THREE.ShaderChunk[ "logdepthbuf_vertex" ], THREE.ShaderChunk[ "worldpos_vertex" ], @@ -17030,18 +23912,26 @@ THREE.ShaderLib = { "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ "uniform vec3 diffuse;", "uniform float opacity;", + "#ifndef FLAT_SHADED", + + " varying vec3 vNormal;", + + "#endif", + THREE.ShaderChunk[ "common" ], THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], THREE.ShaderChunk[ "map_pars_fragment" ], THREE.ShaderChunk[ "alphamap_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], THREE.ShaderChunk[ "envmap_pars_fragment" ], THREE.ShaderChunk[ "fog_pars_fragment" ], THREE.ShaderChunk[ "shadowmap_pars_fragment" ], @@ -17050,7 +23940,6 @@ THREE.ShaderLib = { "void main() {", - " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does " vec4 diffuseColor = vec4( diffuse, opacity );", THREE.ShaderChunk[ "logdepthbuf_fragment" ], @@ -17060,21 +23949,25 @@ THREE.ShaderLib = { THREE.ShaderChunk[ "alphatest_fragment" ], THREE.ShaderChunk[ "specularmap_fragment" ], - " outgoingLight = diffuseColor.rgb;", // simple shader + " ReflectedLight reflectedLight;", + " reflectedLight.directDiffuse = vec3( 0.0 );", + " reflectedLight.directSpecular = vec3( 0.0 );", + " reflectedLight.indirectDiffuse = diffuseColor.rgb;", + " reflectedLight.indirectSpecular = vec3( 0.0 );", + + THREE.ShaderChunk[ "aomap_fragment" ], + + " vec3 outgoingLight = reflectedLight.indirectDiffuse;", - THREE.ShaderChunk[ "lightmap_fragment" ], // TODO: Light map on an otherwise unlit surface doesn't make sense. THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], // TODO: Shadows on an otherwise unlit surface doesn't make sense. - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", "}" - ].join("\n") + ].join( "\n" ) }, @@ -17083,13 +23976,15 @@ THREE.ShaderLib = { uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "lightmap" ], + THREE.UniformsLib[ "emissivemap" ], THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "ambient" ], THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], { - "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) } } ] ), @@ -17107,10 +24002,11 @@ THREE.ShaderLib = { "#endif", THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], THREE.ShaderChunk[ "envmap_pars_vertex" ], - THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], + THREE.ShaderChunk[ "bsdfs" ], + THREE.ShaderChunk[ "lights_pars" ], THREE.ShaderChunk[ "color_pars_vertex" ], THREE.ShaderChunk[ "morphtarget_pars_vertex" ], THREE.ShaderChunk[ "skinning_pars_vertex" ], @@ -17119,18 +24015,20 @@ THREE.ShaderLib = { "void main() {", - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "beginnormal_vertex" ], THREE.ShaderChunk[ "morphnormal_vertex" ], THREE.ShaderChunk[ "skinbase_vertex" ], THREE.ShaderChunk[ "skinnormal_vertex" ], THREE.ShaderChunk[ "defaultnormal_vertex" ], + THREE.ShaderChunk[ "begin_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], THREE.ShaderChunk[ "logdepthbuf_vertex" ], THREE.ShaderChunk[ "worldpos_vertex" ], @@ -17140,7 +24038,7 @@ THREE.ShaderLib = { "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17158,19 +24056,28 @@ THREE.ShaderLib = { THREE.ShaderChunk[ "common" ], THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], THREE.ShaderChunk[ "map_pars_fragment" ], THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "emissivemap_pars_fragment" ], THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "bsdfs" ], + THREE.ShaderChunk[ "ambient_pars" ], + THREE.ShaderChunk[ "lights_pars" ], THREE.ShaderChunk[ "fog_pars_fragment" ], THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "shadowmask_pars_fragment" ], THREE.ShaderChunk[ "specularmap_pars_fragment" ], THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], "void main() {", - " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveLight = emissive;", THREE.ShaderChunk[ "logdepthbuf_fragment" ], THREE.ShaderChunk[ "map_fragment" ], @@ -17178,36 +24085,43 @@ THREE.ShaderLib = { THREE.ShaderChunk[ "alphamap_fragment" ], THREE.ShaderChunk[ "alphatest_fragment" ], THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "emissivemap_fragment" ], + + // accumulation + " reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );", + + THREE.ShaderChunk[ "lightmap_fragment" ], + + " reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );", " #ifdef DOUBLE_SIDED", - //"float isFront = float( gl_FrontFacing );", - //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", - - " if ( gl_FrontFacing )", - " outgoingLight += diffuseColor.rgb * vLightFront + emissive;", - " else", - " outgoingLight += diffuseColor.rgb * vLightBack + emissive;", + " reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;", " #else", - " outgoingLight += diffuseColor.rgb * vLightFront + emissive;", + " reflectedLight.directDiffuse = vLightFront;", " #endif", - THREE.ShaderChunk[ "lightmap_fragment" ], + " reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();", + + // modulation + THREE.ShaderChunk[ "aomap_fragment" ], + + " vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveLight;", + THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], THREE.ShaderChunk[ "linear_to_gamma_fragment" ], THREE.ShaderChunk[ "fog_fragment" ], - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", "}" - ].join("\n") + ].join( "\n" ) }, @@ -17216,17 +24130,20 @@ THREE.ShaderLib = { uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "bump" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "lightmap" ], + THREE.UniformsLib[ "emissivemap" ], + THREE.UniformsLib[ "bumpmap" ], THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "displacementmap" ], THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "ambient" ], THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], { "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, - "shininess": { type: "f", value: 30 }, - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + "shininess": { type: "f", value: 30 } } ] ), @@ -17244,8 +24161,9 @@ THREE.ShaderLib = { "#endif", THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "displacementmap_pars_vertex" ], THREE.ShaderChunk[ "envmap_pars_vertex" ], THREE.ShaderChunk[ "lights_phong_pars_vertex" ], THREE.ShaderChunk[ "color_pars_vertex" ], @@ -17256,10 +24174,11 @@ THREE.ShaderLib = { "void main() {", - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "beginnormal_vertex" ], THREE.ShaderChunk[ "morphnormal_vertex" ], THREE.ShaderChunk[ "skinbase_vertex" ], THREE.ShaderChunk[ "skinnormal_vertex" ], @@ -17271,12 +24190,14 @@ THREE.ShaderLib = { "#endif", + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "displacementmap_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], THREE.ShaderChunk[ "logdepthbuf_vertex" ], - " vViewPosition = -mvPosition.xyz;", + " vViewPosition = - mvPosition.xyz;", THREE.ShaderChunk[ "worldpos_vertex" ], THREE.ShaderChunk[ "envmap_vertex" ], @@ -17285,7 +24206,7 @@ THREE.ShaderLib = { "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17299,11 +24220,18 @@ THREE.ShaderLib = { THREE.ShaderChunk[ "common" ], THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], THREE.ShaderChunk[ "map_pars_fragment" ], THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "emissivemap_pars_fragment" ], THREE.ShaderChunk[ "envmap_pars_fragment" ], THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "bsdfs" ], + THREE.ShaderChunk[ "ambient_pars" ], + THREE.ShaderChunk[ "lights_pars" ], THREE.ShaderChunk[ "lights_phong_pars_fragment" ], THREE.ShaderChunk[ "shadowmap_pars_fragment" ], THREE.ShaderChunk[ "bumpmap_pars_fragment" ], @@ -17313,8 +24241,9 @@ THREE.ShaderLib = { "void main() {", - " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveLight = emissive;", THREE.ShaderChunk[ "logdepthbuf_fragment" ], THREE.ShaderChunk[ "map_fragment" ], @@ -17322,31 +24251,202 @@ THREE.ShaderLib = { THREE.ShaderChunk[ "alphamap_fragment" ], THREE.ShaderChunk[ "alphatest_fragment" ], THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "normal_fragment" ], + THREE.ShaderChunk[ "emissivemap_fragment" ], + // accumulation THREE.ShaderChunk[ "lights_phong_fragment" ], + THREE.ShaderChunk[ "lights_template" ], + + // modulation + THREE.ShaderChunk[ "aomap_fragment" ], + + "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight;", - THREE.ShaderChunk[ "lightmap_fragment" ], THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + + "}" + + ].join( "\n" ) + + }, + + 'standard': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "lightmap" ], + THREE.UniformsLib[ "emissivemap" ], + THREE.UniformsLib[ "bumpmap" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "displacementmap" ], + THREE.UniformsLib[ "roughnessmap" ], + THREE.UniformsLib[ "metalnessmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "ambient" ], + THREE.UniformsLib[ "lights" ], + + { + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "roughness": { type: "f", value: 0.5 }, + "metalness": { type: "f", value: 0 }, + "envMapIntensity" : { type: "f", value: 1 } // temporary + } + + ] ), + + vertexShader: [ + + "#define STANDARD", + + "varying vec3 vViewPosition;", + + "#ifndef FLAT_SHADED", + + " varying vec3 vNormal;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "displacementmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", // STANDARD + + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED + + " vNormal = normalize( transformedNormal );", + + "#endif", + + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "displacementmap_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + " vViewPosition = - mvPosition.xyz;", + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#define STANDARD", + + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float roughness;", + "uniform float metalness;", + "uniform float opacity;", + + "uniform float envMapIntensity;", // temporary + + "varying vec3 vViewPosition;", + + "#ifndef FLAT_SHADED", + + " varying vec3 vNormal;", + + "#endif", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "emissivemap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "bsdfs" ], + THREE.ShaderChunk[ "ambient_pars" ], + THREE.ShaderChunk[ "lights_pars" ], + THREE.ShaderChunk[ "lights_standard_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + THREE.ShaderChunk[ "normalmap_pars_fragment" ], + THREE.ShaderChunk[ "roughnessmap_pars_fragment" ], + THREE.ShaderChunk[ "metalnessmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " vec4 diffuseColor = vec4( diffuse, opacity );", + " ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", + " vec3 totalEmissiveLight = emissive;", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "roughnessmap_fragment" ], + THREE.ShaderChunk[ "metalnessmap_fragment" ], + THREE.ShaderChunk[ "normal_fragment" ], + THREE.ShaderChunk[ "emissivemap_fragment" ], + + // accumulation + THREE.ShaderChunk[ "lights_standard_fragment" ], + THREE.ShaderChunk[ "lights_template" ], + + // modulation + THREE.ShaderChunk[ "aomap_fragment" ], + + "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight;", THREE.ShaderChunk[ "linear_to_gamma_fragment" ], THREE.ShaderChunk[ "fog_fragment" ], - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", "}" - ].join("\n") + ].join( "\n" ) }, - 'particle_basic': { + 'points': { uniforms: THREE.UniformsUtils.merge( [ - THREE.UniformsLib[ "particle" ], - THREE.UniformsLib[ "shadowmap" ] + THREE.UniformsLib[ "points" ], + THREE.UniformsLib[ "fog" ] ] ), @@ -17363,28 +24463,26 @@ THREE.ShaderLib = { "void main() {", THREE.ShaderChunk[ "color_vertex" ], - - " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], " #ifdef USE_SIZEATTENUATION", - " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + " gl_PointSize = size * ( scale / - mvPosition.z );", " #else", " gl_PointSize = size;", " #endif", - " gl_Position = projectionMatrix * mvPosition;", - THREE.ShaderChunk[ "logdepthbuf_vertex" ], THREE.ShaderChunk[ "worldpos_vertex" ], THREE.ShaderChunk[ "shadowmap_vertex" ], "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ - "uniform vec3 psColor;", + "uniform vec3 diffuse;", "uniform float opacity;", THREE.ShaderChunk[ "common" ], @@ -17396,24 +24494,23 @@ THREE.ShaderLib = { "void main() {", - " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does - " vec4 diffuseColor = vec4( psColor, opacity );", + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", THREE.ShaderChunk[ "logdepthbuf_fragment" ], THREE.ShaderChunk[ "map_particle_fragment" ], THREE.ShaderChunk[ "color_fragment" ], THREE.ShaderChunk[ "alphatest_fragment" ], - " outgoingLight = diffuseColor.rgb;", // simple shader + " outgoingLight = diffuseColor.rgb;", - THREE.ShaderChunk[ "shadowmap_fragment" ], THREE.ShaderChunk[ "fog_fragment" ], - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", "}" - ].join("\n") + ].join( "\n" ) }, @@ -17456,7 +24553,7 @@ THREE.ShaderLib = { "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17481,7 +24578,7 @@ THREE.ShaderLib = { " }", - " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does + " vec3 outgoingLight = vec3( 0.0 );", " vec4 diffuseColor = vec4( diffuse, opacity );", THREE.ShaderChunk[ "logdepthbuf_fragment" ], @@ -17491,11 +24588,11 @@ THREE.ShaderLib = { THREE.ShaderChunk[ "fog_fragment" ], - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", "}" - ].join("\n") + ].join( "\n" ) }, @@ -17517,13 +24614,14 @@ THREE.ShaderLib = { "void main() {", + THREE.ShaderChunk[ "begin_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], THREE.ShaderChunk[ "logdepthbuf_vertex" ], "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17549,11 +24647,11 @@ THREE.ShaderLib = { " #endif", " float color = 1.0 - smoothstep( mNear, mFar, depth );", - " gl_FragColor = vec4( vec3( color ), opacity );", // TODO, this should be pre-multiplied to allow for bright highlights on very transparent objects + " gl_FragColor = vec4( vec3( color ), opacity );", "}" - ].join("\n") + ].join( "\n" ) }, @@ -17577,13 +24675,14 @@ THREE.ShaderLib = { " vNormal = normalize( normalMatrix * normal );", + THREE.ShaderChunk[ "begin_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], THREE.ShaderChunk[ "logdepthbuf_vertex" ], "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17601,7 +24700,7 @@ THREE.ShaderLib = { "}" - ].join("\n") + ].join( "\n" ) }, @@ -17611,8 +24710,10 @@ THREE.ShaderLib = { 'cube': { - uniforms: { "tCube": { type: "t", value: null }, - "tFlip": { type: "f", value: - 1 } }, + uniforms: { + "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } + }, vertexShader: [ @@ -17631,7 +24732,7 @@ THREE.ShaderLib = { "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17651,7 +24752,7 @@ THREE.ShaderLib = { "}" - ].join("\n") + ].join( "\n" ) }, @@ -17661,8 +24762,10 @@ THREE.ShaderLib = { 'equirect': { - uniforms: { "tEquirect": { type: "t", value: null }, - "tFlip": { type: "f", value: - 1 } }, + uniforms: { + "tEquirect": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } + }, vertexShader: [ @@ -17681,7 +24784,7 @@ THREE.ShaderLib = { "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17706,7 +24809,7 @@ THREE.ShaderLib = { "}" - ].join("\n") + ].join( "\n" ) }, @@ -17736,14 +24839,16 @@ THREE.ShaderLib = { "void main() {", THREE.ShaderChunk[ "skinbase_vertex" ], + + THREE.ShaderChunk[ "begin_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], THREE.ShaderChunk[ "logdepthbuf_vertex" ], "}" - ].join("\n"), + ].join( "\n" ), fragmentShader: [ @@ -17754,7 +24859,7 @@ THREE.ShaderLib = { " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", - " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );", + " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", " res -= res.xxyz * bit_mask;", " return res;", @@ -17781,7 +24886,74 @@ THREE.ShaderLib = { "}" - ].join("\n") + ].join( "\n" ) + + }, + + + 'distanceRGBA': { + + uniforms: { + + "lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) } + + }, + + vertexShader: [ + + "varying vec4 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + + "vWorldPosition = worldPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform vec3 lightPos;", + "varying vec4 vWorldPosition;", + + THREE.ShaderChunk[ "common" ], + + "vec4 pack1K ( float depth ) {", + + " depth /= 1000.0;", + " const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = mod( depth * bitSh * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", + " res -= res.xxyz * bitMsk;", + " return res; ", + + "}", + + "float unpack1K ( vec4 color ) {", + + " const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", + " return dot( color, bitSh ) * 1000.0;", + + "}", + + "void main () {", + + " gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );", + + "}" + + ].join( "\n" ) } @@ -17792,36 +24964,35 @@ THREE.ShaderLib = { /** * @author szimek / https://github.com/szimek/ * @author alteredq / http://alteredqualia.com/ + * @author Marius Kintel / https://github.com/kintel */ +/* + In options, we can specify: + * Texture parameters for an auto-generated target texture + * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers +*/ THREE.WebGLRenderTarget = function ( width, height, options ) { + this.uuid = THREE.Math.generateUUID(); + this.width = width; this.height = height; + this.scissor = new THREE.Vector4( 0, 0, width, height ); + this.scissorTest = false; + + this.viewport = new THREE.Vector4( 0, 0, width, height ); + options = options || {}; - this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; - this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + if ( options.minFilter === undefined ) options.minFilter = THREE.LinearFilter; - this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; - this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; - - this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; - - this.offset = new THREE.Vector2( 0, 0 ); - this.repeat = new THREE.Vector2( 1, 1 ); - - this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; - this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + this.texture = new THREE.Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy ); this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; - this.generateMipmaps = true; - - this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null; - }; THREE.WebGLRenderTarget.prototype = { @@ -17830,37 +25001,41 @@ THREE.WebGLRenderTarget.prototype = { setSize: function ( width, height ) { - this.width = width; - this.height = height; + if ( this.width !== width || this.height !== height ) { + + this.width = width; + this.height = height; + + this.dispose(); + + } + + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); }, clone: function () { - var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); + return new this.constructor().copy( this ); - tmp.wrapS = this.wrapS; - tmp.wrapT = this.wrapT; + }, - tmp.magFilter = this.magFilter; - tmp.minFilter = this.minFilter; + copy: function ( source ) { - tmp.anisotropy = this.anisotropy; + this.width = source.width; + this.height = source.height; - tmp.offset.copy( this.offset ); - tmp.repeat.copy( this.repeat ); + this.viewport.copy( source.viewport ); - tmp.format = this.format; - tmp.type = this.type; + this.texture = source.texture.clone(); - tmp.depthBuffer = this.depthBuffer; - tmp.stencilBuffer = this.stencilBuffer; + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; - tmp.generateMipmaps = this.generateMipmaps; + this.shareDepthFrom = source.shareDepthFrom; - tmp.shareDepthFrom = this.shareDepthFrom; - - return tmp; + return this; }, @@ -17891,6 +25066,142 @@ THREE.WebGLRenderTargetCube = function ( width, height, options ) { THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; +// File:src/renderers/webgl/WebGLBufferRenderer.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) { + + var mode; + + function setMode( value ) { + + mode = value; + + } + + function render( start, count ) { + + _gl.drawArrays( mode, start, count ); + + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + + } + + function renderInstances( geometry ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + var position = geometry.attributes.position; + + var count = 0; + + if ( position instanceof THREE.InterleavedBufferAttribute ) { + + count = position.data.count; + + extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); + + } else { + + count = position.count; + + extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); + + } + + _infoRender.calls ++; + _infoRender.vertices += count * geometry.maxInstancedCount; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += geometry.maxInstancedCount * count / 3; + + } + + this.setMode = setMode; + this.render = render; + this.renderInstances = renderInstances; + +}; + +// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) { + + var mode; + + function setMode( value ) { + + mode = value; + + } + + var type, size; + + function setIndex( index ) { + + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + } + + function render( start, count ) { + + _gl.drawElements( mode, count, type, start * size ); + + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + + } + + function renderInstances( geometry, start, count ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + extension.drawElementsInstancedANGLE( mode, count, type, start * size, geometry.maxInstancedCount ); + + _infoRender.calls ++; + _infoRender.vertices += count * geometry.maxInstancedCount; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += geometry.maxInstancedCount * count / 3; + } + + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; + +}; + // File:src/renderers/webgl/WebGLExtensions.js /** @@ -17925,6 +25236,10 @@ THREE.WebGLExtensions = function ( gl ) { extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); break; + case 'WEBGL_compressed_texture_etc1': + extension = gl.getExtension( 'WEBGL_compressed_texture_etc1' ); + break; + default: extension = gl.getExtension( name ); @@ -17932,7 +25247,7 @@ THREE.WebGLExtensions = function ( gl ) { if ( extension === null ) { - THREE.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); } @@ -17944,82 +25259,730 @@ THREE.WebGLExtensions = function ( gl ) { }; +// File:src/renderers/webgl/WebGLCapabilities.js + +THREE.WebGLCapabilities = function ( gl, extensions, parameters ) { + + function getMaxPrecision( precision ) { + + if ( precision === 'highp' ) { + + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { + + return 'highp'; + + } + + precision = 'mediump'; + + } + + if ( precision === 'mediump' ) { + + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { + + return 'mediump'; + + } + + } + + return 'lowp'; + + } + + this.getMaxPrecision = getMaxPrecision; + + this.precision = parameters.precision !== undefined ? parameters.precision : 'highp', + this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false; + + this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); + this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + + this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); + this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); + this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); + this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); + + this.vertexTextures = this.maxVertexTextures > 0; + this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); + this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures; + + var _maxPrecision = getMaxPrecision( this.precision ); + + if ( _maxPrecision !== this.precision ) { + + console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' ); + this.precision = _maxPrecision; + + } + + if ( this.logarithmicDepthBuffer ) { + + this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' ); + + } + +}; + +// File:src/renderers/webgl/WebGLGeometries.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLGeometries = function ( gl, properties, info ) { + + var geometries = {}; + + function get( object ) { + + var geometry = object.geometry; + + if ( geometries[ geometry.id ] !== undefined ) { + + return geometries[ geometry.id ]; + + } + + geometry.addEventListener( 'dispose', onGeometryDispose ); + + var buffergeometry; + + if ( geometry instanceof THREE.BufferGeometry ) { + + buffergeometry = geometry; + + } else if ( geometry instanceof THREE.Geometry ) { + + if ( geometry._bufferGeometry === undefined ) { + + geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object ); + + } + + buffergeometry = geometry._bufferGeometry; + + } + + geometries[ geometry.id ] = buffergeometry; + + info.memory.geometries ++; + + return buffergeometry; + + } + + function onGeometryDispose( event ) { + + var geometry = event.target; + var buffergeometry = geometries[ geometry.id ]; + + if ( buffergeometry.index !== null ) { + + deleteAttribute( buffergeometry.index ); + + } + + deleteAttributes( buffergeometry.attributes ); + + geometry.removeEventListener( 'dispose', onGeometryDispose ); + + delete geometries[ geometry.id ]; + + // TODO + + var property = properties.get( geometry ); + + if ( property.wireframe ) { + + deleteAttribute( property.wireframe ); + + } + + properties.delete( geometry ); + + var bufferproperty = properties.get( buffergeometry ); + + if ( bufferproperty.wireframe ) { + + deleteAttribute( bufferproperty.wireframe ); + + } + + properties.delete( buffergeometry ); + + // + + info.memory.geometries --; + + } + + function getAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + return properties.get( attribute.data ).__webglBuffer; + + } + + return properties.get( attribute ).__webglBuffer; + + } + + function deleteAttribute( attribute ) { + + var buffer = getAttributeBuffer( attribute ); + + if ( buffer !== undefined ) { + + gl.deleteBuffer( buffer ); + removeAttributeBuffer( attribute ); + + } + + } + + function deleteAttributes( attributes ) { + + for ( var name in attributes ) { + + deleteAttribute( attributes[ name ] ); + + } + + } + + function removeAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + properties.delete( attribute.data ); + + } else { + + properties.delete( attribute ); + + } + + } + + this.get = get; + +}; + +// File:src/renderers/webgl/WebGLLights.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLLights = function () { + + var lights = {}; + + this.get = function ( light ) { + + if ( lights[ light.id ] !== undefined ) { + + return lights[ light.id ]; + + } + + var uniforms; + + switch ( light.type ) { + + case 'DirectionalLight': + uniforms = { + direction: new THREE.Vector3(), + color: new THREE.Color(), + + shadow: false, + shadowBias: 0, + shadowRadius: 1, + shadowMapSize: new THREE.Vector2() + }; + break; + + case 'SpotLight': + uniforms = { + position: new THREE.Vector3(), + direction: new THREE.Vector3(), + color: new THREE.Color(), + distance: 0, + angleCos: 0, + exponent: 0, + decay: 0, + + shadow: false, + shadowBias: 0, + shadowRadius: 1, + shadowMapSize: new THREE.Vector2() + }; + break; + + case 'PointLight': + uniforms = { + position: new THREE.Vector3(), + color: new THREE.Color(), + distance: 0, + decay: 0, + + shadow: false, + shadowBias: 0, + shadowRadius: 1, + shadowMapSize: new THREE.Vector2() + }; + break; + + case 'HemisphereLight': + uniforms = { + direction: new THREE.Vector3(), + skyColor: new THREE.Color(), + groundColor: new THREE.Color() + }; + break; + + } + + lights[ light.id ] = uniforms; + + return uniforms; + + }; + +}; + +// File:src/renderers/webgl/WebGLObjects.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLObjects = function ( gl, properties, info ) { + + var geometries = new THREE.WebGLGeometries( gl, properties, info ); + + // + + function update( object ) { + + // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. + + var geometry = geometries.get( object ); + + if ( object.geometry instanceof THREE.Geometry ) { + + geometry.updateFromObject( object ); + + } + + var index = geometry.index; + var attributes = geometry.attributes; + + if ( index !== null ) { + + updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); + + } + + for ( var name in attributes ) { + + updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); + + } + + // morph targets + + var morphAttributes = geometry.morphAttributes; + + for ( var name in morphAttributes ) { + + var array = morphAttributes[ name ]; + + for ( var i = 0, l = array.length; i < l; i ++ ) { + + updateAttribute( array[ i ], gl.ARRAY_BUFFER ); + + } + + } + + return geometry; + + } + + function updateAttribute( attribute, bufferType ) { + + var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute; + + var attributeProperties = properties.get( data ); + + if ( attributeProperties.__webglBuffer === undefined ) { + + createBuffer( attributeProperties, data, bufferType ); + + } else if ( attributeProperties.version !== data.version ) { + + updateBuffer( attributeProperties, data, bufferType ); + + } + + } + + function createBuffer( attributeProperties, data, bufferType ) { + + attributeProperties.__webglBuffer = gl.createBuffer(); + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + + var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; + + gl.bufferData( bufferType, data.array, usage ); + + attributeProperties.version = data.version; + + } + + function updateBuffer( attributeProperties, data, bufferType ) { + + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + + if ( data.dynamic === false || data.updateRange.count === - 1 ) { + + // Not using update ranges + + gl.bufferSubData( bufferType, 0, data.array ); + + } else if ( data.updateRange.count === 0 ) { + + console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); + + } else { + + gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, + data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); + + data.updateRange.count = 0; // reset range + + } + + attributeProperties.version = data.version; + + } + + function getAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + return properties.get( attribute.data ).__webglBuffer; + + } + + return properties.get( attribute ).__webglBuffer; + + } + + function getWireframeAttribute( geometry ) { + + var property = properties.get( geometry ); + + if ( property.wireframe !== undefined ) { + + return property.wireframe; + + } + + var indices = []; + + var index = geometry.index; + var attributes = geometry.attributes; + var position = attributes.position; + + // console.time( 'wireframe' ); + + if ( index !== null ) { + + var edges = {}; + var array = index.array; + + for ( var i = 0, l = array.length; i < l; i += 3 ) { + + var a = array[ i + 0 ]; + var b = array[ i + 1 ]; + var c = array[ i + 2 ]; + + if ( checkEdge( edges, a, b ) ) indices.push( a, b ); + if ( checkEdge( edges, b, c ) ) indices.push( b, c ); + if ( checkEdge( edges, c, a ) ) indices.push( c, a ); + + } + + } else { + + var array = attributes.position.array; + + for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + + var a = i + 0; + var b = i + 1; + var c = i + 2; + + indices.push( a, b, b, c, c, a ); + + } + + } + + // console.timeEnd( 'wireframe' ); + + var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; + var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 ); + + updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); + + property.wireframe = attribute; + + return attribute; + + } + + function checkEdge( edges, a, b ) { + + if ( a > b ) { + + var tmp = a; + a = b; + b = tmp; + + } + + var list = edges[ a ]; + + if ( list === undefined ) { + + edges[ a ] = [ b ]; + return true; + + } else if ( list.indexOf( b ) === -1 ) { + + list.push( b ); + return true; + + } + + return false; + + } + + this.getAttributeBuffer = getAttributeBuffer; + this.getWireframeAttribute = getWireframeAttribute; + + this.update = update; + +}; + // File:src/renderers/webgl/WebGLProgram.js THREE.WebGLProgram = ( function () { var programIdCount = 0; - var generateDefines = function ( defines ) { + // TODO: Combine the regex + var structRe = /^([\w\d_]+)\.([\w\d_]+)$/; + var arrayStructRe = /^([\w\d_]+)\[(\d+)\]\.([\w\d_]+)$/; + var arrayRe = /^([\w\d_]+)\[0\]$/; - var value, chunk, chunks = []; + function generateExtensions( extensions, parameters, rendererExtensions ) { - for ( var d in defines ) { + extensions = extensions || {}; + + var chunks = [ + ( extensions.derivatives || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', + ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', + ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '', + ]; + + return chunks.filter( filterEmptyLine ).join( '\n' ); + + } + + function generateDefines( defines ) { + + var chunks = []; + + for ( var name in defines ) { + + var value = defines[ name ]; - value = defines[ d ]; if ( value === false ) continue; - chunk = '#define ' + d + ' ' + value; - chunks.push( chunk ); + chunks.push( '#define ' + name + ' ' + value ); } return chunks.join( '\n' ); - }; + } - var cacheUniformLocations = function ( gl, program, identifiers ) { + function fetchUniformLocations( gl, program, identifiers ) { var uniforms = {}; - for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); - var id = identifiers[ i ]; - uniforms[ id ] = gl.getUniformLocation( program, id ); + for ( var i = 0; i < n; i ++ ) { + + var info = gl.getActiveUniform( program, i ); + var name = info.name; + var location = gl.getUniformLocation( program, name ); + + //console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name); + + var matches = structRe.exec( name ); + if ( matches ) { + + var structName = matches[ 1 ]; + var structProperty = matches[ 2 ]; + + var uniformsStruct = uniforms[ structName ]; + + if ( ! uniformsStruct ) { + + uniformsStruct = uniforms[ structName ] = {}; + + } + + uniformsStruct[ structProperty ] = location; + + continue; + + } + + matches = arrayStructRe.exec( name ); + + if ( matches ) { + + var arrayName = matches[ 1 ]; + var arrayIndex = matches[ 2 ]; + var arrayProperty = matches[ 3 ]; + + var uniformsArray = uniforms[ arrayName ]; + + if ( ! uniformsArray ) { + + uniformsArray = uniforms[ arrayName ] = []; + + } + + var uniformsArrayIndex = uniformsArray[ arrayIndex ]; + + if ( ! uniformsArrayIndex ) { + + uniformsArrayIndex = uniformsArray[ arrayIndex ] = {}; + + } + + uniformsArrayIndex[ arrayProperty ] = location; + + continue; + + } + + matches = arrayRe.exec( name ); + + if ( matches ) { + + var arrayName = matches[ 1 ]; + + uniforms[ arrayName ] = location; + + continue; + + } + + uniforms[ name ] = location; } return uniforms; - }; + } - var cacheAttributeLocations = function ( gl, program, identifiers ) { + function fetchAttributeLocations( gl, program, identifiers ) { var attributes = {}; - for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); - var id = identifiers[ i ]; - attributes[ id ] = gl.getAttribLocation( program, id ); + for ( var i = 0; i < n; i ++ ) { + + var info = gl.getActiveAttrib( program, i ); + var name = info.name; + + // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); + + attributes[ name ] = gl.getAttribLocation( program, name ); } return attributes; - }; + } - return function ( renderer, code, material, parameters ) { + function filterEmptyLine( string ) { - var _this = renderer; - var _gl = _this.context; + return string !== ''; + } + + function replaceLightNums( string, parameters ) { + + return string + .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) + .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) + .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) + .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); + + } + + function unrollLoops( string ) { + + var pattern = /for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; + + function replace( match, start, end, snippet ) { + + var unroll = ''; + + for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { + + unroll += snippet.replace( /\[ i \]/g, '[ ' + i + ' ]' ); + + } + + return unroll; + + } + + return string.replace( pattern, replace ); + + } + + return function WebGLProgram( renderer, code, material, parameters ) { + + var gl = renderer.context; + + var extensions = material.extensions; var defines = material.defines; - var uniforms = material.__webglShader.uniforms; - var attributes = material.attributes; var vertexShader = material.__webglShader.vertexShader; var fragmentShader = material.__webglShader.fragmentShader; - var index0AttributeName = material.index0AttributeName; - - if ( index0AttributeName === undefined && parameters.morphTargets === true ) { - - // programs with morphTargets displace position out of attribute 0 - - index0AttributeName = 'position'; - - } - var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; if ( parameters.shadowMapType === THREE.PCFShadowMap ) { @@ -18089,73 +26052,73 @@ THREE.WebGLProgram = ( function () { // + var customExtensions = generateExtensions( extensions, parameters, renderer.extensions ); + var customDefines = generateDefines( defines ); // - var program = _gl.createProgram(); + var program = gl.createProgram(); - var prefix_vertex, prefix_fragment; + var prefixVertex, prefixFragment; if ( material instanceof THREE.RawShaderMaterial ) { - prefix_vertex = ''; - prefix_fragment = ''; + prefixVertex = ''; + prefixFragment = ''; } else { - prefix_vertex = [ + prefixVertex = [ 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', + '#define SHADER_NAME ' + material.__webglShader.name, + customDefines, parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - _this.gammaInput ? '#define GAMMA_INPUT' : '', - _this.gammaOutput ? '#define GAMMA_OUTPUT' : '', + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, - '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, - '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, - '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, - '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, - - '#define MAX_SHADOWS ' + parameters.maxShadows, - '#define MAX_BONES ' + parameters.maxBones, parameters.map ? '#define USE_MAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.flatShading ? '#define FLAT_SHADED': '', + parameters.flatShading ? '#define FLAT_SHADED' : '', parameters.skinning ? '#define USE_SKINNING' : '', parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals ? '#define USE_MORPHNORMALS' : '', - parameters.wrapAround ? '#define WRAP_AROUND' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', - parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '', + parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '', parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - //_this._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 modelMatrix;', @@ -18168,7 +26131,6 @@ THREE.WebGLProgram = ( function () { 'attribute vec3 position;', 'attribute vec3 normal;', 'attribute vec2 uv;', - 'attribute vec2 uv2;', '#ifdef USE_COLOR', @@ -18208,30 +26170,26 @@ THREE.WebGLProgram = ( function () { '#endif', - '' + '\n' - ].join( '\n' ); + ].filter( filterEmptyLine ).join( '\n' ); - prefix_fragment = [ - ( parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', + prefixFragment = [ - 'precision ' + parameters.precision + ' float;', + customExtensions, + + 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', - customDefines, + '#define SHADER_NAME ' + material.__webglShader.name, - '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, - '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, - '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, - '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, - - '#define MAX_SHADOWS ' + parameters.maxShadows, + customDefines, parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', - _this.gammaInput ? '#define GAMMA_INPUT' : '', - _this.gammaOutput ? '#define GAMMA_OUTPUT' : '', + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', @@ -18243,153 +26201,199 @@ THREE.WebGLProgram = ( function () { parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.envMap ? '#define ' + envMapBlendingDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.flatShading ? '#define FLAT_SHADED': '', + parameters.flatShading ? '#define FLAT_SHADED' : '', - parameters.metal ? '#define METAL' : '', - parameters.wrapAround ? '#define WRAP_AROUND' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', - parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '', + parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - //_this._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + + parameters.envMap && renderer.extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '', 'uniform mat4 viewMatrix;', 'uniform vec3 cameraPosition;', - '' - ].join( '\n' ); + '\n' + + ].filter( filterEmptyLine ).join( '\n' ); } - var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader ); - var glFragmentShader = new THREE.WebGLShader( _gl, _gl.FRAGMENT_SHADER, prefix_fragment + fragmentShader ); + vertexShader = replaceLightNums( vertexShader, parameters ); + fragmentShader = replaceLightNums( fragmentShader, parameters ); - _gl.attachShader( program, glVertexShader ); - _gl.attachShader( program, glFragmentShader ); + if ( material instanceof THREE.ShaderMaterial === false ) { - if ( index0AttributeName !== undefined ) { - - // Force a particular attribute to index 0. - // because potentially expensive emulation is done by browser if attribute 0 is disabled. - // And, color, for example is often automatically bound to index 0 so disabling it - - _gl.bindAttribLocation( program, 0, index0AttributeName ); + vertexShader = unrollLoops( vertexShader ); + fragmentShader = unrollLoops( fragmentShader ); } - _gl.linkProgram( program ); + var vertexGlsl = prefixVertex + vertexShader; + var fragmentGlsl = prefixFragment + fragmentShader; - var programLogInfo = _gl.getProgramInfoLog( program ); + // console.log( '*VERTEX*', vertexGlsl ); + // console.log( '*FRAGMENT*', fragmentGlsl ); - if ( _gl.getProgramParameter( program, _gl.LINK_STATUS ) === false ) { + var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); + var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); - THREE.error( 'THREE.WebGLProgram: shader error: ' + _gl.getError(), 'gl.VALIDATE_STATUS', _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ), 'gl.getPRogramInfoLog', programLogInfo ); + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); + + // Force a particular attribute to index 0. + + if ( material.index0AttributeName !== undefined ) { + + gl.bindAttribLocation( program, 0, material.index0AttributeName ); + + } else if ( parameters.morphTargets === true ) { + + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); } - if ( programLogInfo !== '' ) { + gl.linkProgram( program ); - THREE.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()' + programLogInfo ); - // THREE.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); - // THREE.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); + var programLog = gl.getProgramInfoLog( program ); + var vertexLog = gl.getShaderInfoLog( glVertexShader ); + var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); + + var runnable = true; + var haveDiagnostics = true; + + // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); + // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); + + if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { + + runnable = false; + + console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); + + } else if ( programLog !== '' ) { + + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + + } else if ( vertexLog === '' || fragmentLog === '' ) { + + haveDiagnostics = false; + + } + + if ( haveDiagnostics ) { + + this.diagnostics = { + + runnable: runnable, + material: material, + + programLog: programLog, + + vertexShader: { + + log: vertexLog, + prefix: prefixVertex + + }, + + fragmentShader: { + + log: fragmentLog, + prefix: prefixFragment + + } + + }; } // clean up - _gl.deleteShader( glVertexShader ); - _gl.deleteShader( glFragmentShader ); + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); - // cache uniform locations + // set up caching for uniform locations - var identifiers = [ + var cachedUniforms; - 'viewMatrix', - 'modelViewMatrix', - 'projectionMatrix', - 'normalMatrix', - 'modelMatrix', - 'cameraPosition', - 'morphTargetInfluences', - 'bindMatrix', - 'bindMatrixInverse' + this.getUniforms = function() { - ]; + if ( cachedUniforms === undefined ) { - if ( parameters.useVertexTexture ) { + cachedUniforms = fetchUniformLocations( gl, program ); - identifiers.push( 'boneTexture' ); - identifiers.push( 'boneTextureWidth' ); - identifiers.push( 'boneTextureHeight' ); + } - } else { + return cachedUniforms; - identifiers.push( 'boneGlobalMatrices' ); + }; - } + // set up caching for attribute locations - if ( parameters.logarithmicDepthBuffer ) { + var cachedAttributes; - identifiers.push('logDepthBufFC'); + this.getAttributes = function() { - } + if ( cachedAttributes === undefined ) { + cachedAttributes = fetchAttributeLocations( gl, program ); - for ( var u in uniforms ) { + } - identifiers.push( u ); + return cachedAttributes; - } + }; - this.uniforms = cacheUniformLocations( _gl, program, identifiers ); + // free resource - // cache attributes locations + this.destroy = function() { - identifiers = [ + gl.deleteProgram( program ); + this.program = undefined; - 'position', - 'normal', - 'uv', - 'uv2', - 'tangent', - 'color', - 'skinIndex', - 'skinWeight', - 'lineDistance' + }; - ]; + // DEPRECATED - for ( var i = 0; i < parameters.maxMorphTargets; i ++ ) { + Object.defineProperties( this, { - identifiers.push( 'morphTarget' + i ); + uniforms: { + get: function() { - } + console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); + return this.getUniforms(); - for ( var i = 0; i < parameters.maxMorphNormals; i ++ ) { + } + }, - identifiers.push( 'morphNormal' + i ); + attributes: { + get: function() { - } + console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); + return this.getAttributes(); - for ( var a in attributes ) { + } + } - identifiers.push( a ); + } ); - } - - this.attributes = cacheAttributeLocations( _gl, program, identifiers ); - this.attributesKeys = Object.keys( this.attributes ); // @@ -18406,11 +26410,296 @@ THREE.WebGLProgram = ( function () { } )(); +// File:src/renderers/webgl/WebGLPrograms.js + +THREE.WebGLPrograms = function ( renderer, capabilities ) { + + var programs = []; + + var shaderIDs = { + MeshDepthMaterial: 'depth', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + MeshStandardMaterial: 'standard', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points' + }; + + var parameterNames = [ + "precision", "supportsVertexTextures", "map", "envMap", "envMapMode", + "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap", + "roughnessMap", "metalnessMap", + "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", + "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", + "maxBones", "useVertexTexture", "morphTargets", "morphNormals", + "maxMorphTargets", "maxMorphNormals", + "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", + "shadowMapEnabled", "pointLightShadows", + "shadowMapType", + "alphaTest", "doubleSided", "flipSided" + ]; + + + function allocateBones ( object ) { + + if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { + + return 1024; + + } else { + + // default for when object is not specified + // ( for example when prebuilding shader to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var nVertexUniforms = capabilities.maxVertexUniforms; + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + + var maxBones = nVertexMatrices; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = Math.min( object.skeleton.bones.length, maxBones ); + + if ( maxBones < object.skeleton.bones.length ) { + + console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); + + } + + } + + return maxBones; + + } + + } + + this.getParameters = function ( material, lights, fog, object ) { + + var shaderID = shaderIDs[ material.type ]; + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + var maxBones = allocateBones( object ); + var precision = renderer.getPrecision(); + + if ( material.precision !== null ) { + + precision = capabilities.getMaxPrecision( material.precision ); + + if ( precision !== material.precision ) { + + console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); + + } + + } + + var parameters = { + + shaderID: shaderID, + + precision: precision, + supportsVertexTextures: capabilities.vertexTextures, + + map: !! material.map, + envMap: !! material.envMap, + envMapMode: material.envMap && material.envMap.mapping, + lightMap: !! material.lightMap, + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + displacementMap: !! material.displacementMap, + roughnessMap: !! material.roughnessMap, + metalnessMap: !! material.metalnessMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, + + combine: material.combine, + + vertexColors: material.vertexColors, + + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, + + flatShading: material.shading === THREE.FlatShading, + + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, + + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: renderer.maxMorphTargets, + maxMorphNormals: renderer.maxMorphNormals, + + numDirLights: lights.directional.length, + numPointLights: lights.point.length, + numSpotLights: lights.spot.length, + numHemiLights: lights.hemi.length, + + pointLightShadows: lights.shadowsPointLight, + + shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0, + shadowMapType: renderer.shadowMap.type, + + alphaTest: material.alphaTest, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide + + }; + + return parameters; + + }; + + this.getProgramCode = function ( material, parameters ) { + + var chunks = []; + + if ( parameters.shaderID ) { + + chunks.push( parameters.shaderID ); + + } else { + + chunks.push( material.fragmentShader ); + chunks.push( material.vertexShader ); + + } + + if ( material.defines !== undefined ) { + + for ( var name in material.defines ) { + + chunks.push( name ); + chunks.push( material.defines[ name ] ); + + } + + } + + for ( var i = 0; i < parameterNames.length; i ++ ) { + + var parameterName = parameterNames[ i ]; + chunks.push( parameterName ); + chunks.push( parameters[ parameterName ] ); + + } + + return chunks.join(); + + }; + + this.acquireProgram = function ( material, parameters, code ) { + + var program; + + // Check if code has been already compiled + for ( var p = 0, pl = programs.length; p < pl; p ++ ) { + + var programInfo = programs[ p ]; + + if ( programInfo.code === code ) { + + program = programInfo; + ++ program.usedTimes; + + break; + + } + + } + + if ( program === undefined ) { + + program = new THREE.WebGLProgram( renderer, code, material, parameters ); + programs.push( program ); + + } + + return program; + + }; + + this.releaseProgram = function( program ) { + + if ( -- program.usedTimes === 0 ) { + + // Remove from unordered set + var i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); + + // Free WebGL resources + program.destroy(); + + } + + }; + + // Exposed for resource monitoring & error feedback via renderer.info: + this.programs = programs; + +}; + +// File:src/renderers/webgl/WebGLProperties.js + +/** +* @author fordacious / fordacious.github.io +*/ + +THREE.WebGLProperties = function () { + + var properties = {}; + + this.get = function ( object ) { + + var uuid = object.uuid; + var map = properties[ uuid ]; + + if ( map === undefined ) { + + map = {}; + properties[ uuid ] = map; + + } + + return map; + + }; + + this.delete = function ( object ) { + + delete properties[ object.uuid ]; + + }; + + this.clear = function () { + + properties = {}; + + }; + +}; + // File:src/renderers/webgl/WebGLShader.js THREE.WebGLShader = ( function () { - var addLineNumbers = function ( string ) { + function addLineNumbers( string ) { var lines = string.split( '\n' ); @@ -18422,24 +26711,24 @@ THREE.WebGLShader = ( function () { return lines.join( '\n' ); - }; + } - return function ( gl, type, string ) { + return function WebGLShader( gl, type, string ) { - var shader = gl.createShader( type ); + var shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { - THREE.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); } if ( gl.getShaderInfoLog( shader ) !== '' ) { - THREE.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); } @@ -18452,16 +26741,409 @@ THREE.WebGLShader = ( function () { } )(); +// File:src/renderers/webgl/WebGLShadowMap.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { + + var _gl = _renderer.context, + _state = _renderer.state, + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + + _lookTarget = new THREE.Vector3(), + _lightPositionWorld = new THREE.Vector3(), + + _renderList = [], + + _MorphingFlag = 1, + _SkinningFlag = 2, + + _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, + + _depthMaterials = new Array( _NumberOfMaterialVariants ), + _distanceMaterials = new Array( _NumberOfMaterialVariants ); + + var cubeDirections = [ + new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ), + new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 ) + ]; + + var cubeUps = [ + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 1 ) + ]; + + var cube2DViewPorts = [ + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() + ]; + + // init + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + var distanceShader = THREE.ShaderLib[ "distanceRGBA" ]; + var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms ); + + for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { + + var useMorphing = ( i & _MorphingFlag ) !== 0; + var useSkinning = ( i & _SkinningFlag ) !== 0; + + var depthMaterial = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: useMorphing, + skinning: useSkinning + } ); + + depthMaterial._shadowPass = true; + + _depthMaterials[ i ] = depthMaterial; + + var distanceMaterial = new THREE.ShaderMaterial( { + uniforms: distanceUniforms, + vertexShader: distanceShader.vertexShader, + fragmentShader: distanceShader.fragmentShader, + morphTargets: useMorphing, + skinning: useSkinning + } ); + + distanceMaterial._shadowPass = true; + + _distanceMaterials[ i ] = distanceMaterial; + + } + + // + + var scope = this; + + this.enabled = false; + + this.autoUpdate = true; + this.needsUpdate = false; + + this.type = THREE.PCFShadowMap; + this.cullFace = THREE.CullFaceFront; + + this.render = function ( scene, camera ) { + + var faceCount, isPointLight; + + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; + + // Set GL state for depth map. + _state.clearColor( 1, 1, 1, 1 ); + _state.disable( _gl.BLEND ); + _state.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); + _gl.cullFace( scope.cullFace === THREE.CullFaceFront ? _gl.FRONT : _gl.BACK ); + _state.setDepthTest( true ); + _state.setScissorTest( false ); + + // render depth map + + var shadows = _lights.shadows; + + for ( var i = 0, il = shadows.length; i < il; i ++ ) { + + var light = shadows[ i ]; + + var shadow = light.shadow; + var shadowCamera = shadow.camera; + var shadowMapSize = shadow.mapSize; + + if ( light instanceof THREE.PointLight ) { + + faceCount = 6; + isPointLight = true; + + var vpWidth = shadowMapSize.x / 4.0; + var vpHeight = shadowMapSize.y / 2.0; + + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction + + // positive X + cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); + // negative X + cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); + // positive Z + cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); + // negative Z + cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); + // positive Y + cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); + // negative Y + cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); + + } else { + + faceCount = 1; + isPointLight = false; + + } + + if ( shadow.map === null ) { + + var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat }; + + shadow.map = new THREE.WebGLRenderTarget( shadowMapSize.x, shadowMapSize.y, pars ); + + // + + if ( light instanceof THREE.SpotLight ) { + + shadowCamera.aspect = shadowMapSize.x / shadowMapSize.y; + + } + + shadowCamera.updateProjectionMatrix(); + + } + + var shadowMap = shadow.map; + var shadowMatrix = shadow.matrix; + + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld ); + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); + + // render shadow map for each cube face (if omni-directional) or + // run a single pass if not + + for ( var face = 0; face < faceCount; face ++ ) { + + if ( isPointLight ) { + + _lookTarget.copy( shadowCamera.position ); + _lookTarget.add( cubeDirections[ face ] ); + shadowCamera.up.copy( cubeUps[ face ] ); + shadowCamera.lookAt( _lookTarget ); + + var vpDimensions = cube2DViewPorts[ face ]; + _state.viewport( vpDimensions ); + + } else { + + _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget ); + + } + + shadowCamera.updateMatrixWorld(); + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + + // compute shadow matrix + + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); + + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + + // update camera matrices and frustum + + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // set object matrices & frustum culling + + _renderList.length = 0; + + projectObject( scene, camera, shadowCamera ); + + // render shadow map + // render regular objects + + for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { + + var object = _renderList[ j ]; + var geometry = _objects.update( object ); + var material = object.material; + + if ( material instanceof THREE.MultiMaterial ) { + + var groups = geometry.groups; + var materials = material.materials; + + for ( var k = 0, kl = groups.length; k < kl; k ++ ) { + + var group = groups[ k ]; + var groupMaterial = materials[ group.materialIndex ]; + + if ( groupMaterial.visible === true ) { + + var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); + + } + + } + + } else { + + var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); + + } + + } + + } + + // We must call _renderer.resetGLState() at the end of each iteration of + // the light loop in order to force material updates for each light. + _renderer.resetGLState(); + + } + + // Restore GL state. + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + _renderer.setClearColor( clearColor, clearAlpha ); + + _state.enable( _gl.BLEND ); + + if ( scope.cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.resetGLState(); + + scope.needsUpdate = false; + + }; + + function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) { + + var geometry = object.geometry; + + var newMaterial = null; + + var materialVariants = _depthMaterials; + var customMaterial = object.customDepthMaterial; + + if ( isPointLight ) { + + materialVariants = _distanceMaterials; + customMaterial = object.customDistanceMaterial; + + } + + if ( ! customMaterial ) { + + var useMorphing = geometry.morphTargets !== undefined && + geometry.morphTargets.length > 0 && material.morphTargets; + + var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning; + + var variantIndex = 0; + + if ( useMorphing ) variantIndex |= _MorphingFlag; + if ( useSkinning ) variantIndex |= _SkinningFlag; + + newMaterial = materialVariants[ variantIndex ]; + + } else { + + newMaterial = customMaterial; + + } + + newMaterial.visible = material.visible; + newMaterial.wireframe = material.wireframe; + newMaterial.wireframeLinewidth = material.wireframeLinewidth; + + if ( isPointLight && newMaterial.uniforms.lightPos !== undefined ) { + + newMaterial.uniforms.lightPos.value.copy( lightPositionWorld ); + + } + + return newMaterial; + + } + + function projectObject( object, camera, shadowCamera ) { + + if ( object.visible === false ) return; + + if ( object.layers.test( camera.layers ) && ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) ) { + + if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { + + var material = object.material; + + if ( material.visible === true ) { + + object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( object ); + + } + + } + + } + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + projectObject( children[ i ], camera, shadowCamera ); + + } + + } + +}; + // File:src/renderers/webgl/WebGLState.js /** * @author mrdoob / http://mrdoob.com/ */ -THREE.WebGLState = function ( gl, paramThreeToGL ) { +THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) { + + var _this = this; + + var color = new THREE.Vector4(); var newAttributes = new Uint8Array( 16 ); var enabledAttributes = new Uint8Array( 16 ); + var attributeDivisors = new Uint8Array( 16 ); + + var capabilities = {}; + + var compressedTextureFormats = null; var currentBlending = null; var currentBlendEquation = null; @@ -18471,20 +27153,59 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { var currentBlendSrcAlpha = null; var currentBlendDstAlpha = null; - var currentDepthTest = null; + var currentDepthFunc = null; var currentDepthWrite = null; var currentColorWrite = null; - var currentDoubleSided = null; + var currentStencilWrite = null; + var currentStencilFunc = null; + var currentStencilRef = null; + var currentStencilMask = null; + var currentStencilFail = null; + var currentStencilZFail = null; + var currentStencilZPass = null; + var currentFlipSided = null; var currentLineWidth = null; - var currentPolygonOffset = null; var currentPolygonOffsetFactor = null; var currentPolygonOffsetUnits = null; + var currentScissorTest = null; + + var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + + var currentTextureSlot = undefined; + var currentBoundTextures = {}; + + var currentClearColor = new THREE.Vector4(); + var currentClearDepth = null; + var currentClearStencil = null; + + var currentScissor = new THREE.Vector4(); + var currentViewport = new THREE.Vector4(); + + this.init = function () { + + this.clearColor( 0, 0, 0, 1 ); + this.clearDepth( 1 ); + this.clearStencil( 0 ); + + this.enable( gl.DEPTH_TEST ); + gl.depthFunc( gl.LEQUAL ); + + gl.frontFace( gl.CCW ); + gl.cullFace( gl.BACK ); + this.enable( gl.CULL_FACE ); + + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + + }; + this.initAttributes = function () { for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { @@ -18506,6 +27227,35 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { } + if ( attributeDivisors[ attribute ] !== 0 ) { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + extension.vertexAttribDivisorANGLE( attribute, 0 ); + attributeDivisors[ attribute ] = 0; + + } + + }; + + this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) { + + newAttributes[ attribute ] = 1; + + if ( enabledAttributes[ attribute ] === 0 ) { + + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; + + } + + if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { + + extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); + attributeDivisors[ attribute ] = meshPerAttribute; + + } + }; this.disableUnusedAttributes = function () { @@ -18523,41 +27273,89 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { }; + this.enable = function ( id ) { + + if ( capabilities[ id ] !== true ) { + + gl.enable( id ); + capabilities[ id ] = true; + + } + + }; + + this.disable = function ( id ) { + + if ( capabilities[ id ] !== false ) { + + gl.disable( id ); + capabilities[ id ] = false; + + } + + }; + + this.getCompressedTextureFormats = function () { + + if ( compressedTextureFormats === null ) { + + compressedTextureFormats = []; + + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || + extensions.get( 'WEBGL_compressed_texture_s3tc' ) || + extensions.get( 'WEBGL_compressed_texture_etc1' )) { + + var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); + + for ( var i = 0; i < formats.length; i ++ ) { + + compressedTextureFormats.push( formats[ i ] ); + + } + + } + + } + + return compressedTextureFormats; + + }; + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) { + if ( blending === THREE.NoBlending ) { + + this.disable( gl.BLEND ); + + } else { + + this.enable( gl.BLEND ); + + } + if ( blending !== currentBlending ) { - if ( blending === THREE.NoBlending ) { + if ( blending === THREE.AdditiveBlending ) { - gl.disable( gl.BLEND ); - - } else if ( blending === THREE.AdditiveBlending ) { - - gl.enable( gl.BLEND ); gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); } else if ( blending === THREE.SubtractiveBlending ) { // TODO: Find blendFuncSeparate() combination - gl.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); } else if ( blending === THREE.MultiplyBlending ) { // TODO: Find blendFuncSeparate() combination - gl.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); - } else if ( blending === THREE.CustomBlending ) { - - gl.enable( gl.BLEND ); - } else { - gl.enable( gl.BLEND ); gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); @@ -18606,21 +27404,81 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { }; - this.setDepthTest = function ( depthTest ) { + this.setDepthFunc = function ( depthFunc ) { - if ( currentDepthTest !== depthTest ) { + if ( currentDepthFunc !== depthFunc ) { - if ( depthTest ) { + if ( depthFunc ) { - gl.enable( gl.DEPTH_TEST ); + switch ( depthFunc ) { + + case THREE.NeverDepth: + + gl.depthFunc( gl.NEVER ); + break; + + case THREE.AlwaysDepth: + + gl.depthFunc( gl.ALWAYS ); + break; + + case THREE.LessDepth: + + gl.depthFunc( gl.LESS ); + break; + + case THREE.LessEqualDepth: + + gl.depthFunc( gl.LEQUAL ); + break; + + case THREE.EqualDepth: + + gl.depthFunc( gl.EQUAL ); + break; + + case THREE.GreaterEqualDepth: + + gl.depthFunc( gl.GEQUAL ); + break; + + case THREE.GreaterDepth: + + gl.depthFunc( gl.GREATER ); + break; + + case THREE.NotEqualDepth: + + gl.depthFunc( gl.NOTEQUAL ); + break; + + default: + + gl.depthFunc( gl.LEQUAL ); + + } } else { - gl.disable( gl.DEPTH_TEST ); + gl.depthFunc( gl.LEQUAL ); } - currentDepthTest = depthTest; + currentDepthFunc = depthFunc; + + } + + }; + + this.setDepthTest = function ( depthTest ) { + + if ( depthTest ) { + + this.enable( gl.DEPTH_TEST ); + + } else { + + this.disable( gl.DEPTH_TEST ); } @@ -18628,6 +27486,8 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { this.setDepthWrite = function ( depthWrite ) { + // TODO: Rename to setDepthMask + if ( currentDepthWrite !== depthWrite ) { gl.depthMask( depthWrite ); @@ -18639,6 +27499,8 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { this.setColorWrite = function ( colorWrite ) { + // TODO: Rename to setColorMask + if ( currentColorWrite !== colorWrite ) { gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite ); @@ -18648,21 +27510,60 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { }; - this.setDoubleSided = function ( doubleSided ) { + this.setStencilFunc = function ( stencilFunc, stencilRef, stencilMask ) { - if ( currentDoubleSided !== doubleSided ) { + if ( currentStencilFunc !== stencilFunc || + currentStencilRef !== stencilRef || + currentStencilMask !== stencilMask ) { - if ( doubleSided ) { + gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - gl.disable( gl.CULL_FACE ); + currentStencilFunc = stencilFunc; + currentStencilRef = stencilRef; + currentStencilMask = stencilMask; - } else { + } - gl.enable( gl.CULL_FACE ); + }; - } + this.setStencilOp = function ( stencilFail, stencilZFail, stencilZPass ) { - currentDoubleSided = doubleSided; + if ( currentStencilFail !== stencilFail || + currentStencilZFail !== stencilZFail || + currentStencilZPass !== stencilZPass ) { + + gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); + + currentStencilFail = stencilFail; + currentStencilZFail = stencilZFail; + currentStencilZPass = stencilZPass; + + } + + }; + + this.setStencilTest = function ( stencilTest ) { + + if ( stencilTest ) { + + this.enable( gl.STENCIL_TEST ); + + } else { + + this.disable( gl.STENCIL_TEST ); + + } + + }; + + this.setStencilWrite = function ( stencilWrite ) { + + // TODO: Rename to setStencilMask + + if ( currentStencilWrite !== stencilWrite ) { + + gl.stencilMask( stencilWrite ); + currentStencilWrite = stencilWrite; } @@ -18700,25 +27601,19 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { }; - this.setPolygonOffset = function ( polygonoffset, factor, units ) { + this.setPolygonOffset = function ( polygonOffset, factor, units ) { - if ( currentPolygonOffset !== polygonoffset ) { + if ( polygonOffset ) { - if ( polygonoffset ) { + this.enable( gl.POLYGON_OFFSET_FILL ); - gl.enable( gl.POLYGON_OFFSET_FILL ); + } else { - } else { - - gl.disable( gl.POLYGON_OFFSET_FILL ); - - } - - currentPolygonOffset = polygonoffset; + this.disable( gl.POLYGON_OFFSET_FILL ); } - if ( polygonoffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) { + if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) { gl.polygonOffset( factor, units ); @@ -18729,19 +27624,185 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { }; + this.getScissorTest = function () { + + return currentScissorTest; + + }; + + this.setScissorTest = function ( scissorTest ) { + + currentScissorTest = scissorTest; + + if ( scissorTest ) { + + this.enable( gl.SCISSOR_TEST ); + + } else { + + this.disable( gl.SCISSOR_TEST ); + + } + + }; + + // texture + + this.activeTexture = function ( webglSlot ) { + + if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; + + if ( currentTextureSlot !== webglSlot ) { + + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; + + } + + }; + + this.bindTexture = function ( webglType, webglTexture ) { + + if ( currentTextureSlot === undefined ) { + + _this.activeTexture(); + + } + + var boundTexture = currentBoundTextures[ currentTextureSlot ]; + + if ( boundTexture === undefined ) { + + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ currentTextureSlot ] = boundTexture; + + } + + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + + gl.bindTexture( webglType, webglTexture ); + + boundTexture.type = webglType; + boundTexture.texture = webglTexture; + + } + + }; + + this.compressedTexImage2D = function () { + + try { + + gl.compressedTexImage2D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( error ); + + } + + }; + + this.texImage2D = function () { + + try { + + gl.texImage2D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( error ); + + } + + }; + + // clear values + + this.clearColor = function ( r, g, b, a ) { + + color.set( r, g, b, a ); + + if ( currentClearColor.equals( color ) === false ) { + + gl.clearColor( r, g, b, a ); + currentClearColor.copy( color ); + + } + + }; + + this.clearDepth = function ( depth ) { + + if ( currentClearDepth !== depth ) { + + gl.clearDepth( depth ); + currentClearDepth = depth; + + } + + }; + + this.clearStencil = function ( stencil ) { + + if ( currentClearStencil !== stencil ) { + + gl.clearStencil( stencil ); + currentClearStencil = stencil; + + } + + }; + + // + + this.scissor = function ( scissor ) { + + if ( currentScissor.equals( scissor ) === false ) { + + gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); + currentScissor.copy( scissor ); + + } + + }; + + this.viewport = function ( viewport ) { + + if ( currentViewport.equals( viewport ) === false ) { + + gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); + currentViewport.copy( viewport ); + + } + + }; + + // + this.reset = function () { for ( var i = 0; i < enabledAttributes.length; i ++ ) { - enabledAttributes[ i ] = 0; + if ( enabledAttributes[ i ] === 1 ) { + + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; + + } } + capabilities = {}; + + compressedTextureFormats = null; + currentBlending = null; - currentDepthTest = null; - currentDepthWrite = null; + currentColorWrite = null; - currentDoubleSided = null; + currentDepthWrite = null; + currentStencilWrite = null; + currentFlipSided = null; }; @@ -18758,6 +27819,7 @@ THREE.WebGLState = function ( gl, paramThreeToGL ) { THREE.LensFlarePlugin = function ( renderer, flares ) { var gl = renderer.context; + var state = renderer.state; var vertexBuffer, elementBuffer; var program, attributes, uniforms; @@ -18765,13 +27827,13 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { var tempTexture, occlusionTexture; - var init = function () { + function init() { var vertices = new Float32Array( [ - -1, -1, 0, 0, - 1, -1, 1, 0, + - 1, - 1, 0, 0, + 1, - 1, 1, 0, 1, 1, 1, 1, - -1, 1, 0, 1 + - 1, 1, 0, 1 ] ); var faces = new Uint16Array( [ @@ -18795,14 +27857,14 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { tempTexture = gl.createTexture(); occlusionTexture = gl.createTexture(); - gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - gl.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); @@ -18839,7 +27901,7 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { "vec2 pos = position;", - "if( renderType == 2 ) {", + "if ( renderType == 2 ) {", "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", @@ -18882,13 +27944,13 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { // pink square - "if( renderType == 0 ) {", + "if ( renderType == 0 ) {", "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", // restore - "} else if( renderType == 1 ) {", + "} else if ( renderType == 1 ) {", "gl_FragColor = texture2D( map, vUV );", @@ -18932,7 +27994,7 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { "vec2 pos = position;", - "if( renderType == 2 ) {", + "if ( renderType == 2 ) {", "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", @@ -18962,13 +28024,13 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { // pink square - "if( renderType == 0 ) {", + "if ( renderType == 0 ) {", "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", // restore - "} else if( renderType == 1 ) {", + "} else if ( renderType == 1 ) {", "gl_FragColor = texture2D( map, vUV );", @@ -19002,7 +28064,7 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { attributes = { vertex: gl.getAttribLocation ( program, "position" ), uv: gl.getAttribLocation ( program, "uv" ) - } + }; uniforms = { renderType: gl.getUniformLocation( program, "renderType" ), @@ -19015,7 +28077,7 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { screenPosition: gl.getUniformLocation( program, "screenPosition" ) }; - }; + } /* * Render lens flares @@ -19023,17 +28085,17 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { * reads these back and calculates occlusion. */ - this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + this.render = function ( scene, camera, viewport ) { if ( flares.length === 0 ) return; var tempPosition = new THREE.Vector3(); - var invAspect = viewportHeight / viewportWidth, - halfViewportWidth = viewportWidth * 0.5, - halfViewportHeight = viewportHeight * 0.5; + var invAspect = viewport.w / viewport.z, + halfViewportWidth = viewport.z * 0.5, + halfViewportHeight = viewport.w * 0.5; - var size = 16 / viewportHeight, + var size = 16 / viewport.w, scale = new THREE.Vector2( size * invAspect, size ); var screenPosition = new THREE.Vector3( 1, 1, 0 ), @@ -19047,11 +28109,13 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { gl.useProgram( program ); - gl.enableVertexAttribArray( attributes.vertex ); - gl.enableVertexAttribArray( attributes.uv ); + state.initAttributes(); + state.enableAttribute( attributes.vertex ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); // loop through all lens flares to update their occlusion and positions - // setup gl and common used attribs/unforms + // setup gl and common used attribs/uniforms gl.uniform1i( uniforms.occlusionMap, 0 ); gl.uniform1i( uniforms.map, 1 ); @@ -19062,26 +28126,26 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - gl.disable( gl.CULL_FACE ); - gl.depthMask( false ); + state.disable( gl.CULL_FACE ); + state.setDepthWrite( false ); for ( var i = 0, l = flares.length; i < l; i ++ ) { - size = 16 / viewportHeight; + size = 16 / viewport.w; scale.set( size * invAspect, size ); // calc object screen position var flare = flares[ i ]; - tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); + tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); tempPosition.applyMatrix4( camera.matrixWorldInverse ); tempPosition.applyProjection( camera.projectionMatrix ); // setup arrays for gl programs - screenPosition.copy( tempPosition ) + screenPosition.copy( tempPosition ); screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; @@ -19090,15 +28154,17 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { if ( hasVertexTexture || ( screenPositionPixels.x > 0 && - screenPositionPixels.x < viewportWidth && + screenPositionPixels.x < viewport.z && screenPositionPixels.y > 0 && - screenPositionPixels.y < viewportHeight ) ) { + screenPositionPixels.y < viewport.w ) ) { // save current RGB to temp texture - gl.activeTexture( gl.TEXTURE1 ); - gl.bindTexture( gl.TEXTURE_2D, tempTexture ); - gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, null ); + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, viewport.x + screenPositionPixels.x - 8, viewport.y + screenPositionPixels.y - 8, 16, 16, 0 ); // render pink quad @@ -19107,32 +28173,32 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { gl.uniform2f( uniforms.scale, scale.x, scale.y ); gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - gl.disable( gl.BLEND ); - gl.enable( gl.DEPTH_TEST ); + state.disable( gl.BLEND ); + state.enable( gl.DEPTH_TEST ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); // copy result to occlusionMap - gl.activeTexture( gl.TEXTURE0 ); - gl.bindTexture( gl.TEXTURE_2D, occlusionTexture ); - gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, viewport.x + screenPositionPixels.x - 8, viewport.y + screenPositionPixels.y - 8, 16, 16, 0 ); // restore graphics gl.uniform1i( uniforms.renderType, 1 ); - gl.disable( gl.DEPTH_TEST ); + state.disable( gl.DEPTH_TEST ); - gl.activeTexture( gl.TEXTURE1 ); - gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); // update object positions - flare.positionScreen.copy( screenPosition ) + flare.positionScreen.copy( screenPosition ); if ( flare.customUpdateCallback ) { @@ -19147,7 +28213,7 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { // render flares gl.uniform1i( uniforms.renderType, 2 ); - gl.enable( gl.BLEND ); + state.enable( gl.BLEND ); for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { @@ -19159,7 +28225,7 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { screenPosition.y = sprite.y; screenPosition.z = sprite.z; - size = sprite.size * sprite.scale / viewportHeight; + size = sprite.size * sprite.scale / viewport.w; scale.x = size * invAspect; scale.y = size; @@ -19171,7 +28237,7 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { gl.uniform1f( uniforms.opacity, sprite.opacity ); gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); - renderer.state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); renderer.setTexture( sprite.texture, 1 ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); @@ -19186,9 +28252,9 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { // restore gl - gl.enable( gl.CULL_FACE ); - gl.enable( gl.DEPTH_TEST ); - gl.depthMask( true ); + state.enable( gl.CULL_FACE ); + state.enable( gl.DEPTH_TEST ); + state.setDepthWrite( true ); renderer.resetGLState(); @@ -19220,531 +28286,6 @@ THREE.LensFlarePlugin = function ( renderer, flares ) { }; -// File:src/renderers/webgl/plugins/ShadowMapPlugin.js - -/** - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObjectsImmediate ) { - - var _gl = _renderer.context; - - var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, - - _frustum = new THREE.Frustum(), - _projScreenMatrix = new THREE.Matrix4(), - - _min = new THREE.Vector3(), - _max = new THREE.Vector3(), - - _matrixPosition = new THREE.Vector3(), - - _renderList = []; - - // init - - var depthShader = THREE.ShaderLib[ "depthRGBA" ]; - var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); - - _depthMaterial = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader - } ); - - _depthMaterialMorph = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader, - morphTargets: true - } ); - - _depthMaterialSkin = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader, - skinning: true - } ); - - _depthMaterialMorphSkin = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader, - morphTargets: true, - skinning: true - } ); - - _depthMaterial._shadowPass = true; - _depthMaterialMorph._shadowPass = true; - _depthMaterialSkin._shadowPass = true; - _depthMaterialMorphSkin._shadowPass = true; - - this.render = function ( scene, camera ) { - - if ( _renderer.shadowMapEnabled === false ) return; - - var i, il, j, jl, n, - - shadowMap, shadowMatrix, shadowCamera, - buffer, material, - webglObject, object, light, - - lights = [], - k = 0, - - fog = null; - - // set GL state for depth map - - _gl.clearColor( 1, 1, 1, 1 ); - _gl.disable( _gl.BLEND ); - - _gl.enable( _gl.CULL_FACE ); - _gl.frontFace( _gl.CCW ); - - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { - - _gl.cullFace( _gl.FRONT ); - - } else { - - _gl.cullFace( _gl.BACK ); - - } - - _renderer.state.setDepthTest( true ); - - // preprocess lights - // - skip lights that are not casting shadows - // - create virtual lights for cascaded shadow maps - - for ( i = 0, il = _lights.length; i < il; i ++ ) { - - light = _lights[ i ]; - - if ( ! light.castShadow ) continue; - - if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { - - for ( n = 0; n < light.shadowCascadeCount; n ++ ) { - - var virtualLight; - - if ( ! light.shadowCascadeArray[ n ] ) { - - virtualLight = createVirtualLight( light, n ); - virtualLight.originalCamera = camera; - - var gyro = new THREE.Gyroscope(); - gyro.position.copy( light.shadowCascadeOffset ); - - gyro.add( virtualLight ); - gyro.add( virtualLight.target ); - - camera.add( gyro ); - - light.shadowCascadeArray[ n ] = virtualLight; - - //console.log( "Created virtualLight", virtualLight ); - - } else { - - virtualLight = light.shadowCascadeArray[ n ]; - - } - - updateVirtualLight( light, n ); - - lights[ k ] = virtualLight; - k ++; - - } - - } else { - - lights[ k ] = light; - k ++; - - } - - } - - // render depth map - - for ( i = 0, il = lights.length; i < il; i ++ ) { - - light = lights[ i ]; - - if ( ! light.shadowMap ) { - - var shadowFilter = THREE.LinearFilter; - - if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { - - shadowFilter = THREE.NearestFilter; - - } - - var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; - - light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); - light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); - - light.shadowMatrix = new THREE.Matrix4(); - - } - - if ( ! light.shadowCamera ) { - - if ( light instanceof THREE.SpotLight ) { - - light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); - - } else if ( light instanceof THREE.DirectionalLight ) { - - light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); - - } else { - - THREE.error( "THREE.ShadowMapPlugin: Unsupported light type for shadow", light ); - continue; - - } - - scene.add( light.shadowCamera ); - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - - } - - if ( light.shadowCameraVisible && ! light.cameraHelper ) { - - light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); - scene.add( light.cameraHelper ); - - } - - if ( light.isVirtual && virtualLight.originalCamera == camera ) { - - updateShadowCamera( camera, light ); - - } - - shadowMap = light.shadowMap; - shadowMatrix = light.shadowMatrix; - shadowCamera = light.shadowCamera; - - // - - shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); - _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _matrixPosition ); - shadowCamera.updateMatrixWorld(); - - shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); - - // - - if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; - if ( light.shadowCameraVisible ) light.cameraHelper.update(); - - // compute shadow matrix - - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); - - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - - // update camera matrices and frustum - - _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); - - // render shadow map - - _renderer.setRenderTarget( shadowMap ); - _renderer.clear(); - - // set object matrices & frustum culling - - _renderList.length = 0; - - projectObject( scene, scene, shadowCamera ); - - - // render regular objects - - var objectMaterial, useMorphing, useSkinning; - - for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { - - webglObject = _renderList[ j ]; - - object = webglObject.object; - buffer = webglObject.buffer; - - // culling is overriden globally for all objects - // while rendering depth map - - // need to deal with MeshFaceMaterial somehow - // in that case just use the first of material.materials for now - // (proper solution would require to break objects by materials - // similarly to regular rendering and then set corresponding - // depth materials per each chunk instead of just once per object) - - objectMaterial = getObjectMaterial( object ); - - useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; - useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; - - if ( object.customDepthMaterial ) { - - material = object.customDepthMaterial; - - } else if ( useSkinning ) { - - material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; - - } else if ( useMorphing ) { - - material = _depthMaterialMorph; - - } else { - - material = _depthMaterial; - - } - - _renderer.setMaterialFaces( objectMaterial ); - - if ( buffer instanceof THREE.BufferGeometry ) { - - _renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object ); - - } else { - - _renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object ); - - } - - } - - // set matrices and render immediate objects - - for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) { - - webglObject = _webglObjectsImmediate[ j ]; - object = webglObject.object; - - if ( object.visible && object.castShadow ) { - - object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - - _renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object ); - - } - - } - - } - - // restore GL state - - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); - - _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); - _gl.enable( _gl.BLEND ); - - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { - - _gl.cullFace( _gl.BACK ); - - } - - _renderer.resetGLState(); - - }; - - function projectObject( scene, object, shadowCamera ) { - - if ( object.visible ) { - - var webglObjects = _webglObjects[ object.id ]; - - if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { - - for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { - - var webglObject = webglObjects[ i ]; - - object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - _renderList.push( webglObject ); - - } - - } - - for ( var i = 0, l = object.children.length; i < l; i ++ ) { - - projectObject( scene, object.children[ i ], shadowCamera ); - - } - - } - - } - - function createVirtualLight( light, cascade ) { - - var virtualLight = new THREE.DirectionalLight(); - - virtualLight.isVirtual = true; - - virtualLight.onlyShadow = true; - virtualLight.castShadow = true; - - virtualLight.shadowCameraNear = light.shadowCameraNear; - virtualLight.shadowCameraFar = light.shadowCameraFar; - - virtualLight.shadowCameraLeft = light.shadowCameraLeft; - virtualLight.shadowCameraRight = light.shadowCameraRight; - virtualLight.shadowCameraBottom = light.shadowCameraBottom; - virtualLight.shadowCameraTop = light.shadowCameraTop; - - virtualLight.shadowCameraVisible = light.shadowCameraVisible; - - virtualLight.shadowDarkness = light.shadowDarkness; - - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; - virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; - virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; - - virtualLight.pointsWorld = []; - virtualLight.pointsFrustum = []; - - var pointsWorld = virtualLight.pointsWorld, - pointsFrustum = virtualLight.pointsFrustum; - - for ( var i = 0; i < 8; i ++ ) { - - pointsWorld[ i ] = new THREE.Vector3(); - pointsFrustum[ i ] = new THREE.Vector3(); - - } - - var nearZ = light.shadowCascadeNearZ[ cascade ]; - var farZ = light.shadowCascadeFarZ[ cascade ]; - - pointsFrustum[ 0 ].set( - 1, - 1, nearZ ); - pointsFrustum[ 1 ].set( 1, - 1, nearZ ); - pointsFrustum[ 2 ].set( - 1, 1, nearZ ); - pointsFrustum[ 3 ].set( 1, 1, nearZ ); - - pointsFrustum[ 4 ].set( - 1, - 1, farZ ); - pointsFrustum[ 5 ].set( 1, - 1, farZ ); - pointsFrustum[ 6 ].set( - 1, 1, farZ ); - pointsFrustum[ 7 ].set( 1, 1, farZ ); - - return virtualLight; - - } - - // Synchronize virtual light with the original light - - function updateVirtualLight( light, cascade ) { - - var virtualLight = light.shadowCascadeArray[ cascade ]; - - virtualLight.position.copy( light.position ); - virtualLight.target.position.copy( light.target.position ); - virtualLight.lookAt( virtualLight.target ); - - virtualLight.shadowCameraVisible = light.shadowCameraVisible; - virtualLight.shadowDarkness = light.shadowDarkness; - - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; - - var nearZ = light.shadowCascadeNearZ[ cascade ]; - var farZ = light.shadowCascadeFarZ[ cascade ]; - - var pointsFrustum = virtualLight.pointsFrustum; - - pointsFrustum[ 0 ].z = nearZ; - pointsFrustum[ 1 ].z = nearZ; - pointsFrustum[ 2 ].z = nearZ; - pointsFrustum[ 3 ].z = nearZ; - - pointsFrustum[ 4 ].z = farZ; - pointsFrustum[ 5 ].z = farZ; - pointsFrustum[ 6 ].z = farZ; - pointsFrustum[ 7 ].z = farZ; - - } - - // Fit shadow camera's ortho frustum to camera frustum - - function updateShadowCamera( camera, light ) { - - var shadowCamera = light.shadowCamera, - pointsFrustum = light.pointsFrustum, - pointsWorld = light.pointsWorld; - - _min.set( Infinity, Infinity, Infinity ); - _max.set( - Infinity, - Infinity, - Infinity ); - - for ( var i = 0; i < 8; i ++ ) { - - var p = pointsWorld[ i ]; - - p.copy( pointsFrustum[ i ] ); - p.unproject( camera ); - - p.applyMatrix4( shadowCamera.matrixWorldInverse ); - - if ( p.x < _min.x ) _min.x = p.x; - if ( p.x > _max.x ) _max.x = p.x; - - if ( p.y < _min.y ) _min.y = p.y; - if ( p.y > _max.y ) _max.y = p.y; - - if ( p.z < _min.z ) _min.z = p.z; - if ( p.z > _max.z ) _max.z = p.z; - - } - - shadowCamera.left = _min.x; - shadowCamera.right = _max.x; - shadowCamera.top = _max.y; - shadowCamera.bottom = _min.y; - - // can't really fit near/far - //shadowCamera.near = _min.z; - //shadowCamera.far = _max.z; - - shadowCamera.updateProjectionMatrix(); - - } - - // For the moment just ignore objects that have multiple materials with different animation methods - // Only the first material will be taken into account for deciding which depth material to use for shadow maps - - function getObjectMaterial( object ) { - - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ 0 ] - : object.material; - - }; - -}; - // File:src/renderers/webgl/plugins/SpritePlugin.js /** @@ -19755,6 +28296,7 @@ THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObje THREE.SpritePlugin = function ( renderer, sprites ) { var gl = renderer.context; + var state = renderer.state; var vertexBuffer, elementBuffer; var program, attributes, uniforms; @@ -19767,7 +28309,7 @@ THREE.SpritePlugin = function ( renderer, sprites ) { var spriteRotation = new THREE.Quaternion(); var spriteScale = new THREE.Vector3(); - var init = function () { + function init() { var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0, @@ -19831,7 +28373,7 @@ THREE.SpritePlugin = function ( renderer, sprites ) { texture = new THREE.Texture( canvas ); texture.needsUpdate = true; - }; + } this.render = function ( scene, camera ) { @@ -19847,11 +28389,13 @@ THREE.SpritePlugin = function ( renderer, sprites ) { gl.useProgram( program ); - gl.enableVertexAttribArray( attributes.position ); - gl.enableVertexAttribArray( attributes.uv ); + state.initAttributes(); + state.enableAttribute( attributes.position ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); - gl.disable( gl.CULL_FACE ); - gl.enable( gl.BLEND ); + state.disable( gl.CULL_FACE ); + state.enable( gl.BLEND ); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); @@ -19861,7 +28405,7 @@ THREE.SpritePlugin = function ( renderer, sprites ) { gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - gl.activeTexture( gl.TEXTURE0 ); + state.activeTexture( gl.TEXTURE0 ); gl.uniform1i( uniforms.map, 0 ); var oldFogType = 0; @@ -19906,8 +28450,8 @@ THREE.SpritePlugin = function ( renderer, sprites ) { var sprite = sprites[ i ]; - sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); - sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; + sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; } @@ -19923,7 +28467,7 @@ THREE.SpritePlugin = function ( renderer, sprites ) { var material = sprite.material; gl.uniform1f( uniforms.alphaTest, material.alphaTest ); - gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); + gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); @@ -19963,9 +28507,9 @@ THREE.SpritePlugin = function ( renderer, sprites ) { gl.uniform1f( uniforms.rotation, material.rotation ); gl.uniform2fv( uniforms.scale, scale ); - renderer.state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - renderer.state.setDepthTest( material.depthTest ); - renderer.state.setDepthWrite( material.depthWrite ); + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); if ( material.map && material.map.image && material.map.image.width ) { @@ -19983,7 +28527,7 @@ THREE.SpritePlugin = function ( renderer, sprites ) { // restore gl - gl.enable( gl.CULL_FACE ); + state.enable( gl.CULL_FACE ); renderer.resetGLState(); @@ -20071,7 +28615,7 @@ THREE.SpritePlugin = function ( renderer, sprites ) { '} else {', 'const float LOG2 = 1.442695;', - 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', '}', @@ -20094,11 +28638,15 @@ THREE.SpritePlugin = function ( renderer, sprites ) { return program; - }; + } function painterSortStable ( a, b ) { + + if ( a.renderOrder !== b.renderOrder ) { - if ( a.z !== b.z ) { + return a.renderOrder - b.renderOrder; + + } else if ( a.z !== b.z ) { return b.z - a.z; @@ -20108,21 +28656,693 @@ THREE.SpritePlugin = function ( renderer, sprites ) { } - }; + } }; -// File:src/extras/GeometryUtils.js +// File:src/Three.Legacy.js /** * @author mrdoob / http://mrdoob.com/ */ +Object.defineProperties( THREE.Box2.prototype, { + empty: { + value: function () { + console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); + } + }, + isIntersectionBox: { + value: function ( box ) { + console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); + } + } +} ); + +Object.defineProperties( THREE.Box3.prototype, { + empty: { + value: function () { + console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); + } + }, + isIntersectionBox: { + value: function ( box ) { + console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); + } + }, + isIntersectionSphere: { + value: function ( sphere ) { + console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); + } + } +} ); + +Object.defineProperties( THREE.Matrix3.prototype, { + multiplyVector3: { + value: function ( vector ) { + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); + } + }, + multiplyVector3Array: { + value: function ( a ) { + console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + } + } +} ); + +Object.defineProperties( THREE.Matrix4.prototype, { + extractPosition: { + value: function ( m ) { + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); + } + }, + setRotationFromQuaternion: { + value: function ( q ) { + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + return this.makeRotationFromQuaternion( q ); + } + }, + multiplyVector3: { + value: function ( vector ) { + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); + return vector.applyProjection( this ); + } + }, + multiplyVector4: { + value: function ( vector ) { + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + } + }, + multiplyVector3Array: { + value: function ( a ) { + console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + } + }, + rotateAxis: { + value: function ( v ) { + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + v.transformDirection( this ); + } + }, + crossVector: { + value: function ( vector ) { + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + } + }, + translate: { + value: function ( v ) { + console.error( 'THREE.Matrix4: .translate() has been removed.' ); + } + }, + rotateX: { + value: function ( angle ) { + console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + } + }, + rotateY: { + value: function ( angle ) { + console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + } + }, + rotateZ: { + value: function ( angle ) { + console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + } + }, + rotateByAxis: { + value: function ( axis, angle ) { + console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + } + } +} ); + +Object.defineProperties( THREE.Plane.prototype, { + isIntersectionLine: { + value: function ( line ) { + console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); + return this.intersectsLine( line ); + } + } +} ); + +Object.defineProperties( THREE.Quaternion.prototype, { + multiplyVector3: { + value: function ( vector ) { + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + } + } +} ); + +Object.defineProperties( THREE.Ray.prototype, { + isIntersectionBox: { + value: function ( box ) { + console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); + } + }, + isIntersectionPlane: { + value: function ( plane ) { + console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); + return this.intersectsPlane( plane ); + } + }, + isIntersectionSphere: { + value: function ( sphere ) { + console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); + } + } +} ); + +Object.defineProperties( THREE.Vector3.prototype, { + setEulerFromRotationMatrix: { + value: function () { + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + } + }, + setEulerFromQuaternion: { + value: function () { + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + } + }, + getPositionFromMatrix: { + value: function ( m ) { + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + return this.setFromMatrixPosition( m ); + } + }, + getScaleFromMatrix: { + value: function ( m ) { + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + return this.setFromMatrixScale( m ); + } + }, + getColumnFromMatrix: { + value: function ( index, matrix ) { + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + return this.setFromMatrixColumn( index, matrix ); + } + } +} ); + +// + +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); + return new THREE.Face3( a, b, c, normal, color, materialIndex ); + +}; + +// + +Object.defineProperties( THREE.Object3D.prototype, { + eulerOrder: { + get: function () { + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + return this.rotation.order; + }, + set: function ( value ) { + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + this.rotation.order = value; + } + }, + getChildByName: { + value: function ( name ) { + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name ); + } + }, + renderDepth: { + set: function ( value ) { + console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + } + }, + translate: { + value: function ( distance, axis ) { + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); + } + }, + useQuaternion: { + get: function () { + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + }, + set: function ( value ) { + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + } + } +} ); + +// + +Object.defineProperties( THREE, { + PointCloud: { + value: function ( geometry, material ) { + console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + } + }, + ParticleSystem: { + value: function ( geometry, material ) { + console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + } + } +} ); + +// + +Object.defineProperties( THREE.Light.prototype, { + onlyShadow: { + set: function ( value ) { + console.warn( 'THREE.Light: .onlyShadow has been removed.' ); + } + }, + shadowCameraFov: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); + this.shadow.camera.fov = value; + } + }, + shadowCameraLeft: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); + this.shadow.camera.left = value; + } + }, + shadowCameraRight: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); + this.shadow.camera.right = value; + } + }, + shadowCameraTop: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); + this.shadow.camera.top = value; + } + }, + shadowCameraBottom: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); + this.shadow.camera.bottom = value; + } + }, + shadowCameraNear: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); + this.shadow.camera.near = value; + } + }, + shadowCameraFar: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); + this.shadow.camera.far = value; + } + }, + shadowCameraVisible: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); + } + }, + shadowBias: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); + this.shadow.bias = value; + } + }, + shadowDarkness: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); + } + }, + shadowMapWidth: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); + this.shadow.mapSize.width = value; + } + }, + shadowMapHeight: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); + this.shadow.mapSize.height = value; + } + } +} ); + +// + +Object.defineProperties( THREE.BufferAttribute.prototype, { + length: { + get: function () { + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); + return this.array.length; + } + } +} ); + +Object.defineProperties( THREE.BufferGeometry.prototype, { + drawcalls: { + get: function () { + console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); + return this.groups; + } + }, + offsets: { + get: function () { + console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); + return this.groups; + } + }, + addIndex: { + value: function ( index ) { + console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); + this.setIndex( index ); + } + }, + addDrawCall: { + value: function ( start, count, indexOffset ) { + if ( indexOffset !== undefined ) { + console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + } + console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); + this.addGroup( start, count ); + } + }, + clearDrawCalls: { + value: function () { + console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); + this.clearGroups(); + } + }, + computeTangents: { + value: function () { + console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); + } + }, + computeOffsets: { + value: function () { + console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); + } + } +} ); + +// + +Object.defineProperties( THREE.Material.prototype, { + wrapAround: { + get: function () { + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + }, + set: function ( value ) { + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + } + }, + wrapRGB: { + get: function () { + console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); + return new THREE.Color(); + } + } +} ); + +Object.defineProperties( THREE, { + PointCloudMaterial: { + value: function ( parameters ) { + console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + } + }, + ParticleBasicMaterial: { + value: function ( parameters ) { + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + } + }, + ParticleSystemMaterial:{ + value: function ( parameters ) { + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + } + } +} ); + +Object.defineProperties( THREE.MeshPhongMaterial.prototype, { + metal: { + get: function () { + console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); + return false; + }, + set: function ( value ) { + console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); + } + } +} ); + +Object.defineProperties( THREE.ShaderMaterial.prototype, { + derivatives: { + get: function () { + console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + return this.extensions.derivatives; + }, + set: function ( value ) { + console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + this.extensions.derivatives = value; + } + } +} ); + +// + +// WebGLRenderer is not included in Qt builds, so remove legacy support for it, too +//Object.defineProperties( THREE.WebGLRenderer.prototype, { +// supportsFloatTextures: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); +// return this.extensions.get( 'OES_texture_float' ); +// } +// }, +// supportsHalfFloatTextures: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); +// return this.extensions.get( 'OES_texture_half_float' ); +// } +// }, +// supportsStandardDerivatives: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); +// return this.extensions.get( 'OES_standard_derivatives' ); +// } +// }, +// supportsCompressedTextureS3TC: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); +// return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); +// } +// }, +// supportsCompressedTexturePVRTC: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); +// return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); +// } +// }, +// supportsBlendMinMax: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); +// return this.extensions.get( 'EXT_blend_minmax' ); +// } +// }, +// supportsVertexTextures: { +// value: function () { +// return this.capabilities.vertexTextures; +// } +// }, +// supportsInstancedArrays: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); +// return this.extensions.get( 'ANGLE_instanced_arrays' ); +// } +// }, +// enableScissorTest: { +// value: function ( boolean ) { +// console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); +// this.setScissorTest( boolean ); +// } +// }, +// initMaterial: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); +// } +// }, +// addPrePlugin: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); +// } +// }, +// addPostPlugin: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); +// } +// }, +// updateShadowMap: { +// value: function () { +// console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); +// } +// }, +// shadowMapEnabled: { +// get: function () { +// return this.shadowMap.enabled; +// }, +// set: function ( value ) { +// console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); +// this.shadowMap.enabled = value; +// } +// }, +// shadowMapType: { +// get: function () { +// return this.shadowMap.type; +// }, +// set: function ( value ) { +// console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); +// this.shadowMap.type = value; +// } +// }, +// shadowMapCullFace: { +// get: function () { +// return this.shadowMap.cullFace; +// }, +// set: function ( value ) { +// console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); +// this.shadowMap.cullFace = value; +// } +// } +//} ); + +// + +Object.defineProperties( THREE.WebGLRenderTarget.prototype, { + wrapS: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + return this.texture.wrapS; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + this.texture.wrapS = value; + } + }, + wrapT: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + return this.texture.wrapT; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + this.texture.wrapT = value; + } + }, + magFilter: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + return this.texture.magFilter; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + this.texture.magFilter = value; + } + }, + minFilter: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + return this.texture.minFilter; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + this.texture.minFilter = value; + } + }, + anisotropy: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + return this.texture.anisotropy; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + this.texture.anisotropy = value; + } + }, + offset: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + return this.texture.offset; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + this.texture.offset = value; + } + }, + repeat: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + return this.texture.repeat; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + this.texture.repeat = value; + } + }, + format: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + return this.texture.format; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + this.texture.format = value; + } + }, + type: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + return this.texture.type; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + this.texture.type = value; + } + }, + generateMipmaps: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + return this.texture.generateMipmaps; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + this.texture.generateMipmaps = value; + } + } +} ); + +// + THREE.GeometryUtils = { merge: function ( geometry1, geometry2, materialIndexOffset ) { - THREE.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); var matrix; @@ -20141,91 +29361,42 @@ THREE.GeometryUtils = { center: function ( geometry ) { - THREE.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); return geometry.center(); } }; -// File:src/extras/ImageUtils.js - -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author Daosheng Mu / https://github.com/DaoshengMu/ - */ - THREE.ImageUtils = { crossOrigin: undefined, loadTexture: function ( url, mapping, onLoad, onError ) { - var loader = new THREE.ImageLoader(); - loader.crossOrigin = this.crossOrigin; + console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - var texture = new THREE.Texture( undefined, mapping ); + var loader = new THREE.TextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( image ) { + var texture = loader.load( url, onLoad, undefined, onError ); - texture.image = image; - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - }, undefined, function ( event ) { - - if ( onError ) onError( event ); - - } ); - - texture.sourceFile = url; + if ( mapping ) texture.mapping = mapping; return texture; }, - loadTextureCube: function ( array, mapping, onLoad, onError ) { + loadTextureCube: function ( urls, mapping, onLoad, onError ) { - var images = []; + console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - var loader = new THREE.ImageLoader(); - loader.crossOrigin = this.crossOrigin; + var loader = new THREE.CubeTextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - var texture = new THREE.CubeTexture( images, mapping ); + var texture = loader.load( urls, onLoad, undefined, onError ); - // no flipping needed for cube textures - - texture.flipY = false; - - var loaded = 0; - - var loadTexture = function ( i ) { - - loader.load( array[ i ], function ( image ) { - - texture.images[ i ] = image; - - loaded += 1; - - if ( loaded === 6 ) { - - texture.needsUpdate = true; - - if ( onLoad ) onLoad( texture ); - - } - - }, undefined, onError ); - - } - - for ( var i = 0, il = array.length; i < il; ++ i ) { - - loadTexture( i ); - - } + if ( mapping ) texture.mapping = mapping; return texture; @@ -20233,142 +29404,111 @@ THREE.ImageUtils = { loadCompressedTexture: function () { - THREE.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); }, loadCompressedTextureCube: function () { - THREE.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); + + } + +}; + +// + +THREE.Projector = function () { + + console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); + + this.projectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); + + }; + + this.unprojectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); + + }; + + this.pickingRay = function ( vector, camera ) { + + console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); + + }; + +}; + +// + +THREE.CanvasRenderer = function () { + + console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); + + this.domElement = document.createElement( 'canvas' ); + this.clear = function () {}; + this.render = function () {}; + this.setClearColor = function () {}; + this.setSize = function () {}; + +}; + +// + +THREE.MeshFaceMaterial = THREE.MultiMaterial; + +// File:src/extras/CurveUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.CurveUtils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); }, - getNormalMap: function ( image, depth ) { + // Puay Bing, thanks for helping with this derivative! - // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { - var cross = function ( a, b ) { - - return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; - - } - - var subtract = function ( a, b ) { - - return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; - - } - - var normalize = function ( a ) { - - var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); - return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; - - } - - depth = depth | 1; - - var width = image.width; - var height = image.height; - - // TODO: Make this work in Qt Quick - - var canvas = document.createElement( 'canvas' ); - canvas.width = width; - canvas.height = height; - - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0 ); - - var data = context.getImageData( 0, 0, width, height ).data; - var imageData = context.createImageData( width, height ); - var output = imageData.data; - - for ( var x = 0; x < width; x ++ ) { - - for ( var y = 0; y < height; y ++ ) { - - var ly = y - 1 < 0 ? 0 : y - 1; - var uy = y + 1 > height - 1 ? height - 1 : y + 1; - var lx = x - 1 < 0 ? 0 : x - 1; - var ux = x + 1 > width - 1 ? width - 1 : x + 1; - - var points = []; - var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; - points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); - points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); - points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); - points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); - points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); - - var normals = []; - var num_points = points.length; - - for ( var i = 0; i < num_points; i ++ ) { - - var v1 = points[ i ]; - var v2 = points[ ( i + 1 ) % num_points ]; - v1 = subtract( v1, origin ); - v2 = subtract( v2, origin ); - normals.push( normalize( cross( v1, v2 ) ) ); - - } - - var normal = [ 0, 0, 0 ]; - - for ( var i = 0; i < normals.length; i ++ ) { - - normal[ 0 ] += normals[ i ][ 0 ]; - normal[ 1 ] += normals[ i ][ 1 ]; - normal[ 2 ] += normals[ i ][ 2 ]; - - } - - normal[ 0 ] /= normals.length; - normal[ 1 ] /= normals.length; - normal[ 2 ] /= normals.length; - - var idx = ( y * width + x ) * 4; - - output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; - output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; - output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; - output[ idx + 3 ] = 255; - - } - - } - - context.putImageData( imageData, 0, 0 ); - - return canvas; + return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + + 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + + 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + + 3 * t * t * p3; }, - generateDataTexture: function ( width, height, color ) { + tangentSpline: function ( t, p0, p1, p2, p3 ) { - var size = width * height; - var data = new Uint8Array( 3 * size ); + // To check if my formulas are correct - var r = Math.floor( color.r * 255 ); - var g = Math.floor( color.g * 255 ); - var b = Math.floor( color.b * 255 ); + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 - for ( var i = 0; i < size; i ++ ) { + return h00 + h10 + h01 + h11; - data[ i * 3 ] = r; - data[ i * 3 + 1 ] = g; - data[ i * 3 + 2 ] = b; + }, - } + // Catmull-Rom - var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); - texture.needsUpdate = true; + interpolate: function( p0, p1, p2, p3, t ) { - return texture; + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } @@ -20384,7 +29524,7 @@ THREE.SceneUtils = { createMultiMaterialObject: function ( geometry, materials ) { - var group = new THREE.Object3D(); + var group = new THREE.Group(); for ( var i = 0, l = materials.length; i < l; i ++ ) { @@ -20417,399 +29557,17 @@ THREE.SceneUtils = { }; -// File:src/extras/FontUtils.js +// File:src/extras/ShapeUtils.js /** * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * For Text operations in three.js (See TextGeometry) - * - * It uses techniques used in: - * - * typeface.js and canvastext - * For converting fonts and rendering with javascript - * http://typeface.neocracy.org - * - * Triangulation ported from AS3 - * Simple Polygon Triangulation - * http://actionsnippet.com/?p=1462 - * - * A Method to triangulate shapes with holes - * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ - * */ -THREE.FontUtils = { - - faces: {}, - - // Just for now. face[weight][style] - - face: 'helvetiker', - weight: 'normal', - style: 'normal', - size: 150, - divisions: 10, - - getFace: function () { - - try { - - return this.faces[ this.face ][ this.weight ][ this.style ]; - - } catch (e) { - - throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." - - }; - - }, - - loadFace: function ( data ) { - - var family = data.familyName.toLowerCase(); - - var ThreeFont = this; - - ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; - - ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; - ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; - - ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; - - return data; - - }, - - drawText: function ( text ) { - - // RenderText - - var i, - face = this.getFace(), - scale = this.size / face.resolution, - offset = 0, - chars = String( text ).split( '' ), - length = chars.length; - - var fontPaths = []; - - for ( i = 0; i < length; i ++ ) { - - var path = new THREE.Path(); - - var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); - offset += ret.offset; - - fontPaths.push( ret.path ); - - } - - // get the width - - var width = offset / 2; - // - // for ( p = 0; p < allPts.length; p++ ) { - // - // allPts[ p ].x -= width; - // - // } - - //var extract = this.extractPoints( allPts, characterPts ); - //extract.contour = allPts; - - //extract.paths = fontPaths; - //extract.offset = width; - - return { paths: fontPaths, offset: width }; - - }, - - - - - extractGlyphPoints: function ( c, face, scale, offset, path ) { - - var pts = []; - - var i, i2, divisions, - outline, action, length, - scaleX, scaleY, - x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, - laste, - glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; - - if ( ! glyph ) return; - - if ( glyph.o ) { - - outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - length = outline.length; - - scaleX = scale; - scaleY = scale; - - for ( i = 0; i < length; ) { - - action = outline[ i ++ ]; - - //console.log( action ); - - switch ( action ) { - - case 'm': - - // Move To - - x = outline[ i ++ ] * scaleX + offset; - y = outline[ i ++ ] * scaleY; - - path.moveTo( x, y ); - break; - - case 'l': - - // Line To - - x = outline[ i ++ ] * scaleX + offset; - y = outline[ i ++ ] * scaleY; - path.lineTo( x, y ); - break; - - case 'q': - - // QuadraticCurveTo - - cpx = outline[ i ++ ] * scaleX + offset; - cpy = outline[ i ++ ] * scaleY; - cpx1 = outline[ i ++ ] * scaleX + offset; - cpy1 = outline[ i ++ ] * scaleY; - - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - - laste = pts[ pts.length - 1 ]; - - if ( laste ) { - - cpx0 = laste.x; - cpy0 = laste.y; - - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { - - var t = i2 / divisions; - THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); - } - - } - - break; - - case 'b': - - // Cubic Bezier Curve - - cpx = outline[ i ++ ] * scaleX + offset; - cpy = outline[ i ++ ] * scaleY; - cpx1 = outline[ i ++ ] * scaleX + offset; - cpy1 = outline[ i ++ ] * scaleY; - cpx2 = outline[ i ++ ] * scaleX + offset; - cpy2 = outline[ i ++ ] * scaleY; - - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - - laste = pts[ pts.length - 1 ]; - - if ( laste ) { - - cpx0 = laste.x; - cpy0 = laste.y; - - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { - - var t = i2 / divisions; - THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); - - } - - } - - break; - - } - - } - } - - - - return { offset: glyph.ha * scale, path:path }; - } - -}; - - -THREE.FontUtils.generateShapes = function ( text, parameters ) { - - // Parameters - - parameters = parameters || {}; - - var size = parameters.size !== undefined ? parameters.size : 100; - var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; - - var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; - var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; - var style = parameters.style !== undefined ? parameters.style : 'normal'; - - THREE.FontUtils.size = size; - THREE.FontUtils.divisions = curveSegments; - - THREE.FontUtils.face = font; - THREE.FontUtils.weight = weight; - THREE.FontUtils.style = style; - - // Get a Font data json object - - var data = THREE.FontUtils.drawText( text ); - - var paths = data.paths; - var shapes = []; - - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { - - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); - - } - - return shapes; - -}; - - -/** - * This code is a quick port of code written in C++ which was submitted to - * flipcode.com by John W. Ratcliff // July 22, 2000 - * See original code and more information here: - * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml - * - * ported to actionscript by Zevan Rosser - * www.actionsnippet.com - * - * ported to javascript by Joshua Koo - * http://www.lab4games.net/zz85/blog - * - */ - - -//( function ( namespace ) { - - var EPSILON = 0.0000000001; - - // takes in an contour array and returns - -THREE.FontUtils.process = function ( contour, indices ) { - - var n = contour.length; - - if ( n < 3 ) return null; - - var result = [], - verts = [], - vertIndices = []; - - /* we want a counter-clockwise polygon in verts */ - - var u, v, w; - - if ( THREE.FontUtils.area( contour ) > 0.0 ) { - - for ( v = 0; v < n; v ++ ) verts[ v ] = v; - - } else { - - for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; - - } - - var nv = n; - - /* remove nv - 2 vertices, creating 1 triangle every time */ - - var count = 2 * nv; /* error detection */ - - for ( v = nv - 1; nv > 2; ) { - - /* if we loop, it is probably a non-simple polygon */ - - if ( ( count -- ) <= 0 ) { - - //** Triangulate: ERROR - probable bad polygon! - - //throw ( "Warning, unable to triangulate polygon!" ); - //return null; - // Sometimes warning is fine, especially polygons are triangulated in reverse. - THREE.warn( 'THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()' ); - - if ( indices ) return vertIndices; - return result; - - } - - /* three consecutive vertices in current polygon, */ - - u = v; if ( nv <= u ) u = 0; /* previous */ - v = u + 1; if ( nv <= v ) v = 0; /* new v */ - w = v + 1; if ( nv <= w ) w = 0; /* next */ - - if ( THREE.FontUtils.snip( contour, u, v, w, nv, verts ) ) { - - var a, b, c, s, t; - - /* true names of the vertices */ - - a = verts[ u ]; - b = verts[ v ]; - c = verts[ w ]; - - /* output Triangle */ - - result.push( [ contour[ a ], - contour[ b ], - contour[ c ] ] ); - - - vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); - - /* remove v from the remaining polygon */ - - for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { - - verts[ s ] = verts[ t ]; - - } - - nv --; - - /* reset error detection counter */ - - count = 2 * nv; - - } - - } - - if ( indices ) return vertIndices; - return result; - - }; +THREE.ShapeUtils = { // calculate area of the contour polygon -THREE.FontUtils.area = function ( contour ) { + area: function ( contour ) { var n = contour.length; var a = 0.0; @@ -20822,263 +29580,736 @@ THREE.FontUtils.area = function ( contour ) { return a * 0.5; - }; + }, -THREE.FontUtils.snip = function ( contour, u, v, w, n, verts ) { + triangulate: ( function () { - var p; - var ax, ay, bx, by; - var cx, cy, px, py; + /** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ - ax = contour[ verts[ u ] ].x; - ay = contour[ verts[ u ] ].y; + function snip( contour, u, v, w, n, verts ) { - bx = contour[ verts[ v ] ].x; - by = contour[ verts[ v ] ].y; + var p; + var ax, ay, bx, by; + var cx, cy, px, py; - cx = contour[ verts[ w ] ].x; - cy = contour[ verts[ w ] ].y; + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; - if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; - var aX, aY, bX, bY, cX, cY; - var apx, apy, bpx, bpy, cpx, cpy; - var cCROSSap, bCROSScp, aCROSSbp; + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; - aX = cx - bx; aY = cy - by; - bX = ax - cx; bY = ay - cy; - cX = bx - ax; cY = by - ay; + if ( Number.EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; - for ( p = 0; p < n; p ++ ) { + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; - px = contour[ verts[ p ] ].x - py = contour[ verts[ p ] ].y + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; - if ( ( ( px === ax ) && ( py === ay ) ) || - ( ( px === bx ) && ( py === by ) ) || - ( ( px === cx ) && ( py === cy ) ) ) continue; + for ( p = 0; p < n; p ++ ) { - apx = px - ax; apy = py - ay; - bpx = px - bx; bpy = py - by; - cpx = px - cx; cpy = py - cy; + px = contour[ verts[ p ] ].x; + py = contour[ verts[ p ] ].y; - // see if p is inside triangle abc + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; - aCROSSbp = aX * bpy - aY * bpx; - cCROSSap = cX * apy - cY * apx; - bCROSScp = bX * cpy - bY * cpx; + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; - if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false; + // see if p is inside triangle abc + + aCROSSbp = aX * bpy - aY * bpx; + cCROSSap = cX * apy - cY * apx; + bCROSScp = bX * cpy - bY * cpx; + + if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false; + + } + + return true; } - return true; + // takes in an contour array and returns - }; + return function ( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( THREE.ShapeUtils.area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v ++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for ( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count -- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); -THREE.FontUtils.Triangulate = THREE.FontUtils.process; -THREE.FontUtils.Triangulate.area = THREE.FontUtils.area; + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); -//return namespace; + /* remove v from the remaining polygon */ -//} )( THREE.FontUtils ); + for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { -// To use the typeface.js face files, hook up the API -var _typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; -THREE.typeface_js = _typeface_js; + verts[ s ] = verts[ t ]; -// File:src/extras/audio/Audio.js + } -/** - * @author mrdoob / http://mrdoob.com/ - */ + nv --; -THREE.Audio = function ( listener ) { + /* reset error detection counter */ - THREE.Object3D.call( this ); + count = 2 * nv; - this.type = 'Audio'; + } - this.context = listener.context; - this.source = this.context.createBufferSource(); - this.source.onended = this.onEnded.bind(this); + } - this.gain = this.context.createGain(); - this.gain.connect( this.context.destination ); + if ( indices ) return vertIndices; + return result; - this.panner = this.context.createPanner(); - this.panner.connect( this.gain ); + } - this.autoplay = false; + } )(), - this.startTime = 0; - this.isPlaying = false; + triangulateShape: function ( contour, holes ) { + + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + + // inOtherPt needs to be collinear to the inSegment + if ( inSegPt1.x !== inSegPt2.x ) { + + if ( inSegPt1.x < inSegPt2.x ) { + + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + + } else { + + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + + } + + } else { + + if ( inSegPt1.y < inSegPt2.y ) { + + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + + } else { + + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + + } + + } + + } + + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + + if ( Math.abs( limit ) > Number.EPSILON ) { + + // not parallel + + var perpSeg2; + if ( limit > 0 ) { + + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + + } else { + + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + + } + + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 === 0 ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt1 ]; + + } + if ( perpSeg2 === limit ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt2 ]; + + } + // intersection at endpoint of segment#2? + if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; + + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; + + } else { + + // parallel or collinear + if ( ( perpSeg1 !== 0 ) || + ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; + + // they are collinear or degenerate + var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point? + var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point? + // both segments are points + if ( seg1Pt && seg2Pt ) { + + if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || + ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point + + } + // segment#1 is a single point + if ( seg1Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; + + } + // segment#2 is a single point + if ( seg2Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; + + } + + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if ( seg1dx !== 0 ) { + + // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + + } + + } else { + + // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + + } + + } + if ( seg1minVal <= seg2minVal ) { + + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal === seg2minVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; + + } else { + + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal === seg2maxVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; + + } + + } + + } + + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + + // The order of legs is important + + // translation of all points, so that Vertex is at (0,0) + var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; + var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; + var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; + + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; + + if ( Math.abs( from2toAngle ) > Number.EPSILON ) { + + // angle != 180 deg. + + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + + if ( from2toAngle > 0 ) { + + // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + + } else { + + // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + + } + + } else { + + // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); + + } + + } + + + function removeHoles( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var hole; + + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; + + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + + var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; + + } + + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; + + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + + insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; + + } + + return true; + + } + + function intersectsShapeEdge( inShapePt, inHolePt ) { + + // checks for intersections with shape edges + var sIdx, nextIdx, intersection; + for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + + nextIdx = sIdx + 1; nextIdx %= shape.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; + + } + + return false; + + } + + var indepHoles = []; + + function intersectsHoleEdge( inShapePt, inHolePt ) { + + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + + chkHole = holes[ indepHoles[ ihIdx ]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + + nextIdx = hIdx + 1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; + + } + + } + return false; + + } + + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + indepHoles.push( h ); + + } + + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { + + counter --; + if ( counter < 0 ) { + + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; + + } + + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; + + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { + + holeIdx = indepHoles[ h ]; + + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[ cutKey ] !== undefined ) continue; + + hole = holes[ holeIdx ]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + + holePt = hole[ h2 ]; + if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + + holeIndex = h2; + indepHoles.splice( h, 1 ); + + tmpShape1 = shape.slice( 0, shapeIndex + 1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex + 1 ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + minShapeIndex = shapeIndex; + + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); + + break; + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + failedCuts[ cutKey ] = true; // remember failure + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + } + + } + + return shape; /* shape with no holes */ + + } + + + var i, il, f, face, + key, index, + allPointsMap = {}; + + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + + var allpoints = contour.concat(); + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + Array.prototype.push.apply( allpoints, holes[ h ] ); + + } + + //console.log( "allpoints",allpoints, allpoints.length ); + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.warn( "THREE.Shape: Duplicate point", key ); + + } + + allPointsMap[ key ] = i; + + } + + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); + + var triangles = THREE.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat(); + + }, + + isClockWise: function ( pts ) { + + return THREE.ShapeUtils.area( pts ) < 0; + + }, + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2: ( function () { + + function b2p0( t, p ) { + + var k = 1 - t; + return k * k * p; + + } + + function b2p1( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + } + + function b2p2( t, p ) { + + return t * t * p; + + } + + return function ( t, p0, p1, p2 ) { + + return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); + + }; + + } )(), + + // Cubic Bezier Functions + + b3: ( function () { + + function b3p0( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + } + + function b3p1( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + } + + function b3p2( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + } + + function b3p3( t, p ) { + + return t * t * t * p; + + } + + return function ( t, p0, p1, p2, p3 ) { + + return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); + + }; + + } )() }; -THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Audio.prototype.constructor = THREE.Audio; - -THREE.Audio.prototype.load = function ( file ) { - - var scope = this; - - var request = new XMLHttpRequest(); - request.open( 'GET', file, true ); - request.responseType = 'arraybuffer'; - request.onload = function ( e ) { - - scope.context.decodeAudioData( this.response, function ( buffer ) { - - scope.source.buffer = buffer; - - if( scope.autoplay ) scope.play(); - - } ); - - }; - request.send(); - - return this; - -}; - -THREE.Audio.prototype.play = function () { - - if ( this.isPlaying === true ) { - - THREE.warn( 'THREE.Audio: Audio is already playing.' ); - return; - - } - - var source = this.context.createBufferSource(); - - source.buffer = this.source.buffer; - source.loop = this.source.loop; - source.onended = this.source.onended; - source.connect( this.panner ); - source.start( 0, this.startTime ); - - this.isPlaying = true; - - this.source = source; - -}; - -THREE.Audio.prototype.pause = function () { - - this.source.stop(); - this.startTime = this.context.currentTime; - -}; - -THREE.Audio.prototype.stop = function () { - - this.source.stop(); - this.startTime = 0; - -}; - -THREE.Audio.prototype.onEnded = function() { - - this.isPlaying = false; - -}; - -THREE.Audio.prototype.setLoop = function ( value ) { - - this.source.loop = value; - -}; - -THREE.Audio.prototype.setRefDistance = function ( value ) { - - this.panner.refDistance = value; - -}; - -THREE.Audio.prototype.setRolloffFactor = function ( value ) { - - this.panner.rolloffFactor = value; - -}; - -THREE.Audio.prototype.setVolume = function ( value ) { - - this.gain.gain.value = value; - -}; - -THREE.Audio.prototype.updateMatrixWorld = ( function () { - - var position = new THREE.Vector3(); - - return function ( force ) { - - THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - - position.setFromMatrixPosition( this.matrixWorld ); - - this.panner.setPosition( position.x, position.y, position.z ); - - }; - -} )(); - -// File:src/extras/audio/AudioListener.js - -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.AudioListener = function () { - - THREE.Object3D.call( this ); - - this.type = 'AudioListener'; - - this.context = new ( window.AudioContext || window.webkitAudioContext )(); - -}; - -THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); -THREE.AudioListener.prototype.constructor = THREE.AudioListener; - -THREE.AudioListener.prototype.updateMatrixWorld = ( function () { - - var position = new THREE.Vector3(); - var quaternion = new THREE.Quaternion(); - var scale = new THREE.Vector3(); - - var orientation = new THREE.Vector3(); - var velocity = new THREE.Vector3(); - - var positionPrev = new THREE.Vector3(); - - return function ( force ) { - - THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - - var listener = this.context.listener; - var up = this.up; - - this.matrixWorld.decompose( position, quaternion, scale ); - - orientation.set( 0, 0, -1 ).applyQuaternion( quaternion ); - velocity.subVectors( position, positionPrev ); - - listener.setPosition( position.x, position.y, position.z ); - listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); - listener.setVelocity( velocity.x, velocity.y, velocity.z ); - - positionPrev.copy( position ); - - }; - -} )(); - // File:src/extras/core/Curve.js /** @@ -21121,283 +30352,234 @@ THREE.Curve = function () { }; -// Virtual base class method to overwrite and implement in subclasses -// - t [0 .. 1] +THREE.Curve.prototype = { -THREE.Curve.prototype.getPoint = function ( t ) { + constructor: THREE.Curve, - THREE.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); - return null; + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] -}; + getPoint: function ( t ) { -// Get point at relative position in curve according to arc length -// - u [0 .. 1] + console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); + return null; -THREE.Curve.prototype.getPointAt = function ( u ) { + }, - var t = this.getUtoTmapping( u ); - return this.getPoint( t ); + // Get point at relative position in curve according to arc length + // - u [0 .. 1] -}; + getPointAt: function ( u ) { -// Get sequence of points using getPoint( t ) + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); -THREE.Curve.prototype.getPoints = function ( divisions ) { + }, - if ( ! divisions ) divisions = 5; + // Get sequence of points using getPoint( t ) - var d, pts = []; + getPoints: function ( divisions ) { - for ( d = 0; d <= divisions; d ++ ) { + if ( ! divisions ) divisions = 5; - pts.push( this.getPoint( d / divisions ) ); + var d, pts = []; - } + for ( d = 0; d <= divisions; d ++ ) { - return pts; - -}; - -// Get sequence of points using getPointAt( u ) - -THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { - - if ( ! divisions ) divisions = 5; - - var d, pts = []; - - for ( d = 0; d <= divisions; d ++ ) { - - pts.push( this.getPointAt( d / divisions ) ); - - } - - return pts; - -}; - -// Get total curve arc length - -THREE.Curve.prototype.getLength = function () { - - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; - -}; - -// Get list of cumulative segment lengths - -THREE.Curve.prototype.getLengths = function ( divisions ) { - - if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions) : 200; - - if ( this.cacheArcLengths - && ( this.cacheArcLengths.length == divisions + 1 ) - && ! this.needsUpdate) { - - //console.log( "cached", this.cacheArcLengths ); - return this.cacheArcLengths; - - } - - this.needsUpdate = false; - - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; - - cache.push( 0 ); - - for ( p = 1; p <= divisions; p ++ ) { - - current = this.getPoint ( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; - - } - - this.cacheArcLengths = cache; - - return cache; // { sums: cache, sum:sum }; Sum is in the last element. - -}; - - -THREE.Curve.prototype.updateArcLengths = function() { - this.needsUpdate = true; - this.getLengths(); -}; - -// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance - -THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { - - var arcLengths = this.getLengths(); - - var i = 0, il = arcLengths.length; - - var targetArcLength; // The targeted u distance value to get - - if ( distance ) { - - targetArcLength = distance; - - } else { - - targetArcLength = u * arcLengths[ il - 1 ]; - - } - - //var time = Date.now(); - - // binary search for the index with largest value smaller than target u distance - - var low = 0, high = il - 1, comparison; - - while ( low <= high ) { - - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - - comparison = arcLengths[ i ] - targetArcLength; - - if ( comparison < 0 ) { - - low = i + 1; - - } else if ( comparison > 0 ) { - - high = i - 1; - - } else { - - high = i; - break; - - // DONE + pts.push( this.getPoint( d / divisions ) ); } - } + return pts; - i = high; + }, - //console.log('b' , i, low, high, Date.now()- time); + // Get sequence of points using getPointAt( u ) - if ( arcLengths[ i ] == targetArcLength ) { + getSpacedPoints: function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); + + } + + return pts; + + }, + + // Get total curve arc length + + getLength: function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + + }, + + // Get list of cumulative segment lengths + + getLengths: function ( divisions ) { + + if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length === divisions + 1 ) + && ! this.needsUpdate ) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + + }, + + updateArcLengths: function() { + + this.needsUpdate = true; + this.getLengths(); + + }, + + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + + getUtoTmapping: function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + + } else if ( comparison > 0 ) { + + high = i - 1; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] === targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il - 1 ); - var t = i / ( il - 1 ); return t; - } + }, - // we could get finer grain at lengths, or use simple interpolatation between two points + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; + getTangent: function( t ) { - var segmentLength = lengthAfter - lengthBefore; + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; - // determine where we are between the 'before' and 'after' points + // Capping in case of danger - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - // add that fractional amount to t + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); - var t = ( i + segmentFraction ) / ( il - 1 ); - - return t; - -}; - -// Returns a unit vector tangent at t -// In case any sub curve does not implement its tangent derivation, -// 2 points a small delta apart will be used to find its gradient -// which seems to give a reasonable approximation - -THREE.Curve.prototype.getTangent = function( t ) { - - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; - - // Capping in case of danger - - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; - - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); - - var vec = pt2.clone().sub(pt1); - return vec.normalize(); - -}; - - -THREE.Curve.prototype.getTangentAt = function ( u ) { - - var t = this.getUtoTmapping( u ); - return this.getTangent( t ); - -}; - - - - - -/************************************************************** - * Utils - **************************************************************/ - -THREE.Curve.Utils = { - - tangentQuadraticBezier: function ( t, p0, p1, p2 ) { - - return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + var vec = pt2.clone().sub( pt1 ); + return vec.normalize(); }, - // Puay Bing, thanks for helping with this derivative! + getTangentAt: function ( u ) { - tangentCubicBezier: function (t, p0, p1, p2, p3 ) { - - return - 3 * p0 * (1 - t) * (1 - t) + - 3 * p1 * (1 - t) * (1 - t) - 6 * t * p1 * (1 - t) + - 6 * t * p2 * (1 - t) - 3 * t * t * p2 + - 3 * t * t * p3; - - }, - - tangentSpline: function ( t, p0, p1, p2, p3 ) { - - // To check if my formulas are correct - - var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 - var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t - var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 - var h11 = 3 * t * t - 2 * t; // t3 − t2 - - return h00 + h10 + h01 + h11; - - }, - - // Catmull-Rom - - interpolate: function( p0, p1, p2, p3, t ) { - - var v0 = ( p2 - p0 ) * 0.5; - var v1 = ( p3 - p1 ) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); } }; - // TODO: Transformation for Curves? /************************************************************** @@ -21431,9 +30613,9 @@ THREE.Curve.create = function ( constructor, getPointFunc ) { THREE.CurvePath = function () { this.curves = []; - this.bends = []; - + this.autoClose = false; // Automatically closes the path + }; THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); @@ -21445,23 +30627,28 @@ THREE.CurvePath.prototype.add = function ( curve ) { }; +/* THREE.CurvePath.prototype.checkConnection = function() { // TODO // If the ending of curve is not connected to the starting // or the next curve, then, this is not a real path }; +*/ THREE.CurvePath.prototype.closePath = function() { + // TODO Test // and verify for vector3 (needs to implement equals) // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[0].getPoint(0); - var endPoint = this.curves[this.curves.length - 1].getPoint(1); - - if (! startPoint.equals(endPoint)) { - this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); + var startPoint = this.curves[ 0 ].getPoint( 0 ); + var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + + if ( ! startPoint.equals( endPoint ) ) { + + this.curves.push( new THREE.LineCurve( endPoint, startPoint ) ); + } - + }; // To get accurate point with reference to @@ -21477,7 +30664,7 @@ THREE.CurvePath.prototype.getPoint = function( t ) { var d = t * this.getLength(); var curveLengths = this.getCurveLengths(); - var i = 0, diff, curve; + var i = 0; // To think about boundaries points. @@ -21485,8 +30672,8 @@ THREE.CurvePath.prototype.getPoint = function( t ) { if ( curveLengths[ i ] >= d ) { - diff = curveLengths[ i ] - d; - curve = this.curves[ i ]; + var diff = curveLengths[ i ] - d; + var curve = this.curves[ i ]; var u = 1 - diff / curve.getLength(); @@ -21506,8 +30693,8 @@ THREE.CurvePath.prototype.getPoint = function( t ) { /* THREE.CurvePath.prototype.getTangent = function( t ) { -};*/ - +}; +*/ // We cannot use the default THREE.Curve getPoint() with getLength() because in // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath @@ -21527,19 +30714,18 @@ THREE.CurvePath.prototype.getCurveLengths = function() { // We use cache values if curves and cache array are same length - if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) { + if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) { return this.cacheLengths; - }; + } - // Get length of subsurve + // Get length of sub-curve // Push sums into cached array var lengths = [], sums = 0; - var i, il = this.curves.length; - for ( i = 0; i < il; i ++ ) { + for ( var i = 0, l = this.curves.length; i < l; i ++ ) { sums += this.curves[ i ].getLength(); lengths.push( sums ); @@ -21554,65 +30740,6 @@ THREE.CurvePath.prototype.getCurveLengths = function() { -// Returns min and max coordinates - -THREE.CurvePath.prototype.getBoundingBox = function () { - - var points = this.getPoints(); - - var maxX, maxY, maxZ; - var minX, minY, minZ; - - maxX = maxY = Number.NEGATIVE_INFINITY; - minX = minY = Number.POSITIVE_INFINITY; - - var p, i, il, sum; - - var v3 = points[0] instanceof THREE.Vector3; - - sum = v3 ? new THREE.Vector3() : new THREE.Vector2(); - - for ( i = 0, il = points.length; i < il; i ++ ) { - - p = points[ i ]; - - if ( p.x > maxX ) maxX = p.x; - else if ( p.x < minX ) minX = p.x; - - if ( p.y > maxY ) maxY = p.y; - else if ( p.y < minY ) minY = p.y; - - if ( v3 ) { - - if ( p.z > maxZ ) maxZ = p.z; - else if ( p.z < minZ ) minZ = p.z; - - } - - sum.add( p ); - - } - - var ret = { - - minX: minX, - minY: minY, - maxX: maxX, - maxY: maxY - - }; - - if ( v3 ) { - - ret.maxZ = maxZ; - ret.minZ = minZ; - - } - - return ret; - -}; - /************************************************************** * Create Geometries Helpers **************************************************************/ @@ -21621,16 +30748,16 @@ THREE.CurvePath.prototype.getBoundingBox = function () { THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { - var pts = this.getPoints( divisions, true ); + var pts = this.getPoints( divisions ); return this.createGeometry( pts ); }; -// Generate geometry from equidistance sampling along the path +// Generate geometry from equidistant sampling along the path THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { - var pts = this.getSpacedPoints( divisions, true ); + var pts = this.getSpacedPoints( divisions ); return this.createGeometry( pts ); }; @@ -21639,9 +30766,10 @@ THREE.CurvePath.prototype.createGeometry = function( points ) { var geometry = new THREE.Geometry(); - for ( var i = 0; i < points.length; i ++ ) { + for ( var i = 0, l = points.length; i < l; i ++ ) { - geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); + var point = points[ i ]; + geometry.vertices.push( new THREE.Vector3( point.x, point.y, point.z || 0 ) ); } @@ -21649,168 +30777,174 @@ THREE.CurvePath.prototype.createGeometry = function( points ) { }; - -/************************************************************** - * Bend / Wrap Helper Methods - **************************************************************/ - -// Wrap path / Bend modifiers? - -THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { - - this.bends.push( bendpath ); - -}; - -THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { - - var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints - var i, il; - - if ( ! bends ) { - - bends = this.bends; - - } - - for ( i = 0, il = bends.length; i < il; i ++ ) { - - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); - - } - - return oldPts; - -}; - -THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { - - var oldPts = this.getSpacedPoints( segments ); - - var i, il; - - if ( ! bends ) { - - bends = this.bends; - - } - - for ( i = 0, il = bends.length; i < il; i ++ ) { - - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); - - } - - return oldPts; - -}; - -// This returns getPoints() bend/wrapped around the contour of a path. -// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html - -THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { - - var bounds = this.getBoundingBox(); - - var i, il, p, oldX, oldY, xNorm; - - for ( i = 0, il = oldPts.length; i < il; i ++ ) { - - p = oldPts[ i ]; - - oldX = p.x; - oldY = p.y; - - xNorm = oldX / bounds.maxX; - - // If using actual distance, for length > path, requires line extrusions - //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance - - xNorm = path.getUtoTmapping( xNorm, oldX ); - - // check for out of bounds? - - var pathPt = path.getPoint( xNorm ); - var normal = path.getTangent( xNorm ); - normal.set( - normal.y, normal.x ).multiplyScalar( oldY ); - - p.x = pathPt.x + normal.x; - p.y = pathPt.y + normal.y; - - } - - return oldPts; - -}; - - -// File:src/extras/core/Gyroscope.js +// File:src/extras/core/Font.js /** - * @author alteredq / http://alteredqualia.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author mrdoob / http://mrdoob.com/ */ -THREE.Gyroscope = function () { +THREE.Font = function ( data ) { - THREE.Object3D.call( this ); + this.data = data; }; -THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Gyroscope.prototype.constructor = THREE.Gyroscope; +THREE.Font.prototype = { -THREE.Gyroscope.prototype.updateMatrixWorld = function () { + constructor: THREE.Font, - var translationObject = new THREE.Vector3(); - var quaternionObject = new THREE.Quaternion(); - var scaleObject = new THREE.Vector3(); + generateShapes: function ( text, size, divisions ) { - var translationWorld = new THREE.Vector3(); - var quaternionWorld = new THREE.Quaternion(); - var scaleWorld = new THREE.Vector3(); + function createPaths( text ) { - return function ( force ) { + var chars = String( text ).split( '' ); + var scale = size / data.resolution; + var offset = 0; - this.matrixAutoUpdate && this.updateMatrix(); + var paths = []; - // update matrixWorld + for ( var i = 0; i < chars.length; i ++ ) { - if ( this.matrixWorldNeedsUpdate || force ) { + var ret = createPath( chars[ i ], scale, offset ); + offset += ret.offset; - if ( this.parent ) { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld ); - this.matrix.decompose( translationObject, quaternionObject, scaleObject ); - - this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld ); - - - } else { - - this.matrixWorld.copy( this.matrix ); + paths.push( ret.path ); } - - this.matrixWorldNeedsUpdate = false; - - force = true; + return paths; } - // update children + function createPath( c, scale, offset ) { - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + var glyph = data.glyphs[ c ] || data.glyphs[ '?' ]; - this.children[ i ].updateMatrixWorld( force ); + if ( ! glyph ) return; + + var path = new THREE.Path(); + + var pts = [], b2 = THREE.ShapeUtils.b2, b3 = THREE.ShapeUtils.b3; + var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste; + + if ( glyph.o ) { + + var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + + for ( var i = 0, l = outline.length; i < l; ) { + + var action = outline[ i ++ ]; + + switch ( action ) { + + case 'm': // moveTo + + x = outline[ i ++ ] * scale + offset; + y = outline[ i ++ ] * scale; + + path.moveTo( x, y ); + + break; + + case 'l': // lineTo + + x = outline[ i ++ ] * scale + offset; + y = outline[ i ++ ] * scale; + + path.lineTo( x, y ); + + break; + + case 'q': // quadraticCurveTo + + cpx = outline[ i ++ ] * scale + offset; + cpy = outline[ i ++ ] * scale; + cpx1 = outline[ i ++ ] * scale + offset; + cpy1 = outline[ i ++ ] * scale; + + path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( var i2 = 1; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + b2( t, cpx0, cpx1, cpx ); + b2( t, cpy0, cpy1, cpy ); + + } + + } + + break; + + case 'b': // bezierCurveTo + + cpx = outline[ i ++ ] * scale + offset; + cpy = outline[ i ++ ] * scale; + cpx1 = outline[ i ++ ] * scale + offset; + cpy1 = outline[ i ++ ] * scale; + cpx2 = outline[ i ++ ] * scale + offset; + cpy2 = outline[ i ++ ] * scale; + + path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( var i2 = 1; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + b3( t, cpx0, cpx1, cpx2, cpx ); + b3( t, cpy0, cpy1, cpy2, cpy ); + + } + + } + + break; + + } + + } + + } + + return { offset: glyph.ha * scale, path: path }; } - }; - -}(); + // + + if ( size === undefined ) size = 100; + if ( divisions === undefined ) divisions = 4; + + var data = this.data; + + var paths = createPaths( text ); + var shapes = []; + + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; + + } + +}; // File:src/extras/core/Path.js @@ -21822,7 +30956,7 @@ THREE.Gyroscope.prototype.updateMatrixWorld = function () { THREE.Path = function ( points ) { - THREE.CurvePath.call(this); + THREE.CurvePath.call( this ); this.actions = []; @@ -21837,17 +30971,6 @@ THREE.Path = function ( points ) { THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); THREE.Path.prototype.constructor = THREE.Path; -THREE.PathActions = { - - MOVE_TO: 'moveTo', - LINE_TO: 'lineTo', - QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve - BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve - CSPLINE_THRU: 'splineThru', // Catmull-rom spline - ARC: 'arc', // Circle - ELLIPSE: 'ellipse' -}; - // TODO Clean up PATH API // Create path using straight lines to connect all points @@ -21857,11 +30980,11 @@ THREE.Path.prototype.fromPoints = function ( vectors ) { this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); - for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + for ( var i = 1, l = vectors.length; i < l; i ++ ) { - this.lineTo( vectors[ v ].x, vectors[ v ].y ); + this.lineTo( vectors[ i ].x, vectors[ i ].y ); - }; + } }; @@ -21869,15 +30992,12 @@ THREE.Path.prototype.fromPoints = function ( vectors ) { THREE.Path.prototype.moveTo = function ( x, y ) { - var args = Array.prototype.slice.call( arguments ); - this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); + this.actions.push( { action: 'moveTo', args: [ x, y ] } ); }; THREE.Path.prototype.lineTo = function ( x, y ) { - var args = Array.prototype.slice.call( arguments ); - var lastargs = this.actions[ this.actions.length - 1 ].args; var x0 = lastargs[ lastargs.length - 2 ]; @@ -21886,116 +31006,120 @@ THREE.Path.prototype.lineTo = function ( x, y ) { var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); this.curves.push( curve ); - this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + this.actions.push( { action: 'lineTo', args: [ x, y ] } ); }; THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { - var args = Array.prototype.slice.call( arguments ); - var lastargs = this.actions[ this.actions.length - 1 ].args; var x0 = lastargs[ lastargs.length - 2 ]; var y0 = lastargs[ lastargs.length - 1 ]; - var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), - new THREE.Vector2( aCPx, aCPy ), - new THREE.Vector2( aX, aY ) ); + var curve = new THREE.QuadraticBezierCurve( + new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) + ); + this.curves.push( curve ); - this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + this.actions.push( { action: 'quadraticCurveTo', args: [ aCPx, aCPy, aX, aY ] } ); }; -THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, - aCP2x, aCP2y, - aX, aY ) { - - var args = Array.prototype.slice.call( arguments ); +THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { var lastargs = this.actions[ this.actions.length - 1 ].args; var x0 = lastargs[ lastargs.length - 2 ]; var y0 = lastargs[ lastargs.length - 1 ]; - var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), - new THREE.Vector2( aCP1x, aCP1y ), - new THREE.Vector2( aCP2x, aCP2y ), - new THREE.Vector2( aX, aY ) ); + var curve = new THREE.CubicBezierCurve( + new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) + ); + this.curves.push( curve ); - this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + this.actions.push( { action: 'bezierCurveTo', args: [ aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ] } ); }; THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { var args = Array.prototype.slice.call( arguments ); + var lastargs = this.actions[ this.actions.length - 1 ].args; var x0 = lastargs[ lastargs.length - 2 ]; var y0 = lastargs[ lastargs.length - 1 ]; -//--- + var npts = [ new THREE.Vector2( x0, y0 ) ]; Array.prototype.push.apply( npts, pts ); var curve = new THREE.SplineCurve( npts ); this.curves.push( curve ); - this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + this.actions.push( { action: 'splineThru', args: args } ); }; // FUTURE: Change the API or follow canvas API? -THREE.Path.prototype.arc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { +THREE.Path.prototype.arc = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - var lastargs = this.actions[ this.actions.length - 1].args; + var lastargs = this.actions[ this.actions.length - 1 ].args; var x0 = lastargs[ lastargs.length - 2 ]; var y0 = lastargs[ lastargs.length - 1 ]; - this.absarc(aX + x0, aY + y0, aRadius, + this.absarc( aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise ); }; - THREE.Path.prototype.absarc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { - this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); + THREE.Path.prototype.absarc = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + }; -THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ) { +THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - var lastargs = this.actions[ this.actions.length - 1].args; + var lastargs = this.actions[ this.actions.length - 1 ].args; var x0 = lastargs[ lastargs.length - 2 ]; var y0 = lastargs[ lastargs.length - 1 ]; - this.absellipse(aX + x0, aY + y0, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ); + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); }; -THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ) { +THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - var args = Array.prototype.slice.call( arguments ); - var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ); + var args = [ + aX, aY, + xRadius, yRadius, + aStartAngle, aEndAngle, + aClockwise, + aRotation || 0 // aRotation is optional. + ]; + + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); this.curves.push( curve ); - var lastPoint = curve.getPoint(1); - args.push(lastPoint.x); - args.push(lastPoint.y); + var lastPoint = curve.getPoint( 1 ); + args.push( lastPoint.x ); + args.push( lastPoint.y ); - this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + this.actions.push( { action: 'ellipse', args: args } ); }; -THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { +THREE.Path.prototype.getSpacedPoints = function ( divisions ) { if ( ! divisions ) divisions = 40; @@ -22005,15 +31129,15 @@ THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { points.push( this.getPoint( i / divisions ) ); - //if( !this.getPoint( i / divisions ) ) throw "DIE"; + //if ( !this.getPoint( i / divisions ) ) throw "DIE"; } - // if ( closedPath ) { - // - // points.push( points[ 0 ] ); - // - // } + if ( this.autoClose ) { + + points.push( points[ 0 ] ); + + } return points; @@ -22021,44 +31145,40 @@ THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { /* Return an array of vectors based on contour of the path */ -THREE.Path.prototype.getPoints = function( divisions, closedPath ) { - - if (this.useSpacedPoints) { - console.log('tata'); - return this.getSpacedPoints( divisions, closedPath ); - } +THREE.Path.prototype.getPoints = function( divisions ) { divisions = divisions || 12; + var b2 = THREE.ShapeUtils.b2; + var b3 = THREE.ShapeUtils.b3; + var points = []; - var i, il, item, action, args; var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, - laste, j, - t, tx, ty; + laste, tx, ty; - for ( i = 0, il = this.actions.length; i < il; i ++ ) { + for ( var i = 0, l = this.actions.length; i < l; i ++ ) { - item = this.actions[ i ]; + var item = this.actions[ i ]; - action = item.action; - args = item.args; + var action = item.action; + var args = item.args; switch ( action ) { - case THREE.PathActions.MOVE_TO: + case 'moveTo': points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); break; - case THREE.PathActions.LINE_TO: + case 'lineTo': points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); break; - case THREE.PathActions.QUADRATIC_CURVE_TO: + case 'quadraticCurveTo': cpx = args[ 2 ]; cpy = args[ 3 ]; @@ -22082,12 +31202,12 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { } - for ( j = 1; j <= divisions; j ++ ) { + for ( var j = 1; j <= divisions; j ++ ) { - t = j / divisions; + var t = j / divisions; - tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + tx = b2( t, cpx0, cpx1, cpx ); + ty = b2( t, cpy0, cpy1, cpy ); points.push( new THREE.Vector2( tx, ty ) ); @@ -22095,7 +31215,7 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { break; - case THREE.PathActions.BEZIER_CURVE_TO: + case 'bezierCurveTo': cpx = args[ 4 ]; cpy = args[ 5 ]; @@ -22123,12 +31243,12 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { } - for ( j = 1; j <= divisions; j ++ ) { + for ( var j = 1; j <= divisions; j ++ ) { - t = j / divisions; + var t = j / divisions; - tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + tx = b3( t, cpx0, cpx1, cpx2, cpx ); + ty = b3( t, cpy0, cpy1, cpy2, cpy ); points.push( new THREE.Vector2( tx, ty ) ); @@ -22136,7 +31256,7 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { break; - case THREE.PathActions.CSPLINE_THRU: + case 'splineThru': laste = this.actions[ i - 1 ].args; @@ -22149,15 +31269,15 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { var spline = new THREE.SplineCurve( spts ); - for ( j = 1; j <= n; j ++ ) { + for ( var j = 1; j <= n; j ++ ) { - points.push( spline.getPointAt( j / n ) ) ; + points.push( spline.getPointAt( j / n ) ); } break; - case THREE.PathActions.ARC: + case 'arc': var aX = args[ 0 ], aY = args[ 1 ], aRadius = args[ 2 ], @@ -22168,9 +31288,9 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { var angle; var tdivisions = divisions * 2; - for ( j = 1; j <= tdivisions; j ++ ) { + for ( var j = 1; j <= tdivisions; j ++ ) { - t = j / tdivisions; + var t = j / tdivisions; if ( ! aClockwise ) { @@ -22192,23 +31312,32 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { //console.log(points); break; - - case THREE.PathActions.ELLIPSE: + + case 'ellipse': var aX = args[ 0 ], aY = args[ 1 ], xRadius = args[ 2 ], yRadius = args[ 3 ], aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], - aClockwise = !! args[ 6 ]; + aClockwise = !! args[ 6 ], + aRotation = args[ 7 ]; var deltaAngle = aEndAngle - aStartAngle; var angle; var tdivisions = divisions * 2; - for ( j = 1; j <= tdivisions; j ++ ) { + var cos, sin; + if ( aRotation !== 0 ) { - t = j / tdivisions; + cos = Math.cos( aRotation ); + sin = Math.sin( aRotation ); + + } + + for ( var j = 1; j <= tdivisions; j ++ ) { + + var t = j / tdivisions; if ( ! aClockwise ) { @@ -22221,6 +31350,16 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { tx = aX + xRadius * Math.cos( angle ); ty = aY + yRadius * Math.sin( angle ); + if ( aRotation !== 0 ) { + + var x = tx, y = ty; + + // Rotate the point about the center of the ellipse. + tx = ( x - aX ) * cos - ( y - aY ) * sin + aX; + ty = ( x - aX ) * sin + ( y - aY ) * cos + aY; + + } + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); points.push( new THREE.Vector2( tx, ty ) ); @@ -22238,12 +31377,12 @@ THREE.Path.prototype.getPoints = function( divisions, closedPath ) { // Normalize to remove the closing point by default. - var lastPoint = points[ points.length - 1]; - var EPSILON = 0.0000000001; - if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && - Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) - points.splice( points.length - 1, 1); - if ( closedPath ) { + var lastPoint = points[ points.length - 1 ]; + if ( Math.abs( lastPoint.x - points[ 0 ].x ) < Number.EPSILON && + Math.abs( lastPoint.y - points[ 0 ].y ) < Number.EPSILON ) + points.splice( points.length - 1, 1 ); + + if ( this.autoClose ) { points.push( points[ 0 ] ); @@ -22269,20 +31408,18 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { function extractSubpaths( inActions ) { - var i, il, item, action, args; - var subPaths = [], lastPath = new THREE.Path(); - for ( i = 0, il = inActions.length; i < il; i ++ ) { + for ( var i = 0, l = inActions.length; i < l; i ++ ) { - item = inActions[ i ]; + var item = inActions[ i ]; - args = item.args; - action = item.action; + var args = item.args; + var action = item.action; - if ( action == THREE.PathActions.MOVE_TO ) { + if ( action === 'moveTo' ) { - if ( lastPath.actions.length != 0 ) { + if ( lastPath.actions.length !== 0 ) { subPaths.push( lastPath ); lastPath = new THREE.Path(); @@ -22295,7 +31432,7 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { } - if ( lastPath.actions.length != 0 ) { + if ( lastPath.actions.length !== 0 ) { subPaths.push( lastPath ); @@ -22304,13 +31441,14 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { // console.log(subPaths); return subPaths; + } function toShapesNoHoles( inSubpaths ) { var shapes = []; - for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) { + for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { var tmpPath = inSubpaths[ i ]; @@ -22319,15 +31457,16 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); + } //console.log("shape", shapes); return shapes; - }; + + } function isPointInsidePolygon( inPt, inPolygon ) { - var EPSILON = 0.0000000001; var polyLen = inPolygon.length; @@ -22337,52 +31476,68 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { // not counting lowerY endpoints of edges and whole edges on that line var inside = false; for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + var edgeLowPt = inPolygon[ p ]; var edgeHighPt = inPolygon[ q ]; var edgeDx = edgeHighPt.x - edgeLowPt.x; var edgeDy = edgeHighPt.y - edgeLowPt.y; - if ( Math.abs(edgeDy) > EPSILON ) { // not parallel + if ( Math.abs( edgeDy ) > Number.EPSILON ) { + + // not parallel if ( edgeDy < 0 ) { + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + } if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - if ( inPt.y == edgeLowPt.y ) { - if ( inPt.x == edgeLowPt.x ) return true; // inPt is on contour ? + if ( inPt.y === edgeLowPt.y ) { + + if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn't count !!! + } else { - var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); - if ( perpEdge == 0 ) return true; // inPt is on contour ? + + var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); + if ( perpEdge === 0 ) return true; // inPt is on contour ? if ( perpEdge < 0 ) continue; inside = ! inside; // true intersection left of inPt + } - } else { // parallel or colinear - if ( inPt.y != edgeLowPt.y ) continue; // parallel - // egde lies on the same horizontal line as inPt + + } else { + + // parallel or collinear + if ( inPt.y !== edgeLowPt.y ) continue; // parallel + // edge lies on the same horizontal line as inPt if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! // continue; + } + } return inside; + } + var isClockWise = THREE.ShapeUtils.isClockWise; var subPaths = extractSubpaths( this.actions ); - if ( subPaths.length == 0 ) return []; + if ( subPaths.length === 0 ) return []; if ( noHoles === true ) return toShapesNoHoles( subPaths ); var solid, tmpPath, tmpShape, shapes = []; - if ( subPaths.length == 1) { + if ( subPaths.length === 1 ) { - tmpPath = subPaths[0]; + tmpPath = subPaths[ 0 ]; tmpShape = new THREE.Shape(); tmpShape.actions = tmpPath.actions; tmpShape.curves = tmpPath.curves; @@ -22391,45 +31546,43 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { } - var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); + var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; // console.log("Holes first", holesFirst); - + var betterShapeHoles = []; var newShapes = []; var newShapeHoles = []; var mainIdx = 0; var tmpPoints; - newShapes[mainIdx] = undefined; - newShapeHoles[mainIdx] = []; + newShapes[ mainIdx ] = undefined; + newShapeHoles[ mainIdx ] = []; - var i, il; - - for ( i = 0, il = subPaths.length; i < il; i ++ ) { + for ( var i = 0, l = subPaths.length; i < l; i ++ ) { tmpPath = subPaths[ i ]; tmpPoints = tmpPath.getPoints(); - solid = THREE.Shape.Utils.isClockWise( tmpPoints ); + solid = isClockWise( tmpPoints ); solid = isCCW ? ! solid : solid; if ( solid ) { - if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx ++; + if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; + + newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[ mainIdx ].s.actions = tmpPath.actions; + newShapes[ mainIdx ].s.curves = tmpPath.curves; - newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints }; - newShapes[mainIdx].s.actions = tmpPath.actions; - newShapes[mainIdx].s.curves = tmpPath.curves; - if ( holesFirst ) mainIdx ++; - newShapeHoles[mainIdx] = []; + newShapeHoles[ mainIdx ] = []; //console.log('cw', i); } else { - newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } ); + newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); //console.log('ccw', i); @@ -22438,50 +31591,81 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { } // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[0] ) return toShapesNoHoles( subPaths ); + if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); if ( newShapes.length > 1 ) { - var ambigious = false; + + var ambiguous = false; var toChange = []; - for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - betterShapeHoles[sIdx] = []; + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + + betterShapeHoles[ sIdx ] = []; + } - for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - var sho = newShapeHoles[sIdx]; - for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) { - var ho = sho[hIdx]; + + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + + var sho = newShapeHoles[ sIdx ]; + + for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + + var ho = sho[ hIdx ]; var hole_unassigned = true; - for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) { - if ( sIdx != s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + + for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + + if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { + + if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); if ( hole_unassigned ) { + hole_unassigned = false; - betterShapeHoles[s2Idx].push( ho ); + betterShapeHoles[ s2Idx ].push( ho ); + } else { - ambigious = true; + + ambiguous = true; + } + } + } - if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); } + if ( hole_unassigned ) { + + betterShapeHoles[ sIdx ].push( ho ); + + } + } + } - // console.log("ambigious: ", ambigious); + // console.log("ambiguous: ", ambiguous); if ( toChange.length > 0 ) { + // console.log("to change: ", toChange); - if (! ambigious) newShapeHoles = betterShapeHoles; + if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + } + } - var tmpHoles, j, jl; - for ( i = 0, il = newShapes.length; i < il; i ++ ) { - tmpShape = newShapes[i].s; + var tmpHoles; + + for ( var i = 0, il = newShapes.length; i < il; i ++ ) { + + tmpShape = newShapes[ i ].s; shapes.push( tmpShape ); - tmpHoles = newShapeHoles[i]; - for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - tmpShape.holes.push( tmpHoles[j].h ); + tmpHoles = newShapeHoles[ i ]; + + for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + + tmpShape.holes.push( tmpHoles[ j ].h ); + } + } //console.log("shape", shapes); @@ -22506,6 +31690,7 @@ THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { THREE.Shape = function () { THREE.Path.apply( this, arguments ); + this.holes = []; }; @@ -22517,8 +31702,7 @@ THREE.Shape.prototype.constructor = THREE.Shape; THREE.Shape.prototype.extrude = function ( options ) { - var extruded = new THREE.ExtrudeGeometry( this, options ); - return extruded; + return new THREE.ExtrudeGeometry( this, options ); }; @@ -22526,8 +31710,7 @@ THREE.Shape.prototype.extrude = function ( options ) { THREE.Shape.prototype.makeGeometry = function ( options ) { - var geometry = new THREE.ShapeGeometry( this, options ); - return geometry; + return new THREE.ShapeGeometry( this, options ); }; @@ -22535,27 +31718,11 @@ THREE.Shape.prototype.makeGeometry = function ( options ) { THREE.Shape.prototype.getPointsHoles = function ( divisions ) { - var i, il = this.holes.length, holesPts = []; + var holesPts = []; - for ( i = 0; i < il; i ++ ) { + for ( var i = 0, l = this.holes.length; i < l; i ++ ) { - holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); - - } - - return holesPts; - -}; - -// Get points of holes (spaced by regular distance) - -THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { - - var i, il = this.holes.length, holesPts = []; - - for ( i = 0; i < il; i ++ ) { - - holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } @@ -22570,7 +31737,7 @@ THREE.Shape.prototype.extractAllPoints = function ( divisions ) { return { - shape: this.getTransformedPoints( divisions ), + shape: this.getPoints( divisions ), holes: this.getPointsHoles( divisions ) }; @@ -22579,497 +31746,10 @@ THREE.Shape.prototype.extractAllPoints = function ( divisions ) { THREE.Shape.prototype.extractPoints = function ( divisions ) { - if (this.useSpacedPoints) { - return this.extractAllSpacedPoints(divisions); - } - - return this.extractAllPoints(divisions); + return this.extractAllPoints( divisions ); }; -// -// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { -// -// return { -// -// shape: this.transform( bend, divisions ), -// holes: this.getPointsHoles( divisions, bend ) -// -// }; -// -// }; - -// Get points of shape and holes (spaced by regular distance) - -THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { - - return { - - shape: this.getTransformedSpacedPoints( divisions ), - holes: this.getSpacedPointsHoles( divisions ) - - }; - -}; - -/************************************************************** - * Utils - **************************************************************/ - -THREE.Shape.Utils = { - - triangulateShape: function ( contour, holes ) { - - function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { - // inOtherPt needs to be colinear to the inSegment - if ( inSegPt1.x != inSegPt2.x ) { - if ( inSegPt1.x < inSegPt2.x ) { - return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); - } else { - return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); - } - } else { - if ( inSegPt1.y < inSegPt2.y ) { - return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); - } else { - return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); - } - } - } - - function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { - var EPSILON = 0.0000000001; - - var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; - var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; - - var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; - var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; - - var limit = seg1dy * seg2dx - seg1dx * seg2dy; - var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; - - if ( Math.abs(limit) > EPSILON ) { // not parallel - - var perpSeg2; - if ( limit > 0 ) { - if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; - } else { - if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; - } - - // i.e. to reduce rounding errors - // intersection at endpoint of segment#1? - if ( perpSeg2 == 0 ) { - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; - return [ inSeg1Pt1 ]; - } - if ( perpSeg2 == limit ) { - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; - return [ inSeg1Pt2 ]; - } - // intersection at endpoint of segment#2? - if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ]; - if ( perpSeg1 == limit ) return [ inSeg2Pt2 ]; - - // return real intersection point - var factorSeg1 = perpSeg2 / limit; - return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, - y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; - - } else { // parallel or colinear - if ( ( perpSeg1 != 0 ) || - ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return []; - - // they are collinear or degenerate - var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point? - var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point? - // both segments are points - if ( seg1Pt && seg2Pt ) { - if ( (inSeg1Pt1.x != inSeg2Pt1.x) || - (inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points - return [ inSeg1Pt1 ]; // they are the same point - } - // segment#1 is a single point - if ( seg1Pt ) { - if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 - return [ inSeg1Pt1 ]; - } - // segment#2 is a single point - if ( seg2Pt ) { - if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 - return [ inSeg2Pt1 ]; - } - - // they are collinear segments, which might overlap - var seg1min, seg1max, seg1minVal, seg1maxVal; - var seg2min, seg2max, seg2minVal, seg2maxVal; - if (seg1dx != 0) { // the segments are NOT on a vertical line - if ( inSeg1Pt1.x < inSeg1Pt2.x ) { - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; - } else { - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; - } - if ( inSeg2Pt1.x < inSeg2Pt2.x ) { - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; - } else { - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; - } - } else { // the segments are on a vertical line - if ( inSeg1Pt1.y < inSeg1Pt2.y ) { - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; - } else { - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; - } - if ( inSeg2Pt1.y < inSeg2Pt2.y ) { - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; - } else { - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; - } - } - if ( seg1minVal <= seg2minVal ) { - if ( seg1maxVal < seg2minVal ) return []; - if ( seg1maxVal == seg2minVal ) { - if ( inExcludeAdjacentSegs ) return []; - return [ seg2min ]; - } - if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; - return [ seg2min, seg2max ]; - } else { - if ( seg1minVal > seg2maxVal ) return []; - if ( seg1minVal == seg2maxVal ) { - if ( inExcludeAdjacentSegs ) return []; - return [ seg1min ]; - } - if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; - return [ seg1min, seg2max ]; - } - } - } - - function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { - // The order of legs is important - - var EPSILON = 0.0000000001; - - // translation of all points, so that Vertex is at (0,0) - var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; - var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; - var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; - - // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. - var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; - var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; - - if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg. - - var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; - // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); - - if ( from2toAngle > 0 ) { // main angle < 180 deg. - return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); - } else { // main angle > 180 deg. - return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); - } - } else { // angle == 180 deg. - // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); - return ( from2otherAngle > 0 ); - } - } - - - function removeHoles( contour, holes ) { - - var shape = contour.concat(); // work on this shape - var hole; - - function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { - // Check if hole point lies within angle around shape point - var lastShapeIdx = shape.length - 1; - - var prevShapeIdx = inShapeIdx - 1; - if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; - - var nextShapeIdx = inShapeIdx + 1; - if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; - - var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] ); - if (! insideAngle ) { - // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); - return false; - } - - // Check if shape point lies within angle around hole point - var lastHoleIdx = hole.length - 1; - - var prevHoleIdx = inHoleIdx - 1; - if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; - - var nextHoleIdx = inHoleIdx + 1; - if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; - - insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] ); - if (! insideAngle ) { - // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); - return false; - } - - return true; - } - - function intersectsShapeEdge( inShapePt, inHolePt ) { - // checks for intersections with shape edges - var sIdx, nextIdx, intersection; - for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { - nextIdx = sIdx + 1; nextIdx %= shape.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true ); - if ( intersection.length > 0 ) return true; - } - - return false; - } - - var indepHoles = []; - - function intersectsHoleEdge( inShapePt, inHolePt ) { - // checks for intersections with hole edges - var ihIdx, chkHole, - hIdx, nextIdx, intersection; - for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { - chkHole = holes[indepHoles[ihIdx]]; - for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { - nextIdx = hIdx + 1; nextIdx %= chkHole.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true ); - if ( intersection.length > 0 ) return true; - } - } - return false; - } - - var holeIndex, shapeIndex, - shapePt, holePt, - holeIdx, cutKey, failedCuts = [], - tmpShape1, tmpShape2, - tmpHole1, tmpHole2; - - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - - indepHoles.push( h ); - - } - - var minShapeIndex = 0; - var counter = indepHoles.length * 2; - while ( indepHoles.length > 0 ) { - counter --; - if ( counter < 0 ) { - console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); - break; - } - - // search for shape-vertex and hole-vertex, - // which can be connected without intersections - for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { - - shapePt = shape[ shapeIndex ]; - holeIndex = - 1; - - // search for hole which can be reached without intersections - for ( var h = 0; h < indepHoles.length; h ++ ) { - holeIdx = indepHoles[h]; - - // prevent multiple checks - cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; - if ( failedCuts[cutKey] !== undefined ) continue; - - hole = holes[holeIdx]; - for ( var h2 = 0; h2 < hole.length; h2 ++ ) { - holePt = hole[ h2 ]; - if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; - if ( intersectsShapeEdge( shapePt, holePt ) ) continue; - if ( intersectsHoleEdge( shapePt, holePt ) ) continue; - - holeIndex = h2; - indepHoles.splice(h, 1); - - tmpShape1 = shape.slice( 0, shapeIndex + 1 ); - tmpShape2 = shape.slice( shapeIndex ); - tmpHole1 = hole.slice( holeIndex ); - tmpHole2 = hole.slice( 0, holeIndex + 1 ); - - shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); - - minShapeIndex = shapeIndex; - - // Debug only, to show the selected cuts - // glob_CutLines.push( [ shapePt, holePt ] ); - - break; - } - if ( holeIndex >= 0 ) break; // hole-vertex found - - failedCuts[cutKey] = true; // remember failure - } - if ( holeIndex >= 0 ) break; // hole-vertex found - } - } - - return shape; /* shape with no holes */ - } - - - var i, il, f, face, - key, index, - allPointsMap = {}; - - // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. - - var allpoints = contour.concat(); - - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - - Array.prototype.push.apply( allpoints, holes[h] ); - - } - - //console.log( "allpoints",allpoints, allpoints.length ); - - // prepare all points map - - for ( i = 0, il = allpoints.length; i < il; i ++ ) { - - key = allpoints[ i ].x + ":" + allpoints[ i ].y; - - if ( allPointsMap[ key ] !== undefined ) { - - THREE.warn( "THREE.Shape: Duplicate point", key ); - - } - - allPointsMap[ key ] = i; - - } - - // remove holes by cutting paths to holes and adding them to the shape - var shapeWithoutHoles = removeHoles( contour, holes ); - - var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape - //console.log( "triangles",triangles, triangles.length ); - - // check all face vertices against all points map - - for ( i = 0, il = triangles.length; i < il; i ++ ) { - - face = triangles[ i ]; - - for ( f = 0; f < 3; f ++ ) { - - key = face[ f ].x + ":" + face[ f ].y; - - index = allPointsMap[ key ]; - - if ( index !== undefined ) { - - face[ f ] = index; - - } - - } - - } - - return triangles.concat(); - - }, - - isClockWise: function ( pts ) { - - return THREE.FontUtils.Triangulate.area( pts ) < 0; - - }, - - // Bezier Curves formulas obtained from - // http://en.wikipedia.org/wiki/B%C3%A9zier_curve - - // Quad Bezier Functions - - b2p0: function ( t, p ) { - - var k = 1 - t; - return k * k * p; - - }, - - b2p1: function ( t, p ) { - - return 2 * ( 1 - t ) * t * p; - - }, - - b2p2: function ( t, p ) { - - return t * t * p; - - }, - - b2: function ( t, p0, p1, p2 ) { - - return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); - - }, - - // Cubic Bezier Functions - - b3p0: function ( t, p ) { - - var k = 1 - t; - return k * k * k * p; - - }, - - b3p1: function ( t, p ) { - - var k = 1 - t; - return 3 * k * k * t * p; - - }, - - b3p2: function ( t, p ) { - - var k = 1 - t; - return 3 * k * t * t * p; - - }, - - b3p3: function ( t, p ) { - - return t * t * t * p; - - }, - - b3: function ( t, p0, p1, p2, p3 ) { - - return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); - - } - -}; - - // File:src/extras/curves/LineCurve.js /************************************************************** @@ -23088,7 +31768,7 @@ THREE.LineCurve.prototype.constructor = THREE.LineCurve; THREE.LineCurve.prototype.getPoint = function ( t ) { - var point = this.v2.clone().sub(this.v1); + var point = this.v2.clone().sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); return point; @@ -23105,7 +31785,7 @@ THREE.LineCurve.prototype.getPointAt = function ( u ) { THREE.LineCurve.prototype.getTangent = function( t ) { - var tangent = this.v2.clone().sub(this.v1); + var tangent = this.v2.clone().sub( this.v1 ); return tangent.normalize(); @@ -23132,26 +31812,24 @@ THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve; THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { - var vector = new THREE.Vector2(); + var b2 = THREE.ShapeUtils.b2; - vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); - vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); - - return vector; + return new THREE.Vector2( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ) + ); }; THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { - var vector = new THREE.Vector2(); + var tangentQuadraticBezier = THREE.CurveUtils.tangentQuadraticBezier; - vector.x = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ); - vector.y = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ); - - // returns unit vector - - return vector.normalize(); + return new THREE.Vector2( + tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ), + tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ) + ).normalize(); }; @@ -23175,26 +31853,23 @@ THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve; THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { - var tx, ty; + var b3 = THREE.ShapeUtils.b3; - tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); - - return new THREE.Vector2( tx, ty ); + return new THREE.Vector2( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ); }; THREE.CubicBezierCurve.prototype.getTangent = function( t ) { - var tx, ty; + var tangentCubicBezier = THREE.CurveUtils.tangentCubicBezier; - tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); - - var tangent = new THREE.Vector2( tx, ty ); - tangent.normalize(); - - return tangent; + return new THREE.Vector2( + tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ).normalize(); }; @@ -23221,17 +31896,17 @@ THREE.SplineCurve.prototype.getPoint = function ( t ) { var intPoint = Math.floor( point ); var weight = point - intPoint; - var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ] - var point1 = points[ intPoint ] - var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ] - var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ] + var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + var point1 = points[ intPoint ]; + var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - var vector = new THREE.Vector2(); + var interpolate = THREE.CurveUtils.interpolate; - vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); - vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); - - return vector; + return new THREE.Vector2( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ) + ); }; @@ -23241,7 +31916,7 @@ THREE.SplineCurve.prototype.getPoint = function ( t ) { * Ellipse curve **************************************************************/ -THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) { +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { this.aX = aX; this.aY = aY; @@ -23253,6 +31928,8 @@ THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; + + this.aRotation = aRotation || 0; }; @@ -23278,12 +31955,23 @@ THREE.EllipseCurve.prototype.getPoint = function ( t ) { } - var vector = new THREE.Vector2(); + var x = this.aX + this.xRadius * Math.cos( angle ); + var y = this.aY + this.yRadius * Math.sin( angle ); - vector.x = this.aX + this.xRadius * Math.cos( angle ); - vector.y = this.aY + this.yRadius * Math.sin( angle ); + if ( this.aRotation !== 0 ) { - return vector; + var cos = Math.cos( this.aRotation ); + var sin = Math.sin( this.aRotation ); + + var tx = x, ty = y; + + // Rotate the point about the center of the ellipse. + x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX; + y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY; + + } + + return new THREE.Vector2( x, y ); }; @@ -23296,6 +31984,7 @@ THREE.EllipseCurve.prototype.getPoint = function ( t ) { THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + }; THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); @@ -23348,13 +32037,13 @@ THREE.QuadraticBezierCurve3 = THREE.Curve.create( function ( t ) { - var vector = new THREE.Vector3(); + var b2 = THREE.ShapeUtils.b2; - vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); - vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); - vector.z = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); - - return vector; + return new THREE.Vector3( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ), + b2( t, this.v0.z, this.v1.z, this.v2.z ) + ); } @@ -23379,13 +32068,13 @@ THREE.CubicBezierCurve3 = THREE.Curve.create( function ( t ) { - var vector = new THREE.Vector3(); + var b3 = THREE.ShapeUtils.b3; - vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); - vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); - - return vector; + return new THREE.Vector3( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ), + b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ) + ); } @@ -23400,8 +32089,9 @@ THREE.CubicBezierCurve3 = THREE.Curve.create( THREE.SplineCurve3 = THREE.Curve.create( - function ( points /* array of Vector3 */) { + function ( points /* array of Vector3 */ ) { + console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); this.points = ( points == undefined ) ? [] : points; }, @@ -23419,18 +32109,203 @@ THREE.SplineCurve3 = THREE.Curve.create( var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - var vector = new THREE.Vector3(); + var interpolate = THREE.CurveUtils.interpolate; - vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); - vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); - vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); - - return vector; + return new THREE.Vector3( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ), + interpolate( point0.z, point1.z, point2.z, point3.z, weight ) + ); } ); +// File:src/extras/curves/CatmullRomCurve3.js + +/** + * @author zz85 https://github.com/zz85 + * + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ + +THREE.CatmullRomCurve3 = ( function() { + + var + tmp = new THREE.Vector3(), + px = new CubicPoly(), + py = new CubicPoly(), + pz = new CubicPoly(); + + /* + Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM + + This CubicPoly class could be used for reusing some variables and calculations, + but for three.js curve use, it could be possible inlined and flatten into a single function call + which can be placed in CurveUtils. + */ + + function CubicPoly() { + + } + + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + CubicPoly.prototype.init = function( x0, x1, t0, t1 ) { + + this.c0 = x0; + this.c1 = t0; + this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + this.c3 = 2 * x0 - 2 * x1 + t0 + t1; + + }; + + CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { + + // compute tangents when parameterized in [t1,t2] + var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; + + // initCubicPoly + this.init( x1, x2, t1, t2 ); + + }; + + // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 + CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { + + this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + + }; + + CubicPoly.prototype.calc = function( t ) { + + var t2 = t * t; + var t3 = t2 * t; + return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; + + }; + + // Subclass Three.js curve + return THREE.Curve.create( + + function ( p /* array of Vector3 */ ) { + + this.points = p || []; + this.closed = false; + + }, + + function ( t ) { + + var points = this.points, + point, intPoint, weight, l; + + l = points.length; + + if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); + + point = ( l - ( this.closed ? 0 : 1 ) ) * t; + intPoint = Math.floor( point ); + weight = point - intPoint; + + if ( this.closed ) { + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + + } else if ( weight === 0 && intPoint === l - 1 ) { + + intPoint = l - 2; + weight = 1; + + } + + var p0, p1, p2, p3; // 4 points + + if ( this.closed || intPoint > 0 ) { + + p0 = points[ ( intPoint - 1 ) % l ]; + + } else { + + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; + + } + + p1 = points[ intPoint % l ]; + p2 = points[ ( intPoint + 1 ) % l ]; + + if ( this.closed || intPoint + 2 < l ) { + + p3 = points[ ( intPoint + 2 ) % l ]; + + } else { + + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); + p3 = tmp; + + } + + if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { + + // init Centripetal / Chordal Catmull-Rom + var pow = this.type === 'chordal' ? 0.5 : 0.25; + var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; + + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + + } else if ( this.type === 'catmullrom' ) { + + var tension = this.tension !== undefined ? this.tension : 0.5; + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension ); + + } + + var v = new THREE.Vector3( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); + + return v; + + } + + ); + +} )(); + // File:src/extras/curves/ClosedSplineCurve3.js /************************************************************** @@ -23438,978 +32313,17 @@ THREE.SplineCurve3 = THREE.Curve.create( **************************************************************/ -THREE.ClosedSplineCurve3 = THREE.Curve.create( +THREE.ClosedSplineCurve3 = function ( points ) { - function ( points /* array of Vector3 */) { + console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Please use THREE.CatmullRomCurve3.' ); - this.points = ( points == undefined ) ? [] : points; - - }, - - function ( t ) { - - var points = this.points; - var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 - - var intPoint = Math.floor( point ); - var weight = point - intPoint; - - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; - - var point0 = points[ ( intPoint - 1 ) % points.length ]; - var point1 = points[ ( intPoint ) % points.length ]; - var point2 = points[ ( intPoint + 1 ) % points.length ]; - var point3 = points[ ( intPoint + 2 ) % points.length ]; - - var vector = new THREE.Vector3(); - - vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); - vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); - vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); - - return vector; - - } - -); - -// File:src/extras/animation/AnimationHandler.js - -/** - * @author mikael emtinger / http://gomo.se/ - */ - -THREE.AnimationHandler = { - - LINEAR: 0, - CATMULLROM: 1, - CATMULLROM_FORWARD: 2, - - // - - add: function () { THREE.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); }, - get: function () { THREE.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); }, - remove: function () { THREE.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); }, - - // - - animations: [], - - init: function ( data ) { - - if ( data.initialized === true ) return data; - - // loop through all keys - - for ( var h = 0; h < data.hierarchy.length; h ++ ) { - - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - - // remove minus times - - if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { - - data.hierarchy[ h ].keys[ k ].time = 0; - - } - - // create quaternions - - if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && - ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { - - var quat = data.hierarchy[ h ].keys[ k ].rot; - data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); - - } - - } - - // prepare morph target keys - - if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { - - // get all used - - var usedMorphTargets = {}; - - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { - - var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; - usedMorphTargets[ morphTargetName ] = - 1; - - } - - } - - data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; - - - // set all used on all frames - - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - - var influences = {}; - - for ( var morphTargetName in usedMorphTargets ) { - - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { - - if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { - - influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; - break; - - } - - } - - if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { - - influences[ morphTargetName ] = 0; - - } - - } - - data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; - - } - - } - - - // remove all keys that are on the same time - - for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { - - if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { - - data.hierarchy[ h ].keys.splice( k, 1 ); - k --; - - } - - } - - - // set index - - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - - data.hierarchy[ h ].keys[ k ].index = k; - - } - - } - - data.initialized = true; - - return data; - - }, - - parse: function ( root ) { - - var parseRecurseHierarchy = function ( root, hierarchy ) { - - hierarchy.push( root ); - - for ( var c = 0; c < root.children.length; c ++ ) - parseRecurseHierarchy( root.children[ c ], hierarchy ); - - }; - - // setup hierarchy - - var hierarchy = []; - - if ( root instanceof THREE.SkinnedMesh ) { - - for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { - - hierarchy.push( root.skeleton.bones[ b ] ); - - } - - } else { - - parseRecurseHierarchy( root, hierarchy ); - - } - - return hierarchy; - - }, - - play: function ( animation ) { - - if ( this.animations.indexOf( animation ) === - 1 ) { - - this.animations.push( animation ); - - } - - }, - - stop: function ( animation ) { - - var index = this.animations.indexOf( animation ); - - if ( index !== - 1 ) { - - this.animations.splice( index, 1 ); - - } - - }, - - update: function ( deltaTimeMS ) { - - for ( var i = 0; i < this.animations.length; i ++ ) { - - this.animations[ i ].resetBlendWeights( ); - - } - - for ( var i = 0; i < this.animations.length; i ++ ) { - - this.animations[ i ].update( deltaTimeMS ); - - } - - } + THREE.CatmullRomCurve3.call( this, points ); + this.type = 'catmullrom'; + this.closed = true; }; -// File:src/extras/animation/Animation.js - -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.Animation = function ( root, data ) { - - this.root = root; - this.data = THREE.AnimationHandler.init( data ); - this.hierarchy = THREE.AnimationHandler.parse( root ); - - this.currentTime = 0; - this.timeScale = 1; - - this.isPlaying = false; - this.loop = true; - this.weight = 0; - - this.interpolationType = THREE.AnimationHandler.LINEAR; - -}; - -THREE.Animation.prototype = { - - constructor: THREE.Animation, - - keyTypes: [ "pos", "rot", "scl" ], - - play: function ( startTime, weight ) { - - this.currentTime = startTime !== undefined ? startTime : 0; - this.weight = weight !== undefined ? weight : 1; - - this.isPlaying = true; - - this.reset(); - - THREE.AnimationHandler.play( this ); - - }, - - stop: function() { - - this.isPlaying = false; - - THREE.AnimationHandler.stop( this ); - - }, - - reset: function () { - - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { - - var object = this.hierarchy[ h ]; - - if ( object.animationCache === undefined ) { - - object.animationCache = { - animations: {}, - blending: { - positionWeight: 0.0, - quaternionWeight: 0.0, - scaleWeight: 0.0 - } - }; - } - - var name = this.data.name; - var animations = object.animationCache.animations; - var animationCache = animations[ name ]; - - if ( animationCache === undefined ) { - - animationCache = { - prevKey: { pos: 0, rot: 0, scl: 0 }, - nextKey: { pos: 0, rot: 0, scl: 0 }, - originalMatrix: object.matrix - }; - - animations[ name ] = animationCache; - - } - - // Get keys to match our current time - - for ( var t = 0; t < 3; t ++ ) { - - var type = this.keyTypes[ t ]; - - var prevKey = this.data.hierarchy[ h ].keys[ 0 ]; - var nextKey = this.getNextKeyWith( type, h, 1 ); - - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { - - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); - - } - - animationCache.prevKey[ type ] = prevKey; - animationCache.nextKey[ type ] = nextKey; - - } - - } - - }, - - resetBlendWeights: function () { - - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { - - var object = this.hierarchy[ h ]; - var animationCache = object.animationCache; - - if ( animationCache !== undefined ) { - - var blending = animationCache.blending; - - blending.positionWeight = 0.0; - blending.quaternionWeight = 0.0; - blending.scaleWeight = 0.0; - - } - - } - - }, - - update: ( function() { - - var points = []; - var target = new THREE.Vector3(); - var newVector = new THREE.Vector3(); - var newQuat = new THREE.Quaternion(); - - // Catmull-Rom spline - - var interpolateCatmullRom = function ( points, scale ) { - - var c = [], v3 = [], - point, intPoint, weight, w2, w3, - pa, pb, pc, pd; - - point = ( points.length - 1 ) * scale; - intPoint = Math.floor( point ); - weight = point - intPoint; - - c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; - c[ 1 ] = intPoint; - c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; - c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; - - pa = points[ c[ 0 ] ]; - pb = points[ c[ 1 ] ]; - pc = points[ c[ 2 ] ]; - pd = points[ c[ 3 ] ]; - - w2 = weight * weight; - w3 = weight * w2; - - v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); - v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); - v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); - - return v3; - - }; - - var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { - - var v0 = ( p2 - p0 ) * 0.5, - v1 = ( p3 - p1 ) * 0.5; - - return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; - - }; - - return function ( delta ) { - - if ( this.isPlaying === false ) return; - - this.currentTime += delta * this.timeScale; - - if ( this.weight === 0 ) - return; - - // - - var duration = this.data.length; - - if ( this.currentTime > duration || this.currentTime < 0 ) { - - if ( this.loop ) { - - this.currentTime %= duration; - - if ( this.currentTime < 0 ) - this.currentTime += duration; - - this.reset(); - - } else { - - this.stop(); - - } - - } - - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { - - var object = this.hierarchy[ h ]; - var animationCache = object.animationCache.animations[this.data.name]; - var blending = object.animationCache.blending; - - // loop through pos/rot/scl - - for ( var t = 0; t < 3; t ++ ) { - - // get keys - - var type = this.keyTypes[ t ]; - var prevKey = animationCache.prevKey[ type ]; - var nextKey = animationCache.nextKey[ type ]; - - if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) || - ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) { - - prevKey = this.data.hierarchy[ h ].keys[ 0 ]; - nextKey = this.getNextKeyWith( type, h, 1 ); - - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { - - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); - - } - - animationCache.prevKey[ type ] = prevKey; - animationCache.nextKey[ type ] = nextKey; - - } - - var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); - - var prevXYZ = prevKey[ type ]; - var nextXYZ = nextKey[ type ]; - - if ( scale < 0 ) scale = 0; - if ( scale > 1 ) scale = 1; - - // interpolate - - if ( type === "pos" ) { - - if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { - - newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; - - // blend - var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); - object.position.lerp( newVector, proportionalWeight ); - blending.positionWeight += this.weight; - - } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - - points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; - points[ 1 ] = prevXYZ; - points[ 2 ] = nextXYZ; - points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; - - scale = scale * 0.33 + 0.33; - - var currentPoint = interpolateCatmullRom( points, scale ); - var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); - blending.positionWeight += this.weight; - - // blend - - var vector = object.position; - - vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight; - vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight; - vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight; - - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - - var forwardPoint = interpolateCatmullRom( points, scale * 1.01 ); - - target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); - target.sub( vector ); - target.y = 0; - target.normalize(); - - var angle = Math.atan2( target.x, target.z ); - object.rotation.set( 0, angle, 0 ); - - } - - } - - } else if ( type === "rot" ) { - - THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); - - // Avoid paying the cost of an additional slerp if we don't have to - if ( blending.quaternionWeight === 0 ) { - - object.quaternion.copy(newQuat); - blending.quaternionWeight = this.weight; - - } else { - - var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight ); - THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); - blending.quaternionWeight += this.weight; - - } - - } else if ( type === "scl" ) { - - newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; - - var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight ); - object.scale.lerp( newVector, proportionalWeight ); - blending.scaleWeight += this.weight; - - } - - } - - } - - return true; - - }; - - } )(), - - getNextKeyWith: function ( type, h, key ) { - - var keys = this.data.hierarchy[ h ].keys; - - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - - key = key < keys.length - 1 ? key : keys.length - 1; - - } else { - - key = key % keys.length; - - } - - for ( ; key < keys.length; key ++ ) { - - if ( keys[ key ][ type ] !== undefined ) { - - return keys[ key ]; - - } - - } - - return this.data.hierarchy[ h ].keys[ 0 ]; - - }, - - getPrevKeyWith: function ( type, h, key ) { - - var keys = this.data.hierarchy[ h ].keys; - - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - - key = key > 0 ? key : 0; - - } else { - - key = key >= 0 ? key : key + keys.length; - - } - - - for ( ; key >= 0; key -- ) { - - if ( keys[ key ][ type ] !== undefined ) { - - return keys[ key ]; - - } - - } - - return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; - - } - -}; - -// File:src/extras/animation/KeyFrameAnimation.js - -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author khang duong - * @author erik kitson - */ - -THREE.KeyFrameAnimation = function ( data ) { - - this.root = data.node; - this.data = THREE.AnimationHandler.init( data ); - this.hierarchy = THREE.AnimationHandler.parse( this.root ); - this.currentTime = 0; - this.timeScale = 0.001; - this.isPlaying = false; - this.isPaused = true; - this.loop = true; - - // initialize to first keyframes - - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { - - var keys = this.data.hierarchy[h].keys, - sids = this.data.hierarchy[h].sids, - obj = this.hierarchy[h]; - - if ( keys.length && sids ) { - - for ( var s = 0; s < sids.length; s ++ ) { - - var sid = sids[ s ], - next = this.getNextKeyWith( sid, h, 0 ); - - if ( next ) { - - next.apply( sid ); - - } - - } - - obj.matrixAutoUpdate = false; - this.data.hierarchy[h].node.updateMatrix(); - obj.matrixWorldNeedsUpdate = true; - - } - - } - -}; - -THREE.KeyFrameAnimation.prototype = { - - constructor: THREE.KeyFrameAnimation, - - play: function ( startTime ) { - - this.currentTime = startTime !== undefined ? startTime : 0; - - if ( this.isPlaying === false ) { - - this.isPlaying = true; - - // reset key cache - - var h, hl = this.hierarchy.length, - object, - node; - - for ( h = 0; h < hl; h ++ ) { - - object = this.hierarchy[ h ]; - node = this.data.hierarchy[ h ]; - - if ( node.animationCache === undefined ) { - - node.animationCache = {}; - node.animationCache.prevKey = null; - node.animationCache.nextKey = null; - node.animationCache.originalMatrix = object.matrix; - - } - - var keys = this.data.hierarchy[h].keys; - - if (keys.length) { - - node.animationCache.prevKey = keys[ 0 ]; - node.animationCache.nextKey = keys[ 1 ]; - - this.startTime = Math.min( keys[0].time, this.startTime ); - this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); - - } - - } - - this.update( 0 ); - - } - - this.isPaused = false; - - THREE.AnimationHandler.play( this ); - - }, - - stop: function () { - - this.isPlaying = false; - this.isPaused = false; - - THREE.AnimationHandler.stop( this ); - - // reset JIT matrix and remove cache - - for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { - - var obj = this.hierarchy[ h ]; - var node = this.data.hierarchy[ h ]; - - if ( node.animationCache !== undefined ) { - - var original = node.animationCache.originalMatrix; - - original.copy( obj.matrix ); - obj.matrix = original; - - delete node.animationCache; - - } - - } - - }, - - update: function ( delta ) { - - if ( this.isPlaying === false ) return; - - this.currentTime += delta * this.timeScale; - - // - - var duration = this.data.length; - - if ( this.loop === true && this.currentTime > duration ) { - - this.currentTime %= duration; - - } - - this.currentTime = Math.min( this.currentTime, duration ); - - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { - - var object = this.hierarchy[ h ]; - var node = this.data.hierarchy[ h ]; - - var keys = node.keys, - animationCache = node.animationCache; - - - if ( keys.length ) { - - var prevKey = animationCache.prevKey; - var nextKey = animationCache.nextKey; - - if ( nextKey.time <= this.currentTime ) { - - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { - - prevKey = nextKey; - nextKey = keys[ prevKey.index + 1 ]; - - } - - animationCache.prevKey = prevKey; - animationCache.nextKey = nextKey; - - } - - if ( nextKey.time >= this.currentTime ) { - - prevKey.interpolate( nextKey, this.currentTime ); - - } else { - - prevKey.interpolate( nextKey, nextKey.time ); - - } - - this.data.hierarchy[ h ].node.updateMatrix(); - object.matrixWorldNeedsUpdate = true; - - } - - } - - }, - - getNextKeyWith: function ( sid, h, key ) { - - var keys = this.data.hierarchy[ h ].keys; - key = key % keys.length; - - for ( ; key < keys.length; key ++ ) { - - if ( keys[ key ].hasTarget( sid ) ) { - - return keys[ key ]; - - } - - } - - return keys[ 0 ]; - - }, - - getPrevKeyWith: function ( sid, h, key ) { - - var keys = this.data.hierarchy[ h ].keys; - key = key >= 0 ? key : key + keys.length; - - for ( ; key >= 0; key -- ) { - - if ( keys[ key ].hasTarget( sid ) ) { - - return keys[ key ]; - - } - - } - - return keys[ keys.length - 1 ]; - - } - -}; - -// File:src/extras/animation/MorphAnimation.js - -/** - * @author mrdoob / http://mrdoob.com - * @author willy-vvu / http://willy-vvu.github.io - */ - -THREE.MorphAnimation = function ( mesh ) { - - this.mesh = mesh; - this.frames = mesh.morphTargetInfluences.length; - this.currentTime = 0; - this.duration = 1000; - this.loop = true; - this.lastFrame = 0; - this.currentFrame = 0; - - this.isPlaying = false; - -}; - -THREE.MorphAnimation.prototype = { - - constructor: THREE.MorphAnimation, - - play: function () { - - this.isPlaying = true; - - }, - - pause: function () { - - this.isPlaying = false; - - }, - - update: function ( delta ) { - - if ( this.isPlaying === false ) return; - - this.currentTime += delta; - - if ( this.loop === true && this.currentTime > this.duration ) { - - this.currentTime %= this.duration; - - } - - this.currentTime = Math.min( this.currentTime, this.duration ); - - var interpolation = this.duration / this.frames; - var frame = Math.floor( this.currentTime / interpolation ); - - var influences = this.mesh.morphTargetInfluences; - - if ( frame != this.currentFrame ) { - - influences[ this.lastFrame ] = 0; - influences[ this.currentFrame ] = 1; - influences[ frame ] = 0; - - this.lastFrame = this.currentFrame; - this.currentFrame = frame; - - } - - influences[ frame ] = ( this.currentTime % interpolation ) / interpolation; - influences[ this.lastFrame ] = 1 - influences[ frame ]; - - } - -}; +THREE.ClosedSplineCurve3.prototype = Object.create( THREE.CatmullRomCurve3.prototype ); // File:src/extras/geometries/BoxGeometry.js @@ -24541,6 +32455,8 @@ THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegmen THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry; +THREE.CubeGeometry = THREE.BoxGeometry; + // File:src/extras/geometries/CircleGeometry.js /** @@ -24560,62 +32476,82 @@ THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { thetaLength: thetaLength }; - radius = radius || 50; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - var i, uvs = [], - center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 ); - - this.vertices.push(center); - uvs.push( centerUV ); - - for ( i = 0; i <= segments; i ++ ) { - - var vertex = new THREE.Vector3(); - var segment = thetaStart + i / segments * thetaLength; - - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - - this.vertices.push( vertex ); - uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) ); - - } - - var n = new THREE.Vector3( 0, 0, 1 ); - - for ( i = 1; i <= segments; i ++ ) { - - this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] ); - - } - - this.computeFaceNormals(); - - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + this.fromBufferGeometry( new THREE.CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); }; THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry; -// File:src/extras/geometries/CubeGeometry.js +// File:src/extras/geometries/CircleBufferGeometry.js /** - * @author mrdoob / http://mrdoob.com/ + * @author benaadams / https://twitter.com/ben_a_adams */ +THREE.CircleBufferGeometry = function ( radius, segments, thetaStart, thetaLength ) { -THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + THREE.BufferGeometry.call( this ); - THREE.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' ); - return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ); + this.type = 'CircleBufferGeometry'; - }; + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + var vertices = segments + 2; + + var positions = new Float32Array( vertices * 3 ); + var normals = new Float32Array( vertices * 3 ); + var uvs = new Float32Array( vertices * 2 ); + + // center data is already zero, but need to set a few extras + normals[ 2 ] = 1.0; + uvs[ 0 ] = 0.5; + uvs[ 1 ] = 0.5; + + for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { + + var segment = thetaStart + s / segments * thetaLength; + + positions[ i ] = radius * Math.cos( segment ); + positions[ i + 1 ] = radius * Math.sin( segment ); + + normals[ i + 2 ] = 1; // normal z + + uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2; + uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2; + + } + + var indices = []; + + for ( var i = 1; i <= segments; i ++ ) { + + indices.push( i, i + 1, 0 ); + + } + + this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.CircleBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.CircleBufferGeometry.prototype.constructor = THREE.CircleBufferGeometry; // File:src/extras/geometries/CylinderGeometry.js @@ -24751,7 +32687,7 @@ THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegme var uv2 = uvs[ 0 ][ x + 1 ].clone(); var uv3 = new THREE.Vector2( uv2.x, 0 ); - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 1 ) ); this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); } @@ -24778,7 +32714,7 @@ THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegme var uv2 = uvs[ heightSegments ][ x ].clone(); var uv3 = new THREE.Vector2( uv2.x, 1 ); - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 2 ) ); this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); } @@ -24792,6 +32728,104 @@ THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegme THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; +// File:src/extras/geometries/EdgesGeometry.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.EdgesGeometry = function ( geometry, thresholdAngle ) { + + THREE.BufferGeometry.call( this ); + + thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; + + var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) ); + + var edge = [ 0, 0 ], hash = {}; + + function sortFunction( a, b ) { + + return a - b; + + } + + var keys = [ 'a', 'b', 'c' ]; + + var geometry2; + + if ( geometry instanceof THREE.BufferGeometry ) { + + geometry2 = new THREE.Geometry(); + geometry2.fromBufferGeometry( geometry ); + + } else { + + geometry2 = geometry.clone(); + + } + + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); + + var vertices = geometry2.vertices; + var faces = geometry2.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + + } else { + + hash[ key ].face2 = i; + + } + + } + + } + + var coords = []; + + for ( var key in hash ) { + + var h = hash[ key ]; + + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { + + var vertex = vertices[ h.vert1 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); + + vertex = vertices[ h.vert2 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( coords ), 3 ) ); + +}; + +THREE.EdgesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.EdgesGeometry.prototype.constructor = THREE.EdgesGeometry; + // File:src/extras/geometries/ExtrudeGeometry.js /** @@ -24802,7 +32836,7 @@ THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; * parameters = { * * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segements of extrude spline too + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * amount: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel @@ -24813,8 +32847,6 @@ THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) * frames: // containing arrays of tangents, normals, binormals * - * material: // material index for front and back faces - * extrudeMaterial: // material index for extrusion and beveled faces * uvGenerator: // object that provides UV generator functions * * } @@ -24823,15 +32855,17 @@ THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; THREE.ExtrudeGeometry = function ( shapes, options ) { if ( typeof( shapes ) === "undefined" ) { + shapes = []; return; + } THREE.Geometry.call( this ); this.type = 'ExtrudeGeometry'; - shapes = shapes instanceof Array ? shapes : [ shapes ]; + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; this.addShapeList( shapes, options ); @@ -24851,12 +32885,16 @@ THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry; THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + var sl = shapes.length; for ( var s = 0; s < sl; s ++ ) { + var shape = shapes[ s ]; this.addShape( shape, options ); + } + }; THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { @@ -24876,9 +32914,6 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { var extrudePath = options.extrudePath; var extrudePts, extrudeByPath = false; - var material = options.material; - var extrudeMaterial = options.extrudeMaterial; - // Use default WorldUVGenerator if no UV generators are specified. var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; @@ -24895,7 +32930,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { // Reuse TNB from TubeGeomtry for now. // TODO1 - have a .isClosed in spline? - splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames( extrudePath, steps, false ); // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); @@ -24915,7 +32950,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { } - // Variables initalization + // Variables initialization var ahole, h, hl; // looping of holes var scope = this; @@ -24927,7 +32962,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { var vertices = shapePoints.shape; var holes = shapePoints.holes; - var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ; + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); if ( reverse ) { @@ -24939,7 +32974,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { ahole = holes[ h ]; - if ( THREE.Shape.Utils.isClockWise( ahole ) ) { + if ( THREE.ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = ahole.reverse(); @@ -24952,7 +32987,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { } - var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); /* Vertices */ @@ -24969,7 +33004,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { function scalePt2 ( pt, vec, size ) { - if ( ! vec ) THREE.error( "THREE.ExtrudeGeometry: vec does not exist" ); + if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); return vec.clone().multiplyScalar( size ).add( pt ); @@ -24985,15 +33020,13 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { function getBevelVec( inPt, inPrev, inNext ) { - var EPSILON = 0.0000000001; - // computes for inPt the corresponding point inPt' on a new contour - // shiftet by 1 unit (length of normalized vector) to the left + // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt' is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. - + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) @@ -25001,70 +33034,102 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; - + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - - // check for colinear edges - var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear - + + // check for collinear edges + var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + if ( Math.abs( collinear0 ) > Number.EPSILON ) { + + // not collinear + // length of vectors for normalizing - + var v_prev_len = Math.sqrt( v_prev_lensq ); var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - + // shift adjacent points by unit vectors to the left - + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - + // scaling factor for v_prev to intersection point - + var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - + // vector from inPt to intersection point - + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - + // Don't normalize!, otherwise sharp corners become ugly // but prevent crazy spikes - var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ) + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); if ( v_trans_lensq <= 2 ) { + return new THREE.Vector2( v_trans_x, v_trans_y ); + } else { + shrink_by = Math.sqrt( v_trans_lensq / 2 ); + } - - } else { // handle special case of colinear edges + + } else { + + // handle special case of collinear edges var direction_eq = false; // assumes: opposite - if ( v_prev_x > EPSILON ) { - if ( v_next_x > EPSILON ) { direction_eq = true; } - } else { - if ( v_prev_x < - EPSILON ) { - if ( v_next_x < - EPSILON ) { direction_eq = true; } - } else { - if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; } + if ( v_prev_x > Number.EPSILON ) { + + if ( v_next_x > Number.EPSILON ) { + + direction_eq = true; + } + + } else { + + if ( v_prev_x < - Number.EPSILON ) { + + if ( v_next_x < - Number.EPSILON ) { + + direction_eq = true; + + } + + } else { + + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + + direction_eq = true; + + } + + } + } if ( direction_eq ) { + // console.log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); + } else { + // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); + } } @@ -25115,14 +33180,15 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { - //for ( b = bevelSegments; b > 0; b -- ) { + + //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //z = bevelThickness * t; - bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ) ; // curved - //bs = bevelSize * t ; // linear + bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ); // curved + //bs = bevelSize * t; // linear // contract shape @@ -25169,10 +33235,10 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); - binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - position2.copy( extrudePts[0] ).add(normal).add(binormal); + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); @@ -25199,10 +33265,10 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - position2.copy( extrudePts[s] ).add( normal ).add( binormal ); + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); @@ -25221,7 +33287,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); - bs = bevelSize * Math.sin ( t * Math.PI / 2 ) ; + bs = bevelSize * Math.sin ( t * Math.PI / 2 ); // contract shape @@ -25276,7 +33342,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { if ( bevelEnabled ) { - var layer = 0 ; // steps + 1 + var layer = 0; // steps + 1 var offset = vlen * layer; // Bottom faces @@ -25319,6 +33385,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } + } } @@ -25371,6 +33438,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { f4( a, b, c, d, contour, s, sl, j, k ); } + } } @@ -25388,8 +33456,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { b += shapesOffset; c += shapesOffset; - // normal, color, material - scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + scope.faces.push( new THREE.Face3( a, b, c, null, null, 0 ) ); var uvs = uvgen.generateTopUV( scope, a, b, c ); @@ -25404,8 +33471,8 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { c += shapesOffset; d += shapesOffset; - scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) ); - scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) ); + scope.faces.push( new THREE.Face3( a, b, d, null, null, 1 ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, 1 ) ); var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); @@ -25444,20 +33511,25 @@ THREE.ExtrudeGeometry.WorldUVGenerator = { var d = vertices[ indexD ]; if ( Math.abs( a.y - b.y ) < 0.01 ) { + return [ new THREE.Vector2( a.x, 1 - a.z ), new THREE.Vector2( b.x, 1 - b.z ), new THREE.Vector2( c.x, 1 - c.z ), new THREE.Vector2( d.x, 1 - d.z ) ]; + } else { + return [ new THREE.Vector2( a.y, 1 - a.z ), new THREE.Vector2( b.y, 1 - b.z ), new THREE.Vector2( c.y, 1 - c.z ), new THREE.Vector2( d.y, 1 - d.z ) ]; + } + } }; @@ -25485,7 +33557,7 @@ THREE.ShapeGeometry = function ( shapes, options ) { this.type = 'ShapeGeometry'; - if ( shapes instanceof Array === false ) shapes = [ shapes ]; + if ( Array.isArray( shapes ) === false ) shapes = [ shapes ]; this.addShapeList( shapes, options ); @@ -25532,7 +33604,7 @@ THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { var vertices = shapePoints.shape; var holes = shapePoints.holes; - var reverse = ! THREE.Shape.Utils.isClockWise( vertices ); + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); if ( reverse ) { @@ -25544,7 +33616,7 @@ THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { hole = holes[ i ]; - if ( THREE.Shape.Utils.isClockWise( hole ) ) { + if ( THREE.ShapeUtils.isClockWise( hole ) ) { holes[ i ] = hole.reverse(); @@ -25556,12 +33628,10 @@ THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { } - var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); // Vertices - var contour = vertices; - for ( i = 0, l = holes.length; i < l; i ++ ) { hole = holes[ i ]; @@ -25602,10 +33672,10 @@ THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { /** * @author astrodud / http://astrodud.isgreat.org/ * @author zz85 / https://github.com/zz85 - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ -// points - to create a closed torus, one must use a set of points +// points - to create a closed torus, one must use a set of points // like so: [ a, b, c, d, a ], see first is the same as last. // segments - the number of circumference segments to create // phiStart - the starting radian @@ -25636,18 +33706,18 @@ THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { var phi = phiStart + i * inverseSegments * phiLength; - var c = Math.cos( phi ), - s = Math.sin( phi ); + var sin = Math.sin( phi ); + var cos = Math.cos( phi ); for ( var j = 0, jl = points.length; j < jl; j ++ ) { - var pt = points[ j ]; + var point = points[ j ]; var vertex = new THREE.Vector3(); - vertex.x = c * pt.x - s * pt.y; - vertex.y = s * pt.x + c * pt.y; - vertex.z = pt.z; + vertex.x = point.x * sin; + vertex.y = point.y; + vertex.z = point.x * cos; this.vertices.push( vertex ); @@ -25715,8 +33785,6 @@ THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry; THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { - console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' ); - THREE.Geometry.call( this ); this.type = 'PlaneGeometry'; @@ -25758,8 +33826,8 @@ THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegme var width_half = width / 2; var height_half = height / 2; - var gridX = widthSegments || 1; - var gridY = heightSegments || 1; + var gridX = Math.floor( widthSegments ) || 1; + var gridY = Math.floor( heightSegments ) || 1; var gridX1 = gridX + 1; var gridY1 = gridY + 1; @@ -25782,12 +33850,12 @@ THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegme var x = ix * segment_width - width_half; - vertices[ offset ] = x; + vertices[ offset ] = x; vertices[ offset + 1 ] = - y; normals[ offset + 2 ] = 1; - uvs[ offset2 ] = ix / gridX; + uvs[ offset2 ] = ix / gridX; uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); offset += 3; @@ -25810,7 +33878,7 @@ THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegme var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = ( ix + 1 ) + gridX1 * iy; - indices[ offset ] = a; + indices[ offset ] = a; indices[ offset + 1 ] = b; indices[ offset + 2 ] = d; @@ -25824,7 +33892,7 @@ THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegme } - this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) ); + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); @@ -25866,9 +33934,13 @@ THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegm var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); - for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring + for ( i = 0; i < phiSegments + 1; i ++ ) { - for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle + // concentric circles inside ring + + for ( o = 0; o < thetaSegments + 1; o ++ ) { + + // number of segments per circle var vertex = new THREE.Vector3(); var segment = thetaStart + o / thetaSegments * thetaLength; @@ -25877,6 +33949,7 @@ THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegm this.vertices.push( vertex ); uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) ); + } radius += radiusStep; @@ -25885,11 +33958,15 @@ THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegm var n = new THREE.Vector3( 0, 0, 1 ); - for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring + for ( i = 0; i < phiSegments; i ++ ) { - var thetaSegment = i * (thetaSegments + 1); + // concentric circles inside ring - for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle + var thetaSegment = i * ( thetaSegments + 1 ); + + for ( o = 0; o < thetaSegments ; o ++ ) { + + // number of segments per circle var segment = o + thetaSegment; @@ -25898,16 +33975,17 @@ THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegm var v3 = segment + thetaSegments + 2; this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] ); v1 = segment; v2 = segment + thetaSegments + 2; v3 = segment + 1; this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] ); } + } this.computeFaceNormals(); @@ -25919,7 +33997,6 @@ THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegm THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.RingGeometry.prototype.constructor = THREE.RingGeometry; - // File:src/extras/geometries/SphereGeometry.js /** @@ -25939,7 +34016,37 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, - thetaLength: thetaLength + thetaLength: thetaLength + }; + + this.fromBufferGeometry( new THREE.SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); + +}; + +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; + +// File:src/extras/geometries/SphereBufferGeometry.js + +/** + * @author benaadams / https://twitter.com/ben_a_adams + * based on THREE.SphereGeometry + */ + +THREE.SphereBufferGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'SphereBufferGeometry'; + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength }; radius = radius || 50; @@ -25953,88 +34060,75 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - var x, y, vertices = [], uvs = []; + var thetaEnd = thetaStart + thetaLength; - for ( y = 0; y <= heightSegments; y ++ ) { + var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); + + var positions = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + + var index = 0, vertices = [], normal = new THREE.Vector3(); + + for ( var y = 0; y <= heightSegments; y ++ ) { var verticesRow = []; - var uvsRow = []; - for ( x = 0; x <= widthSegments; x ++ ) { + var v = y / heightSegments; + + for ( var x = 0; x <= widthSegments; x ++ ) { var u = x / widthSegments; - var v = y / heightSegments; - var vertex = new THREE.Vector3(); - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + var py = radius * Math.cos( thetaStart + v * thetaLength ); + var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - this.vertices.push( vertex ); + normal.set( px, py, pz ).normalize(); - verticesRow.push( this.vertices.length - 1 ); - uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + positions.setXYZ( index, px, py, pz ); + normals.setXYZ( index, normal.x, normal.y, normal.z ); + uvs.setXY( index, u, 1 - v ); + + verticesRow.push( index ); + + index ++; } vertices.push( verticesRow ); - uvs.push( uvsRow ); } - for ( y = 0; y < heightSegments; y ++ ) { + var indices = []; - for ( x = 0; x < widthSegments; x ++ ) { + for ( var y = 0; y < heightSegments; y ++ ) { + + for ( var x = 0; x < widthSegments; x ++ ) { var v1 = vertices[ y ][ x + 1 ]; var v2 = vertices[ y ][ x ]; var v3 = vertices[ y + 1 ][ x ]; var v4 = vertices[ y + 1 ][ x + 1 ]; - var n1 = this.vertices[ v1 ].clone().normalize(); - var n2 = this.vertices[ v2 ].clone().normalize(); - var n3 = this.vertices[ v3 ].clone().normalize(); - var n4 = this.vertices[ v4 ].clone().normalize(); - - var uv1 = uvs[ y ][ x + 1 ].clone(); - var uv2 = uvs[ y ][ x ].clone(); - var uv3 = uvs[ y + 1 ][ x ].clone(); - var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); - - if ( Math.abs( this.vertices[ v1 ].y ) === radius ) { - - uv1.x = ( uv1.x + uv2.x ) / 2; - this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); - - } else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) { - - uv3.x = ( uv3.x + uv4.x ) / 2; - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); - - } else { - - this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); - - this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); - - } + if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); + if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); } } - this.computeFaceNormals(); + this.setIndex( new ( positions.count > 65535 ? THREE.Uint32Attribute : THREE.Uint16Attribute )( indices, 1 ) ); + this.addAttribute( 'position', positions ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); }; -THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); -THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; +THREE.SphereBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.SphereBufferGeometry.prototype.constructor = THREE.SphereBufferGeometry; // File:src/extras/geometries/TextGeometry.js @@ -26042,45 +34136,35 @@ THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; * @author zz85 / http://www.lab4games.net/zz85/blog * @author alteredq / http://alteredqualia.com/ * - * For creating 3D text geometry in three.js - * * Text = 3D Text * * parameters = { - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves + * font: , // font * - * font: , // font name - * weight: , // font weight (normal, bold) - * style: , // font style (normal, italics) - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline is bevel - * } + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: // how far from text outline is bevel + * } */ -/* Usage Examples - - // TextGeometry wrapper - - var text3d = new TextGeometry( text, options ); - - // Complete manner - - var textShapes = THREE.FontUtils.generateShapes( text, options ); - var text3d = new ExtrudeGeometry( textShapes, options ); - -*/ - - THREE.TextGeometry = function ( text, parameters ) { parameters = parameters || {}; - var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + var font = parameters.font; + + if ( font instanceof THREE.Font === false ) { + + console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); + return new THREE.Geometry(); + + } + + var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); // translate parameters to ExtrudeGeometry API @@ -26092,7 +34176,7 @@ THREE.TextGeometry = function ( text, parameters ) { if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; - THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + THREE.ExtrudeGeometry.call( this, shapes, parameters ); this.type = 'TextGeometry'; @@ -26213,7 +34297,7 @@ THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegmen p = p || 2; q = q || 3; heightScale = heightScale || 1; - + var grid = new Array( radialSegments ); var tang = new THREE.Vector3(); var n = new THREE.Vector3(); @@ -26274,6 +34358,7 @@ THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegmen this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); } + } this.computeFaceNormals(); @@ -26326,7 +34411,8 @@ THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, segments: segments, radius: radius, radialSegments: radialSegments, - closed: closed + closed: closed, + taper: taper }; segments = segments || 64; @@ -26370,7 +34456,7 @@ THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, } - // consruct the grid + // construct the grid for ( i = 0; i < numpoints; i ++ ) { @@ -26401,6 +34487,7 @@ THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); } + } @@ -26410,8 +34497,8 @@ THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, for ( j = 0; j < radialSegments; j ++ ) { - ip = ( closed ) ? (i + 1) % segments : i + 1; - jp = (j + 1) % radialSegments; + ip = ( closed ) ? ( i + 1 ) % segments : i + 1; + jp = ( j + 1 ) % radialSegments; a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** b = grid[ ip ][ j ]; @@ -26430,6 +34517,7 @@ THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); } + } this.computeFaceNormals(); @@ -26466,7 +34554,6 @@ THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { numpoints = segments + 1, theta, - epsilon = 0.0001, smallest, tx, ty, tz, @@ -26516,7 +34603,8 @@ THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { */ function initialNormal3() { - // select an initial normal vector perpenicular to the first tangent vector, + + // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the smallest tangent xyz component normals[ 0 ] = new THREE.Vector3(); @@ -26527,23 +34615,30 @@ THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { tz = Math.abs( tangents[ 0 ].z ); if ( tx <= smallest ) { + smallest = tx; normal.set( 1, 0, 0 ); + } if ( ty <= smallest ) { + smallest = ty; normal.set( 0, 1, 0 ); + } if ( tz <= smallest ) { + normal.set( 0, 0, 1 ); + } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + } @@ -26557,7 +34652,7 @@ THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - if ( vec.length() > epsilon ) { + if ( vec.length() > Number.EPSILON ) { vec.normalize(); @@ -26594,6 +34689,7 @@ THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { } } + }; // File:src/extras/geometries/PolyhedronGeometry.js @@ -26634,11 +34730,11 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { - var v1 = p[ indices[ i ] ]; + var v1 = p[ indices[ i ] ]; var v2 = p[ indices[ i + 1 ] ]; var v3 = p[ indices[ i + 2 ] ]; - faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, j ); } @@ -26661,10 +34757,12 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { var x1 = uvs[ 1 ].x; var x2 = uvs[ 2 ].x; - var max = Math.max( x0, Math.max( x1, x2 ) ); - var min = Math.min( x0, Math.min( x1, x2 ) ); + var max = Math.max( x0, x1, x2 ); + var min = Math.min( x0, x1, x2 ); - if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary + if ( max > 0.9 && min < 0.1 ) { + + // 0.9 is somewhat arbitrary if ( x0 < 0.2 ) uvs[ 0 ].x += 1; if ( x1 < 0.2 ) uvs[ 1 ].x += 1; @@ -26713,9 +34811,9 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { // Approximate a curved face with recursively sub-divided triangles. - function make( v1, v2, v3 ) { + function make( v1, v2, v3, materialIndex ) { - var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, materialIndex ); that.faces.push( face ); centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); @@ -26735,12 +34833,14 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { function subdivide( face, detail ) { - var cols = Math.pow(2, detail); + var cols = Math.pow( 2, detail ); var a = prepare( that.vertices[ face.a ] ); var b = prepare( that.vertices[ face.b ] ); var c = prepare( that.vertices[ face.c ] ); var v = []; + var materialIndex = face.materialIndex; + // Construct all of the vertices for this subdivision. for ( var i = 0 ; i <= cols; i ++ ) { @@ -26751,9 +34851,9 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { var bj = prepare( b.clone().lerp( c, i / cols ) ); var rows = cols - i; - for ( var j = 0; j <= rows; j ++) { + for ( var j = 0; j <= rows; j ++ ) { - if ( j == 0 && i == cols ) { + if ( j === 0 && i === cols ) { v[ i ][ j ] = aj; @@ -26771,24 +34871,26 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { for ( var i = 0; i < cols ; i ++ ) { - for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { + for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { var k = Math.floor( j / 2 ); - if ( j % 2 == 0 ) { + if ( j % 2 === 0 ) { make( - v[ i ][ k + 1], + v[ i ][ k + 1 ], v[ i + 1 ][ k ], - v[ i ][ k ] + v[ i ][ k ], + materialIndex ); } else { make( v[ i ][ k + 1 ], - v[ i + 1][ k + 1], - v[ i + 1 ][ k ] + v[ i + 1 ][ k + 1 ], + v[ i + 1 ][ k ], + materialIndex ); } @@ -26842,33 +34944,28 @@ THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry; THREE.DodecahedronGeometry = function ( radius, detail ) { - this.parameters = { - radius: radius, - detail: detail - }; - var t = ( 1 + Math.sqrt( 5 ) ) / 2; var r = 1 / t; var vertices = [ // (±1, ±1, ±1) - -1, -1, -1, -1, -1, 1, - -1, 1, -1, -1, 1, 1, - 1, -1, -1, 1, -1, 1, - 1, 1, -1, 1, 1, 1, + - 1, - 1, - 1, - 1, - 1, 1, + - 1, 1, - 1, - 1, 1, 1, + 1, - 1, - 1, 1, - 1, 1, + 1, 1, - 1, 1, 1, 1, // (0, ±1/φ, ±φ) - 0, -r, -t, 0, -r, t, - 0, r, -t, 0, r, t, + 0, - r, - t, 0, - r, t, + 0, r, - t, 0, r, t, // (±1/φ, ±φ, 0) - -r, -t, 0, -r, t, 0, - r, -t, 0, r, t, 0, + - r, - t, 0, - r, t, 0, + r, - t, 0, r, t, 0, // (±φ, 0, ±1/φ) - -t, 0, -r, t, 0, -r, - -t, 0, r, t, 0, r + - t, 0, - r, t, 0, - r, + - t, 0, r, t, 0, r ]; var indices = [ @@ -26888,9 +34985,16 @@ THREE.DodecahedronGeometry = function ( radius, detail ) { THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + this.type = 'DodecahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + }; -THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.DodecahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry; // File:src/extras/geometries/IcosahedronGeometry.js @@ -26924,9 +35028,10 @@ THREE.IcosahedronGeometry = function ( radius, detail ) { radius: radius, detail: detail }; + }; -THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.IcosahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; // File:src/extras/geometries/OctahedronGeometry.js @@ -26937,13 +35042,8 @@ THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; THREE.OctahedronGeometry = function ( radius, detail ) { - this.parameters = { - radius: radius, - detail: detail - }; - var vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1 + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 ]; var indices = [ @@ -26958,9 +35058,10 @@ THREE.OctahedronGeometry = function ( radius, detail ) { radius: radius, detail: detail }; + }; -THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.OctahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry; // File:src/extras/geometries/TetrahedronGeometry.js @@ -26990,7 +35091,7 @@ THREE.TetrahedronGeometry = function ( radius, detail ) { }; -THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TetrahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry; // File:src/extras/geometries/ParametricGeometry.js @@ -27037,6 +35138,7 @@ THREE.ParametricGeometry = function ( func, slices, stacks ) { verts.push( p ); } + } var a, b, c, d; @@ -27048,8 +35150,8 @@ THREE.ParametricGeometry = function ( func, slices, stacks ) { a = i * sliceCount + j; b = i * sliceCount + j + 1; - c = (i + 1) * sliceCount + j + 1; - d = (i + 1) * sliceCount + j; + c = ( i + 1 ) * sliceCount + j + 1; + d = ( i + 1 ) * sliceCount + j; uva = new THREE.Vector2( j / slices, i / stacks ); uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); @@ -27080,6 +35182,191 @@ THREE.ParametricGeometry = function ( func, slices, stacks ) { THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry; +// File:src/extras/geometries/WireframeGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeGeometry = function ( geometry ) { + + THREE.BufferGeometry.call( this ); + + var edge = [ 0, 0 ], hash = {}; + + function sortFunction( a, b ) { + + return a - b; + + } + + var keys = [ 'a', 'b', 'c' ]; + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var faces = geometry.faces; + var numEdges = 0; + + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var vertex = vertices[ edges [ 2 * i + j ] ]; + + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else if ( geometry instanceof THREE.BufferGeometry ) { + + if ( geometry.index !== null ) { + + // Indexed BufferGeometry + + var indices = geometry.index.array; + var vertices = geometry.attributes.position; + var groups = geometry.groups; + var numEdges = 0; + + if ( groups.length === 0 ) { + + geometry.addGroup( 0, indices.length ); + + } + + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); + + for ( var o = 0, ol = groups.length; o < ol; ++ o ) { + + var group = groups[ o ]; + + var start = group.start; + var count = group.count; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = indices[ i + j ]; + edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var index = 6 * i + 3 * j; + var index2 = edges[ 2 * i + j ]; + + coords[ index + 0 ] = vertices.getX( index2 ); + coords[ index + 1 ] = vertices.getY( index2 ); + coords[ index + 2 ] = vertices.getZ( index2 ); + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else { + + // non-indexed BufferGeometry + + var vertices = geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numTris; i < l; i ++ ) { + + for ( var j = 0; j < 3; j ++ ) { + + var index = 18 * i + 6 * j; + + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; + + var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); + coords[ index + 3 ] = vertices[ index2 ]; + coords[ index + 4 ] = vertices[ index2 + 1 ]; + coords[ index + 5 ] = vertices[ index2 + 2 ]; + + } + + } + + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } + + } + +}; + +THREE.WireframeGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.WireframeGeometry.prototype.constructor = THREE.WireframeGeometry; + // File:src/extras/helpers/AxisHelper.js /** @@ -27109,11 +35396,11 @@ THREE.AxisHelper = function ( size ) { var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + THREE.LineSegments.call( this, geometry, material ); }; -THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.AxisHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.AxisHelper.prototype.constructor = THREE.AxisHelper; // File:src/extras/helpers/ArrowHelper.js @@ -27121,7 +35408,7 @@ THREE.AxisHelper.prototype.constructor = THREE.AxisHelper; /** * @author WestLangley / http://github.com/WestLangley * @author zz85 / http://github.com/zz85 - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * * Creates an arrow for visualizing directions * @@ -27140,9 +35427,9 @@ THREE.ArrowHelper = ( function () { lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); - coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + coneGeometry.translate( 0, - 0.5, 0 ); - return function ( dir, origin, length, color, headLength, headWidth ) { + return function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { // dir is assumed to be normalized @@ -27154,7 +35441,7 @@ THREE.ArrowHelper = ( function () { if ( headWidth === undefined ) headWidth = 0.2 * headLength; this.position.copy( origin ); - + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); this.line.matrixAutoUpdate = false; this.add( this.line ); @@ -27178,7 +35465,7 @@ THREE.ArrowHelper.prototype.setDirection = ( function () { var axis = new THREE.Vector3(); var radians; - return function ( dir ) { + return function setDirection( dir ) { // dir is assumed to be normalized @@ -27209,7 +35496,7 @@ THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; - this.line.scale.set( 1, length - headLength, 1 ); + this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); this.line.updateMatrix(); this.cone.scale.set( headWidth, headLength, headWidth ); @@ -27233,10 +35520,14 @@ THREE.ArrowHelper.prototype.setColor = function ( color ) { THREE.BoxHelper = function ( object ) { - var geometry = new THREE.BufferGeometry(); - geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) ); + var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); + var positions = new Float32Array( 8 * 3 ); - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ) ); if ( object !== undefined ) { @@ -27246,88 +35537,57 @@ THREE.BoxHelper = function ( object ) { }; -THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.BoxHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.BoxHelper.prototype.constructor = THREE.BoxHelper; -THREE.BoxHelper.prototype.update = function ( object ) { +THREE.BoxHelper.prototype.update = ( function () { - var geometry = object.geometry; + var box = new THREE.Box3(); - if ( geometry.boundingBox === null ) { + return function ( object ) { - geometry.computeBoundingBox(); + box.setFromObject( object ); - } + if ( box.isEmpty() ) return; - var min = geometry.boundingBox.min; - var max = geometry.boundingBox.max; + var min = box.min; + var max = box.max; - /* - 5____4 - 1/___0/| - | 6__|_7 - 2/___3/ + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ - 0: max.x, max.y, max.z - 1: min.x, max.y, max.z - 2: min.x, min.y, max.z - 3: max.x, min.y, max.z - 4: max.x, max.y, min.z - 5: min.x, max.y, min.z - 6: min.x, min.y, min.z - 7: max.x, min.y, min.z - */ + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ - var vertices = this.geometry.attributes.position.array; + var position = this.geometry.attributes.position; + var array = position.array; - vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z; - vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z; + array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; + array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; + array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; + array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; + array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; + array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; + array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; + array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; - vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z; - vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z; + position.needsUpdate = true; - vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z; - vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z; + this.geometry.computeBoundingSphere(); - vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z; - vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z; + }; - // - - vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z; - vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z; - - vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z; - vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z; - - vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z; - vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z; - - vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z; - vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z; - - // - - vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z; - vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z; - - vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z; - vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z; - - vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z; - vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z; - - vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z; - vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z; - - this.geometry.attributes.position.needsUpdate = true; - - this.geometry.computeBoundingSphere(); - - this.matrix = object.matrixWorld; - this.matrixAutoUpdate = false; - -}; +} )(); // File:src/extras/helpers/BoundingBoxHelper.js @@ -27457,9 +35717,11 @@ THREE.CameraHelper = function ( camera ) { } - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + THREE.LineSegments.call( this, geometry, material ); this.camera = camera; + this.camera.updateProjectionMatrix(); + this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; @@ -27469,17 +35731,17 @@ THREE.CameraHelper = function ( camera ) { }; -THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.CameraHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.CameraHelper.prototype.constructor = THREE.CameraHelper; THREE.CameraHelper.prototype.update = function () { var geometry, pointMap; - + var vector = new THREE.Vector3(); var camera = new THREE.Camera(); - var setPoint = function ( point, x, y, z ) { + function setPoint( point, x, y, z ) { vector.set( x, y, z ).unproject( camera ); @@ -27495,7 +35757,7 @@ THREE.CameraHelper.prototype.update = function () { } - }; + } return function () { @@ -27612,6 +35874,7 @@ THREE.DirectionalLightHelper.prototype.dispose = function () { this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); + }; THREE.DirectionalLightHelper.prototype.update = function () { @@ -27643,7 +35906,7 @@ THREE.DirectionalLightHelper.prototype.update = function () { * @author WestLangley / http://github.com/WestLangley * @param object THREE.Mesh whose geometry will be used * @param hex line color - * @param thresholdAngle the minimim angle (in degrees), + * @param thresholdAngle the minimum angle (in degrees), * between the face normals of adjacent faces, * that is required to render an edge. A value of 10 means * an edge is only rendered if the angle is at least 10 degrees. @@ -27652,97 +35915,15 @@ THREE.DirectionalLightHelper.prototype.update = function () { THREE.EdgesHelper = function ( object, hex, thresholdAngle ) { var color = ( hex !== undefined ) ? hex : 0xffffff; - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) ); - - var edge = [ 0, 0 ], hash = {}; - var sortFunction = function ( a, b ) { return a - b }; - - var keys = [ 'a', 'b', 'c' ]; - var geometry = new THREE.BufferGeometry(); - - var geometry2; - - if ( object.geometry instanceof THREE.BufferGeometry ) { - - geometry2 = new THREE.Geometry(); - geometry2.fromBufferGeometry( object.geometry ); - - } else { - - geometry2 = object.geometry.clone(); - - } - - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); - - var vertices = geometry2.vertices; - var faces = geometry2.faces; - var numEdges = 0; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; - numEdges ++; - - } else { - - hash[ key ].face2 = i; - - } - - } - - } - - var coords = new Float32Array( numEdges * 2 * 3 ); - - var index = 0; - - for ( var key in hash ) { - - var h = hash[ key ]; - - if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { - - var vertex = vertices[ h.vert1 ]; - coords[ index ++ ] = vertex.x; - coords[ index ++ ] = vertex.y; - coords[ index ++ ] = vertex.z; - - vertex = vertices[ h.vert2 ]; - coords[ index ++ ] = vertex.x; - coords[ index ++ ] = vertex.y; - coords[ index ++ ] = vertex.z; - - } - - } - - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + THREE.LineSegments.call( this, new THREE.EdgesGeometry( object.geometry, thresholdAngle ), new THREE.LineBasicMaterial( { color: color } ) ); this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; }; -THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.EdgesHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper; // File:src/extras/helpers/FaceNormalsHelper.js @@ -27754,6 +35935,8 @@ THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper; THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { + // FaceNormalsHelper only supports THREE.Geometry + this.object = object; this.size = ( size !== undefined ) ? size : 1; @@ -27762,66 +35945,99 @@ THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { var width = ( linewidth !== undefined ) ? linewidth : 1; - var geometry = new THREE.Geometry(); + // - var faces = this.object.geometry.faces; + var nNormals = 0; - for ( var i = 0, l = faces.length; i < l; i ++ ) { + var objGeometry = this.object.geometry; - geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + if ( objGeometry instanceof THREE.Geometry ) { + + nNormals = objGeometry.faces.length; + + } else { + + console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' ); } - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + // + + var geometry = new THREE.BufferGeometry(); + + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); + + geometry.addAttribute( 'position', positions ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); + + // this.matrixAutoUpdate = false; - - this.normalMatrix = new THREE.Matrix3(); - this.update(); }; -THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.FaceNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper; -THREE.FaceNormalsHelper.prototype.update = function () { +THREE.FaceNormalsHelper.prototype.update = ( function () { - var vertices = this.geometry.vertices; + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); - var object = this.object; - var objectVertices = object.geometry.vertices; - var objectFaces = object.geometry.faces; - var objectWorldMatrix = object.matrixWorld; + return function update() { - object.updateMatrixWorld( true ); + this.object.updateMatrixWorld( true ); - this.normalMatrix.getNormalMatrix( objectWorldMatrix ); + normalMatrix.getNormalMatrix( this.object.matrixWorld ); - for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) { + var matrixWorld = this.object.matrixWorld; - var face = objectFaces[ i ]; + var position = this.geometry.attributes.position; - vertices[ i2 ].copy( objectVertices[ face.a ] ) - .add( objectVertices[ face.b ] ) - .add( objectVertices[ face.c ] ) - .divideScalar( 3 ) - .applyMatrix4( objectWorldMatrix ); + // - vertices[ i2 + 1 ].copy( face.normal ) - .applyMatrix3( this.normalMatrix ) - .normalize() - .multiplyScalar( this.size ) - .add( vertices[ i2 ] ); + var objGeometry = this.object.geometry; + + var vertices = objGeometry.vertices; + + var faces = objGeometry.faces; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + var normal = face.normal; + + v1.copy( vertices[ face.a ] ) + .add( vertices[ face.b ] ) + .add( vertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( matrixWorld ); + + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + position.needsUpdate = true; + + return this; } - this.geometry.verticesNeedUpdate = true; - - return this; - -}; - +}() ); // File:src/extras/helpers/GridHelper.js @@ -27850,11 +36066,11 @@ THREE.GridHelper = function ( size, step ) { } - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + THREE.LineSegments.call( this, geometry, material ); }; -THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.GridHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.GridHelper.prototype.constructor = THREE.GridHelper; THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { @@ -27864,7 +36080,7 @@ THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { this.geometry.colorsNeedUpdate = true; -} +}; // File:src/extras/helpers/HemisphereLightHelper.js @@ -27886,7 +36102,7 @@ THREE.HemisphereLightHelper = function ( light, sphereSize ) { this.colors = [ new THREE.Color(), new THREE.Color() ]; var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + geometry.rotateX( - Math.PI / 2 ); for ( var i = 0, il = 8; i < il; i ++ ) { @@ -27907,8 +36123,10 @@ THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper; THREE.HemisphereLightHelper.prototype.dispose = function () { + this.lightSphere.geometry.dispose(); this.lightSphere.material.dispose(); + }; THREE.HemisphereLightHelper.prototype.update = function () { @@ -27979,6 +36197,7 @@ THREE.PointLightHelper.prototype.dispose = function () { this.geometry.dispose(); this.material.dispose(); + }; THREE.PointLightHelper.prototype.update = function () { @@ -28032,9 +36251,11 @@ THREE.SkeletonHelper = function ( object ) { } + geometry.dynamic = true; + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + THREE.LineSegments.call( this, geometry, material ); this.root = object; @@ -28046,7 +36267,7 @@ THREE.SkeletonHelper = function ( object ) { }; -THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.SkeletonHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper; THREE.SkeletonHelper.prototype.getBoneList = function( object ) { @@ -28123,8 +36344,8 @@ THREE.SpotLightHelper = function ( light ) { var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); - geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + geometry.translate( 0, - 0.5, 0 ); + geometry.rotateX( - Math.PI / 2 ); var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); @@ -28139,8 +36360,10 @@ THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper; THREE.SpotLightHelper.prototype.dispose = function () { + this.cone.geometry.dispose(); this.cone.material.dispose(); + }; THREE.SpotLightHelper.prototype.update = function () { @@ -28183,187 +36406,136 @@ THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { var width = ( linewidth !== undefined ) ? linewidth : 1; - var geometry = new THREE.Geometry(); + // - var faces = object.geometry.faces; + var nNormals = 0; - for ( var i = 0, l = faces.length; i < l; i ++ ) { + var objGeometry = this.object.geometry; - var face = faces[ i ]; + if ( objGeometry instanceof THREE.Geometry ) { - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + nNormals = objGeometry.faces.length * 3; - geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + } else if ( objGeometry instanceof THREE.BufferGeometry ) { - } + nNormals = objGeometry.attributes.normal.count } - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + // + + var geometry = new THREE.BufferGeometry(); + + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); + + geometry.addAttribute( 'position', positions ); + + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); + + // this.matrixAutoUpdate = false; - this.normalMatrix = new THREE.Matrix3(); - this.update(); }; -THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.VertexNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper; -THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { +THREE.VertexNormalsHelper.prototype.update = ( function () { var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); - return function( object ) { + return function update() { - var keys = [ 'a', 'b', 'c', 'd' ]; + var keys = [ 'a', 'b', 'c' ]; this.object.updateMatrixWorld( true ); - this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + normalMatrix.getNormalMatrix( this.object.matrixWorld ); - var vertices = this.geometry.vertices; + var matrixWorld = this.object.matrixWorld; - var verts = this.object.geometry.vertices; + var position = this.geometry.attributes.position; - var faces = this.object.geometry.faces; + // - var worldMatrix = this.object.matrixWorld; + var objGeometry = this.object.geometry; - var idx = 0; + if ( objGeometry instanceof THREE.Geometry ) { - for ( var i = 0, l = faces.length; i < l; i ++ ) { + var vertices = objGeometry.vertices; - var face = faces[ i ]; + var faces = objGeometry.faces; - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + var idx = 0; - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - var normal = face.vertexNormals[ j ]; + var face = faces[ i ]; - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); + var vertex = vertices[ face[ keys[ j ] ] ]; + + var normal = face.vertexNormals[ j ]; + + v1.copy( vertex ).applyMatrix4( matrixWorld ); + + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); + + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; + + } + + } + + } else if ( objGeometry instanceof THREE.BufferGeometry ) { + + var objPos = objGeometry.attributes.position; + + var objNorm = objGeometry.attributes.normal; + + var idx = 0; + + // for simplicity, ignore index and drawcalls, and render every normal + + for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { + + v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); + + v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); + + v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); + + position.setXYZ( idx, v1.x, v1.y, v1.z ); - v1.add( vertices[ idx ] ); idx = idx + 1; - vertices[ idx ].copy( v1 ); + position.setXYZ( idx, v2.x, v2.y, v2.z ); + idx = idx + 1; } } - this.geometry.verticesNeedUpdate = true; + position.needsUpdate = true; return this; } -}()); - -// File:src/extras/helpers/VertexTangentsHelper.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ - -THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { - - this.object = object; - - this.size = ( size !== undefined ) ? size : 1; - - var color = ( hex !== undefined ) ? hex : 0x0000ff; - - var width = ( linewidth !== undefined ) ? linewidth : 1; - - var geometry = new THREE.Geometry(); - - var faces = object.geometry.faces; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { - - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); - - } - - } - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - - this.matrixAutoUpdate = false; - - this.update(); - -}; - -THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); -THREE.VertexTangentsHelper.prototype.constructor = THREE.VertexTangentsHelper; - -THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { - - var v1 = new THREE.Vector3(); - - return function( object ) { - - var keys = [ 'a', 'b', 'c', 'd' ]; - - this.object.updateMatrixWorld( true ); - - var vertices = this.geometry.vertices; - - var verts = this.object.geometry.vertices; - - var faces = this.object.geometry.faces; - - var worldMatrix = this.object.matrixWorld; - - var idx = 0; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { - - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; - - var tangent = face.vertexTangents[ j ]; - - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); - - v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); - - v1.add( vertices[ idx ] ); - idx = idx + 1; - - vertices[ idx ].copy( v1 ); - idx = idx + 1; - - } - - } - - this.geometry.verticesNeedUpdate = true; - - return this; - - } - -}()); +}() ); // File:src/extras/helpers/WireframeHelper.js @@ -28375,174 +36547,14 @@ THREE.WireframeHelper = function ( object, hex ) { var color = ( hex !== undefined ) ? hex : 0xffffff; - var edge = [ 0, 0 ], hash = {}; - var sortFunction = function ( a, b ) { return a - b }; - - var keys = [ 'a', 'b', 'c' ]; - var geometry = new THREE.BufferGeometry(); - - if ( object.geometry instanceof THREE.Geometry ) { - - var vertices = object.geometry.vertices; - var faces = object.geometry.faces; - var numEdges = 0; - - // allocate maximal size - var edges = new Uint32Array( 6 * faces.length ); - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; - - } - - } - - } - - var coords = new Float32Array( numEdges * 2 * 3 ); - - for ( var i = 0, l = numEdges; i < l; i ++ ) { - - for ( var j = 0; j < 2; j ++ ) { - - var vertex = vertices[ edges [ 2 * i + j] ]; - - var index = 6 * i + 3 * j; - coords[ index + 0 ] = vertex.x; - coords[ index + 1 ] = vertex.y; - coords[ index + 2 ] = vertex.z; - - } - - } - - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - - } else if ( object.geometry instanceof THREE.BufferGeometry ) { - - if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry - - var vertices = object.geometry.attributes.position.array; - var indices = object.geometry.attributes.index.array; - var drawcalls = object.geometry.drawcalls; - var numEdges = 0; - - if ( drawcalls.length === 0 ) { - - drawcalls = [ { count : indices.length, index : 0, start : 0 } ]; - - } - - // allocate maximal size - var edges = new Uint32Array( 2 * indices.length ); - - for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) { - - var start = drawcalls[ o ].start; - var count = drawcalls[ o ].count; - var index = drawcalls[ o ].index; - - for ( var i = start, il = start + count; i < il; i += 3 ) { - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = index + indices[ i + j ]; - edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; - - } - - } - - } - - } - - var coords = new Float32Array( numEdges * 2 * 3 ); - - for ( var i = 0, l = numEdges; i < l; i ++ ) { - - for ( var j = 0; j < 2; j ++ ) { - - var index = 6 * i + 3 * j; - var index2 = 3 * edges[ 2 * i + j]; - coords[ index + 0 ] = vertices[ index2 ]; - coords[ index + 1 ] = vertices[ index2 + 1 ]; - coords[ index + 2 ] = vertices[ index2 + 2 ]; - - } - - } - - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - - } else { // non-indexed BufferGeometry - - var vertices = object.geometry.attributes.position.array; - var numEdges = vertices.length / 3; - var numTris = numEdges / 3; - - var coords = new Float32Array( numEdges * 2 * 3 ); - - for ( var i = 0, l = numTris; i < l; i ++ ) { - - for ( var j = 0; j < 3; j ++ ) { - - var index = 18 * i + 6 * j; - - var index1 = 9 * i + 3 * j; - coords[ index + 0 ] = vertices[ index1 ]; - coords[ index + 1 ] = vertices[ index1 + 1 ]; - coords[ index + 2 ] = vertices[ index1 + 2 ]; - - var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); - coords[ index + 3 ] = vertices[ index2 ]; - coords[ index + 4 ] = vertices[ index2 + 1 ]; - coords[ index + 5 ] = vertices[ index2 + 2 ]; - - } - - } - - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - - } - - } - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + THREE.LineSegments.call( this, new THREE.WireframeGeometry( object.geometry ), new THREE.LineBasicMaterial( { color: color } ) ); this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; }; -THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.WireframeHelper.prototype = Object.create( THREE.LineSegments.prototype ); THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper; // File:src/extras/objects/ImmediateRenderObject.js @@ -28551,10 +36563,11 @@ THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper; * @author alteredq / http://alteredqualia.com/ */ -THREE.ImmediateRenderObject = function () { +THREE.ImmediateRenderObject = function ( material ) { THREE.Object3D.call( this ); + this.material = material; this.render = function ( renderCallback ) {}; }; @@ -28599,8 +36612,8 @@ THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fp var animation = { - startFrame: start, - endFrame: end, + start: start, + end: end, length: end - start + 1, @@ -28628,7 +36641,7 @@ THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fp THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { - var pattern = /([a-z]+)_?(\d+)/; + var pattern = /([a-z]+)_?(\d+)/i; var firstAnimation, frameRanges = {}; @@ -28786,7 +36799,7 @@ THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { } else { - THREE.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" ); + console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" ); } @@ -28846,7 +36859,7 @@ THREE.MorphBlendMesh.prototype.update = function ( delta ) { } - var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); var weight = animation.weight; if ( keyframe !== animation.currentFrame ) { @@ -28865,8 +36878,16 @@ THREE.MorphBlendMesh.prototype.update = function ( delta ) { if ( animation.directionBackwards ) mix = 1 - mix; - this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; - this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + if ( animation.currentFrame !== animation.lastFrame ) { + + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + + } else { + + this.morphTargetInfluences[ animation.currentFrame ] = weight; + + } } @@ -29039,40 +37060,33 @@ function HtmlDiv() { THREE.Canvas3DRenderer = function ( parameters ) { - console.log( 'THREE.Canvas3DRenderer', THREE.REVISION ); + console.log( 'THREE.Canvas3DRenderer', THREE.REVISION ); parameters = parameters || {}; if (parameters.canvas === undefined) { - console.error("parameter.canvas must be set when using THREE.Canvas3DRenderer"); + console.error("THREE.Canvas3DRenderer: parameter.canvas must be set when using THREE.Canvas3DRenderer"); return; } var _canvas = parameters.canvas, - _context = parameters.context !== undefined ? parameters.context : null, - - pixelRatio = 1, - - _precision = parameters.precision !== undefined ? parameters.precision : 'highp', + _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false, - - _clearColor = new THREE.Color( 0x000000 ), - _clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0; + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false; var lights = []; - var _webglObjects = {}; - var _webglObjectsImmediate = []; - var opaqueObjects = []; + var opaqueObjectsLastIndex = - 1; var transparentObjects = []; + var transparentObjectsLastIndex = - 1; + + var morphInfluences = new Float32Array( 8 ); var sprites = []; var lensFlares = []; @@ -29081,11 +37095,6 @@ THREE.Canvas3DRenderer = function ( parameters ) { this.domElement = _canvas; this.context = null; - pixelRatio = parameters.devicePixelRatio !== undefined - ? parameters.devicePixelRatio - : self.pixelRatio !== undefined - ? self.pixelRatio - : 1; // clearing @@ -29104,14 +37113,6 @@ THREE.Canvas3DRenderer = function ( parameters ) { this.gammaInput = false; this.gammaOutput = false; - // shadow map - - this.shadowMapEnabled = false; - this.shadowMapType = THREE.PCFShadowMap; - this.shadowMapCullFace = THREE.CullFaceFront; - this.shadowMapDebug = false; - this.shadowMapCascade = false; - // morphs this.maxMorphTargets = 8; @@ -29121,57 +37122,48 @@ THREE.Canvas3DRenderer = function ( parameters ) { this.autoScaleCubemaps = true; - // info - - this.info = { - - memory: { - - programs: 0, - geometries: 0, - textures: 0 - - }, - - render: { - - calls: 0, - vertices: 0, - faces: 0, - points: 0 - - } - - }; - // internal properties var _this = this, - _programs = [], - // internal state cache _currentProgram = null, + _currentRenderTarget = null, _currentFramebuffer = null, _currentMaterialId = - 1, _currentGeometryProgram = '', _currentCamera = null, + _currentScissor = new THREE.Vector4(), + _currentScissorTest = null, + + _currentViewport = new THREE.Vector4(), + + // + _usedTextureUnits = 0, - _viewportX = 0, - _viewportY = 0, - _viewportWidth = _canvas.width, - _viewportHeight = _canvas.height, - _currentWidth = 0, - _currentHeight = 0, + // + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0, + + _width = _canvas.width, + _height = _canvas.height, + + _pixelRatio = 1, + + _scissor = new THREE.Vector4( 0, 0, _width, _height ), + _scissorTest = false, + + _viewport = new THREE.Vector4( 0, 0, _width, _height ), // frustum _frustum = new THREE.Frustum(), - // camera matrices cache + // camera matrices cache _projScreenMatrix = new THREE.Matrix4(), @@ -29179,20 +37171,59 @@ THREE.Canvas3DRenderer = function ( parameters ) { // light arrays cache - _direction = new THREE.Vector3(), - - _lightsNeedUpdate = true, - _lights = { + hash: '', + ambient: [ 0, 0, 0 ], - directional: { length: 0, colors:[], positions: [] }, - point: { length: 0, colors: [], positions: [], distances: [], decays: [] }, - spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] }, - hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } + directional: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotShadowMap: [], + spotShadowMatrix: [], + point: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [], + + shadows: [], + shadowsPointLight: 0 + + }, + + // info + + _infoMemory = { + + geometries: 0, + textures: 0 + + }, + + _infoRender = { + + calls: 0, + vertices: 0, + faces: 0, + points: 0 }; + this.info = { + + render: _infoRender, + memory: _infoMemory, + programs: null + + }; + + _pixelRatio = parameters.devicePixelRatio !== undefined + ? parameters.devicePixelRatio + : self.pixelRatio !== undefined + ? self.pixelRatio + : 1; + // initialize var _gl; @@ -29212,7 +37243,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( _gl === null ) { - if ( _canvas.getContext( 'webgl') !== null ) { + if ( _canvas.getContext( 'webgl' ) !== null ) { throw 'Error creating WebGL context with your selected attributes.'; @@ -29237,23 +37268,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { // } catch ( error ) { - THREE.error( 'THREE.Canvas3DRenderer: ' + error ); - - } - - var state = new THREE.WebGLState( _gl, paramThreeToGL ); - - if ( _gl.getShaderPrecisionFormat === undefined ) { - - _gl.getShaderPrecisionFormat = function () { - - return { - 'rangeMin': 1, - 'rangeMax': 1, - 'precision': 1 - }; - - } + console.error( 'THREE.Canvas3DRenderer: ' + error ); } @@ -29264,16 +37279,36 @@ THREE.Canvas3DRenderer = function ( parameters ) { extensions.get( 'OES_texture_half_float' ); extensions.get( 'OES_texture_half_float_linear' ); extensions.get( 'OES_standard_derivatives' ); + extensions.get( 'ANGLE_instanced_arrays' ); - if ( _logarithmicDepthBuffer ) { + if ( extensions.get( 'OES_element_index_uint' ) ) { - extensions.get( 'EXT_frag_depth' ); + THREE.BufferGeometry.MaxIndex = 4294967296; } + var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters ); + + var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL ); + var properties = new THREE.WebGLProperties(); + var objects = new THREE.WebGLObjects( _gl, properties, this.info ); + var programCache = new THREE.WebGLPrograms( this, capabilities ); + var lightCache = new THREE.WebGLLights(); + + this.info.programs = programCache.programs; + + var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender ); + var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); + // - var glClearColor = function ( r, g, b, a ) { + function getTargetPixelRatio() { + + return _currentRenderTarget === null ? _pixelRatio : 1; + + } + + function glClearColor( r, g, b, a ) { if ( _premultipliedAlpha === true ) { @@ -29281,34 +37316,22 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - _gl.clearColor( r, g, b, a ); + state.clearColor( r, g, b, a ); - }; + } - var setDefaultGLState = function () { + function setDefaultGLState() { - _gl.clearColor( 0, 0, 0, 1 ); - _gl.clearDepth( 1 ); - _gl.clearStencil( 0 ); + state.init(); - _gl.enable( _gl.DEPTH_TEST ); - _gl.depthFunc( _gl.LEQUAL ); - - _gl.frontFace( _gl.CCW ); - _gl.cullFace( _gl.BACK ); - _gl.enable( _gl.CULL_FACE ); - - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); - - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - }; + } - var resetGLState = function () { + function resetGLState() { _currentProgram = null; _currentCamera = null; @@ -29316,99 +37339,27 @@ THREE.Canvas3DRenderer = function ( parameters ) { _currentGeometryProgram = ''; _currentMaterialId = - 1; - _lightsNeedUpdate = true; - state.reset(); - }; + } setDefaultGLState(); this.context = _gl; + this.capabilities = capabilities; + this.extensions = extensions; + this.properties = properties; this.state = state; - // GPU capabilities + // shadow map - var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); - var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); - var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); - var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + var shadowMap = new THREE.WebGLShadowMap( this, _lights, objects ); - var _supportsVertexTextures = _maxVertexTextures > 0; - var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' ); + this.shadowMap = shadowMap; - // - - var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT ); - var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT ); - - var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT ); - var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT ); - - var getCompressedTextureFormats = ( function () { - - var array; - - return function () { - - if ( array !== undefined ) { - - return array; - - } - - array = []; - - if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { - - var formats = _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ); - - for ( var i = 0; i < formats.length; i ++ ) { - - array.push( formats[ i ] ); - - } - - } - - return array; - - }; - - } )(); - - // clamp precision to maximum available - - var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; - var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; - - if ( _precision === 'highp' && ! highpAvailable ) { - - if ( mediumpAvailable ) { - - _precision = 'mediump'; - THREE.warn( 'THREE.Canvas3DRenderer: highp not supported, using mediump.' ); - - } else { - - _precision = 'lowp'; - THREE.warn( 'THREE.Canvas3DRenderer: highp and mediump not supported, using lowp.' ); - - } - - } - - if ( _precision === 'mediump' && ! mediumpAvailable ) { - - _precision = 'lowp'; - THREE.warn( 'THREE.Canvas3DRenderer: mediump not supported, using lowp.' ); - - } // Plugins - var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate ); - var spritePlugin = new THREE.SpritePlugin( this, sprites ); var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); @@ -29420,105 +37371,87 @@ THREE.Canvas3DRenderer = function ( parameters ) { }; + this.getContextAttributes = function () { + + return _gl.getContextAttributes(); + + }; + this.forceContextLoss = function () { //extensions.get( 'WEBGL_lose_context' ).loseContext(); }; - this.supportsVertexTextures = function () { - - return _supportsVertexTextures; - - }; - - this.supportsFloatTextures = function () { - - return extensions.get( 'OES_texture_float' ); - - }; - - this.supportsHalfFloatTextures = function () { - - return extensions.get( 'OES_texture_half_float' ); - - }; - - this.supportsStandardDerivatives = function () { - - return extensions.get( 'OES_standard_derivatives' ); - - }; - - this.supportsCompressedTextureS3TC = function () { - - return extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - }; - - this.supportsCompressedTexturePVRTC = function () { - - return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - }; - - this.supportsBlendMinMax = function () { - - return extensions.get( 'EXT_blend_minmax' ); - - }; - this.getMaxAnisotropy = ( function () { var value; - return function () { + return function getMaxAnisotropy() { - if ( value !== undefined ) { - - return value; - - } + if ( value !== undefined ) return value; var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - value = extension !== null ? _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; + if ( extension !== null ) { + + value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); + + } else { + + value = 0; + + } return value; - } + }; } )(); this.getPrecision = function () { - return _precision; + return capabilities.precision; }; this.getPixelRatio = function () { - return pixelRatio; + return _pixelRatio; }; this.setPixelRatio = function ( value ) { - pixelRatio = value; + if ( value === undefined ) return; + + _pixelRatio = value; + + this.setSize( _viewport.z, _viewport.w, false ); + + }; + + this.getSize = function () { + + return { + width: _width, + height: _height + }; }; this.setSize = function ( width, height, updateStyle ) { - _canvas.pixelSize = Qt.size(width * pixelRatio, height * pixelRatio) + _width = width; + _height = height; - if ( updateStyle !== false ) { + _canvas.pixelSize = Qt.size(width * _pixelRatio, height * _pixelRatio) +// if ( updateStyle !== false ) { // Update styles is ignored in Canvas3D // _canvas.style.width = width + 'px'; // _canvas.style.height = height + 'px'; - - } +// } this.setViewport( 0, 0, width, height ); @@ -29526,35 +37459,22 @@ THREE.Canvas3DRenderer = function ( parameters ) { this.setViewport = function ( x, y, width, height ) { - _viewportX = x * pixelRatio; - _viewportY = y * pixelRatio; - - _viewportWidth = width * pixelRatio; - _viewportHeight = height * pixelRatio; - - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + state.viewport( _viewport.set( x, y, width, height ) ); }; this.setScissor = function ( x, y, width, height ) { - _gl.scissor( - x * pixelRatio, - y * pixelRatio, - width * pixelRatio, - height * pixelRatio - ); + state.scissor( _scissor.set( x, y, width, height ) ); }; - this.enableScissorTest = function ( enable ) { + this.setScissorTest = function ( enable ) { - if (enable) - _gl.enable( _gl.SCISSOR_TEST ) - else - _gl.disable( _gl.SCISSOR_TEST ); + _scissorTest = enable; + state.setScissorTest( enable ); - }; + }; // Clearing @@ -29602,19 +37522,19 @@ THREE.Canvas3DRenderer = function ( parameters ) { this.clearColor = function () { - _gl.clear( _gl.COLOR_BUFFER_BIT ); + this.clear( true, false, false ); }; this.clearDepth = function () { - _gl.clear( _gl.DEPTH_BUFFER_BIT ); + this.clear( false, true, false ); }; this.clearStencil = function () { - _gl.clear( _gl.STENCIL_BUFFER_BIT ); + this.clear( false, false, true ); }; @@ -29629,119 +37549,26 @@ THREE.Canvas3DRenderer = function ( parameters ) { this.resetGLState = resetGLState; - // Buffer allocation + this.dispose = function() { - function createParticleBuffers ( geometry ) { - - geometry.__webglVertexBuffer = _gl.createBuffer(); - geometry.__webglVertexBuffer.name = "Particle__webglVertexBuffer"; - geometry.__webglColorBuffer = _gl.createBuffer(); - geometry.__webglColorBuffer.name = "Particle__webglColorBuffer"; - - _this.info.memory.geometries ++; - - }; - - function createLineBuffers ( geometry ) { - - geometry.__webglVertexBuffer = _gl.createBuffer(); - geometry.__webglColorBuffer = _gl.createBuffer(); - geometry.__webglLineDistanceBuffer = _gl.createBuffer(); - geometry.__webglVertexBuffer.name = "Line__webglVertexBuffer"; - geometry.__webglColorBuffer.name = "Line__webglColorBuffer"; - geometry.__webglLineDistanceBuffer.name = "Line__webglLineDistanceBuffer"; - - _this.info.memory.geometries ++; - - }; - - function createMeshBuffers ( geometryGroup ) { - - geometryGroup.__webglVertexBuffer = _gl.createBuffer(); - geometryGroup.__webglNormalBuffer = _gl.createBuffer(); - geometryGroup.__webglTangentBuffer = _gl.createBuffer(); - geometryGroup.__webglColorBuffer = _gl.createBuffer(); - geometryGroup.__webglUVBuffer = _gl.createBuffer(); - geometryGroup.__webglUV2Buffer = _gl.createBuffer(); - geometryGroup.__webglVertexBuffer.name = "Mesh__webglVertexBuffer"; - geometryGroup.__webglNormalBuffer.name = "Mesh__webglNormalBuffer"; - geometryGroup.__webglTangentBuffer.name = "Mesh__webglTangentBuffer"; - geometryGroup.__webglColorBuffer.name = "Mesh__webglColorBuffer"; - geometryGroup.__webglUVBuffer.name = "Mesh__webglUVBuffer"; - geometryGroup.__webglUV2Buffer.name = "Mesh__webglUV2Buffer"; - - geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); - geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); - geometryGroup.__webglSkinIndicesBuffer.name = "Mesh__webglSkinIndicesBuffer"; - geometryGroup.__webglSkinWeightsBuffer.name = "Mesh__webglSkinWeightsBuffer"; - - geometryGroup.__webglFaceBuffer = _gl.createBuffer(); - geometryGroup.__webglLineBuffer = _gl.createBuffer(); - geometryGroup.__webglFaceBuffer.name = "Mesh__webglFaceBuffer"; - geometryGroup.__webglLineBuffer.name = "Mesh__webglLineBuffer"; - - var m, ml; - var numMorphTargets = geometryGroup.numMorphTargets; - - if ( numMorphTargets ) { - - geometryGroup.__webglMorphTargetsBuffers = []; - - for ( m = 0, ml = numMorphTargets; m < ml; m ++ ) { - var buf = _gl.createBuffer(); - buf.name = "Mesh__MorphTarget_"+m; - geometryGroup.__webglMorphTargetsBuffers.push(buf); - - } - - } - - var numMorphNormals = geometryGroup.numMorphNormals; - - if ( numMorphNormals ) { - - geometryGroup.__webglMorphNormalsBuffers = []; - - for ( m = 0, ml = numMorphNormals; m < ml; m ++ ) { - var nbuf = _gl.createBuffer(); - nbuf.name = "Mesh__MorphNormal_"+m; - geometryGroup.__webglMorphNormalsBuffers.push( nbuf ); - - } - - } - - _this.info.memory.geometries ++; + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); }; // Events - var onObjectRemoved = function ( event ) { + function onContextLost( event ) { - var object = event.target; + event.preventDefault(); - object.traverse( function ( child ) { + resetGLState(); + setDefaultGLState(); - child.removeEventListener( 'remove', onObjectRemoved ); + properties.clear(); - removeObject( child ); + } - } ); - - }; - - var onGeometryDispose = function ( event ) { - - var geometry = event.target; - - geometry.removeEventListener( 'dispose', onGeometryDispose ); - - deallocateGeometry( geometry ); - - }; - - var onTextureDispose = function ( event ) { + function onTextureDispose( event ) { var texture = event.target; @@ -29749,12 +37576,12 @@ THREE.Canvas3DRenderer = function ( parameters ) { deallocateTexture( texture ); - _this.info.memory.textures --; + _infoMemory.textures --; - }; + } - var onRenderTargetDispose = function ( event ) { + function onRenderTargetDispose( event ) { var renderTarget = event.target; @@ -29762,11 +37589,11 @@ THREE.Canvas3DRenderer = function ( parameters ) { deallocateRenderTarget( renderTarget ); - _this.info.memory.textures --; + _infoMemory.textures --; - }; + } - var onMaterialDispose = function ( event ) { + function onMaterialDispose( event ) { var material = event.target; @@ -29774,1678 +37601,138 @@ THREE.Canvas3DRenderer = function ( parameters ) { deallocateMaterial( material ); - }; + } // Buffer deallocation - var deleteBuffers = function ( geometry ) { + function deallocateTexture( texture ) { - var buffers = [ - '__webglVertexBuffer', - '__webglNormalBuffer', - '__webglTangentBuffer', - '__webglColorBuffer', - '__webglUVBuffer', - '__webglUV2Buffer', + var textureProperties = properties.get( texture ); - '__webglSkinIndicesBuffer', - '__webglSkinWeightsBuffer', - - '__webglFaceBuffer', - '__webglLineBuffer', - - '__webglLineDistanceBuffer' - ]; - - for ( var i = 0, l = buffers.length; i < l; i ++ ) { - - var name = buffers[ i ]; - - if ( geometry[ name ] !== undefined ) { - - _gl.deleteBuffer( geometry[ name ] ); - - delete geometry[ name ]; - - } - - } - - // custom attributes - - if ( geometry.__webglCustomAttributesList !== undefined ) { - - for ( var name in geometry.__webglCustomAttributesList ) { - - _gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer ); - - } - - delete geometry.__webglCustomAttributesList; - - } - - _this.info.memory.geometries --; - - }; - - var deallocateGeometry = function ( geometry ) { - - delete geometry.__webglInit; - - if ( geometry instanceof THREE.BufferGeometry ) { - - for ( var name in geometry.attributes ) { - - var attribute = geometry.attributes[ name ]; - - if ( attribute.buffer !== undefined ) { - - _gl.deleteBuffer( attribute.buffer ); - - delete attribute.buffer; - - } - - } - - _this.info.memory.geometries --; - - } else { - - var geometryGroupsList = geometryGroups[ geometry.id ]; - - if ( geometryGroupsList !== undefined ) { - - for ( var i = 0, l = geometryGroupsList.length; i < l; i ++ ) { - - var geometryGroup = geometryGroupsList[ i ]; - - if ( geometryGroup.numMorphTargets !== undefined ) { - - for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { - - _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); - - } - - delete geometryGroup.__webglMorphTargetsBuffers; - - } - - if ( geometryGroup.numMorphNormals !== undefined ) { - - for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { - - _gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); - - } - - delete geometryGroup.__webglMorphNormalsBuffers; - - } - - deleteBuffers( geometryGroup ); - - } - - delete geometryGroups[ geometry.id ]; - - } else { - - deleteBuffers( geometry ); - - } - - } - - // TOFIX: Workaround for deleted geometry being currently bound - - _currentGeometryProgram = ''; - - }; - - var deallocateTexture = function ( texture ) { - - if ( texture.image && texture.image.__webglTextureCube ) { + if ( texture.image && textureProperties.__image__webglTextureCube ) { // cube texture - _gl.deleteTexture( texture.image.__webglTextureCube ); - - delete texture.image.__webglTextureCube; + _gl.deleteTexture( textureProperties.__image__webglTextureCube ); } else { // 2D texture - if ( texture.__webglInit === undefined ) return; + if ( textureProperties.__webglInit === undefined ) return; - _gl.deleteTexture( texture.__webglTexture ); - - delete texture.__webglTexture; - delete texture.__webglInit; + _gl.deleteTexture( textureProperties.__webglTexture ); } - }; + // remove all webgl properties + properties.delete( texture ); - var deallocateRenderTarget = function ( renderTarget ) { + } - if ( ! renderTarget || renderTarget.__webglTexture === undefined ) return; + function deallocateRenderTarget( renderTarget ) { - _gl.deleteTexture( renderTarget.__webglTexture ); + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); - delete renderTarget.__webglTexture; + if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return; + + _gl.deleteTexture( textureProperties.__webglTexture ); if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { for ( var i = 0; i < 6; i ++ ) { - _gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] ); - _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] ); + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { - _gl.deleteFramebuffer( renderTarget.__webglFramebuffer ); - _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer ); + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); } - delete renderTarget.__webglFramebuffer; - delete renderTarget.__webglRenderbuffer; + properties.delete( renderTarget.texture ); + properties.delete( renderTarget ); - }; + } - var deallocateMaterial = function ( material ) { + function deallocateMaterial( material ) { - var program = material.program.program; + releaseMaterialProgramReference( material ); - if ( program === undefined ) return; + properties.delete( material ); + + } + + + function releaseMaterialProgramReference( material ) { + + var programInfo = properties.get( material ).program; material.program = undefined; - // only deallocate GL program if this was the last use of shared program - // assumed there is only single copy of any program in the _programs list - // (that's how it's constructed) + if ( programInfo !== undefined ) { - var i, il, programInfo; - var deleteProgram = false; - - for ( i = 0, il = _programs.length; i < il; i ++ ) { - - programInfo = _programs[ i ]; - - if ( programInfo.program === program ) { - - programInfo.usedTimes --; - - if ( programInfo.usedTimes === 0 ) { - - deleteProgram = true; - - } - - break; - - } - - } - - if ( deleteProgram === true ) { - - // avoid using array.splice, this is costlier than creating new array from scratch - - var newPrograms = []; - - for ( i = 0, il = _programs.length; i < il; i ++ ) { - - programInfo = _programs[ i ]; - - if ( programInfo.program !== program ) { - - newPrograms.push( programInfo ); - - } - - } - - _programs = newPrograms; - - _gl.deleteProgram( program ); - - _this.info.memory.programs --; - - } - - }; - - // Buffer initialization - - function initCustomAttributes ( object ) { - - var geometry = object.geometry; - var material = object.material; - - var nvertices = geometry.vertices.length; - - if ( material.attributes ) { - - if ( geometry.__webglCustomAttributesList === undefined ) { - - geometry.__webglCustomAttributesList = []; - - } - - for ( var name in material.attributes ) { - - var attribute = material.attributes[ name ]; - - if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { - - attribute.__webglInitialized = true; - - var size = 1; // "f" and "i" - - if ( attribute.type === 'v2' ) size = 2; - else if ( attribute.type === 'v3' ) size = 3; - else if ( attribute.type === 'v4' ) size = 4; - else if ( attribute.type === 'c' ) size = 3; - - attribute.size = size; - - attribute.array = new Float32Array( nvertices * size ); - attribute.array.name = ""+attribute+"attribute.array"; - - attribute.buffer = _gl.createBuffer(); - attribute.buffer.belongsToAttribute = name; - - attribute.needsUpdate = true; - - } - - geometry.__webglCustomAttributesList.push( attribute ); - - } - - } - - }; - - function initParticleBuffers ( geometry, object ) { - - var nvertices = geometry.vertices.length; - - geometry.__vertexArray = new Float32Array( nvertices * 3 ); - geometry.__colorArray = new Float32Array( nvertices * 3 ); - geometry.__vertexArray.name = "geometry.__vertexArray"; - geometry.__colorArray.name = "geometry.__colorArray"; - - geometry.__webglParticleCount = nvertices; - - initCustomAttributes( object ); - - }; - - function initLineBuffers ( geometry, object ) { - - var nvertices = geometry.vertices.length; - - geometry.__vertexArray = new Float32Array( nvertices * 3 ); - geometry.__colorArray = new Float32Array( nvertices * 3 ); - geometry.__lineDistanceArray = new Float32Array( nvertices * 1 ); - geometry.__vertexArray.name = "geometry.__vertexArray"; - geometry.__colorArray.name = "geometry.__colorArray"; - geometry.__lineDistanceArray.name = "geometry.__lineDistanceArray"; - - geometry.__webglLineCount = nvertices; - - initCustomAttributes( object ); - - }; - - function initMeshBuffers ( geometryGroup, object ) { - - var geometry = object.geometry, - faces3 = geometryGroup.faces3, - - nvertices = faces3.length * 3, - ntris = faces3.length * 1, - nlines = faces3.length * 3, - - material = getBufferMaterial( object, geometryGroup ); - - geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); - geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); - geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); - geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); - geometryGroup.__vertexArray.name = "geometryGroup.__vertexArray"; - geometryGroup.__normalArray.name = "geometryGroup.__normalArray"; - geometryGroup.__colorArray.name = "geometryGroup.__colorArray"; - geometryGroup.__uvArray.name = "geometryGroup.__uvArray"; - - if ( geometry.faceVertexUvs.length > 1 ) { - - geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); - geometryGroup.__uv2Array.name = "geometryGroup.__uv2Array"; - - } - - if ( geometry.hasTangents ) { - - geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); - geometryGroup.__tangentArray.name = "geometryGroup.__tangentArray"; - - } - - if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { - - geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); - geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); - geometryGroup.__skinIndexArray.name = "geometryGroup.__skinIndexArray"; - geometryGroup.__skinWeightArray.name = "geometryGroup.__skinWeightArray"; - } - - var UintArray = extensions.get( 'OES_element_index_uint' ) !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 - - geometryGroup.__typeArray = UintArray; - geometryGroup.__faceArray = new UintArray( ntris * 3 ); - geometryGroup.__lineArray = new UintArray( nlines * 2 ); - geometryGroup.__faceArray.name = "geometryGroup.__faceArray"; - geometryGroup.__lineArray.name = "geometryGroup.__lineArray"; - - var m, ml; - var numMorphTargets = geometryGroup.numMorphTargets; - - if ( numMorphTargets ) { - - geometryGroup.__morphTargetsArrays = []; - - for ( m = 0, ml = numMorphTargets; m < ml; m ++ ) { - var mta = new Float32Array( nvertices * 3 ); - mta.name = "morphTargetArray_"+m; - geometryGroup.__morphTargetsArrays.push(mta); - } - - } - - var numMorphNormals = geometryGroup.numMorphNormals; - - if ( numMorphNormals ) { - - geometryGroup.__morphNormalsArrays = []; - - for ( m = 0, ml = numMorphNormals; m < ml; m ++ ) { - var mna = new Float32Array( nvertices * 3 ); - mna.name = "morphNormalsArray_"+m; - geometryGroup.__morphNormalsArrays.push( mna ); - } - - } - - geometryGroup.__webglFaceCount = ntris * 3; - geometryGroup.__webglLineCount = nlines * 2; - - - // custom attributes - - if ( material.attributes ) { - - if ( geometryGroup.__webglCustomAttributesList === undefined ) { - - geometryGroup.__webglCustomAttributesList = []; - - } - - for ( var name in material.attributes ) { - - // Do a shallow copy of the attribute object so different geometryGroup chunks use different - // attribute buffers which are correctly indexed in the setMeshBuffers function - - var originalAttribute = material.attributes[ name ]; - - var attribute = {}; - - for ( var property in originalAttribute ) { - - attribute[ property ] = originalAttribute[ property ]; - - } - - if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { - - attribute.__webglInitialized = true; - - var size = 1; // "f" and "i" - - if ( attribute.type === 'v2' ) size = 2; - else if ( attribute.type === 'v3' ) size = 3; - else if ( attribute.type === 'v4' ) size = 4; - else if ( attribute.type === 'c' ) size = 3; - - attribute.size = size; - - attribute.array = new Float32Array( nvertices * size ); - - attribute.buffer = _gl.createBuffer(); - attribute.buffer.belongsToAttribute = name; - - originalAttribute.needsUpdate = true; - attribute.__original = originalAttribute; - - } - - geometryGroup.__webglCustomAttributesList.push( attribute ); - - } - - } - - geometryGroup.__inittedArrays = true; - - }; - - function getBufferMaterial( object, geometryGroup ) { - - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ geometryGroup.materialIndex ] - : object.material; - - } - - function materialNeedsFaceNormals ( material ) { - - return material instanceof THREE.MeshPhongMaterial === false && material.shading === THREE.FlatShading; - - } - - // Buffer setting - - function setParticleBuffers ( geometry, hint, object ) { - - var v, c, vertex, offset, color, - - vertices = geometry.vertices, - vl = vertices.length, - - colors = geometry.colors, - cl = colors.length, - - vertexArray = geometry.__vertexArray, - colorArray = geometry.__colorArray, - - dirtyVertices = geometry.verticesNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - - customAttributes = geometry.__webglCustomAttributesList, - i, il, - ca, cal, value, - customAttribute; - - if ( dirtyVertices ) { - - for ( v = 0; v < vl; v ++ ) { - - vertex = vertices[ v ]; - - offset = v * 3; - - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); - - } - - if ( dirtyColors ) { - - for ( c = 0; c < cl; c ++ ) { - - color = colors[ c ]; - - offset = c * 3; - - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); - - } - - if ( customAttributes ) { - - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { - - customAttribute = customAttributes[ i ]; - - if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) { - - cal = customAttribute.value.length; - - offset = 0; - - if ( customAttribute.size === 1 ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - customAttribute.array[ ca ] = customAttribute.value[ ca ]; - - } - - } else if ( customAttribute.size === 2 ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - - offset += 2; - - } - - } else if ( customAttribute.size === 3 ) { - - if ( customAttribute.type === 'c' ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; - - offset += 3; - - } - - } else { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - - offset += 3; - - } - - } - - } else if ( customAttribute.size === 4 ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; - - offset += 4; - - } - - } - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); - - customAttribute.needsUpdate = false; - - } + programCache.releaseProgram( programInfo ); } } - function setLineBuffers ( geometry, hint ) { - - var v, c, d, vertex, offset, color, - - vertices = geometry.vertices, - colors = geometry.colors, - lineDistances = geometry.lineDistances, - - vl = vertices.length, - cl = colors.length, - dl = lineDistances.length, - - vertexArray = geometry.__vertexArray, - colorArray = geometry.__colorArray, - lineDistanceArray = geometry.__lineDistanceArray, - - dirtyVertices = geometry.verticesNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - dirtyLineDistances = geometry.lineDistancesNeedUpdate, - - customAttributes = geometry.__webglCustomAttributesList, - - i, il, - ca, cal, value, - customAttribute; - - if ( dirtyVertices ) { - - for ( v = 0; v < vl; v ++ ) { - - vertex = vertices[ v ]; - - offset = v * 3; - - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); - - } - - if ( dirtyColors ) { - - for ( c = 0; c < cl; c ++ ) { - - color = colors[ c ]; - - offset = c * 3; - - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); - - } - - if ( dirtyLineDistances ) { - - for ( d = 0; d < dl; d ++ ) { - - lineDistanceArray[ d ] = lineDistances[ d ]; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint ); - - } - - if ( customAttributes ) { - - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { - - customAttribute = customAttributes[ i ]; - - if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) { - - offset = 0; - - cal = customAttribute.value.length; - - if ( customAttribute.size === 1 ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - customAttribute.array[ ca ] = customAttribute.value[ ca ]; - - } - - } else if ( customAttribute.size === 2 ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - - offset += 2; - - } - - } else if ( customAttribute.size === 3 ) { - - if ( customAttribute.type === 'c' ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; - - offset += 3; - - } - - } else { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - - offset += 3; - - } - - } - - } else if ( customAttribute.size === 4 ) { - - for ( ca = 0; ca < cal; ca ++ ) { - - value = customAttribute.value[ ca ]; - - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; - - offset += 4; - - } - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); - - customAttribute.needsUpdate = false; - - } - - } - - } - - } - - function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { - - if ( ! geometryGroup.__inittedArrays ) { - - return; - - } - - var needsFaceNormals = materialNeedsFaceNormals( material ); - - var f, fl, fi, face, - vertexNormals, faceNormal, - vertexColors, faceColor, - vertexTangents, - uv, uv2, v1, v2, v3, t1, t2, t3, n1, n2, n3, - c1, c2, c3, - sw1, sw2, sw3, - si1, si2, si3, - i, il, - vn, uvi, uv2i, - vk, vkl, vka, - nka, chf, faceVertexNormals, - - vertexIndex = 0, - - offset = 0, - offset_uv = 0, - offset_uv2 = 0, - offset_face = 0, - offset_normal = 0, - offset_tangent = 0, - offset_line = 0, - offset_color = 0, - offset_skin = 0, - offset_morphTarget = 0, - offset_custom = 0, - - value, - - vertexArray = geometryGroup.__vertexArray, - uvArray = geometryGroup.__uvArray, - uv2Array = geometryGroup.__uv2Array, - normalArray = geometryGroup.__normalArray, - tangentArray = geometryGroup.__tangentArray, - colorArray = geometryGroup.__colorArray, - - skinIndexArray = geometryGroup.__skinIndexArray, - skinWeightArray = geometryGroup.__skinWeightArray, - - morphTargetsArrays = geometryGroup.__morphTargetsArrays, - morphNormalsArrays = geometryGroup.__morphNormalsArrays, - - customAttributes = geometryGroup.__webglCustomAttributesList, - customAttribute, - - faceArray = geometryGroup.__faceArray, - lineArray = geometryGroup.__lineArray, - - geometry = object.geometry, // this is shared for all chunks - - dirtyVertices = geometry.verticesNeedUpdate, - dirtyElements = geometry.elementsNeedUpdate, - dirtyUvs = geometry.uvsNeedUpdate, - dirtyNormals = geometry.normalsNeedUpdate, - dirtyTangents = geometry.tangentsNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - dirtyMorphTargets = geometry.morphTargetsNeedUpdate, - - vertices = geometry.vertices, - chunk_faces3 = geometryGroup.faces3, - obj_faces = geometry.faces, - - obj_uvs = geometry.faceVertexUvs[ 0 ], - obj_uvs2 = geometry.faceVertexUvs[ 1 ], - - obj_skinIndices = geometry.skinIndices, - obj_skinWeights = geometry.skinWeights, - - morphTargets = geometry.morphTargets, - morphNormals = geometry.morphNormals; - - if ( dirtyVertices ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - v1 = vertices[ face.a ]; - v2 = vertices[ face.b ]; - v3 = vertices[ face.c ]; - - vertexArray[ offset ] = v1.x; - vertexArray[ offset + 1 ] = v1.y; - vertexArray[ offset + 2 ] = v1.z; - - vertexArray[ offset + 3 ] = v2.x; - vertexArray[ offset + 4 ] = v2.y; - vertexArray[ offset + 5 ] = v2.z; - - vertexArray[ offset + 6 ] = v3.x; - vertexArray[ offset + 7 ] = v3.y; - vertexArray[ offset + 8 ] = v3.z; - - offset += 9; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); - - } - - if ( dirtyMorphTargets ) { - - for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { - - offset_morphTarget = 0; - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - chf = chunk_faces3[ f ]; - face = obj_faces[ chf ]; - - // morph positions - - v1 = morphTargets[ vk ].vertices[ face.a ]; - v2 = morphTargets[ vk ].vertices[ face.b ]; - v3 = morphTargets[ vk ].vertices[ face.c ]; - - vka = morphTargetsArrays[ vk ]; - - vka[ offset_morphTarget ] = v1.x; - vka[ offset_morphTarget + 1 ] = v1.y; - vka[ offset_morphTarget + 2 ] = v1.z; - - vka[ offset_morphTarget + 3 ] = v2.x; - vka[ offset_morphTarget + 4 ] = v2.y; - vka[ offset_morphTarget + 5 ] = v2.z; - - vka[ offset_morphTarget + 6 ] = v3.x; - vka[ offset_morphTarget + 7 ] = v3.y; - vka[ offset_morphTarget + 8 ] = v3.z; - - // morph normals - - if ( material.morphNormals ) { - - if ( needsFaceNormals ) { - - n1 = morphNormals[ vk ].faceNormals[ chf ]; - n2 = n1; - n3 = n1; - - } else { - - faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; - - n1 = faceVertexNormals.a; - n2 = faceVertexNormals.b; - n3 = faceVertexNormals.c; - - } - - nka = morphNormalsArrays[ vk ]; - - nka[ offset_morphTarget ] = n1.x; - nka[ offset_morphTarget + 1 ] = n1.y; - nka[ offset_morphTarget + 2 ] = n1.z; - - nka[ offset_morphTarget + 3 ] = n2.x; - nka[ offset_morphTarget + 4 ] = n2.y; - nka[ offset_morphTarget + 5 ] = n2.z; - - nka[ offset_morphTarget + 6 ] = n3.x; - nka[ offset_morphTarget + 7 ] = n3.y; - nka[ offset_morphTarget + 8 ] = n3.z; - - } - - // - - offset_morphTarget += 9; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); - _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); - - if ( material.morphNormals ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); - _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); - - } - - } - - } - - if ( obj_skinWeights.length ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - // weights - - sw1 = obj_skinWeights[ face.a ]; - sw2 = obj_skinWeights[ face.b ]; - sw3 = obj_skinWeights[ face.c ]; - - skinWeightArray[ offset_skin ] = sw1.x; - skinWeightArray[ offset_skin + 1 ] = sw1.y; - skinWeightArray[ offset_skin + 2 ] = sw1.z; - skinWeightArray[ offset_skin + 3 ] = sw1.w; - - skinWeightArray[ offset_skin + 4 ] = sw2.x; - skinWeightArray[ offset_skin + 5 ] = sw2.y; - skinWeightArray[ offset_skin + 6 ] = sw2.z; - skinWeightArray[ offset_skin + 7 ] = sw2.w; - - skinWeightArray[ offset_skin + 8 ] = sw3.x; - skinWeightArray[ offset_skin + 9 ] = sw3.y; - skinWeightArray[ offset_skin + 10 ] = sw3.z; - skinWeightArray[ offset_skin + 11 ] = sw3.w; - - // indices - - si1 = obj_skinIndices[ face.a ]; - si2 = obj_skinIndices[ face.b ]; - si3 = obj_skinIndices[ face.c ]; - - skinIndexArray[ offset_skin ] = si1.x; - skinIndexArray[ offset_skin + 1 ] = si1.y; - skinIndexArray[ offset_skin + 2 ] = si1.z; - skinIndexArray[ offset_skin + 3 ] = si1.w; - - skinIndexArray[ offset_skin + 4 ] = si2.x; - skinIndexArray[ offset_skin + 5 ] = si2.y; - skinIndexArray[ offset_skin + 6 ] = si2.z; - skinIndexArray[ offset_skin + 7 ] = si2.w; - - skinIndexArray[ offset_skin + 8 ] = si3.x; - skinIndexArray[ offset_skin + 9 ] = si3.y; - skinIndexArray[ offset_skin + 10 ] = si3.z; - skinIndexArray[ offset_skin + 11 ] = si3.w; - - offset_skin += 12; - - } - - if ( offset_skin > 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); - - } - - } - - if ( dirtyColors ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - vertexColors = face.vertexColors; - faceColor = face.color; - - if ( vertexColors.length === 3 && material.vertexColors === THREE.VertexColors ) { - - c1 = vertexColors[ 0 ]; - c2 = vertexColors[ 1 ]; - c3 = vertexColors[ 2 ]; - - } else { - - c1 = faceColor; - c2 = faceColor; - c3 = faceColor; - - } - - colorArray[ offset_color ] = c1.r; - colorArray[ offset_color + 1 ] = c1.g; - colorArray[ offset_color + 2 ] = c1.b; - - colorArray[ offset_color + 3 ] = c2.r; - colorArray[ offset_color + 4 ] = c2.g; - colorArray[ offset_color + 5 ] = c2.b; - - colorArray[ offset_color + 6 ] = c3.r; - colorArray[ offset_color + 7 ] = c3.g; - colorArray[ offset_color + 8 ] = c3.b; - - offset_color += 9; - - } - - if ( offset_color > 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); - - } - - } - - if ( dirtyTangents && geometry.hasTangents ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - vertexTangents = face.vertexTangents; - - t1 = vertexTangents[ 0 ]; - t2 = vertexTangents[ 1 ]; - t3 = vertexTangents[ 2 ]; - - tangentArray[ offset_tangent ] = t1.x; - tangentArray[ offset_tangent + 1 ] = t1.y; - tangentArray[ offset_tangent + 2 ] = t1.z; - tangentArray[ offset_tangent + 3 ] = t1.w; - - tangentArray[ offset_tangent + 4 ] = t2.x; - tangentArray[ offset_tangent + 5 ] = t2.y; - tangentArray[ offset_tangent + 6 ] = t2.z; - tangentArray[ offset_tangent + 7 ] = t2.w; - - tangentArray[ offset_tangent + 8 ] = t3.x; - tangentArray[ offset_tangent + 9 ] = t3.y; - tangentArray[ offset_tangent + 10 ] = t3.z; - tangentArray[ offset_tangent + 11 ] = t3.w; - - offset_tangent += 12; - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); - - } - - if ( dirtyNormals ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - vertexNormals = face.vertexNormals; - faceNormal = face.normal; - - if ( vertexNormals.length === 3 && needsFaceNormals === false ) { - - for ( i = 0; i < 3; i ++ ) { - - vn = vertexNormals[ i ]; - - normalArray[ offset_normal ] = vn.x; - normalArray[ offset_normal + 1 ] = vn.y; - normalArray[ offset_normal + 2 ] = vn.z; - - offset_normal += 3; - - } - - } else { - - for ( i = 0; i < 3; i ++ ) { - - normalArray[ offset_normal ] = faceNormal.x; - normalArray[ offset_normal + 1 ] = faceNormal.y; - normalArray[ offset_normal + 2 ] = faceNormal.z; - - offset_normal += 3; - - } - - } - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); - - } - - if ( dirtyUvs && obj_uvs ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - fi = chunk_faces3[ f ]; - - uv = obj_uvs[ fi ]; - - if ( uv === undefined ) continue; - - for ( i = 0; i < 3; i ++ ) { - - uvi = uv[ i ]; - - uvArray[ offset_uv ] = uvi.x; - uvArray[ offset_uv + 1 ] = uvi.y; - - offset_uv += 2; - - } - - } - - if ( offset_uv > 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); - - } - - } - - if ( dirtyUvs && obj_uvs2 ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - fi = chunk_faces3[ f ]; - - uv2 = obj_uvs2[ fi ]; - - if ( uv2 === undefined ) continue; - - for ( i = 0; i < 3; i ++ ) { - - uv2i = uv2[ i ]; - - uv2Array[ offset_uv2 ] = uv2i.x; - uv2Array[ offset_uv2 + 1 ] = uv2i.y; - - offset_uv2 += 2; - - } - - } - - if ( offset_uv2 > 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); - - } - - } - - if ( dirtyElements ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - faceArray[ offset_face ] = vertexIndex; - faceArray[ offset_face + 1 ] = vertexIndex + 1; - faceArray[ offset_face + 2 ] = vertexIndex + 2; - - offset_face += 3; - - lineArray[ offset_line ] = vertexIndex; - lineArray[ offset_line + 1 ] = vertexIndex + 1; - - lineArray[ offset_line + 2 ] = vertexIndex; - lineArray[ offset_line + 3 ] = vertexIndex + 2; - - lineArray[ offset_line + 4 ] = vertexIndex + 1; - lineArray[ offset_line + 5 ] = vertexIndex + 2; - - offset_line += 6; - - vertexIndex += 3; - - } - - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); - - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); - - } - - if ( customAttributes ) { - - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { - - customAttribute = customAttributes[ i ]; - - if ( ! customAttribute.__original.needsUpdate ) continue; - - offset_custom = 0; - - if ( customAttribute.size === 1 ) { - - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; - customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; - customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; - - offset_custom += 3; - - } - - } else if ( customAttribute.boundTo === 'faces' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - value = customAttribute.value[ chunk_faces3[ f ] ]; - - customAttribute.array[ offset_custom ] = value; - customAttribute.array[ offset_custom + 1 ] = value; - customAttribute.array[ offset_custom + 2 ] = value; - - offset_custom += 3; - - } - - } - - } else if ( customAttribute.size === 2 ) { - - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; - - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - - customAttribute.array[ offset_custom + 2 ] = v2.x; - customAttribute.array[ offset_custom + 3 ] = v2.y; - - customAttribute.array[ offset_custom + 4 ] = v3.x; - customAttribute.array[ offset_custom + 5 ] = v3.y; - - offset_custom += 6; - - } - - } else if ( customAttribute.boundTo === 'faces' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - value = customAttribute.value[ chunk_faces3[ f ] ]; - - v1 = value; - v2 = value; - v3 = value; - - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - - customAttribute.array[ offset_custom + 2 ] = v2.x; - customAttribute.array[ offset_custom + 3 ] = v2.y; - - customAttribute.array[ offset_custom + 4 ] = v3.x; - customAttribute.array[ offset_custom + 5 ] = v3.y; - - offset_custom += 6; - - } - - } - - } else if ( customAttribute.size === 3 ) { - - var pp; - - if ( customAttribute.type === 'c' ) { - - pp = [ 'r', 'g', 'b' ]; - - } else { - - pp = [ 'x', 'y', 'z' ]; - - } - - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; - - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; - - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; - - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; - - offset_custom += 9; - - } - - } else if ( customAttribute.boundTo === 'faces' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - value = customAttribute.value[ chunk_faces3[ f ] ]; - - v1 = value; - v2 = value; - v3 = value; - - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; - - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; - - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; - - offset_custom += 9; - - } - - } else if ( customAttribute.boundTo === 'faceVertices' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - value = customAttribute.value[ chunk_faces3[ f ] ]; - - v1 = value[ 0 ]; - v2 = value[ 1 ]; - v3 = value[ 2 ]; - - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; - - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; - - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; - - offset_custom += 9; - - } - - } - - } else if ( customAttribute.size === 4 ) { - - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; - - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; - - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; - - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; - - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; - - offset_custom += 12; - - } - - } else if ( customAttribute.boundTo === 'faces' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - value = customAttribute.value[ chunk_faces3[ f ] ]; - - v1 = value; - v2 = value; - v3 = value; - - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; - - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; - - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; - - offset_custom += 12; - - } - - } else if ( customAttribute.boundTo === 'faceVertices' ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - value = customAttribute.value[ chunk_faces3[ f ] ]; - - v1 = value[ 0 ]; - v2 = value[ 1 ]; - v3 = value[ 2 ]; - - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; - - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; - - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; - - offset_custom += 12; - - } - - } - - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); - - } - - } - - if ( dispose ) { - - delete geometryGroup.__inittedArrays; - delete geometryGroup.__colorArray; - delete geometryGroup.__normalArray; - delete geometryGroup.__tangentArray; - delete geometryGroup.__uvArray; - delete geometryGroup.__uv2Array; - delete geometryGroup.__faceArray; - delete geometryGroup.__vertexArray; - delete geometryGroup.__lineArray; - delete geometryGroup.__skinIndexArray; - delete geometryGroup.__skinWeightArray; - - } - - }; - // Buffer rendering this.renderBufferImmediate = function ( object, program, material ) { state.initAttributes(); - if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); - if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); - if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); - if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); + var buffers = properties.get( object ); + + if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); + if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); + if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); + if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + + var attributes = program.getAttributes(); if ( object.hasPositions ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); - state.enableAttribute( program.attributes.position ); - - _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + state.enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormals ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); - if ( material instanceof THREE.MeshPhongMaterial === false && - material.shading === THREE.FlatShading ) { + if ( material.type !== 'MeshPhongMaterial' && material.type !== 'MeshStandardMaterial' && material.shading === THREE.FlatShading ) { - var nx, ny, nz, - nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, - normalArray, - i, il = object.count * 3; + for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { - for ( i = 0; i < il; i += 9 ) { + var array = object.normalArray; - normalArray = object.normalArray; + var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; + var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; + var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; - nax = normalArray[ i ]; - nay = normalArray[ i + 1 ]; - naz = normalArray[ i + 2 ]; + array[ i + 0 ] = nx; + array[ i + 1 ] = ny; + array[ i + 2 ] = nz; - nbx = normalArray[ i + 3 ]; - nby = normalArray[ i + 4 ]; - nbz = normalArray[ i + 5 ]; + array[ i + 3 ] = nx; + array[ i + 4 ] = ny; + array[ i + 5 ] = nz; - ncx = normalArray[ i + 6 ]; - ncy = normalArray[ i + 7 ]; - ncz = normalArray[ i + 8 ]; - - nx = ( nax + nbx + ncx ) / 3; - ny = ( nay + nby + ncy ) / 3; - nz = ( naz + nbz + ncz ) / 3; - - normalArray[ i ] = nx; - normalArray[ i + 1 ] = ny; - normalArray[ i + 2 ] = nz; - - normalArray[ i + 3 ] = nx; - normalArray[ i + 4 ] = ny; - normalArray[ i + 5 ] = nz; - - normalArray[ i + 6 ] = nx; - normalArray[ i + 7 ] = ny; - normalArray[ i + 8 ] = nz; + array[ i + 6 ] = nx; + array[ i + 7 ] = ny; + array[ i + 8 ] = nz; } @@ -31453,31 +37740,31 @@ THREE.Canvas3DRenderer = function ( parameters ) { _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); - state.enableAttribute( program.attributes.normal ); + state.enableAttribute( attributes.normal ); - _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasUvs && material.map ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); - state.enableAttribute( program.attributes.uv ); + state.enableAttribute( attributes.uv ); - _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); } if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); - state.enableAttribute( program.attributes.color ); + state.enableAttribute( attributes.color ); - _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } @@ -31489,41 +37776,320 @@ THREE.Canvas3DRenderer = function ( parameters ) { }; + this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { + + setMaterial( material ); + + var program = setProgram( camera, fog, material, object ); + + var updateBuffers = false; + var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; + + if ( geometryProgram !== _currentGeometryProgram ) { + + _currentGeometryProgram = geometryProgram; + updateBuffers = true; + + } + + // morph targets + + var morphTargetInfluences = object.morphTargetInfluences; + + if ( morphTargetInfluences !== undefined ) { + + var activeInfluences = []; + + for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { + + var influence = morphTargetInfluences[ i ]; + activeInfluences.push( [ influence, i ] ); + + } + + activeInfluences.sort( absNumericalSort ); + + if ( activeInfluences.length > 8 ) { + + activeInfluences.length = 8; + + } + + var morphAttributes = geometry.morphAttributes; + + for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { + + var influence = activeInfluences[ i ]; + morphInfluences[ i ] = influence[ 0 ]; + + if ( influence[ 0 ] !== 0 ) { + + var index = influence[ 1 ]; + + if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); + if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); + + } else { + + if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); + if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); + + } + + } + + var uniforms = program.getUniforms(); + + if ( uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences ); + + } + + updateBuffers = true; + + } + + // + + var index = geometry.index; + var position = geometry.attributes.position; + + if ( material.wireframe === true ) { + + index = objects.getWireframeAttribute( geometry ); + + } + + var renderer; + + if ( index !== null ) { + + renderer = indexedBufferRenderer; + renderer.setIndex( index ); + + } else { + + renderer = bufferRenderer; + + } + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry ); + + if ( index !== null ) { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); + + } + + } + + // + + var dataStart = 0; + var dataCount = Infinity; + + if ( index !== null ) { + + dataCount = index.count; + + } else if ( position !== undefined ) { + + dataCount = position.count; + + } + + var rangeStart = geometry.drawRange.start; + var rangeCount = geometry.drawRange.count; + + var groupStart = group !== null ? group.start : 0; + var groupCount = group !== null ? group.count : Infinity; + + var drawStart = Math.max( dataStart, rangeStart, groupStart ); + var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; + + var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); + + // + + if ( object instanceof THREE.Mesh ) { + + if ( material.wireframe === true ) { + + state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); + renderer.setMode( _gl.LINES ); + + } else { + + switch ( object.drawMode ) { + + case THREE.TrianglesDrawMode: + renderer.setMode( _gl.TRIANGLES ); + break; + + case THREE.TriangleStripDrawMode: + renderer.setMode( _gl.TRIANGLE_STRIP ); + break; + + case THREE.TriangleFanDrawMode: + renderer.setMode( _gl.TRIANGLE_FAN ); + break; + + } + + } + + + } else if ( object instanceof THREE.Line ) { + + var lineWidth = material.linewidth; + + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + + state.setLineWidth( lineWidth * getTargetPixelRatio() ); + + if ( object instanceof THREE.LineSegments ) { + + renderer.setMode( _gl.LINES ); + + } else { + + renderer.setMode( _gl.LINE_STRIP ); + + } + + } else if ( object instanceof THREE.Points ) { + + renderer.setMode( _gl.POINTS ); + + } + + if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) { + + renderer.renderInstances( geometry, drawStart, drawCount ); + + } else { + + renderer.render( drawStart, drawCount ); + + } + + }; + function setupVertexAttributes( material, program, geometry, startIndex ) { + var extension; + + if ( geometry instanceof THREE.InstancedBufferGeometry ) { + + extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.Canvas3DRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } + + } + + if ( startIndex === undefined ) startIndex = 0; + + state.initAttributes(); + var geometryAttributes = geometry.attributes; - var programAttributes = program.attributes; - var programAttributesKeys = program.attributesKeys; + var programAttributes = program.getAttributes(); - for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) { + var materialDefaultAttributeValues = material.defaultAttributeValues; - var key = programAttributesKeys[ i ]; - var programAttribute = programAttributes[ key ]; + for ( var name in programAttributes ) { + + var programAttribute = programAttributes[ name ]; if ( programAttribute >= 0 ) { - var geometryAttribute = geometryAttributes[ key ]; + var geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute !== undefined ) { var size = geometryAttribute.itemSize; + var buffer = objects.getAttributeBuffer( geometryAttribute ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer ); + if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) { - state.enableAttribute( programAttribute ); + var data = geometryAttribute.data; + var stride = data.stride; + var offset = geometryAttribute.offset; - _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 + if ( data instanceof THREE.InstancedInterleavedBuffer ) { - } else if ( material.defaultAttributeValues !== undefined ) { + state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension ); - if ( material.defaultAttributeValues[ key ].length === 2 ) { + if ( geometry.maxInstancedCount === undefined ) { - _gl.vertexAttrib2fv( programAttribute, material.defaultAttributeValues[ key ] ); + geometry.maxInstancedCount = data.meshPerAttribute * data.count; - } else if ( material.defaultAttributeValues[ key ].length === 3 ) { + } - _gl.vertexAttrib3fv( programAttribute, material.defaultAttributeValues[ key ] ); + } else { + + state.enableAttribute( programAttribute ); + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT ); + + } else { + + if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) { + + state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension ); + + if ( geometry.maxInstancedCount === undefined ) { + + geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + + } + + } else { + + state.enableAttribute( programAttribute ); + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 + + } + + } else if ( materialDefaultAttributeValues !== undefined ) { + + var value = materialDefaultAttributeValues[ name ]; + + if ( value !== undefined ) { + + switch ( value.length ) { + + case 2: + _gl.vertexAttrib2fv( programAttribute, value ); + break; + + case 3: + _gl.vertexAttrib3fv( programAttribute, value ); + break; + + case 4: + _gl.vertexAttrib4fv( programAttribute, value ); + break; + + default: + _gl.vertexAttrib1fv( programAttribute, value ); + + } } @@ -31537,765 +38103,14 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { - - if ( material.visible === false ) return; - - updateObject( object ); - - var program = setProgram( camera, lights, fog, material, object ); - - var updateBuffers = false, - wireframeBit = material.wireframe ? 1 : 0, - geometryProgram = 'direct_' + geometry.id + '_' + program.id + '_' + wireframeBit; - - if ( geometryProgram !== _currentGeometryProgram ) { - - _currentGeometryProgram = geometryProgram; - updateBuffers = true; - - } - - if ( updateBuffers ) { - - state.initAttributes(); - - } - - // render mesh - - if ( object instanceof THREE.Mesh ) { - - var mode = material.wireframe === true ? _gl.LINES : _gl.TRIANGLES; - - var index = geometry.attributes.index; - - if ( index ) { - - // indexed triangles - - var type, size; - - if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { - - type = _gl.UNSIGNED_INT; - size = 4; - - } else { - - type = _gl.UNSIGNED_SHORT; - size = 2; - - } - - var offsets = geometry.offsets; - - if ( offsets.length === 0 ) { - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, 0 ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - - } - - _gl.drawElements( mode, index.array.length, type, 0 ); - - _this.info.render.calls ++; - _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared - _this.info.render.faces += index.array.length / 3; - - } else { - - // if there is more than 1 chunk - // must set attribute pointers to use new offsets for each chunk - // even if geometry and materials didn't change - - updateBuffers = true; - - for ( var i = 0, il = offsets.length; i < il; i ++ ) { - - var startIndex = offsets[ i ].index; - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, startIndex ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - - } - - // render indexed triangles - - _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); - - _this.info.render.calls ++; - _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared - _this.info.render.faces += offsets[ i ].count / 3; - - } - - } - - } else { - - // non-indexed triangles - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, 0 ); - - } - - var position = geometry.attributes[ 'position' ]; - - // render non-indexed triangles - - _gl.drawArrays( mode, 0, position.array.length / position.itemSize ); - - _this.info.render.calls ++; - _this.info.render.vertices += position.array.length / position.itemSize; - _this.info.render.faces += position.array.length / ( 3 * position.itemSize ); - - } - - } else if ( object instanceof THREE.PointCloud ) { - - // render particles - - var mode = _gl.POINTS; - - var index = geometry.attributes.index; - - if ( index ) { - - // indexed points - - var type, size; - - if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { - - type = _gl.UNSIGNED_INT; - size = 4; - - } else { - - type = _gl.UNSIGNED_SHORT; - size = 2; - - } - - var offsets = geometry.offsets; - - if ( offsets.length === 0 ) { - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, 0 ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - - } - - _gl.drawElements( mode, index.array.length, type, 0); - - _this.info.render.calls ++; - _this.info.render.points += index.array.length; - - } else { - - // if there is more than 1 chunk - // must set attribute pointers to use new offsets for each chunk - // even if geometry and materials didn't change - - if ( offsets.length > 1 ) updateBuffers = true; - - for ( var i = 0, il = offsets.length; i < il; i ++ ) { - - var startIndex = offsets[ i ].index; - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, startIndex ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - - } - - // render indexed points - - _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); - - _this.info.render.calls ++; - _this.info.render.points += offsets[ i ].count; - - } - - } - - } else { - - // non-indexed points - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, 0 ); - - } - - var position = geometry.attributes.position; - var offsets = geometry.offsets; - - if ( offsets.length === 0 ) { - - _gl.drawArrays( mode, 0, position.array.length / 3 ); - - _this.info.render.calls ++; - _this.info.render.points += position.array.length / 3; - - } else { - - for ( var i = 0, il = offsets.length; i < il; i ++ ) { - - _gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count ); - - _this.info.render.calls ++; - _this.info.render.points += offsets[ i ].count; - - } - - } - - } - - } else if ( object instanceof THREE.Line ) { - - var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; - - state.setLineWidth( material.linewidth * pixelRatio ); - - var index = geometry.attributes.index; - - if ( index ) { - - // indexed lines - - var type, size; - - if ( index.array instanceof Uint32Array ) { - - type = _gl.UNSIGNED_INT; - size = 4; - - } else { - - type = _gl.UNSIGNED_SHORT; - size = 2; - - } - - var offsets = geometry.offsets; - - if ( offsets.length === 0 ) { - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, 0 ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - - } - - _gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array - - _this.info.render.calls ++; - _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared - - } else { - - // if there is more than 1 chunk - // must set attribute pointers to use new offsets for each chunk - // even if geometry and materials didn't change - - if ( offsets.length > 1 ) updateBuffers = true; - - for ( var i = 0, il = offsets.length; i < il; i ++ ) { - - var startIndex = offsets[ i ].index; - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, startIndex ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - - } - - // render indexed lines - - _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array - - _this.info.render.calls ++; - _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared - - } - - } - - } else { - - // non-indexed lines - - if ( updateBuffers ) { - - setupVertexAttributes( material, program, geometry, 0 ); - - } - - var position = geometry.attributes.position; - var offsets = geometry.offsets; - - if ( offsets.length === 0 ) { - - _gl.drawArrays( mode, 0, position.array.length / 3 ); - - _this.info.render.calls ++; - _this.info.render.vertices += position.array.length / 3; - - } else { - - for ( var i = 0, il = offsets.length; i < il; i ++ ) { - - _gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count ); - - _this.info.render.calls ++; - _this.info.render.vertices += offsets[ i ].count; - - } - - } - - } - - } - - }; - - this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { - - if ( material.visible === false ) return; - - updateObject( object ); - - var program = setProgram( camera, lights, fog, material, object ); - - var attributes = program.attributes; - - var updateBuffers = false, - wireframeBit = material.wireframe ? 1 : 0, - geometryProgram = geometryGroup.id + '_' + program.id + '_' + wireframeBit; - - if ( geometryProgram !== _currentGeometryProgram ) { - - _currentGeometryProgram = geometryProgram; - updateBuffers = true; - - } - - if ( updateBuffers ) { - - state.initAttributes(); - - } - - // vertices - - if ( ! material.morphTargets && attributes.position >= 0 ) { - - if ( updateBuffers ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - - state.enableAttribute( attributes.position ); - - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - - } - - } else { - - if ( object.morphTargetBase ) { - - setupMorphTargets( material, geometryGroup, object ); - - } - - } - - - if ( updateBuffers ) { - - // custom attributes - - // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers - - if ( geometryGroup.__webglCustomAttributesList ) { - - for ( var i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { - - var attribute = geometryGroup.__webglCustomAttributesList[ i ]; - - if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); - - state.enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); - - _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); - - } - - } - - } - - - // colors - - if ( attributes.color >= 0 ) { - - if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); - - state.enableAttribute( attributes.color ); - - _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); - - } else if ( material.defaultAttributeValues !== undefined ) { - - - _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); - - } - - } - - // normals - - if ( attributes.normal >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); - - state.enableAttribute( attributes.normal ); - - _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); - - } - - // tangents - - if ( attributes.tangent >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); - - state.enableAttribute( attributes.tangent ); - - _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); - - } - - // uvs - - if ( attributes.uv >= 0 ) { - - if ( object.geometry.faceVertexUvs[ 0 ] ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); - - state.enableAttribute( attributes.uv ); - - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); - - } else if ( material.defaultAttributeValues !== undefined ) { - - - _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); - - } - - } - - if ( attributes.uv2 >= 0 ) { - - if ( object.geometry.faceVertexUvs[ 1 ] ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); - - state.enableAttribute( attributes.uv2 ); - - _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); - - } else if ( material.defaultAttributeValues !== undefined ) { - - - _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); - - } - - } - - if ( material.skinning && - attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); - - state.enableAttribute( attributes.skinIndex ); - - _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); - - state.enableAttribute( attributes.skinWeight ); - - _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); - - } - - // line distances - - if ( attributes.lineDistance >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); - - state.enableAttribute( attributes.lineDistance ); - - _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); - - } - - } - - state.disableUnusedAttributes(); - - // render mesh - - if ( object instanceof THREE.Mesh ) { - - var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; - - // wireframe - - if ( material.wireframe ) { - - state.setLineWidth( material.wireframeLinewidth * pixelRatio ); - - if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); - _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 ); - - // triangles - - } else { - - if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); - _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 0 ); - - } - - _this.info.render.calls ++; - _this.info.render.vertices += geometryGroup.__webglFaceCount; - _this.info.render.faces += geometryGroup.__webglFaceCount / 3; - - // render lines - - } else if ( object instanceof THREE.Line ) { - - var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; - - state.setLineWidth( material.linewidth * pixelRatio ); - - _gl.drawArrays( mode, 0, geometryGroup.__webglLineCount ); - - _this.info.render.calls ++; - - // render particles - - } else if ( object instanceof THREE.PointCloud ) { - - _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); - - _this.info.render.calls ++; - _this.info.render.points += geometryGroup.__webglParticleCount; - - } - - }; - - function setupMorphTargets ( material, geometryGroup, object ) { - - // set base - - var attributes = material.program.attributes; - - if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); - - state.enableAttribute( attributes.position ); - - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - - } else if ( attributes.position >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - - state.enableAttribute( attributes.position ); - - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - - } - - if ( object.morphTargetForcedOrder.length ) { - - // set forced order - - var m = 0; - var order = object.morphTargetForcedOrder; - var influences = object.morphTargetInfluences; - - var attribute; - - while ( m < material.numSupportedMorphTargets && m < order.length ) { - - attribute = attributes[ 'morphTarget' + m ]; - - if ( attribute >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); - - state.enableAttribute( attribute ); - - _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); - - } - - attribute = attributes[ 'morphNormal' + m ]; - - if ( attribute >= 0 && material.morphNormals ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); - - state.enableAttribute( attribute ); - - _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); - - } - - object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; - - m ++; - - } - - } else { - - // find the most influencing - - var activeInfluenceIndices = []; - var influences = object.morphTargetInfluences; - var morphTargets = object.geometry.morphTargets; - - if ( influences.length > morphTargets.length ) { - - console.warn( 'THREE.Canvas3DRenderer: Influences array is bigger than morphTargets array.' ); - influences.length = morphTargets.length; - - } - - for ( var i = 0, il = influences.length; i < il; i ++ ) { - - var influence = influences[ i ]; - - activeInfluenceIndices.push( [ influence, i ] ); - - } - - if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { - - activeInfluenceIndices.sort( numericalSort ); - activeInfluenceIndices.length = material.numSupportedMorphTargets; - - } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { - - activeInfluenceIndices.sort( numericalSort ); - - } else if ( activeInfluenceIndices.length === 0 ) { - - activeInfluenceIndices.push( [ 0, 0 ] ); - - } - - var attribute; - - for ( var m = 0, ml = material.numSupportedMorphTargets; m < ml; m ++ ) { - - if ( activeInfluenceIndices[ m ] ) { - - var influenceIndex = activeInfluenceIndices[ m ][ 1 ]; - - attribute = attributes[ 'morphTarget' + m ]; - - if ( attribute >= 0 ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); - - state.enableAttribute( attribute ); - - _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); - - } - - attribute = attributes[ 'morphNormal' + m ]; - - if ( attribute >= 0 && material.morphNormals ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); - - state.enableAttribute( attribute ); - - _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); - - } - - object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; - - } else { - - /* - _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); - - if ( material.morphNormals ) { - - _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); - - } - */ - - object.__webglMorphTargetInfluences[ m ] = 0; - - } - - } - - } - - // load updated influences uniform - - if ( material.program.uniforms.morphTargetInfluences !== null ) { - - _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); - - } - - } - // Sorting + function absNumericalSort( a, b ) { + + return Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] ); + + } + function painterSortStable ( a, b ) { if ( a.object.renderOrder !== b.object.renderOrder ) { @@ -32336,19 +38151,13 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - function numericalSort ( a, b ) { - - return b[ 0 ] - a[ 0 ]; - - } - // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { if ( camera instanceof THREE.Camera === false ) { - THREE.error( 'THREE.Canvas3DRenderer.render: camera is not an instance of THREE.Camera.' ); + console.error( 'THREE.Canvas3DRenderer.render: camera is not an instance of THREE.Camera.' ); return; } @@ -32360,7 +38169,6 @@ THREE.Canvas3DRenderer = function ( parameters ) { _currentGeometryProgram = ''; _currentMaterialId = - 1; _currentCamera = null; - _lightsNeedUpdate = true; // update scene graph @@ -32368,19 +38176,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { // update camera matrices and frustum - if ( camera.parent === undefined ) camera.updateMatrixWorld(); - - // update Skeleton objects - - scene.traverse( function ( object ) { - - if ( object instanceof THREE.SkinnedMesh ) { - - object.skeleton.update(); - - } - - } ); + if ( camera.parent === null ) camera.updateMatrixWorld(); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); @@ -32388,13 +38184,17 @@ THREE.Canvas3DRenderer = function ( parameters ) { _frustum.setFromMatrix( _projScreenMatrix ); lights.length = 0; - opaqueObjects.length = 0; - transparentObjects.length = 0; + + opaqueObjectsLastIndex = - 1; + transparentObjectsLastIndex = - 1; sprites.length = 0; lensFlares.length = 0; - projectObject( scene ); + projectObject( scene, camera ); + + opaqueObjects.length = opaqueObjectsLastIndex + 1; + transparentObjects.length = transparentObjectsLastIndex + 1; if ( _this.sortObjects === true ) { @@ -32403,16 +38203,24 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - // custom render plugins (pre pass) - - shadowMapPlugin.render( scene, camera ); + setupLights( lights, camera ); // - _this.info.render.calls = 0; - _this.info.render.vertices = 0; - _this.info.render.faces = 0; - _this.info.render.points = 0; + shadowMap.render( scene, camera ); + + // + + _infoRender.calls = 0; + _infoRender.vertices = 0; + _infoRender.faces = 0; + _infoRender.points = 0; + + if ( renderTarget === undefined ) { + + renderTarget = null; + + } this.setRenderTarget( renderTarget ); @@ -32422,59 +38230,46 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - // set matrices for immediate objects - - for ( var i = 0, il = _webglObjectsImmediate.length; i < il; i ++ ) { - - var webglObject = _webglObjectsImmediate[ i ]; - var object = webglObject.object; - - if ( object.visible ) { - - setupMatrices( object, camera ); - - unrollImmediateBufferMaterial( webglObject ); - - } - - } + // if ( scene.overrideMaterial ) { var overrideMaterial = scene.overrideMaterial; - setMaterial( overrideMaterial ); - - renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial ); - renderObjects( transparentObjects, camera, lights, fog, overrideMaterial ); - renderObjectsImmediate( _webglObjectsImmediate, '', camera, lights, fog, overrideMaterial ); + renderObjects( opaqueObjects, camera, fog, overrideMaterial ); + renderObjects( transparentObjects, camera, fog, overrideMaterial ); } else { // opaque pass (front-to-back order) state.setBlending( THREE.NoBlending ); - - renderObjects( opaqueObjects, camera, lights, fog, null ); - renderObjectsImmediate( _webglObjectsImmediate, 'opaque', camera, lights, fog, null ); + renderObjects( opaqueObjects, camera, fog ); // transparent pass (back-to-front order) - renderObjects( transparentObjects, camera, lights, fog, null ); - renderObjectsImmediate( _webglObjectsImmediate, 'transparent', camera, lights, fog, null ); + renderObjects( transparentObjects, camera, fog ); } // custom render plugins (post pass) spritePlugin.render( scene, camera ); - lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); + lensFlarePlugin.render( scene, camera, _currentViewport ); // Generate mipmap if we're using any kind of mipmap filtering - if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + if ( renderTarget ) { - updateRenderTargetMipmap( renderTarget ); + var texture = renderTarget.texture; + + if ( texture.generateMipmaps && isPowerOfTwo( renderTarget ) && + texture.minFilter !== THREE.NearestFilter && + texture.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } } @@ -32488,17 +38283,60 @@ THREE.Canvas3DRenderer = function ( parameters ) { }; - function projectObject( object ) { + function pushRenderItem( object, geometry, material, z, group ) { - if ( object.visible === false ) return; + var array, index; - if ( object instanceof THREE.Scene || object instanceof THREE.Group ) { + // allocate the next position in the appropriate array - // skip + if ( material.transparent ) { + + array = transparentObjects; + index = ++ transparentObjectsLastIndex; } else { - initObject( object ); + array = opaqueObjects; + index = ++ opaqueObjectsLastIndex; + + } + + // recycle existing render item or grow the array + + var renderItem = array[ index ]; + + if ( renderItem !== undefined ) { + + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.z = _vector3.z; + renderItem.group = group; + + } else { + + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + z: _vector3.z, + group: group + }; + + // assert( index === array.length ); + array.push( renderItem ); + + } + + } + + function projectObject( object, camera ) { + + if ( object.visible === false ) return; + + if ( object.layers.test( camera.layers ) ) { if ( object instanceof THREE.Light ) { @@ -32506,32 +38344,71 @@ THREE.Canvas3DRenderer = function ( parameters ) { } else if ( object instanceof THREE.Sprite ) { - sprites.push( object ); + if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + + sprites.push( object ); + + } } else if ( object instanceof THREE.LensFlare ) { lensFlares.push( object ); - } else { + } else if ( object instanceof THREE.ImmediateRenderObject ) { - var webglObjects = _webglObjects[ object.id ]; + if ( _this.sortObjects === true ) { - if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); - for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { + } - var webglObject = webglObjects[ i ]; + pushRenderItem( object, null, object.material, _vector3.z, null ); - unrollBufferMaterial( webglObject ); + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { - webglObject.render = true; + if ( object instanceof THREE.SkinnedMesh ) { + + object.skeleton.update(); + + } + + if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + + var material = object.material; + + if ( material.visible === true ) { if ( _this.sortObjects === true ) { _vector3.setFromMatrixPosition( object.matrixWorld ); _vector3.applyProjection( _projScreenMatrix ); - webglObject.z = _vector3.z; + } + + var geometry = objects.update( object ); + + if ( material instanceof THREE.MultiMaterial ) { + + var groups = geometry.groups; + var materials = material.materials; + + for ( var i = 0, l = groups.length; i < l; i ++ ) { + + var group = groups[ i ]; + var groupMaterial = materials[ group.materialIndex ]; + + if ( groupMaterial.visible === true ) { + + pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); + + } + + } + + } else { + + pushRenderItem( object, geometry, material, _vector3.z, null ); } @@ -32543,50 +38420,47 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - for ( var i = 0, l = object.children.length; i < l; i ++ ) { + var children = object.children; - projectObject( object.children[ i ] ); + for ( var i = 0, l = children.length; i < l; i ++ ) { + + projectObject( children[ i ], camera ); } } - function renderObjects( renderList, camera, lights, fog, overrideMaterial ) { - - var material; + function renderObjects( renderList, camera, fog, overrideMaterial ) { for ( var i = 0, l = renderList.length; i < l; i ++ ) { - var webglObject = renderList[ i ]; + var renderItem = renderList[ i ]; - var object = webglObject.object; - var buffer = webglObject.buffer; + var object = renderItem.object; + var geometry = renderItem.geometry; + var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; + var group = renderItem.group; - setupMatrices( object, camera ); + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - if ( overrideMaterial ) { - - material = overrideMaterial; - - } else { - - material = webglObject.material; - - if ( ! material ) continue; + if ( object instanceof THREE.ImmediateRenderObject ) { setMaterial( material ); - } + var program = setProgram( camera, fog, material, object ); - _this.setMaterialFaces( material ); + _currentGeometryProgram = ''; - if ( buffer instanceof THREE.BufferGeometry ) { + object.render( function ( object ) { - _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); + _this.renderBufferImmediate( object, program, material ); + + } ); } else { - _this.renderBuffer( camera, lights, fog, material, buffer, object ); + _this.renderBufferDirect( camera, fog, geometry, material, object, group ); } @@ -32594,753 +38468,80 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, overrideMaterial ) { + function initMaterial( material, fog, object ) { - var material; + var materialProperties = properties.get( material ); - for ( var i = 0, l = renderList.length; i < l; i ++ ) { + var parameters = programCache.getParameters( material, _lights, fog, object ); + var code = programCache.getProgramCode( material, parameters ); - var webglObject = renderList[ i ]; - var object = webglObject.object; - - if ( object.visible ) { - - if ( overrideMaterial ) { - - material = overrideMaterial; - - } else { - - material = webglObject[ materialType ]; - - if ( ! material ) continue; - - setMaterial( material ); - - } - - _this.renderImmediateObject( camera, lights, fog, material, object ); - - } - - } - - } - - this.renderImmediateObject = function ( camera, lights, fog, material, object ) { - - var program = setProgram( camera, lights, fog, material, object ); - - _currentGeometryProgram = ''; - - _this.setMaterialFaces( material ); - - if ( object.immediateRenderCallback ) { - - object.immediateRenderCallback( program, _gl, _frustum ); - - } else { - - object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); - - } - - }; - - function unrollImmediateBufferMaterial ( globject ) { - - var object = globject.object, - material = object.material; - - if ( material.transparent ) { - - globject.transparent = material; - globject.opaque = null; - - } else { - - globject.opaque = material; - globject.transparent = null; - - } - - } - - function unrollBufferMaterial ( globject ) { - - var object = globject.object; - var buffer = globject.buffer; - - var geometry = object.geometry; - var material = object.material; - - if ( material instanceof THREE.MeshFaceMaterial ) { - - var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex; - - material = material.materials[ materialIndex ]; - - globject.material = material; - - if ( material.transparent ) { - - transparentObjects.push( globject ); - - } else { - - opaqueObjects.push( globject ); - - } - - } else if ( material ) { - - globject.material = material; - - if ( material.transparent ) { - - transparentObjects.push( globject ); - - } else { - - opaqueObjects.push( globject ); - - } - - } - - } - - function initObject( object ) { - - if ( object.__webglInit === undefined ) { - - object.__webglInit = true; - object._modelViewMatrix = new THREE.Matrix4(); - object._normalMatrix = new THREE.Matrix3(); - - object.addEventListener( 'removed', onObjectRemoved ); - - } - - var geometry = object.geometry; - - if ( geometry === undefined ) { - - // ImmediateRenderObject - - } else if ( geometry.__webglInit === undefined ) { - - geometry.__webglInit = true; - geometry.addEventListener( 'dispose', onGeometryDispose ); - - if ( geometry instanceof THREE.BufferGeometry ) { - - _this.info.memory.geometries ++; - - } else if ( object instanceof THREE.Mesh ) { - - initGeometryGroups( object, geometry ); - - } else if ( object instanceof THREE.Line ) { - - if ( geometry.__webglVertexBuffer === undefined ) { - - createLineBuffers( geometry ); - initLineBuffers( geometry, object ); - - geometry.verticesNeedUpdate = true; - geometry.colorsNeedUpdate = true; - geometry.lineDistancesNeedUpdate = true; - - } - - } else if ( object instanceof THREE.PointCloud ) { - - if ( geometry.__webglVertexBuffer === undefined ) { - - createParticleBuffers( geometry ); - initParticleBuffers( geometry, object ); - - geometry.verticesNeedUpdate = true; - geometry.colorsNeedUpdate = true; - - } - - } - - } - - if ( object.__webglActive === undefined) { - - object.__webglActive = true; - - if ( object instanceof THREE.Mesh ) { - - if ( geometry instanceof THREE.BufferGeometry ) { - - addBuffer( _webglObjects, geometry, object ); - - } else if ( geometry instanceof THREE.Geometry ) { - - var geometryGroupsList = geometryGroups[ geometry.id ]; - - for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) { - - addBuffer( _webglObjects, geometryGroupsList[ i ], object ); - - } - - } - - } else if ( object instanceof THREE.Line || object instanceof THREE.PointCloud ) { - - addBuffer( _webglObjects, geometry, object ); - - } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { - - addBufferImmediate( _webglObjectsImmediate, object ); - - } - - } - - } - - // Geometry splitting - - var geometryGroups = {}; - var geometryGroupCounter = 0; - - function makeGroups( geometry, usesFaceMaterial ) { - - var maxVerticesInGroup = extensions.get( 'OES_element_index_uint' ) ? 4294967296 : 65535; - - var groupHash, hash_map = {}; - - var numMorphTargets = geometry.morphTargets.length; - var numMorphNormals = geometry.morphNormals.length; - - var group; - var groups = {}; - var groupsList = []; - - for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { - - var face = geometry.faces[ f ]; - var materialIndex = usesFaceMaterial ? face.materialIndex : 0; - - if ( ! ( materialIndex in hash_map ) ) { - - hash_map[ materialIndex ] = { hash: materialIndex, counter: 0 }; - - } - - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; - - if ( ! ( groupHash in groups ) ) { - - group = { - id: geometryGroupCounter ++, - faces3: [], - materialIndex: materialIndex, - vertices: 0, - numMorphTargets: numMorphTargets, - numMorphNormals: numMorphNormals - }; - - groups[ groupHash ] = group; - groupsList.push( group ); - - } - - if ( groups[ groupHash ].vertices + 3 > maxVerticesInGroup ) { - - hash_map[ materialIndex ].counter += 1; - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; - - if ( ! ( groupHash in groups ) ) { - - group = { - id: geometryGroupCounter ++, - faces3: [], - materialIndex: materialIndex, - vertices: 0, - numMorphTargets: numMorphTargets, - numMorphNormals: numMorphNormals - }; - - groups[ groupHash ] = group; - groupsList.push( group ); - - } - - } - - groups[ groupHash ].faces3.push( f ); - groups[ groupHash ].vertices += 3; - - } - - return groupsList; - - } - - function initGeometryGroups( object, geometry ) { - - var material = object.material, addBuffers = false; - - if ( geometryGroups[ geometry.id ] === undefined || geometry.groupsNeedUpdate === true ) { - - delete _webglObjects[ object.id ]; - - geometryGroups[ geometry.id ] = makeGroups( geometry, material instanceof THREE.MeshFaceMaterial ); - - geometry.groupsNeedUpdate = false; - - } - - var geometryGroupsList = geometryGroups[ geometry.id ]; - - // create separate VBOs per geometry chunk - - for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { - - var geometryGroup = geometryGroupsList[ i ]; - - // initialise VBO on the first access - - if ( geometryGroup.__webglVertexBuffer === undefined ) { - - createMeshBuffers( geometryGroup ); - initMeshBuffers( geometryGroup, object ); - - geometry.verticesNeedUpdate = true; - geometry.morphTargetsNeedUpdate = true; - geometry.elementsNeedUpdate = true; - geometry.uvsNeedUpdate = true; - geometry.normalsNeedUpdate = true; - geometry.tangentsNeedUpdate = true; - geometry.colorsNeedUpdate = true; - - addBuffers = true; - - } else { - - addBuffers = false; - - } - - if ( addBuffers || object.__webglActive === undefined ) { - - addBuffer( _webglObjects, geometryGroup, object ); - - } - - } - - object.__webglActive = true; - - } - - function addBuffer( objlist, buffer, object ) { - - var id = object.id; - objlist[id] = objlist[id] || []; - objlist[id].push( - { - id: id, - buffer: buffer, - object: object, - material: null, - z: 0 - } - ); - - }; - - function addBufferImmediate( objlist, object ) { - - objlist.push( - { - id: null, - object: object, - opaque: null, - transparent: null, - z: 0 - } - ); - - }; - - // Objects updates - - function updateObject( object ) { - - var geometry = object.geometry; - - if ( geometry instanceof THREE.BufferGeometry ) { - - var attributes = geometry.attributes; - var attributesKeys = geometry.attributesKeys; - - for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) { - - var key = attributesKeys[ i ]; - var attribute = attributes[ key ]; - var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; - - if ( attribute.buffer === undefined ) { - - attribute.buffer = _gl.createBuffer(); - _gl.bindBuffer( bufferType, attribute.buffer ); - _gl.bufferData( bufferType, attribute.array, ( attribute instanceof THREE.DynamicBufferAttribute ) ? _gl.DYNAMIC_DRAW : _gl.STATIC_DRAW ); - - attribute.needsUpdate = false; - - } else if ( attribute.needsUpdate === true ) { - - _gl.bindBuffer( bufferType, attribute.buffer ); - - if ( attribute.updateRange === undefined || attribute.updateRange.count === -1 ) { // Not using update ranges - - _gl.bufferSubData( bufferType, 0, attribute.array ); - - } else if ( attribute.updateRange.count === 0 ) { - - console.error( 'THREE.Canvas3DRenderer.updateObject: using updateRange for THREE.DynamicBufferAttribute and marked as needsUpdate but count is 0, ensure you are using set methods or updating manually.' ); - - } else { - - _gl.bufferSubData( bufferType, attribute.updateRange.offset * attribute.array.BYTES_PER_ELEMENT, - attribute.array.subarray( attribute.updateRange.offset, attribute.updateRange.offset + attribute.updateRange.count ) ); - - attribute.updateRange.count = 0; // reset range - - } - - attribute.needsUpdate = false; - - } - - } - - } else if ( object instanceof THREE.Mesh ) { - - // check all geometry groups - - if ( geometry.groupsNeedUpdate === true ) { - - initGeometryGroups( object, geometry ); - - } - - var geometryGroupsList = geometryGroups[ geometry.id ]; - - for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { - - var geometryGroup = geometryGroupsList[ i ]; - var material = getBufferMaterial( object, geometryGroup ); - - var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); - - if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || - geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || - geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { - - setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, ! geometry.dynamic, material ); - - } - - } - - geometry.verticesNeedUpdate = false; - geometry.morphTargetsNeedUpdate = false; - geometry.elementsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.tangentsNeedUpdate = false; - - if (material.attributes) clearCustomAttributes( material ); - - } else if ( object instanceof THREE.Line ) { - - var material = getBufferMaterial( object, geometry ); - var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); - - if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) { - - setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); - - } - - geometry.verticesNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.lineDistancesNeedUpdate = false; - - if (material.attributes) clearCustomAttributes( material ); - - } else if ( object instanceof THREE.PointCloud ) { - - var material = getBufferMaterial( object, geometry ); - var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); - - if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || customAttributesDirty ) { - - setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); - - } - - geometry.verticesNeedUpdate = false; - geometry.colorsNeedUpdate = false; - - if(material.attributes) clearCustomAttributes( material ); - - } - - } - - // Objects updates - custom attributes check - - function areCustomAttributesDirty( material ) { - - for ( var name in material.attributes ) { - - if ( material.attributes[ name ].needsUpdate ) return true; - - } - - return false; - - } - - function clearCustomAttributes( material ) { - - for ( var name in material.attributes ) { - - material.attributes[ name ].needsUpdate = false; - - } - - } - - // Objects removal - - function removeObject( object ) { - - if ( object instanceof THREE.Mesh || - object instanceof THREE.PointCloud || - object instanceof THREE.Line ) { - - delete _webglObjects[ object.id ]; - - } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { - - removeInstances( _webglObjectsImmediate, object ); - - } - - delete object.__webglInit; - delete object._modelViewMatrix; - delete object._normalMatrix; - - delete object.__webglActive; - - } - - function removeInstances( objlist, object ) { - - for ( var o = objlist.length - 1; o >= 0; o -- ) { - - if ( objlist[ o ].object === object ) { - - objlist.splice( o, 1 ); - - } - - } - - } - - // Materials - - var shaderIDs = { - MeshDepthMaterial: 'depth', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointCloudMaterial: 'particle_basic' - }; - - function initMaterial( material, lights, fog, object ) { - - material.addEventListener( 'dispose', onMaterialDispose ); - - var shaderID = shaderIDs[ material.type ]; - - if ( shaderID ) { - - var shader = THREE.ShaderLib[ shaderID ]; - - material.__webglShader = { - uniforms: THREE.UniformsUtils.clone( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - } - - } else { - - material.__webglShader = { - uniforms: material.uniforms, - vertexShader: material.vertexShader, - fragmentShader: material.fragmentShader - } - - } - - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) - - var maxLightCount = allocateLights( lights ); - var maxShadows = allocateShadows( lights ); - var maxBones = allocateBones( object ); - - var parameters = { - - precision: _precision, - supportsVertexTextures: _supportsVertexTextures, - - map: !! material.map, - envMap: !! material.envMap, - envMapMode: material.envMap && material.envMap.mapping, - lightMap: !! material.lightMap, - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, - - combine: material.combine, - - vertexColors: material.vertexColors, - - fog: fog, - useFog: material.fog, - fogExp: fog instanceof THREE.FogExp2, - - flatShading: material.shading === THREE.FlatShading, - - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: _logarithmicDepthBuffer, - - skinning: material.skinning, - maxBones: maxBones, - useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture, - - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: _this.maxMorphTargets, - maxMorphNormals: _this.maxMorphNormals, - - maxDirLights: maxLightCount.directional, - maxPointLights: maxLightCount.point, - maxSpotLights: maxLightCount.spot, - maxHemiLights: maxLightCount.hemi, - - maxShadows: maxShadows, - shadowMapEnabled: _this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, - shadowMapType: _this.shadowMapType, - shadowMapDebug: _this.shadowMapDebug, - shadowMapCascade: _this.shadowMapCascade, - - alphaTest: material.alphaTest, - metal: material.metal, - wrapAround: material.wrapAround, - doubleSided: material.side === THREE.DoubleSide, - flipSided: material.side === THREE.BackSide - - }; - - // Generate code - - var chunks = []; - - if ( shaderID ) { - - chunks.push( shaderID ); - - } else { - - chunks.push( material.fragmentShader ); - chunks.push( material.vertexShader ); - - } - - if ( material.defines !== undefined ) { - - for ( var name in material.defines ) { - - chunks.push( name ); - chunks.push( material.defines[ name ] ); - - } - - } - - for ( var name in parameters ) { - - chunks.push( name ); - chunks.push( parameters[ name ] ); - - } - - var code = chunks.join(); - - var program; - - // Check if code has been already compiled - - for ( var p = 0, pl = _programs.length; p < pl; p ++ ) { - - var programInfo = _programs[ p ]; - - if ( programInfo.code === code ) { - - program = programInfo; - program.usedTimes ++; - - break; - - } - - } + var program = materialProperties.program; + var programChange = true; if ( program === undefined ) { - program = new THREE.WebGLProgram( _this, code, material, parameters ); - _programs.push( program ); + // new material + material.addEventListener( 'dispose', onMaterialDispose ); - _this.info.memory.programs = _programs.length; + } else if ( program.code !== code ) { + + // changed glsl or parameters + releaseMaterialProgramReference( material ); + + } else if ( parameters.shaderID !== undefined ) { + + // same glsl and uniform list + return; + + } else { + + // only rebuild uniform list + programChange = false; } - material.program = program; + if ( programChange ) { - var attributes = program.attributes; + if ( parameters.shaderID ) { + + var shader = THREE.ShaderLib[ parameters.shaderID ]; + + materialProperties.__webglShader = { + name: material.type, + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + }; + + } else { + + materialProperties.__webglShader = { + name: material.type, + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader + }; + + } + + material.__webglShader = materialProperties.__webglShader; + + program = programCache.acquireProgram( material, parameters, code ); + + materialProperties.program = program; + material.program = program; + + } + + var attributes = program.getAttributes(); if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; - var id, base = 'morphTarget'; - for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { - id = base + i; - - if ( attributes[ id ] >= 0 ) { + if ( attributes[ 'morphTarget' + i ] >= 0 ) { material.numSupportedMorphTargets ++; @@ -33354,13 +38555,9 @@ THREE.Canvas3DRenderer = function ( parameters ) { material.numSupportedMorphNormals = 0; - var id, base = 'morphNormal'; + for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { - for ( i = 0; i < _this.maxMorphNormals; i ++ ) { - - id = base + i; - - if ( attributes[ id ] >= 0 ) { + if ( attributes[ 'morphNormal' + i ] >= 0 ) { material.numSupportedMorphNormals ++; @@ -33370,14 +38567,62 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - material.uniformsList = []; + materialProperties.uniformsList = []; - for ( var u in material.__webglShader.uniforms ) { + var uniforms = materialProperties.__webglShader.uniforms, + uniformLocations = materialProperties.program.getUniforms(); - var location = material.program.uniforms[ u ]; + for ( var u in uniforms ) { + + var location = uniformLocations[ u ]; if ( location ) { - material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] ); + + materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshStandardMaterial || + material.lights ) { + + // store the light setup it was created for + + materialProperties.lightsHash = _lights.hash; + + // wire up the material to this renderer's lighting state + + uniforms.ambientLightColor.value = _lights.ambient; + uniforms.directionalLights.value = _lights.directional; + uniforms.spotLights.value = _lights.spot; + uniforms.pointLights.value = _lights.point; + uniforms.hemisphereLights.value = _lights.hemi; + + uniforms.directionalShadowMap.value = _lights.directionalShadowMap; + uniforms.directionalShadowMatrix.value = _lights.directionalShadowMatrix; + uniforms.spotShadowMap.value = _lights.spotShadowMap; + uniforms.spotShadowMatrix.value = _lights.spotShadowMatrix; + uniforms.pointShadowMap.value = _lights.pointShadowMap; + uniforms.pointShadowMatrix.value = _lights.pointShadowMatrix; + + } + + // detect dynamic uniforms + + materialProperties.hasDynamicUniforms = false; + + for ( var j = 0, jl = materialProperties.uniformsList.length; j < jl; j ++ ) { + + var uniform = materialProperties.uniformsList[ j ][ 0 ]; + + if ( uniform.dynamic === true ) { + + materialProperties.hasDynamicUniforms = true; + break; + } } @@ -33386,6 +38631,8 @@ THREE.Canvas3DRenderer = function ( parameters ) { function setMaterial( material ) { + setMaterialFaces( material ); + if ( material.transparent === true ) { state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); @@ -33396,6 +38643,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { } + state.setDepthFunc( material.depthFunc ); state.setDepthTest( material.depthTest ); state.setDepthWrite( material.depthWrite ); state.setColorWrite( material.colorWrite ); @@ -33403,26 +38651,39 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - function setProgram( camera, lights, fog, material, object ) { + function setMaterialFaces( material ) { + + if (material.side !== THREE.DoubleSide) + state.enable( _gl.CULL_FACE ) + else + state.disable( _gl.CULL_FACE ); + state.setFlipSided( material.side === THREE.BackSide ); + + } + + function setProgram( camera, fog, material, object ) { _usedTextureUnits = 0; - if ( material.needsUpdate ) { + var materialProperties = properties.get( material ); - if ( material.program ) deallocateMaterial( material ); + if ( materialProperties.program === undefined ) { - initMaterial( material, lights, fog, object ); - material.needsUpdate = false; + material.needsUpdate = true; } - if ( material.morphTargets ) { + if ( materialProperties.lightsHash !== undefined && + materialProperties.lightsHash !== _lights.hash ) { - if ( ! object.__webglMorphTargetInfluences ) { + material.needsUpdate = true; - object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); + } - } + if ( material.needsUpdate ) { + + initMaterial( material, fog, object ); + material.needsUpdate = false; } @@ -33430,9 +38691,9 @@ THREE.Canvas3DRenderer = function ( parameters ) { var refreshMaterial = false; var refreshLights = false; - var program = material.program, - p_uniforms = program.uniforms, - m_uniforms = material.__webglShader.uniforms; + var program = materialProperties.program, + p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.__webglShader.uniforms; if ( program.id !== _currentProgram ) { @@ -33447,7 +38708,6 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( material.id !== _currentMaterialId ) { - if ( _currentMaterialId === -1 ) refreshLights = true; _currentMaterialId = material.id; refreshMaterial = true; @@ -33458,23 +38718,35 @@ THREE.Canvas3DRenderer = function ( parameters ) { _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - if ( _logarithmicDepthBuffer ) { + if ( capabilities.logarithmicDepthBuffer ) { _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } - if ( camera !== _currentCamera ) _currentCamera = camera; + if ( camera !== _currentCamera ) { + + _currentCamera = camera; + + // lighting uniforms depend on the camera so enforce an update + // now, in case this material supports lights - or later, when + // the next material that does gets activated: + + refreshMaterial = true; // set to true on material change + refreshLights = true; // remains set until update done + + } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material instanceof THREE.ShaderMaterial || material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshStandardMaterial || material.envMap ) { - if ( p_uniforms.cameraPosition !== null ) { + if ( p_uniforms.cameraPosition !== undefined ) { _vector3.setFromMatrixPosition( camera.matrixWorld ); _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); @@ -33486,10 +38758,11 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( material instanceof THREE.MeshPhongMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshStandardMaterial || material instanceof THREE.ShaderMaterial || material.skinning ) { - if ( p_uniforms.viewMatrix !== null ) { + if ( p_uniforms.viewMatrix !== undefined ) { _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); @@ -33505,21 +38778,21 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( material.skinning ) { - if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) { + if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) { _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); } - if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) { + if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) { _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); } - if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) { + if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) { - if ( p_uniforms.boneTexture !== null ) { + if ( p_uniforms.boneTexture !== undefined ) { var textureUnit = getTextureUnit(); @@ -33528,13 +38801,13 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - if ( p_uniforms.boneTextureWidth !== null ) { + if ( p_uniforms.boneTextureWidth !== undefined ) { _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); } - if ( p_uniforms.boneTextureHeight !== null ) { + if ( p_uniforms.boneTextureHeight !== undefined ) { _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); @@ -33542,7 +38815,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { } else if ( object.skeleton && object.skeleton.boneMatrices ) { - if ( p_uniforms.boneGlobalMatrices !== null ) { + if ( p_uniforms.boneGlobalMatrices !== undefined ) { _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); @@ -33554,6 +38827,24 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( refreshMaterial ) { + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshStandardMaterial || + material.lights ) { + + // the current material requires lighting info + + // note: all lighting uniforms are always set correctly + // they simply reference the renderer's state for their + // values + // + // use the current material's .needsUpdate flags to set + // the GL state when required + + markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); + + } + // refresh uniforms common to several materials if ( fog && material.fog ) { @@ -33562,29 +38853,10 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - if ( material instanceof THREE.MeshPhongMaterial || - material instanceof THREE.MeshLambertMaterial || - material.lights ) { - - if ( _lightsNeedUpdate ) { - - refreshLights = true; - setupLights( lights ); - _lightsNeedUpdate = false; - } - - if ( refreshLights ) { - refreshUniformsLights( m_uniforms, _lights ); - markUniformsLightsNeedsUpdate( m_uniforms, true ); - } else { - markUniformsLightsNeedsUpdate( m_uniforms, false ); - } - - } - if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.MeshPhongMaterial ) { + material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshStandardMaterial ) { refreshUniformsCommon( m_uniforms, material ); @@ -33601,17 +38873,21 @@ THREE.Canvas3DRenderer = function ( parameters ) { refreshUniformsLine( m_uniforms, material ); refreshUniformsDash( m_uniforms, material ); - } else if ( material instanceof THREE.PointCloudMaterial ) { + } else if ( material instanceof THREE.PointsMaterial ) { - refreshUniformsParticle( m_uniforms, material ); + refreshUniformsPoints( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + refreshUniformsLambert( m_uniforms, material ); } else if ( material instanceof THREE.MeshPhongMaterial ) { refreshUniformsPhong( m_uniforms, material ); - } else if ( material instanceof THREE.MeshLambertMaterial ) { + } else if ( material instanceof THREE.MeshStandardMaterial ) { - refreshUniformsLambert( m_uniforms, material ); + refreshUniformsStandard( m_uniforms, material ); } else if ( material instanceof THREE.MeshDepthMaterial ) { @@ -33625,30 +38901,52 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - if ( object.receiveShadow && ! material._shadowPass ) { - - refreshUniformsShadow( m_uniforms, lights ); - - } - // load common uniforms - loadUniformsGeneric( material.uniformsList ); + loadUniformsGeneric( materialProperties.uniformsList ); } loadUniformsMatrices( p_uniforms, object ); - if ( p_uniforms.modelMatrix !== null ) { + if ( p_uniforms.modelMatrix !== undefined ) { _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); } + if ( materialProperties.hasDynamicUniforms === true ) { + + updateDynamicUniforms( materialProperties.uniformsList, object, camera ); + + } + return program; } + function updateDynamicUniforms ( uniforms, object, camera ) { + + var dynamicUniforms = []; + + for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { + + var uniform = uniforms[ j ][ 0 ]; + var onUpdateCallback = uniform.onUpdateCallback; + + if ( onUpdateCallback !== undefined ) { + + onUpdateCallback.bind( uniform )( object, camera ); + dynamicUniforms.push( uniforms[ j ] ); + + } + + } + + loadUniformsGeneric( dynamicUniforms ); + + } + // Uniforms (refresh uniforms objects) function refreshUniformsCommon ( uniforms, material ) { @@ -33657,31 +38955,30 @@ THREE.Canvas3DRenderer = function ( parameters ) { uniforms.diffuse.value = material.color; - uniforms.map.value = material.map; - uniforms.lightMap.value = material.lightMap; - uniforms.specularMap.value = material.specularMap; - uniforms.alphaMap.value = material.alphaMap; + if ( material.emissive ) { - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; + uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } - if ( material.normalMap ) { + uniforms.map.value = material.map; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); + if ( material.aoMap ) { + + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. normal map - // 4. bump map - // 5. alpha map + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + // 6. emissive map var uvScaleMap; @@ -33693,6 +38990,10 @@ THREE.Canvas3DRenderer = function ( parameters ) { uvScaleMap = material.specularMap; + } else if ( material.displacementMap ) { + + uvScaleMap = material.displacementMap; + } else if ( material.normalMap ) { uvScaleMap = material.normalMap; @@ -33701,14 +39002,32 @@ THREE.Canvas3DRenderer = function ( parameters ) { uvScaleMap = material.bumpMap; + } else if ( material.roughnessMap ) { + + uvScaleMap = material.roughnessMap; + + } else if ( material.metalnessMap ) { + + uvScaleMap = material.metalnessMap; + } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; + } else if ( material.emissiveMap ) { + + uvScaleMap = material.emissiveMap; + } if ( uvScaleMap !== undefined ) { + if ( uvScaleMap instanceof THREE.WebGLRenderTarget ) { + + uvScaleMap = uvScaleMap.texture; + + } + var offset = uvScaleMap.offset; var repeat = uvScaleMap.repeat; @@ -33739,25 +39058,25 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - function refreshUniformsParticle ( uniforms, material ) { + function refreshUniformsPoints ( uniforms, material ) { - uniforms.psColor.value = material.color; - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size; - uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size * _pixelRatio; + uniforms.scale.value = _height / 2.0; // TODO: Cache this. - uniforms.map.value = material.map; + uniforms.map.value = material.map; - if ( material.map !== null ) { + if ( material.map !== null ) { - var offset = material.map.offset; - var repeat = material.map.repeat; + var offset = material.map.offset; + var repeat = material.map.repeat; - uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); - } + } - } + } function refreshUniformsFog ( uniforms, fog ) { @@ -33776,56 +39095,123 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - function refreshUniformsPhong ( uniforms, material ) { - - uniforms.shininess.value = material.shininess; - - uniforms.emissive.value = material.emissive; - uniforms.specular.value = material.specular; - - if ( material.wrapAround ) { - - uniforms.wrapRGB.value.copy( material.wrapRGB ); - - } - - } - function refreshUniformsLambert ( uniforms, material ) { - uniforms.emissive.value = material.emissive; + if ( material.lightMap ) { - if ( material.wrapAround ) { + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; - uniforms.wrapRGB.value.copy( material.wrapRGB ); + } + + if ( material.emissiveMap ) { + + uniforms.emissiveMap.value = material.emissiveMap; } } - function refreshUniformsLights ( uniforms, lights ) { + function refreshUniformsPhong ( uniforms, material ) { - uniforms.ambientLightColor.value = lights.ambient; + uniforms.specular.value = material.specular; + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - uniforms.directionalLightColor.value = lights.directional.colors; - uniforms.directionalLightDirection.value = lights.directional.positions; + if ( material.lightMap ) { - uniforms.pointLightColor.value = lights.point.colors; - uniforms.pointLightPosition.value = lights.point.positions; - uniforms.pointLightDistance.value = lights.point.distances; - uniforms.pointLightDecay.value = lights.point.decays; + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; - uniforms.spotLightColor.value = lights.spot.colors; - uniforms.spotLightPosition.value = lights.spot.positions; - uniforms.spotLightDistance.value = lights.spot.distances; - uniforms.spotLightDirection.value = lights.spot.directions; - uniforms.spotLightAngleCos.value = lights.spot.anglesCos; - uniforms.spotLightExponent.value = lights.spot.exponents; - uniforms.spotLightDecay.value = lights.spot.decays; + } - uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; - uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; - uniforms.hemisphereLightDirection.value = lights.hemi.positions; + if ( material.emissiveMap ) { + + uniforms.emissiveMap.value = material.emissiveMap; + + } + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + if ( material.displacementMap ) { + + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + + } + + } + + function refreshUniformsStandard ( uniforms, material ) { + + uniforms.roughness.value = material.roughness; + uniforms.metalness.value = material.metalness; + + if ( material.roughnessMap ) { + + uniforms.roughnessMap.value = material.roughnessMap; + + } + + if ( material.metalnessMap ) { + + uniforms.metalnessMap.value = material.metalnessMap; + + } + + if ( material.lightMap ) { + + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; + + } + + if ( material.emissiveMap ) { + + uniforms.emissiveMap.value = material.emissiveMap; + + } + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + if ( material.displacementMap ) { + + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + + } + + if ( material.envMap ) { + + //uniforms.envMap.value = material.envMap; // part of uniforms common + uniforms.envMapIntensity.value = material.envMapIntensity; + + } } @@ -33835,57 +39221,10 @@ THREE.Canvas3DRenderer = function ( parameters ) { uniforms.ambientLightColor.needsUpdate = value; - uniforms.directionalLightColor.needsUpdate = value; - uniforms.directionalLightDirection.needsUpdate = value; - - uniforms.pointLightColor.needsUpdate = value; - uniforms.pointLightPosition.needsUpdate = value; - uniforms.pointLightDistance.needsUpdate = value; - uniforms.pointLightDecay.needsUpdate = value; - - uniforms.spotLightColor.needsUpdate = value; - uniforms.spotLightPosition.needsUpdate = value; - uniforms.spotLightDistance.needsUpdate = value; - uniforms.spotLightDirection.needsUpdate = value; - uniforms.spotLightAngleCos.needsUpdate = value; - uniforms.spotLightExponent.needsUpdate = value; - uniforms.spotLightDecay.needsUpdate = value; - - uniforms.hemisphereLightSkyColor.needsUpdate = value; - uniforms.hemisphereLightGroundColor.needsUpdate = value; - uniforms.hemisphereLightDirection.needsUpdate = value; - - } - - function refreshUniformsShadow ( uniforms, lights ) { - - if ( uniforms.shadowMatrix ) { - - var j = 0; - - for ( var i = 0, il = lights.length; i < il; i ++ ) { - - var light = lights[ i ]; - - if ( ! light.castShadow ) continue; - - if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { - - uniforms.shadowMap.value[ j ] = light.shadowMap; - uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; - - uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; - - uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; - uniforms.shadowBias.value[ j ] = light.shadowBias; - - j ++; - - } - - } - - } + uniforms.directionalLights.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; } @@ -33893,11 +39232,11 @@ THREE.Canvas3DRenderer = function ( parameters ) { function loadUniformsMatrices ( uniforms, object ) { - _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements ); if ( uniforms.normalMatrix ) { - _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements ); } @@ -33907,9 +39246,9 @@ THREE.Canvas3DRenderer = function ( parameters ) { var textureUnit = _usedTextureUnits; - if ( textureUnit >= _maxTextures ) { + if ( textureUnit >= capabilities.maxTextures ) { - THREE.warn( 'Canvas3DRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures ); + console.warn( 'THREE.Canvas3DRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); } @@ -33921,7 +39260,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { function loadUniformsGeneric ( uniforms ) { - var texture, textureUnit, offset; + var texture, textureUnit; for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { @@ -33980,6 +39319,10 @@ THREE.Canvas3DRenderer = function ( parameters ) { _gl.uniform4fv( location, value ); break; + case 'Matrix2fv': + _gl.uniformMatrix2fv( location, false, value ); + break; + case 'Matrix3fv': _gl.uniformMatrix3fv( location, false, value ); break; @@ -34032,6 +39375,83 @@ THREE.Canvas3DRenderer = function ( parameters ) { break; + /* + case 's': + + // TODO: Optimize this. + for( var propertyName in uniform.properties ) { + + var property = uniform.properties[ propertyName ]; + var locationProperty = location[ propertyName ]; + var valueProperty = value[ propertyName ]; + + switch( property.type ) { + case 'i': + _gl.uniform1i( locationProperty, valueProperty ); + break; + case 'f': + _gl.uniform1f( locationProperty, valueProperty ); + break; + case 'v2': + _gl.uniform2f( locationProperty, valueProperty.x, valueProperty.y ); + break; + case 'v3': + _gl.uniform3f( locationProperty, valueProperty.x, valueProperty.y, valueProperty.z ); + break; + case 'v4': + _gl.uniform4f( locationProperty, valueProperty.x, valueProperty.y, valueProperty.z, valueProperty.w ); + break; + case 'c': + _gl.uniform3f( locationProperty, valueProperty.r, valueProperty.g, valueProperty.b ); + break; + }; + + } + + break; + */ + + case 'sa': + + // TODO: Optimize this. + for ( var i = 0; i < value.length; i ++ ) { + + for ( var propertyName in uniform.properties ) { + + var property = uniform.properties[ propertyName ]; + var locationProperty = location[ i ][ propertyName ]; + var valueProperty = value[ i ][ propertyName ]; + + switch ( property.type ) { + case 'i': + _gl.uniform1i( locationProperty, valueProperty ); + break; + case 'f': + _gl.uniform1f( locationProperty, valueProperty ); + break; + case 'v2': + _gl.uniform2f( locationProperty, valueProperty.x, valueProperty.y ); + break; + case 'v3': + _gl.uniform3f( locationProperty, valueProperty.x, valueProperty.y, valueProperty.z ); + break; + case 'v4': + _gl.uniform4f( locationProperty, valueProperty.x, valueProperty.y, valueProperty.z, valueProperty.w ); + break; + case 'c': + _gl.uniform3f( locationProperty, valueProperty.r, valueProperty.g, valueProperty.b ); + break; + case 'm4': + _gl.uniformMatrix4fv( locationProperty, false, valueProperty.elements ); + break; + } + + } + + } + + break; + case 'iv1': // flat array of integers (JS or typed array) @@ -34070,12 +39490,10 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - for ( var i = 0, il = value.length; i < il; i ++ ) { + for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) { - offset = i * 2; - - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ i2 + 0 ] = value[ i ].x; + uniform._array[ i2 + 1 ] = value[ i ].y; } @@ -34093,13 +39511,11 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - for ( var i = 0, il = value.length; i < il; i ++ ) { + for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) { - offset = i * 3; - - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; - uniform._array[ offset + 2 ] = value[ i ].z; + uniform._array[ i3 + 0 ] = value[ i ].x; + uniform._array[ i3 + 1 ] = value[ i ].y; + uniform._array[ i3 + 2 ] = value[ i ].z; } @@ -34117,14 +39533,12 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - for ( var i = 0, il = value.length; i < il; i ++ ) { + for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) { - offset = i * 4; - - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; - uniform._array[ offset + 2 ] = value[ i ].z; - uniform._array[ offset + 3 ] = value[ i ].w; + uniform._array[ i4 + 0 ] = value[ i ].x; + uniform._array[ i4 + 1 ] = value[ i ].y; + uniform._array[ i4 + 2 ] = value[ i ].z; + uniform._array[ i4 + 3 ] = value[ i ].w; } @@ -34132,6 +39546,13 @@ THREE.Canvas3DRenderer = function ( parameters ) { break; + case 'm2': + + // single THREE.Matrix2 + _gl.uniformMatrix2fv( location, false, value.elements ); + + break; + case 'm3': // single THREE.Matrix3 @@ -34198,13 +39619,19 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( ! texture ) continue; if ( texture instanceof THREE.CubeTexture || - ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ + ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ setCubeTexture( texture, textureUnit ); } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { - setCubeTextureDynamic( texture, textureUnit ); + setCubeTextureDynamic( texture.texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTarget ) { + + _this.setTexture( texture.texture, textureUnit ); } else { @@ -34216,7 +39643,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { case 'tv': - // array of THREE.Texture (2d) + // array of THREE.Texture (2d or cube) if ( uniform._array === undefined ) { @@ -34239,7 +39666,26 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( ! texture ) continue; - _this.setTexture( texture, textureUnit ); + if ( texture instanceof THREE.CubeTexture || + ( texture.image instanceof Array && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTarget ) { + + _this.setTexture( texture.texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture.texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } } @@ -34247,7 +39693,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { default: - THREE.warn( 'THREE.Canvas3DRenderer: Unknown uniform type: ' + type ); + console.warn( 'THREE.Canvas3DRenderer: Unknown uniform type: ' + type ); } @@ -34255,203 +39701,167 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - function setupMatrices ( object, camera ) { - - object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); - - } - - function setColorLinear( array, offset, color, intensity ) { - - array[ offset ] = color.r * intensity; - array[ offset + 1 ] = color.g * intensity; - array[ offset + 2 ] = color.b * intensity; - - } - - function setupLights ( lights ) { + function setupLights ( lights, camera ) { var l, ll, light, r = 0, g = 0, b = 0, - color, skyColor, groundColor, + color, intensity, distance, - zlights = _lights, + viewMatrix = camera.matrixWorldInverse, - dirColors = zlights.directional.colors, - dirPositions = zlights.directional.positions, - - pointColors = zlights.point.colors, - pointPositions = zlights.point.positions, - pointDistances = zlights.point.distances, - pointDecays = zlights.point.decays, - - spotColors = zlights.spot.colors, - spotPositions = zlights.spot.positions, - spotDistances = zlights.spot.distances, - spotDirections = zlights.spot.directions, - spotAnglesCos = zlights.spot.anglesCos, - spotExponents = zlights.spot.exponents, - spotDecays = zlights.spot.decays, - - hemiSkyColors = zlights.hemi.skyColors, - hemiGroundColors = zlights.hemi.groundColors, - hemiPositions = zlights.hemi.positions, - - dirLength = 0, + directionalLength = 0, pointLength = 0, spotLength = 0, hemiLength = 0, - dirCount = 0, - pointCount = 0, - spotCount = 0, - hemiCount = 0, + shadowsLength = 0; - dirOffset = 0, - pointOffset = 0, - spotOffset = 0, - hemiOffset = 0; + _lights.shadowsPointLight = 0; for ( l = 0, ll = lights.length; l < ll; l ++ ) { light = lights[ l ]; - if ( light.onlyShadow ) continue; - color = light.color; intensity = light.intensity; distance = light.distance; if ( light instanceof THREE.AmbientLight ) { - if ( ! light.visible ) continue; - - r += color.r; - g += color.g; - b += color.b; + r += color.r * intensity; + g += color.g * intensity; + b += color.b * intensity; } else if ( light instanceof THREE.DirectionalLight ) { - dirCount += 1; + var uniforms = lightCache.get( light ); - if ( ! light.visible ) continue; - - _direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); _vector3.setFromMatrixPosition( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.normalize(); + uniforms.direction.sub( _vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - dirOffset = dirLength * 3; + uniforms.shadow = light.castShadow; - dirPositions[ dirOffset ] = _direction.x; - dirPositions[ dirOffset + 1 ] = _direction.y; - dirPositions[ dirOffset + 2 ] = _direction.z; + if ( light.castShadow ) { - setColorLinear( dirColors, dirOffset, color, intensity ); + uniforms.shadowBias = light.shadow.bias; + uniforms.shadowRadius = light.shadow.radius; + uniforms.shadowMapSize = light.shadow.mapSize; - dirLength += 1; + _lights.shadows[ shadowsLength ++ ] = light; - } else if ( light instanceof THREE.PointLight ) { + } - pointCount += 1; - - if ( ! light.visible ) continue; - - pointOffset = pointLength * 3; - - setColorLinear( pointColors, pointOffset, color, intensity ); - - _vector3.setFromMatrixPosition( light.matrixWorld ); - - pointPositions[ pointOffset ] = _vector3.x; - pointPositions[ pointOffset + 1 ] = _vector3.y; - pointPositions[ pointOffset + 2 ] = _vector3.z; - - // distance is 0 if decay is 0, because there is no attenuation at all. - pointDistances[ pointLength ] = distance; - pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; - - pointLength += 1; + _lights.directionalShadowMap[ directionalLength ] = light.shadow.map; + _lights.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; + _lights.directional[ directionalLength ++ ] = uniforms; } else if ( light instanceof THREE.SpotLight ) { - spotCount += 1; + var uniforms = lightCache.get( light ); - if ( ! light.visible ) continue; + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - spotOffset = spotLength * 3; - - setColorLinear( spotColors, spotOffset, color, intensity ); - - _direction.setFromMatrixPosition( light.matrixWorld ); - - spotPositions[ spotOffset ] = _direction.x; - spotPositions[ spotOffset + 1 ] = _direction.y; - spotPositions[ spotOffset + 2 ] = _direction.z; - - spotDistances[ spotLength ] = distance; + uniforms.color.copy( color ).multiplyScalar( intensity ); + uniforms.distance = distance; + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); _vector3.setFromMatrixPosition( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.normalize(); + uniforms.direction.sub( _vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - spotDirections[ spotOffset ] = _direction.x; - spotDirections[ spotOffset + 1 ] = _direction.y; - spotDirections[ spotOffset + 2 ] = _direction.z; + uniforms.angleCos = Math.cos( light.angle ); + uniforms.exponent = light.exponent; + uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; - spotAnglesCos[ spotLength ] = Math.cos( light.angle ); - spotExponents[ spotLength ] = light.exponent; - spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; + uniforms.shadow = light.castShadow; - spotLength += 1; + if ( light.castShadow ) { + + uniforms.shadowBias = light.shadow.bias; + uniforms.shadowRadius = light.shadow.radius; + uniforms.shadowMapSize = light.shadow.mapSize; + + _lights.shadows[ shadowsLength ++ ] = light; + + } + + _lights.spotShadowMap[ spotLength ] = light.shadow.map; + _lights.spotShadowMatrix[ spotLength ] = light.shadow.matrix; + _lights.spot[ spotLength ++ ] = uniforms; + + } else if ( light instanceof THREE.PointLight ) { + + var uniforms = lightCache.get( light ); + + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); + + uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + uniforms.distance = light.distance; + uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; + + uniforms.shadow = light.castShadow; + + if ( light.castShadow ) { + + uniforms.shadowBias = light.shadow.bias; + uniforms.shadowRadius = light.shadow.radius; + uniforms.shadowMapSize = light.shadow.mapSize; + + _lights.shadows[ shadowsLength ++ ] = light; + + } + + _lights.pointShadowMap[ pointLength ] = light.shadow.map; + + if ( _lights.pointShadowMatrix[ pointLength ] === undefined ) { + + _lights.pointShadowMatrix[ pointLength ] = new THREE.Matrix4(); + + } + + // for point lights we set the shadow matrix to be a translation-only matrix + // equal to inverse of the light's position + _vector3.setFromMatrixPosition( light.matrixWorld ).negate(); + _lights.pointShadowMatrix[ pointLength ].identity().setPosition( _vector3 ); + + _lights.point[ pointLength ++ ] = uniforms; } else if ( light instanceof THREE.HemisphereLight ) { - hemiCount += 1; + var uniforms = lightCache.get( light ); - if ( ! light.visible ) continue; + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.direction.transformDirection( viewMatrix ); + uniforms.direction.normalize(); - _direction.setFromMatrixPosition( light.matrixWorld ); - _direction.normalize(); + uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); + uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); - hemiOffset = hemiLength * 3; - - hemiPositions[ hemiOffset ] = _direction.x; - hemiPositions[ hemiOffset + 1 ] = _direction.y; - hemiPositions[ hemiOffset + 2 ] = _direction.z; - - skyColor = light.color; - groundColor = light.groundColor; - - setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); - setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); - - hemiLength += 1; + _lights.hemi[ hemiLength ++ ] = uniforms; } } - // null eventual remains from removed lights - // (this is to avoid if in shader) + _lights.ambient[ 0 ] = r; + _lights.ambient[ 1 ] = g; + _lights.ambient[ 2 ] = b; - for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; - for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; - for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; - for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; - for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + _lights.directional.length = directionalLength; + _lights.spot.length = spotLength; + _lights.point.length = pointLength; + _lights.hemi.length = hemiLength; - zlights.directional.length = dirLength; - zlights.point.length = pointLength; - zlights.spot.length = spotLength; - zlights.hemi.length = hemiLength; + _lights.shadows.length = shadowsLength; - zlights.ambient[ 0 ] = r; - zlights.ambient[ 1 ] = g; - zlights.ambient[ 2 ] = b; + _lights.hash = directionalLength + ',' + pointLength + ',' + spotLength + ',' + hemiLength + ',' + shadowsLength; } @@ -34461,7 +39871,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( cullFace === THREE.CullFaceNone ) { - _gl.disable( _gl.CULL_FACE ); + state.disable( _gl.CULL_FACE ); } else { @@ -34489,26 +39899,19 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - _gl.enable( _gl.CULL_FACE ); + state.enable( _gl.CULL_FACE ); } }; - this.setMaterialFaces = function ( material ) { - - state.setDoubleSided( material.side === THREE.DoubleSide ); - state.setFlipSided( material.side === THREE.BackSide ); - - }; - // Textures - function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + function setTextureParameters ( textureType, texture, isPowerOfTwoImage ) { var extension; - if ( isImagePowerOfTwo ) { + if ( isPowerOfTwoImage ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); @@ -34523,7 +39926,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { - THREE.warn( 'THREE.Canvas3DRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping. ( ' + texture.sourceFile + ' )' ); + console.warn( 'THREE.Canvas3DRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture ); } @@ -34532,7 +39935,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { - THREE.warn( 'THREE.Canvas3DRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. ( ' + texture.sourceFile + ' )' ); + console.warn( 'THREE.Canvas3DRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture ); } @@ -34540,12 +39943,15 @@ THREE.Canvas3DRenderer = function ( parameters ) { extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - if ( extension && texture.type !== THREE.FloatType && texture.type !== THREE.HalfFloatType ) { + if ( extension ) { - if ( texture.anisotropy > 1 || texture.__currentAnisotropy ) { + if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; + if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; + + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); - texture.__currentAnisotropy = texture.anisotropy; + properties.get( texture ).__currentAnisotropy = texture.anisotropy; } @@ -34553,20 +39959,17 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - this.uploadTexture = function ( texture ) { - + function uploadTexture( textureProperties, texture, slot ) { if ( texture instanceof THREE.QtQuickItemTexture ) { - var canvasTextureProvider = _gl.getExtension("QTCANVAS3D_texture_provider"); - - texture.__webglInit = true; + textureProperties.__webglInit = true; if ( canvasTextureProvider !== null ) - texture.__webglTexture = canvasTextureProvider.createTextureFromSource(texture.quickItem); + textureProperties.__webglTexture = canvasTextureProvider.createTextureFromSource(texture.quickItem); else - texture.__webglTexture = 0; + textureProperties.__webglTexture = 0; - _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + _gl.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); var isImagePowerOfTwo = THREE.Math.isPowerOfTwo( texture.quickItem.width ) && THREE.Math.isPowerOfTwo( texture.quickItem.height ); @@ -34577,33 +39980,39 @@ THREE.Canvas3DRenderer = function ( parameters ) { THREE.warn( 'THREE.Canvas3DRenderer: Quick item width and/or height are not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); } } else { + if ( textureProperties.__webglInit === undefined ) { - if ( texture.__webglInit === undefined ) { - - texture.__webglInit = true; + textureProperties.__webglInit = true; texture.addEventListener( 'dispose', onTextureDispose ); - texture.__webglTexture = _gl.createTexture(); + textureProperties.__webglTexture = _gl.createTexture(); - _this.info.memory.textures ++; + _infoMemory.textures ++; } - _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); - texture.image = clampToMaxSize( texture.image, _maxTextureSize ); + var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); - var image = texture.image, - isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { + + // No way to make the image power of two, so just warn about it + console.warn( 'THREE.Canvas3DRenderer: image is not power of two (' + image.width + 'x' + image.height + ').', image ); + + } + + var isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = paramThreeToGL( texture.format ), glType = paramThreeToGL( texture.type ); - setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); var mipmap, mipmaps = texture.mipmaps; @@ -34613,12 +40022,12 @@ THREE.Canvas3DRenderer = function ( parameters ) { // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } @@ -34626,7 +40035,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { } else { - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); } @@ -34638,36 +40047,38 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { - THREE.warn( "THREE.Canvas3DRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); + console.warn( "THREE.Canvas3DRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } - } else { // regular Texture (image, video, canvas) + } else { + + // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); } @@ -34675,35 +40086,52 @@ THREE.Canvas3DRenderer = function ( parameters ) { } else { - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image.texImage() ); + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image.texImage() ); } } - if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + if ( texture.generateMipmaps && isPowerOfTwoImage ) _gl.generateMipmap( _gl.TEXTURE_2D ); } - texture.needsUpdate = false; + textureProperties.__version = texture.version; - if ( texture.onUpdate ) texture.onUpdate(); + if ( texture.onUpdate ) texture.onUpdate( texture ); - }; + } this.setTexture = function ( texture, slot ) { - _gl.activeTexture( _gl.TEXTURE0 + slot ); + var textureProperties = properties.get( texture ); - if ( texture.needsUpdate ) { + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - _this.uploadTexture( texture ); + var image = texture.image; - } else { + if ( image === undefined ) { - _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + console.warn( 'THREE.Canvas3DRenderer: Texture marked for update but image is undefined', texture ); + return; + + } + + if ( !(texture instanceof THREE.QtQuickItemTexture) && image.complete === false ) { + + console.warn( 'THREE.Canvas3DRenderer: Texture marked for update but image is incomplete', texture ); + return; + + } + + uploadTexture( textureProperties, texture, slot ); + + return; } + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + }; function clampToMaxSize ( image, maxSize ) { @@ -34715,12 +40143,11 @@ THREE.Canvas3DRenderer = function ( parameters ) { var scale = maxSize / Math.max( image.width, image.height ); - var canvasWidth = Math.floor( image.width * scale ); - var canvasHeight = Math.floor( image.height * scale ); - var canvas = image.resize( canvasWidth, canvasHeight ); + var canvasWidth = Math.floor( image.width * scale ); + var canvasHeight = Math.floor( image.height * scale ); + var canvas = image.resize( canvasWidth, canvasHeight ); - - THREE.warn( 'THREE.Canvas3DRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvasWidth + 'x' + canvasHeight, image ); + console.warn( 'THREE.Canvas3DRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); return canvas; @@ -34730,24 +40157,41 @@ THREE.Canvas3DRenderer = function ( parameters ) { } + function isPowerOfTwo( image ) { + + return THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ); + + } + + function textureNeedsPowerOfTwo( texture ) { + + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) return true; + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) return true; + + return false; + + } + function setCubeTexture ( texture, slot ) { + var textureProperties = properties.get( texture ); + if ( texture.image.length === 6 ) { - if ( texture.needsUpdate ) { + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - if ( ! texture.image.__webglTextureCube ) { + if ( ! textureProperties.__image__webglTextureCube ) { texture.addEventListener( 'dispose', onTextureDispose ); - texture.image.__webglTextureCube = _gl.createTexture(); + textureProperties.__image__webglTextureCube = _gl.createTexture(); - _this.info.memory.textures ++; + _infoMemory.textures ++; } - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); @@ -34760,7 +40204,7 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { - cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); } else { @@ -34771,11 +40215,11 @@ THREE.Canvas3DRenderer = function ( parameters ) { } var image = cubeImage[ 0 ], - isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = paramThreeToGL( texture.format ), glType = paramThreeToGL( texture.type ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); for ( var i = 0; i < 6; i ++ ) { @@ -34783,11 +40227,11 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( isDataTexture ) { - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } else { - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ].texImage() ); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ].texImage() ); } @@ -34801,19 +40245,19 @@ THREE.Canvas3DRenderer = function ( parameters ) { if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { - THREE.warn( "THREE.Canvas3DRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" ); + console.warn( "THREE.Canvas3DRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" ); } } else { - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } @@ -34823,20 +40267,20 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - if ( texture.generateMipmaps && isImagePowerOfTwo ) { + if ( texture.generateMipmaps && isPowerOfTwoImage ) { _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); } - texture.needsUpdate = false; + textureProperties.__version = texture.version; - if ( texture.onUpdate ) texture.onUpdate(); + if ( texture.onUpdate ) texture.onUpdate( texture ); } else { - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); } @@ -34846,21 +40290,27 @@ THREE.Canvas3DRenderer = function ( parameters ) { function setCubeTextureDynamic ( texture, slot ) { - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); } // Render targets - function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + // Setup storage for target texture and bind it to correct framebuffer + function setupFrameBufferTexture ( framebuffer, renderTarget, attachment, textureTarget ) { + var glFormat = paramThreeToGL( renderTarget.texture.format ); + var glType = paramThreeToGL( renderTarget.texture.type ); + state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } - function setupRenderBuffer ( renderbuffer, renderTarget ) { + // Setup storage for internal depth/stencil buffers and bind to correct framebuffer + function setupRenderBufferStorage ( renderbuffer, renderTarget ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); @@ -34869,12 +40319,6 @@ THREE.Canvas3DRenderer = function ( parameters ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - /* For some reason this is not working. Defaulting to RGBA4. - } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - */ } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); @@ -34882,202 +40326,242 @@ THREE.Canvas3DRenderer = function ( parameters ) { } else { + // FIXME: We don't support !depth !stencil _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); } + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + + } + + // Setup GL resources for a non-texture depth buffer + function setupDepthRenderbuffer( renderTarget ) { + + var renderTargetProperties = properties.get( renderTarget ); + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + + if ( isCube ) { + + renderTargetProperties.__webglDepthbuffer = []; + + for ( var i = 0; i < 6; i ++ ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); + renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); + + } + + } else { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); + + } + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + + } + + // Set up GL resources for the render target + function setupRenderTarget( renderTarget ) { + + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); + + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + + textureProperties.__webglTexture = _gl.createTexture(); + + _infoMemory.textures ++; + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ); + + // Setup framebuffer + + if ( isCube ) { + + renderTargetProperties.__webglFramebuffer = []; + + for ( var i = 0; i < 6; i ++ ) { + + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + + } + + } else { + + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + + } + + // Setup color buffer + + if ( isCube ) { + + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + + } + + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); + + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + state.bindTexture( _gl.TEXTURE_2D, null ); + + } + + // Setup depth and stencil buffers + + if ( renderTarget.depthBuffer ) { + + setupDepthRenderbuffer( renderTarget ); + + } + } this.setRenderTarget = function ( renderTarget ) { - var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + _currentRenderTarget = renderTarget; - if ( renderTarget && renderTarget.__webglFramebuffer === undefined ) { + if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; - if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; - - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - - renderTarget.__webglTexture = _gl.createTexture(); - - _this.info.memory.textures ++; - - // Setup texture, create render and frame buffers - - var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), - glFormat = paramThreeToGL( renderTarget.format ), - glType = paramThreeToGL( renderTarget.type ); - - if ( isCube ) { - - renderTarget.__webglFramebuffer = []; - renderTarget.__webglRenderbuffer = []; - - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); - - for ( var i = 0; i < 6; i ++ ) { - - renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); - - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - - setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); - setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); - - } - - if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - - } else { - - renderTarget.__webglFramebuffer = _gl.createFramebuffer(); - - if ( renderTarget.shareDepthFrom ) { - - renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; - - } else { - - renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); - - } - - _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); - setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); - - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - - setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); - - if ( renderTarget.shareDepthFrom ) { - - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); - - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); - - } - - } else { - - setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); - - } - - if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); - - } - - // Release everything - - if ( isCube ) { - - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); - - } else { - - _gl.bindTexture( _gl.TEXTURE_2D, null ); - - } - - _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + setupRenderTarget( renderTarget ); } - var framebuffer, width, height, vx, vy; + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + var framebuffer; if ( renderTarget ) { + var renderTargetProperties = properties.get( renderTarget ); + if ( isCube ) { - framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; + framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; } else { - framebuffer = renderTarget.__webglFramebuffer; + framebuffer = renderTargetProperties.__webglFramebuffer; } - width = renderTarget.width; - height = renderTarget.height; + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; - vx = 0; - vy = 0; + _currentViewport.copy( renderTarget.viewport ); } else { framebuffer = null; - width = _viewportWidth; - height = _viewportHeight; + _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); + _currentScissorTest = _scissorTest; - vx = _viewportX; - vy = _viewportY; + _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); } - if ( framebuffer !== _currentFramebuffer ) { + if ( _currentFramebuffer !== framebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.viewport( vx, vy, width, height ); - _currentFramebuffer = framebuffer; } - _currentWidth = width; - _currentHeight = height; + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); + + state.viewport( _currentViewport ); + + if ( isCube ) { + + var textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, 0 ); + + } }; - this.readRenderTargetPixels = function( renderTarget, x, y, width, height, buffer ) { + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { - if ( ! ( renderTarget instanceof THREE.WebGLRenderTarget ) ) { + if ( renderTarget instanceof THREE.WebGLRenderTarget === false ) { - console.error( 'THREE.Canvas3DRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + console.error( 'THREE.Canvas3DRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); return; } - if ( renderTarget.__webglFramebuffer ) { + var framebuffer = properties.get( renderTarget ).__webglFramebuffer; - if ( renderTarget.format !== THREE.RGBAFormat ) { - - console.error( 'THREE.Canvas3DRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format.' ); - return; - - } + if ( framebuffer ) { var restore = false; - if ( renderTarget.__webglFramebuffer !== _currentFramebuffer ) { + if ( framebuffer !== _currentFramebuffer ) { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTarget.__webglFramebuffer ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); restore = true; } - if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { + try { - _gl.readPixels( x, y, width, height, _gl.RGBA, _gl.UNSIGNED_BYTE, buffer ); + var texture = renderTarget.texture; - } else { + if ( texture.format !== THREE.RGBAFormat + && paramThreeToGL( texture.format ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { - console.error( 'THREE.Canvas3DRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + console.error( 'THREE.Canvas3DRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; - } + } - if ( restore ) { + if ( texture.type !== THREE.UnsignedByteType + && paramThreeToGL( texture.type ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) + && ! ( texture.type === THREE.FloatType && extensions.get( 'WEBGL_color_buffer_float' ) ) + && ! ( texture.type === THREE.HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); + console.error( 'THREE.Canvas3DRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; + + } + + if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { + + _gl.readPixels( x, y, width, height, paramThreeToGL( texture.format ), paramThreeToGL( texture.type ), buffer ); + + } else { + + console.error( 'THREE.Canvas3DRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + + } + + } finally { + + if ( restore ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); + + } } @@ -35085,21 +40569,14 @@ THREE.Canvas3DRenderer = function ( parameters ) { }; - function updateRenderTargetMipmap ( renderTarget ) { + function updateRenderTargetMipmap( renderTarget ) { - if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + var target = renderTarget instanceof THREE.WebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; + var texture = properties.get( renderTarget.texture ).__webglTexture; - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); - - } else { - - _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_2D ); - _gl.bindTexture( _gl.TEXTURE_2D, null ); - - } + state.bindTexture( target, texture ); + _gl.generateMipmap( target ); + state.bindTexture( target, null ); } @@ -35200,6 +40677,14 @@ THREE.Canvas3DRenderer = function ( parameters ) { } + extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_ETC1_Format ) return extension.COMPRESSED_RGB_ETC1_WEBGL; + + } + extension = extensions.get( 'EXT_blend_minmax' ); if ( extension !== null ) { @@ -35213,116 +40698,6 @@ THREE.Canvas3DRenderer = function ( parameters ) { } - // Allocations - - function allocateBones ( object ) { - - if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { - - return 1024; - - } else { - - // default for when object is not specified - // ( for example when prebuilding shader - // to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) - - var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); - var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - - var maxBones = nVertexMatrices; - - if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { - - maxBones = Math.min( object.skeleton.bones.length, maxBones ); - - if ( maxBones < object.skeleton.bones.length ) { - - THREE.warn( 'Canvas3DRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); - - } - - } - - return maxBones; - - } - - } - - function allocateLights( lights ) { - - var dirLights = 0; - var pointLights = 0; - var spotLights = 0; - var hemiLights = 0; - - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - - var light = lights[ l ]; - - if ( light.onlyShadow || light.visible === false ) continue; - - if ( light instanceof THREE.DirectionalLight ) dirLights ++; - if ( light instanceof THREE.PointLight ) pointLights ++; - if ( light instanceof THREE.SpotLight ) spotLights ++; - if ( light instanceof THREE.HemisphereLight ) hemiLights ++; - - } - - return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; - - } - - function allocateShadows( lights ) { - - var maxShadows = 0; - - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - - var light = lights[ l ]; - - if ( ! light.castShadow ) continue; - - if ( light instanceof THREE.SpotLight ) maxShadows ++; - if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; - - } - - return maxShadows; - - } - - // DEPRECATED - - this.initMaterial = function () { - - THREE.warn( 'THREE.Canvas3DRenderer: .initMaterial() has been removed.' ); - - }; - - this.addPrePlugin = function () { - - THREE.warn( 'THREE.Canvas3DRenderer: .addPrePlugin() has been removed.' ); - - }; - - this.addPostPlugin = function () { - - THREE.warn( 'THREE.Canvas3DRenderer: .addPostPlugin() has been removed.' ); - - }; - - this.updateShadowMap = function () { - - THREE.warn( 'THREE.Canvas3DRenderer: .updateShadowMap() has been removed.' ); - - }; - }; // File:src/qml/QtQuickItemTexture.js @@ -35336,7 +40711,7 @@ THREE.QtQuickItemTexture = function ( quickItem, mapping, wrapS, wrapT, magFilte THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.generateMipmaps = false; - this._needsUpdate = true; + this.version++; this.quickItem = quickItem; @@ -35345,3 +40720,3134 @@ THREE.QtQuickItemTexture = function ( quickItem, mapping, wrapS, wrapT, magFilte THREE.QtQuickItemTexture.prototype = Object.create( THREE.Texture.prototype ); THREE.QtQuickItemTexture.prototype.constructor = THREE.QtQuickItemTexture; +// File:src/gltf/glTF-parser.js + +// Copyright (c) 2013 Fabrice Robinet +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + The Abstract Loader has two modes: + #1: [static] load all the JSON at once [as of now] + #2: [stream] stream and parse JSON progressively [not yet supported] + + Whatever is the mechanism used to parse the JSON (#1 or #2), + The loader starts by resolving the paths to binaries and referenced json files (by replace the value of the path property with an absolute path if it was relative). + + In case #1: it is guaranteed to call the concrete loader implementation methods in a order that solves the dependencies between the entries. + only the nodes requires an extra pass to set up the hirerarchy. + In case #2: the concrete implementation will have to solve the dependencies. no order is guaranteed. + + When case #1 is used the followed dependency order is: + + scenes -> nodes -> meshes -> materials -> techniques -> shaders + -> buffers + -> cameras + -> lights + + The readers starts with the leafs, i.e: + shaders, techniques, materials, meshes, buffers, cameras, lights, nodes, scenes + + For each called handle method called the client should return true if the next handle can be call right after returning, + or false if a callback on client side will notify the loader that the next handle method can be called. + +*/ +/* Commented out for Qt +var global = window; +(function (root, factory) { + if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like enviroments that support module.exports, + // like Node. + factory(module.exports); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], function () { + return factory(root); + }); + } else { + // Browser globals + factory(root); + } +}(this, function (root) { + "use strict"; +*/ //Commented out for Qt + + // Variable renamed for Qt + var GLTFParserCategoriesDepsOrder = ["buffers", "bufferViews", "images", "videos", "samplers", "textures", "shaders", "programs", "techniques", "materials", "accessors", "meshes", "cameras", "lights", "skins", "nodes", "scenes", "animations"]; // Moved below for Qt + + THREE.glTFParser = Object.create(Object.prototype, { // Added to THREE namespace for Qt + + _rootDescription: { value: null, writable: true }, + + rootDescription: { + set: function(value) { + this._rootDescription = value; + }, + get: function() { + return this._rootDescription; + } + }, + + baseURL: { value: null, writable: true }, + + //detect absolute path following the same protocol than window.location + _isAbsolutePath: { + value: function(path) { + return true; // Changed for Qt + //var isAbsolutePathRegExp = new RegExp("^"+window.location.protocol, "i"); + + //return path.match(isAbsolutePathRegExp) ? true : false; + } + }, + + resolvePathIfNeeded: { + value: function(path) { + if (this._isAbsolutePath(path)) { + return path; + } +/* Commented out for Qt + var isDataUriRegex = /^data:/; + if (isDataUriRegex.test(path)) { + return path; + } +*/ //Commented out for Qt + + return this.baseURL + path; + } + }, + + _resolvePathsForCategories: { + value: function(categories) { + categories.forEach( function(category) { + var descriptions = this.json[category]; + if (descriptions) { + var descriptionKeys = Object.keys(descriptions); + descriptionKeys.forEach( function(descriptionKey) { + var description = descriptions[descriptionKey]; + description.uri = this.resolvePathIfNeeded(description.uri); + }, this); + } + }, this); + } + }, + + _json: { + value: null, + writable: true + }, + + json: { + enumerable: true, + get: function() { + return this._json; + }, + set: function(value) { + if (this._json !== value) { + this._json = value; + this._resolvePathsForCategories(["buffers", "shaders", "images", "videos"]); + } + } + }, + + _path: { + value: null, + writable: true + }, + + getEntryDescription: { + value: function (entryID, entryType) { + var entries = null; + + var category = entryType; + entries = this.rootDescription[category]; + if (!entries) { + console.log("ERROR:CANNOT find expected category named:"+category); + return null; + } + + return entries ? entries[entryID] : null; + } + }, + + _stepToNextCategory: { + value: function() { + this._state.categoryIndex = this.getNextCategoryIndex(this._state.categoryIndex + 1); + if (this._state.categoryIndex !== -1) { + this._state.categoryState.index = 0; + return true; + } + + return false; + } + }, + + _stepToNextDescription: { + enumerable: false, + value: function() { + var categoryState = this._state.categoryState; + var keys = categoryState.keys; + if (!keys) { + console.log("INCONSISTENCY ERROR"); + return false; + } + + categoryState.index++; + categoryState.keys = null; + if (categoryState.index >= keys.length) { + return this._stepToNextCategory(); + } + return false; + } + }, + + hasCategory: { + value: function(category) { + return this.rootDescription[category] ? true : false; + } + }, + + _handleState: { + value: function() { + + var methodForType = { + "buffers" : this.handleBuffer, + "bufferViews" : this.handleBufferView, + "shaders" : this.handleShader, + "programs" : this.handleProgram, + "techniques" : this.handleTechnique, + "materials" : this.handleMaterial, + "meshes" : this.handleMesh, + "cameras" : this.handleCamera, + "lights" : this.handleLight, + "nodes" : this.handleNode, + "scenes" : this.handleScene, + "images" : this.handleImage, + "animations" : this.handleAnimation, + "accessors" : this.handleAccessor, + "skins" : this.handleSkin, + "samplers" : this.handleSampler, + "textures" : this.handleTexture, + "videos" : this.handleVideo + + }; + + var success = true; + while (this._state.categoryIndex !== -1) { + var category = GLTFParserCategoriesDepsOrder[this._state.categoryIndex]; + var categoryState = this._state.categoryState; + var keys = categoryState.keys; + if (!keys) { + categoryState.keys = keys = Object.keys(this.rootDescription[category]); + if (keys) { + if (keys.length == 0) { + this._stepToNextDescription(); + continue; + } + } + } + + var type = category; + var entryID = keys[categoryState.index]; + var description = this.getEntryDescription(entryID, type); + if (!description) { + if (this.handleError) { + this.handleError("INCONSISTENCY ERROR: no description found for entry "+entryID); + success = false; + break; + } + } else { + + if (methodForType[type]) { + if (methodForType[type].call(this, entryID, description, this._state.userInfo) === false) { + success = false; + break; + } + } + + this._stepToNextDescription(); + } + } + + if (this.handleLoadCompleted) { + this.handleLoadCompleted(success); + } + + } + }, + + _loadJSONIfNeeded: { + enumerable: true, + value: function(callback) { + var self = this; + //FIXME: handle error + if (!this._json) { + var jsonPath = this._path; + var i = jsonPath.lastIndexOf("/"); + this.baseURL = (i !== 0) ? jsonPath.substring(0, i + 1) : ''; + var jsonfile = new XMLHttpRequest(); + jsonfile.open("GET", jsonPath, true); + jsonfile.onreadystatechange = function() { + if (jsonfile.readyState == 4) { + if (jsonfile.status == 200) { + self.json = JSON.parse(jsonfile.responseText); + if (callback) { + callback(self.json); + } + } + } + }; + jsonfile.send(null); + } else { + if (callback) { + callback(this.json); + } + } + } + }, + + /* load JSON and assign it as description to the reader */ + _buildLoader: { + value: function(callback) { + var self = this; + function JSONReady(json) { + self.rootDescription = json; + if (callback) + callback(this); + } + + this._loadJSONIfNeeded(JSONReady); + } + }, + + _state: { value: null, writable: true }, + + _getEntryType: { + value: function(entryID) { + var rootKeys = GLTFParserCategoriesDepsOrder; + for (var i = 0 ; i < rootKeys.length ; i++) { + var rootValues = this.rootDescription[rootKeys[i]]; + if (rootValues) { + return rootKeys[i]; + } + } + return null; + } + }, + + getNextCategoryIndex: { + value: function(currentIndex) { + for (var i = currentIndex ; i < GLTFParserCategoriesDepsOrder.length ; i++) { + if (this.hasCategory(GLTFParserCategoriesDepsOrder[i])) { + return i; + } + } + + return -1; + } + }, + + load: { + enumerable: true, + value: function(userInfo, options) { + var self = this; + this._buildLoader(function loaderReady(reader) { + var startCategory = self.getNextCategoryIndex.call(self,0); + if (startCategory !== -1) { + self._state = { "userInfo" : userInfo, + "options" : options, + "categoryIndex" : startCategory, + "categoryState" : { "index" : "0" } }; + self._handleState(); + } + }); + } + }, + + initWithPath: { + value: function(path) { + this._path = path; + this._json = null; + return this; + } + }, + + //this is meant to be global and common for all instances + _knownURLs: { writable: true, value: {} }, + + //to be invoked by subclass, so that ids can be ensured to not overlap + loaderContext: { + value: function() { + if (typeof this._knownURLs[this._path] === "undefined") { + this._knownURLs[this._path] = Object.keys(this._knownURLs).length; + } + return "__" + this._knownURLs[this._path]; + } + }, + + initWithJSON: { + value: function(json, baseURL) { + this.json = json; + this.baseURL = baseURL; + if (!baseURL) { + console.log("WARNING: no base URL passed to Reader:initWithJSON"); + } + return this; + } + } + + }); +/* Commented out for Qt + if(root) { + root.glTFParser = glTFParser; + } + + return glTFParser; + +})); +*/ // Commented out for Qt + + +// File:src/gltf/glTFAnimation.js + +/** + * @author Tony Parisi / http://www.tonyparisi.com/ + */ + +THREE.glTFAnimator = ( function () { + + var animators = []; + + return { + add : function(animator) + { + animators.push(animator); + }, + + remove: function(animator) + { + + var i = animators.indexOf(animator); + + if ( i !== -1 ) { + animators.splice( i, 1 ); + } + }, + + update : function() + { + for (var i = 0; i < animators.length; i ++) // Changed for Qt + { + animators[i].update(); + } + }, + }; +})(); + +// Construction/initialization +THREE.glTFAnimation = function(interps) +{ + this.running = false; + this.loop = false; + this.duration = 0; + this.startTime = 0; + this.interps = []; + + if (interps) + { + this.createInterpolators(interps); + } +} + +THREE.glTFAnimation.prototype.createInterpolators = function(interps) +{ + var i, len = interps.length; + for (i = 0; i < len; i++) + { + var interp = new THREE.glTFInterpolator(interps[i]); + this.interps.push(interp); + this.duration = Math.max(this.duration, interp.duration); + } +} + +// Start/stop +THREE.glTFAnimation.prototype.play = function() +{ + if (this.running) + return; + + this.startTime = Date.now(); + this.running = true; + THREE.glTFAnimator.add(this); +} + +THREE.glTFAnimation.prototype.stop = function() +{ + this.running = false; + THREE.glTFAnimator.remove(this); +} + +// Update - drive key frame evaluation +THREE.glTFAnimation.prototype.update = function() +{ + if (!this.running) + return; + + var now = Date.now(); + var deltat = (now - this.startTime) / 1000; + var t = deltat % this.duration; + var nCycles = Math.floor(deltat / this.duration); + + if (nCycles >= 1 && !this.loop) + { + this.running = false; + var i, len = this.interps.length; + for (i = 0; i < len; i++) + { + this.interps[i].interp(this.duration); + } + this.stop(); + return; + } + else + { + var i, len = this.interps.length; + for (i = 0; i < len; i++) + { + this.interps[i].interp(t); + } + } +} + +//Interpolator class +//Construction/initialization +THREE.glTFInterpolator = function(param) +{ + this.keys = param.keys; + this.values = param.values; + this.count = param.count; + this.type = param.type; + this.path = param.path; + this.isRot = false; + + var node = param.target; + node.updateMatrix(); + node.matrixAutoUpdate = true; + this.targetNode = node; + + switch (param.path) { + case "translation" : + this.target = node.position; + this.originalValue = node.position.clone(); + break; + case "rotation" : + this.target = node.quaternion; + this.originalValue = node.quaternion.clone(); + this.isRot = true; + break; + case "scale" : + this.target = node.scale; + this.originalValue = node.scale.clone(); + break; + } + + this.duration = this.keys[this.count - 1]; + + this.vec1 = new THREE.Vector3; + this.vec2 = new THREE.Vector3; + this.vec3 = new THREE.Vector3; + this.quat1 = new THREE.Quaternion; + this.quat2 = new THREE.Quaternion; + this.quat3 = new THREE.Quaternion; +} + +//Interpolation and tweening methods +THREE.glTFInterpolator.prototype.interp = function(t) +{ + var i, j; + if (t == this.keys[0]) + { + if (this.isRot) { + this.quat3.set(this.values[0], this.values[1], this.values[2], this.values[3]); + } + else { + this.vec3.set(this.values[0], this.values[1], this.values[2]); + } + } + else if (t < this.keys[0]) + { + if (this.isRot) { + this.quat1.set(this.originalValue.x, + this.originalValue.y, + this.originalValue.z, + this.originalValue.w); + this.quat2.set(this.values[0], + this.values[1], + this.values[2], + this.values[3]); + THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, t / this.keys[0]); + } + else { + this.vec3.set(this.originalValue.x, + this.originalValue.y, + this.originalValue.z); + this.vec2.set(this.values[0], + this.values[1], + this.values[2]); + + this.vec3.lerp(this.vec2, t / this.keys[0]); + } + } + else if (t >= this.keys[this.count - 1]) + { + if (this.isRot) { + this.quat3.set(this.values[(this.count - 1) * 4], + this.values[(this.count - 1) * 4 + 1], + this.values[(this.count - 1) * 4 + 2], + this.values[(this.count - 1) * 4 + 3]); + } + else { + this.vec3.set(this.values[(this.count - 1) * 3], + this.values[(this.count - 1) * 3 + 1], + this.values[(this.count - 1) * 3 + 2]); + } + } + else + { + for (i = 0; i < this.count - 1; i++) + { + var key1 = this.keys[i]; + var key2 = this.keys[i + 1]; + + if (t >= key1 && t <= key2) + { + if (this.isRot) { + this.quat1.set(this.values[i * 4], + this.values[i * 4 + 1], + this.values[i * 4 + 2], + this.values[i * 4 + 3]); + this.quat2.set(this.values[(i + 1) * 4], + this.values[(i + 1) * 4 + 1], + this.values[(i + 1) * 4 + 2], + this.values[(i + 1) * 4 + 3]); + THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, (t - key1) / (key2 - key1)); + } + else { + this.vec3.set(this.values[i * 3], + this.values[i * 3 + 1], + this.values[i * 3 + 2]); + this.vec2.set(this.values[(i + 1) * 3], + this.values[(i + 1) * 3 + 1], + this.values[(i + 1) * 3 + 2]); + + this.vec3.lerp(this.vec2, (t - key1) / (key2 - key1)); + } + } + } + } + + if (this.target) + { + this.copyValue(this.target); + } +} + +THREE.glTFInterpolator.prototype.copyValue = function(target) { + + if (this.isRot) { + target.copy(this.quat3); + } + else { + target.copy(this.vec3); + } +} + +// File:src/gltf/glTFLoader.js + +/** + * @author Tony Parisi / http://www.tonyparisi.com/ + */ + +THREE.glTFLoader = function (showStatus) { + this.useBufferGeometry = (THREE.glTFLoader.useBufferGeometry !== undefined ) ? + THREE.glTFLoader.useBufferGeometry : true; + this.useShaders = (THREE.glTFLoader.useShaders !== undefined ) ? + THREE.glTFLoader.useShaders : true; + this.meshesRequested = 0; + this.meshesLoaded = 0; + this.pendingMeshes = []; + this.animationsRequested = 0; + this.animationsLoaded = 0; + this.animations = []; + this.shadersRequested = 0; + this.shadersLoaded = 0; + this.shaders = {}; + THREE.glTFShaders.removeAll(); + THREE.Loader.call( this, showStatus ); + + // Removed for Qt + //this.idx = {}; + //for (n in WebGLRenderingContext) { z = WebGLRenderingContext[n]; this.idx[z] = n; } +} + +THREE.glTFLoader.prototype = new THREE.Loader(); +THREE.glTFLoader.prototype.constructor = THREE.glTFLoader; + +THREE.glTFLoader.prototype.load = function( url, callback ) { + + var theLoader = this; + // Utilities + + function RgbArraytoHex(colorArray) { + if(!colorArray) return 0xFFFFFFFF; + var r = Math.floor(colorArray[0] * 255), + g = Math.floor(colorArray[1] * 255), + b = Math.floor(colorArray[2] * 255), + a = 255; + + var color = (a << 24) + (r << 16) + (g << 8) + b; + + return color; + } + + function convertAxisAngleToQuaternion(rotations, count) + { + var q = new THREE.Quaternion; + var axis = new THREE.Vector3; + var euler = new THREE.Vector3; + + var i; + for (i = 0; i < count; i++) { + axis.set(rotations[i * 4], rotations[i * 4 + 1], + rotations[i * 4 + 2]).normalize(); + var angle = rotations[i * 4 + 3]; + q.setFromAxisAngle(axis, angle); + rotations[i * 4] = q.x; + rotations[i * 4 + 1] = q.y; + rotations[i * 4 + 2] = q.z; + rotations[i * 4 + 3] = q.w; + } + } + + function componentsPerElementForGLType(type) { + var nElements; // Added for Qt + switch(type) { + case "SCALAR" : + nElements = 1; + break; + case "VEC2" : + nElements = 2; + break; + case "VEC3" : + nElements = 3; + break; + case "VEC4" : + nElements = 4; + break; + case "MAT2" : + nElements = 4; + break; + case "MAT3" : + nElements = 9; + break; + case "MAT4" : + nElements = 16; + break; + default : + console.log("Invalid type in componentsPerElementForGLType:", type); //debugger; // Changed for Qt, debugger keyword is not supported + break; + } + + return nElements; + } + + function replaceShaderDefinitions(shader, material) { +/* var s = shader; + s = s.replace(/a_position/g, 'position'); + s = s.replace(/a_normal/g, 'normal'); + s = s.replace(/a_texcoord0/g, 'uv'); + s = s.replace(/u_normalMatrix/g, 'normalMatrix'); + s = s.replace(/u_modelViewMatrix/g, 'modelViewMatrix'); + s = s.replace(/u_projectionMatrix/g, 'projectionMatrix'); + return s;*/ + + var program = material.params.program; + var shaderParams = material.params.parameters; + var params = {}; + + for (var uniform in program.uniforms) { + var pname = program.uniforms[uniform]; + var shaderParam = shaderParams[pname]; + var semantic = shaderParam.semantic; + if (semantic) { + params[uniform] = shaderParam; + } + } + + for (var attribute in program.attributes) { + var pname = program.attributes[attribute]; + var shaderParam = shaderParams[pname]; + var semantic = shaderParam.semantic; + if (semantic) { + params[attribute] = shaderParam; + } + } + + + var s = shader; + var r = ""; + for (var pname in params) { + var param = params[pname]; + var semantic = param.semantic; + + // THIS: for (n in WebGLRenderingContext) { z = WebGLRenderingContext[n]; idx[z] = n; } + //console.log("shader uniform param type: ", ptype, "-", theLoader.idx[ptype]) + + r = eval("/" + pname + "/g"); + + switch (semantic) { + case "POSITION" : + s = s.replace(r, 'position'); + break; + case "NORMAL" : + s = s.replace(r, 'normal'); + break; + case "TEXCOORD_0" : + s = s.replace(r, 'uv'); + break; + case "MODELVIEW" : + if (!param.source) { + s = s.replace(r, 'modelViewMatrix'); + } + break; + case "MODELVIEWINVERSETRANSPOSE" : + if (!param.source) { + s = s.replace(r, 'normalMatrix'); + } + break; + case "PROJECTION" : + if (!param.source) { + s = s.replace(r, 'projectionMatrix'); + } + break; + case "WEIGHT" : + s = s.replace(r, 'skinWeight'); + break; + case "JOINT" : + s = s.replace(r, 'skinIndex'); + break; + default : + break; + } + + } + + return s; + } + + function replaceShaderSemantics(material) { + + var fragmentShader = theLoader.shaders[material.params.fragmentShader]; + if (fragmentShader) { + fragmentShader = replaceShaderDefinitions(fragmentShader, material); + theLoader.shaders[material.params.fragmentShader] = fragmentShader; + } + + var vertexShader = theLoader.shaders[material.params.vertexShader]; + if (vertexShader) { + vertexShader = replaceShaderDefinitions(vertexShader, material); + theLoader.shaders[material.params.vertexShader] = vertexShader; + } + + } + + function createShaderMaterial(material, geometry) { + + // replace named attributes and uniforms with Three.js built-ins + replaceShaderSemantics(material); + + var fragmentShader = theLoader.shaders[material.params.fragmentShader]; + if (!fragmentShader) { + console.log("ERROR: Missing fragment shader definition:", material.params.fragmentShader); + return new THREE.MeshPhongMaterial; + } + + var vertexShader = theLoader.shaders[material.params.vertexShader]; + if (!vertexShader) { + console.log("ERROR: Missing vertex shader definition:", material.params.vertexShader); + return new THREE.MeshPhongMaterial; + } + + var shaderMaterial = new THREE.RawShaderMaterial( { + + fragmentShader: fragmentShader, + vertexShader: vertexShader, + uniforms: material.params.uniforms, + transparent: material.params.transparent, + + } ); + +/* var attributes = material.params.attributes; + var idx = 0; + for (var aname in attributes) { + var attribute = attributes[aname]; + var semantic = attribute.semantic; + if (semantic == "POSITION") { + console.log("Position attribute", attribute); + var attr = geometry.attributes.position; + var array = new Float32Array(attr.array.buffer, 0, attr.array.length); + geometry.addAttribute(aname, new THREE.BufferAttribute(array, attr.itemSize)); +// shaderMaterial.index0AttributeName = aname; + } + else if (semantic == "NORMAL") { + console.log("Normal attribute", attribute); + var attr = geometry.attributes.normal; + var array = new Float32Array(attr.array.buffer, 0, attr.array.length); + geometry.addAttribute(aname, new THREE.BufferAttribute(array, attr.itemSize)); + } + else if (semantic == "TEXCOORD_0") { + console.log("Texcoord0 attribute", attribute); + var attr = geometry.attributes.uv; + var array = new Float32Array(attr.array.buffer, 0, attr.array.length); + geometry.addAttribute(aname, new THREE.BufferAttribute(array, attr.itemSize)); + } + }*/ + + return shaderMaterial; + + return new THREE.MeshPhongMaterial(material.params); + } + + + function LoadTexture(src) { + if(!src) { return null; } + + var isDataUriRegex = /^data:/; + + var loadImage = function(url, success, error) { + var image = new Image(); + + image.onload = function() { + success(image); + }; + + if (typeof error !== 'undefined') { + image.onerror = error; + } + + image.src = url; + }; + + function loadImageFromTypedArray(uint8Array, format) { + //>>includeStart('debug', pragmas.debug); + if (!defined(uint8Array)) { + throw new DeveloperError('uint8Array is required.'); + } + + if (!defined(format)) { + throw new DeveloperError('format is required.'); + } + //>>includeEnd('debug'); + + var blob = new Blob([uint8Array], { + type : format + }); + + }; + + function decodeDataUriText(isBase64, data) { + var result = decodeURIComponent(data); + if (isBase64) { + return atob(result); + } + return result; + } + + function decodeDataUriArrayBuffer(isBase64, data) { + var byteString = decodeDataUriText(isBase64, data); + var buffer = new ArrayBuffer(byteString.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < byteString.length; i++) { + view[i] = byteString.charCodeAt(i); + } + return buffer; + } + + function decodeDataUri(dataUriRegexResult, responseType) { + responseType = typeof responseType !== 'undefined' ? responseType : ''; + var mimeType = dataUriRegexResult[1]; + var isBase64 = !!dataUriRegexResult[2]; + var data = dataUriRegexResult[3]; + + switch (responseType) { + case '': + case 'text': + return decodeDataUriText(isBase64, data); + case 'ArrayBuffer': + return decodeDataUriArrayBuffer(isBase64, data); + case 'blob': + var buffer = decodeDataUriArrayBuffer(isBase64, data); + return new Blob([buffer], { + type : mimeType + }); + case 'document': + var parser = new DOMParser(); + return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType); + case 'json': + return JSON.parse(decodeDataUriText(isBase64, data)); + default: + throw 'Unhandled responseType: ' + responseType; + } + } + +// Commented out for Qt +// var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; +// var dataUriRegexResult = dataUriRegex.exec(src); +// if (dataUriRegexResult !== null) { +// var texture = new THREE.Texture; +// var blob = decodeDataUri(dataUriRegexResult, 'blob'); +// var blobUrl = window.URL.createObjectURL(blob); +// loadImage(blobUrl, function(img) { +// texture.image = img; +// texture.needsUpdate = true; +// }); +// return texture; +// } + + return new THREE.TextureLoader().load(src); + } + + function CreateTexture(resources, resource) { + var texturePath = null; + var textureParams = null; + + if (resource) + { + var texture = resource; + if (texture) { + var textureEntry = resources.getEntry(texture); + if (textureEntry) { + { + var imageEntry = resources.getEntry(textureEntry.description.source); + if (imageEntry) { + texturePath = imageEntry.description.uri; + } + + var samplerEntry = resources.getEntry(textureEntry.description.sampler); + if (samplerEntry) { + textureParams = samplerEntry.description; + } + } + } + } + } + + var texture = LoadTexture(texturePath); + if (texture && textureParams) { + + if (textureParams.wrapS === Context3D.REPEAT) // Changed for Qt + texture.wrapS = THREE.RepeatWrapping; + + if (textureParams.wrapT === Context3D.REPEAT) // Changed for Qt + texture.wrapT = THREE.RepeatWrapping; + + if (textureParams.magFilter === Context3D.LINEAR) // Changed for Qt + texture.magFilter = THREE.LinearFilter; + +// if (textureParams.minFilter == "LINEAR") +// texture.minFilter = THREE.LinearFilter; + } + + return texture; + } + + // Geometry processing + + var ClassicGeometry = function() { + + if (theLoader.useBufferGeometry) { + this.geometry = new THREE.BufferGeometry; + } + else { + this.geometry = new THREE.Geometry; + } + this.totalAttributes = 0; + this.loadedAttributes = 0; + this.indicesLoaded = false; + this.finished = false; + + this.onload = null; + + this.uvs = null; + this.indexArray = null; + }; + + ClassicGeometry.prototype.constructor = ClassicGeometry; + + ClassicGeometry.prototype.buildArrayGeometry = function() { + + // Build indexed mesh + var geometry = this.geometry; + var normals = geometry.normals; + var indexArray = this.indexArray; + var uvs = this.uvs; + var a, b, c; + var i, l; + var faceNormals = null; + var faceTexcoords = null; + + for(i = 0, l = this.indexArray.length; i < l; i += 3) { + a = indexArray[i]; + b = indexArray[i+1]; + c = indexArray[i+2]; + if(normals) { + faceNormals = [normals[a], normals[b], normals[c]]; + } + geometry.faces.push( new THREE.Face3( a, b, c, faceNormals, null, null ) ); + if(uvs) { + geometry.faceVertexUvs[0].push([ uvs[a], uvs[b], uvs[c] ]); + } + } + + // Allow Three.js to calculate some values for us + geometry.computeBoundingBox(); + geometry.computeBoundingSphere(); + geometry.computeFaceNormals(); + if(!normals) { + geometry.computeVertexNormals(); + } + + } + + ClassicGeometry.prototype.buildBufferGeometry = function() { + // Build indexed mesh + var geometry = this.geometry; + geometry.setIndex( new THREE.BufferAttribute( this.indexArray, 1 ) ); + + var offset = { + start: 0, + index: 0, + count: this.indexArray.length + }; + + geometry.groups.push( offset ); + + geometry.computeBoundingSphere(); + } + + ClassicGeometry.prototype.checkFinished = function() { + if(this.indexArray && this.loadedAttributes === this.totalAttributes) { + + if (theLoader.useBufferGeometry) { + this.buildBufferGeometry(); + } + else { + this.buildArrayGeometry(); + } + + this.finished = true; + + if(this.onload) { + this.onload(); + } + } + }; + + // Delegate for processing index buffers + var IndicesDelegate = function() {}; + + IndicesDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(IndicesDelegate):"+errorCode+":"+info); + }; + + IndicesDelegate.prototype.convert = function(resource, ctx) { + return new Uint16Array(resource, 0, ctx.indices.count); + }; + + IndicesDelegate.prototype.resourceAvailable = function(glResource, ctx) { + var geometry = ctx.geometry; + geometry.indexArray = glResource; + geometry.checkFinished(); + return true; + }; + + var indicesDelegate = new IndicesDelegate(); + + var IndicesContext = function(indices, geometry) { + this.indices = indices; + this.geometry = geometry; + }; + + // Delegate for processing vertex attribute buffers + var VertexAttributeDelegate = function() {}; + + VertexAttributeDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(VertexAttributeDelegate):"+errorCode+":"+info); + }; + + VertexAttributeDelegate.prototype.convert = function(resource, ctx) { + return resource; + }; + + + + VertexAttributeDelegate.prototype.arrayResourceAvailable = function(glResource, ctx) { + var geom = ctx.geometry; + var attribute = ctx.attribute; + var semantic = ctx.semantic; + var floatArray; + var i, l; + //FIXME: Float32 is assumed here, but should be checked. + + if(semantic == "POSITION") { + // TODO: Should be easy to take strides into account here + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + for(i = 0, l = floatArray.length; i < l; i += 3) { + geom.geometry.vertices.push( new THREE.Vector3( floatArray[i], floatArray[i+1], floatArray[i+2] ) ); + } + } else if(semantic == "NORMAL") { + geom.geometry.normals = []; + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + for(i = 0, l = floatArray.length; i < l; i += 3) { + geom.geometry.normals.push( new THREE.Vector3( floatArray[i], floatArray[i+1], floatArray[i+2] ) ); + } + } else if ((semantic == "TEXCOORD_0") || (semantic == "TEXCOORD" )) { + geom.uvs = []; + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + for(i = 0, l = floatArray.length; i < l; i += 2) { + geom.uvs.push( new THREE.Vector2( floatArray[i], 1.0 - floatArray[i+1] ) ); + } + } + else if (semantic == "WEIGHT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + for(i = 0, l = floatArray.length; i < l; i += 4) { + geom.geometry.skinWeights.push( new THREE.Vector4( floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3] ) ); + } + } + else if (semantic == "JOINT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + for(i = 0, l = floatArray.length; i < l; i += 4) { + geom.geometry.skinIndices.push( new THREE.Vector4( floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3] ) ); + } + } + } + + VertexAttributeDelegate.prototype.bufferResourceAvailable = function(glResource, ctx) { + var geom = ctx.geometry; + var attribute = ctx.attribute; + var semantic = ctx.semantic; + var floatArray; + var i, l; + var nComponents; + //FIXME: Float32 is assumed here, but should be checked. + + if(semantic == "POSITION") { + // TODO: Should be easy to take strides into account here + floatArray = new Float32Array(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + geom.geometry.addAttribute( 'position', new THREE.BufferAttribute( floatArray, 3 ) ); + } else if(semantic == "NORMAL") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + geom.geometry.addAttribute( 'normal', new THREE.BufferAttribute( floatArray, 3 ) ); + } else if ((semantic == "TEXCOORD_0") || (semantic == "TEXCOORD" )) { + + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + // N.B.: flip Y value... should we just set texture.flipY everywhere? + for (i = 0; i < floatArray.length / 2; i++) { + floatArray[i*2+1] = 1.0 - floatArray[i*2+1]; + } + geom.geometry.addAttribute( 'uv', new THREE.BufferAttribute( floatArray, nComponents ) ); + } + else if (semantic == "WEIGHT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + geom.geometry.addAttribute( 'skinWeight', new THREE.BufferAttribute( floatArray, nComponents ) ); + } + else if (semantic == "JOINT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32Array(glResource, 0, attribute.count * nComponents); + geom.geometry.addAttribute( 'skinIndex', new THREE.BufferAttribute( floatArray, nComponents ) ); + } + } + + VertexAttributeDelegate.prototype.resourceAvailable = function(glResource, ctx) { + if (theLoader.useBufferGeometry) { + this.bufferResourceAvailable(glResource, ctx); + } + else { + this.arrayResourceAvailable(glResource, ctx); + } + + var geom = ctx.geometry; + geom.loadedAttributes++; + geom.checkFinished(); + return true; + }; + + var vertexAttributeDelegate = new VertexAttributeDelegate(); + + var VertexAttributeContext = function(attribute, semantic, geometry) { + this.attribute = attribute; + this.semantic = semantic; + this.geometry = geometry; + }; + + var Mesh = function() { + this.primitives = []; + this.materialsPending = []; + this.loadedGeometry = 0; + this.onCompleteCallbacks = []; + }; + + Mesh.prototype.addPrimitive = function(geometry, material) { + + var self = this; + geometry.onload = function() { + self.loadedGeometry++; + self.checkComplete(); + }; + + this.primitives.push({ + geometry: geometry, + material: material, + mesh: null + }); + }; + + Mesh.prototype.onComplete = function(callback) { + this.onCompleteCallbacks.push(callback); + this.checkComplete(); + }; + + Mesh.prototype.checkComplete = function() { + var self = this; + if(this.onCompleteCallbacks.length && this.primitives.length == this.loadedGeometry) { + this.onCompleteCallbacks.forEach(function(callback) { + callback(self); + }); + this.onCompleteCallbacks = []; + } + }; + + Mesh.prototype.attachToNode = function(threeNode) { + // Assumes that the geometry is complete + this.primitives.forEach(function(primitive) { + /*if(!primitive.mesh) { + primitive.mesh = new THREE.Mesh(primitive.geometry, primitive.material); + }*/ + var material = primitive.material; + var materialParams = material.params; + if (!(material instanceof THREE.Material)) { + material = createShaderMaterial(material, primitive.geometry.geometry); + } + + var threeMesh = new THREE.Mesh(primitive.geometry.geometry, material); + threeMesh.castShadow = true; + threeNode.add(threeMesh); + + if (material instanceof THREE.ShaderMaterial) { + var glTFShader = new THREE.glTFShader(material, materialParams, threeMesh, theLoader.rootObj); + THREE.glTFShaders.add(glTFShader); + + } + }); + }; + + // Delayed-loaded material + var Material = function(params) { + this.params = params; + }; + + // Delegate for processing animation parameter buffers + var AnimationParameterDelegate = function() {}; + + AnimationParameterDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(AnimationParameterDelegate):"+errorCode+":"+info); + }; + + AnimationParameterDelegate.prototype.convert = function(resource, ctx) { + var parameter = ctx.parameter; + + var glResource = null; + switch (parameter.type) { + case "SCALAR" : + case "VEC2" : + case "VEC3" : + case "VEC4" : + glResource = new Float32Array(resource, 0, parameter.count * componentsPerElementForGLType(parameter.type)); + break; + default: + break; + } + + return glResource; + }; + + AnimationParameterDelegate.prototype.resourceAvailable = function(glResource, ctx) { + var animation = ctx.animation; + var parameter = ctx.parameter; + parameter.data = glResource; + animation.handleParameterLoaded(parameter); + return true; + }; + + var animationParameterDelegate = new AnimationParameterDelegate(); + + var AnimationParameterContext = function(parameter, animation) { + this.parameter = parameter; + this.animation = animation; + }; + + // Animations + var Animation = function() { + + // create Three.js keyframe here + this.totalParameters = 0; + this.loadedParameters = 0; + this.parameters = {}; + this.finishedLoading = false; + this.onload = null; + + }; + + Animation.prototype.constructor = Animation; + + Animation.prototype.handleParameterLoaded = function(parameter) { + this.parameters[parameter.name] = parameter; + this.loadedParameters++; + this.checkFinished(); + }; + + Animation.prototype.checkFinished = function() { + if(this.loadedParameters === this.totalParameters) { + // Build animation + this.finishedLoading = true; + + if (this.onload) { + this.onload(); + } + } + }; + + // Delegate for processing inverse bind matrices buffer + var InverseBindMatricesDelegate = function() {}; + + InverseBindMatricesDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(InverseBindMatricesDelegate):"+errorCode+":"+info); + }; + + InverseBindMatricesDelegate.prototype.convert = function(resource, ctx) { + var parameter = ctx.parameter; + + var glResource = null; + switch (parameter.type) { + case "MAT4" : + glResource = new Float32Array(resource, 0, parameter.count * componentsPerElementForGLType(parameter.type)); + break; + default: + break; + } + + return glResource; + }; + + InverseBindMatricesDelegate.prototype.resourceAvailable = function(glResource, ctx) { + var skin = ctx.skin; + skin.inverseBindMatrices = glResource; + return true; + }; + + var inverseBindMatricesDelegate = new InverseBindMatricesDelegate(); + + var InverseBindMatricesContext = function(param, skin) { + this.parameter = param; + this.skin = skin; + }; + + // Delegate for processing shaders from external files + var ShaderDelegate = function() {}; + + ShaderDelegate.prototype.handleError = function(errorCode, info) { + // FIXME: report error + console.log("ERROR(ShaderDelegate):"+errorCode+":"+info); + }; + + ShaderDelegate.prototype.convert = function(resource, ctx) { + return resource; + } + + ShaderDelegate.prototype.resourceAvailable = function(data, ctx) { + theLoader.shadersLoaded++; + theLoader.shaders[ctx.id] = data; + theLoader.checkComplete(); // Added for Qt + return true; + }; + + var shaderDelegate = new ShaderDelegate(); + + var ShaderContext = function(id, path) { + this.id = id; + this.uri = path; + }; + + // Resource management + + var ResourceEntry = function(entryID, object, description) { + this.entryID = entryID; + this.object = object; + this.description = description; + }; + + var Resources = function() { + this._entries = {}; + }; + + Resources.prototype.setEntry = function(entryID, object, description) { + if (!entryID) { + console.error("No EntryID provided, cannot store", description); + return; + } + + if (this._entries[entryID]) { + console.warn("entry["+entryID+"] is being overwritten"); + } + + this._entries[entryID] = new ResourceEntry(entryID, object, description ); + }; + + Resources.prototype.getEntry = function(entryID) { + return this._entries[entryID]; + }; + + Resources.prototype.clearEntries = function() { + this._entries = {}; + }; + + var LoadDelegate = function() { // Changed for Qt + } + + LoadDelegate.prototype.loadCompleted = function(callback, obj) { + callback.call(this, obj); // Changed for Qt + } + + // Loader + + var ThreeGLTFLoader = Object.create(THREE.glTFParser, { + + load: { + enumerable: true, + value: function(userInfo, options) { + this.resources = new Resources(); + this.cameras = []; + this.lights = []; + this.animations = []; + this.joints = {}; + this.skeletons = {}; // Changed for Qt + THREE.GLTFLoaderUtils.init(); + THREE.glTFParser.load.call(this, userInfo, options); + } + }, + + cameras: { + enumerable: true, + writable: true, + value : [] + }, + + lights: { + enumerable: true, + writable: true, + value : [] + }, + + animations: { + enumerable: true, + writable: true, + value : [] + }, + + // Implement WebGLTFLoader handlers + + handleBuffer: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + description.type = "ArrayBuffer"; + return true; + } + }, + + handleBufferView: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + + var buffer = this.resources.getEntry(description.buffer); + description.type = "ArrayBufferView"; + + var bufferViewEntry = this.resources.getEntry(entryID); + bufferViewEntry.buffer = buffer; + return true; + } + }, + + handleShader: { + value: function(entryID, description, userInfo) { + description.uri = "qrc:" + description.uri // Added for Qt + this.resources.setEntry(entryID, null, description); + var shaderRequest = { + id : entryID, + uri : description.uri, + }; + + var shaderContext = new ShaderContext(entryID, description.uri); + + theLoader.shadersRequested++; + THREE.GLTFLoaderUtils.getFile(shaderRequest, shaderDelegate, shaderContext); + + return true; + } + }, + + handleProgram: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + handleTechnique: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + + createShaderParams : { + value: function(materialId, values, params, instanceProgram, shaderParams) { + var program = this.resources.getEntry(instanceProgram.program); + + params.uniforms = {}; + params.attributes = {}; + params.program = instanceProgram; + params.parameters = shaderParams; + if (program) { + params.fragmentShader = program.description.fragmentShader; + params.vertexShader = program.description.vertexShader; + for (var uniform in instanceProgram.uniforms) { + var pname = instanceProgram.uniforms[uniform]; + var shaderParam = shaderParams[pname]; + var ptype = shaderParam.type; + var pcount = shaderParam.count; + var value = values[pname]; + var utype = ""; + var uvalue; + var ulength; + + // THIS: for (n in WebGLRenderingContext) { z = WebGLRenderingContext[n]; idx[z] = n; } + //console.log("shader uniform param type: ", ptype, "-", theLoader.idx[ptype]) + + + switch (ptype) { + case Context3D.FLOAT : // Changed for Qt + utype = "f"; + uvalue = value; + if (pname == "transparency") { + var USE_A_ONE = true; // for now, hack because file format isn't telling us + var opacity = USE_A_ONE ? value : (1.0 - value); + uvalue = opacity; + params.transparent = true; + } + break; + case Context3D.FLOAT_VEC3 : // Changed for Qt + utype = "v3"; + uvalue = new THREE.Vector3; + if (shaderParam && shaderParam.value) { + var v3 = shaderParam.value; + uvalue.fromArray(v3); + } + if (value) { + uvalue.fromArray(value); + } + break; + case Context3D.FLOAT_VEC4 : // Changed for Qt + utype = "v4"; + uvalue = new THREE.Vector4; + if (shaderParam && shaderParam.value) { + var v4 = shaderParam.value; + uvalue.fromArray(v4); + } + if (value) { + uvalue.fromArray(value); + } + break; + case Context3D.FLOAT_MAT3 : // Changed for Qt + utype = "m3"; + uvalue = new THREE.Matrix3; + if (shaderParam && shaderParam.value) { + var m3 = shaderParam.value; + uvalue.fromArray(m3); + } + if (value) { + uvalue.fromArray(value); + } + break; + case Context3D.FLOAT_MAT4 : // Changed for Qt + if (pcount !== undefined) { + utype = "m4v"; + uvalue = new Array(pcount); + for (var mi = 0; mi < pcount; mi++) { + uvalue[mi] = new THREE.Matrix4; + } + ulength = pcount; + + if (shaderParam && shaderParam.value) { + var m4v = shaderParam.value; + uvalue.fromArray(m4v); + } + if (value) { + uvalue.fromArray(value); + + } + } + else { + utype = "m4"; + uvalue = new THREE.Matrix4; + + if (shaderParam && shaderParam.value) { + var m4 = shaderParam.value; + uvalue.fromArray(m4); + } + if (value) { + uvalue.fromArray(value); + + } + } +/* if (pname == "light0Transform") { + uvalue.set( + -0.954692, + 0.218143, + -0.202428, + 0, + 0.0146721, + 0.713885, + 0.700109, + 0, + 0.297235, + 0.665418, + -0.684741, + 0, + 1.48654, + 1.83672, + -2.92179, + 1); + }*/ + + + break; + case Context3D.SAMPLER_2D : // Changed for Qt + utype = "t"; + uvalue = value ? CreateTexture(this.resources, value) : null; + break; + default : + console.log("Unknown shader uniform param type: ", ptype, "-", theLoader.idx[ptype]) + break; + } + + + var udecl = { type : utype, value : uvalue, length : ulength }; + + params.uniforms[uniform] = udecl; + } + + for (var attribute in instanceProgram.attributes) { + var aname = instanceProgram.attributes[attribute]; + var atype = shaderParams[aname].type; + var semantic = shaderParams[aname].semantic; + var adecl = { type : atype, semantic : semantic }; + + params.attributes[attribute] = adecl; + } + + } + } + }, + + threeJSMaterialType : { + value: function(materialId, technique, values, params) { + + var materialType = THREE.MeshPhongMaterial; + var defaultPass = null; + var description = technique ? technique.description : null; + if (description && description.passes) + defaultPass = description.passes.defaultPass; + + if (defaultPass) { + if (!theLoader.useShaders && defaultPass.details && defaultPass.details.commonProfile) { + var profile = description.passes.defaultPass.details.commonProfile; + if (profile) + { + switch (profile.lightingModel) + { + case 'Blinn' : + case 'Phong' : + materialType = THREE.MeshPhongMaterial; + break; + + case 'Lambert' : + materialType = THREE.MeshLambertMaterial; + break; + + default : + materialType = THREE.MeshBasicMaterial; + break; + } + + if (profile.extras && profile.extras.doubleSided) + { + params.side = THREE.DoubleSide; + } + } + } + else if (defaultPass.instanceProgram) { + + var instanceProgram = defaultPass.instanceProgram; + this.createShaderParams(materialId, values, params, instanceProgram, technique.description.parameters); + + var loadshaders = true; + + if (loadshaders) { + materialType = Material; + } + } + } + + params.map = CreateTexture(this.resources, values.diffuse); + params.envMap = CreateTexture(this.resources, values.reflective);; + + + var shininess = values.shininesss || values.shininess; // N.B.: typo in converter! + if (shininess) + { + shininess = shininess; + } + + var diffuseColor = null; + if (!params.map) { + diffuseColor = values.diffuse; + } + var opacity = 1.0; + if (values.hasOwnProperty("transparency")) + { + var USE_A_ONE = true; // for now, hack because file format isn't telling us + opacity = USE_A_ONE ? values.transparency : (1.0 - values.transparency); + } + + // if (diffuseColor) diffuseColor = [0, 1, 0]; + + params.color = RgbArraytoHex(diffuseColor); + params.opacity = opacity; + params.transparent = opacity < 1.0; + // hack hack hack + if (params.map && params.map.sourceFile.toLowerCase().indexOf(".png") != -1) + params.transparent = true; + + if (!(shininess === undefined)) + { + params.shininess = shininess; + } + +// Qt: Commented out, r74 no longer supports this for materials +// if (!(values.ambient === undefined) && !(typeof(values.ambient) == 'string')) +// { +// params.ambient = RgbArraytoHex(values.ambient); +// } + + if (!(values.emission === undefined)) + { + params.emissive = RgbArraytoHex(values.emission); + } + + if (!(values.specular === undefined)) + { + params.specular = RgbArraytoHex(values.specular); + } + + return materialType; + + } + }, + + handleMaterial: { + value: function(entryID, description, userInfo) { + //this should be rewritten using the meta datas that actually create the shader. + //here we will infer what needs to be pass to Three.js by looking inside the technique parameters. + // Changed for Qt --> + var technique; + var values; + var materialParams = {}; + if (description.instanceTechnique === undefined) { + technique = this.resources.getEntry(description.technique); + values = description.values; + } else { + technique = this.resources.getEntry(description.instanceTechnique.technique); + values = description.instanceTechnique.values; + } + //var technique = this.resources.getEntry(description.instanceTechnique.technique); + //var materialParams = {}; + //var values = description.instanceTechnique.values; + // <-- Changed for Qt + var materialType = this.threeJSMaterialType(entryID, technique, values, materialParams); + + var material = new materialType(materialParams); + + this.resources.setEntry(entryID, material, description); + + return true; + } + }, + + handleMesh: { + value: function(entryID, description, userInfo) { + var mesh = new Mesh(); + this.resources.setEntry(entryID, mesh, description); + var primitivesDescription = description.primitives; + if (!primitivesDescription) { + //FIXME: not implemented in delegate + console.log("MISSING_PRIMITIVES for mesh:"+ entryID); + return false; + } + + for (var i = 0 ; i < primitivesDescription.length ; i++) { + var primitiveDescription = primitivesDescription[i]; + + if (primitiveDescription.primitive === Context3D.TRIANGLES + || primitiveDescription.mode === Context3D.TRIANGLES) { // Changed for Qt + + var geometry = new ClassicGeometry(); + var materialEntry = this.resources.getEntry(primitiveDescription.material); + + mesh.addPrimitive(geometry, materialEntry.object); + + var allAttributes = Object.keys(primitiveDescription.attributes); + + // count them first, async issues otherwise + allAttributes.forEach( function(semantic) { + geometry.totalAttributes++; + }, this); + + var indices = this.resources.getEntry(primitiveDescription.indices); + var bufferEntry = this.resources.getEntry(indices.description.bufferView); + var indicesObject = { + bufferView : bufferEntry, + byteOffset : indices.description.byteOffset, + count : indices.description.count, + id : indices.entryID, + componentType : indices.description.componentType, + type : indices.description.type + }; + + var indicesContext = new IndicesContext(indicesObject, geometry); + var alreadyProcessedIndices = THREE.GLTFLoaderUtils.getBuffer(indicesObject, indicesDelegate, indicesContext); + /*if(alreadyProcessedIndices) { + indicesDelegate.resourceAvailable(alreadyProcessedIndices, indicesContext); + }*/ + + // Load Vertex Attributes + allAttributes.forEach( function(semantic) { + + var attribute; + var attributeID = primitiveDescription.attributes[semantic]; + var attributeEntry = this.resources.getEntry(attributeID); + if (!attributeEntry) { + //let's just use an anonymous object for the attribute + attribute = description.attributes[attributeID]; + attribute.id = attributeID; + this.resources.setEntry(attributeID, attribute, attribute); + + var bufferEntry = this.resources.getEntry(attribute.bufferView); + attributeEntry = this.resources.getEntry(attributeID); + + } else { + attribute = attributeEntry.object; + attribute.id = attributeID; + var bufferEntry = this.resources.getEntry(attribute.bufferView); + } + + var attributeObject = { + bufferView : bufferEntry, + byteOffset : attribute.byteOffset, + byteStride : attribute.byteStride, + count : attribute.count, + max : attribute.max, + min : attribute.min, + componentType : attribute.componentType, + type : attribute.type, + id : attributeID + }; + + var attribContext = new VertexAttributeContext(attributeObject, semantic, geometry); + + var alreadyProcessedAttribute = THREE.GLTFLoaderUtils.getBuffer(attributeObject, vertexAttributeDelegate, attribContext); + /*if(alreadyProcessedAttribute) { + vertexAttributeDelegate.resourceAvailable(alreadyProcessedAttribute, attribContext); + }*/ + }, this); + } + } + return true; + } + }, + + handleCamera: { + value: function(entryID, description, userInfo) { + var camera; + if (description.type == "perspective") + { + var znear = description.perspective.znear; + var zfar = description.perspective.zfar; + var yfov = description.perspective.yfov; + var xfov = description.perspective.xfov; + var aspect_ratio = description.perspective.aspect_ratio; + + if (!aspect_ratio) + aspect_ratio = 1; + + if (xfov === undefined) { + if (yfov) + { + // According to COLLADA spec... + // aspect_ratio = xfov / yfov + xfov = yfov * aspect_ratio; + } + } + + if (yfov === undefined) + { + if (xfov) + { + // According to COLLADA spec... + // aspect_ratio = xfov / yfov + yfov = xfov / aspect_ratio; + } + + } + + if (xfov) + { + camera = new THREE.PerspectiveCamera(xfov, aspect_ratio, znear, zfar); + } + } + else + { + // Changed for Qt, window is not supported, so just create default size + //camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, znear, zfar ); + camera = new THREE.OrthographicCamera( -1, 1, 1, -1, znear, zfar ); + } + + if (camera) + { + this.resources.setEntry(entryID, camera, description); + } + + return true; + } + }, + + handleLight: { + value: function(entryID, description, userInfo) { + + var light = null; + var type = description.type; + if (type && description[type]) + { + var lparams = description[type]; + var color = RgbArraytoHex(lparams.color); + + switch (type) { + case "directional" : + light = new THREE.DirectionalLight(color); + light.position.set(0, 0, 1); + break; + + case "point" : + light = new THREE.PointLight(color); + break; + + case "spot " : + light = new THREE.SpotLight(color); + light.position.set(0, 0, 1); + break; + + case "ambient" : + light = new THREE.AmbientLight(color); + break; + } + } + + if (light) + { + this.resources.setEntry(entryID, light, description); + } + + return true; + } + }, + + addPendingMesh: { + value: function(mesh, threeNode) { + theLoader.pendingMeshes.push({ + mesh: mesh, + node: threeNode + }); + } + }, + + handleNode: { + value: function(entryID, description, userInfo) { + + var threeNode = null; + if (description.jointName) { + threeNode = new THREE.Bone(); + threeNode.jointName = description.jointName; + this.joints[description.jointName] = entryID; + } + else { + threeNode = new THREE.Object3D(); + } + + threeNode.name = description.name; + threeNode.glTFID = entryID; + + this.resources.setEntry(entryID, threeNode, description); + + var m = description.matrix; + if(m) { + threeNode.matrixAutoUpdate = false; + threeNode.applyMatrix(new THREE.Matrix4().set( + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15] + )); + } + else { + var t = description.translation; + var r = description.rotation; + var s = description.scale; + + var position = t ? new THREE.Vector3(t[0], t[1], t[2]) : + new THREE.Vector3; + if (r) { + convertAxisAngleToQuaternion(r, 1); + } + var rotation = r ? new THREE.Quaternion(r[0], r[1], r[2], r[3]) : + new THREE.Quaternion; + var scale = s ? new THREE.Vector3(s[0], s[1], s[2]) : + new THREE.Vector3(1, 1, 1); + + var matrix = new THREE.Matrix4; + matrix.compose(position, rotation, scale); + threeNode.matrixAutoUpdate = false; + threeNode.applyMatrix(matrix); + } + + var self = this; + + // Iterate through all node meshes and attach the appropriate objects + //FIXME: decision needs to be made between these 2 ways, probably meshes will be discarded. + var meshEntry; + if (description.mesh) { + meshEntry = this.resources.getEntry(description.mesh); + theLoader.meshesRequested++; + meshEntry.object.onComplete(function(mesh) { + self.addPendingMesh(mesh, threeNode); + theLoader.meshesLoaded++; + theLoader.checkComplete(); + }); + } + + if (description.meshes) { + description.meshes.forEach( function(meshID) { + meshEntry = this.resources.getEntry(meshID); + theLoader.meshesRequested++; + meshEntry.object.onComplete(function(mesh) { + self.addPendingMesh(mesh, threeNode); + theLoader.meshesLoaded++; + theLoader.checkComplete(); + }); + }, this); + } + + if (description.instanceSkin) { + + var skinEntry = this.resources.getEntry(description.instanceSkin.skin); + + if (skinEntry) { + + var skin = skinEntry.object; + description.instanceSkin.skin = skin; + threeNode.instanceSkin = description.instanceSkin; + + var meshes = description.instanceSkin.meshes; + skin.meshes = []; + meshes.forEach( function(meshID) { + meshEntry = this.resources.getEntry(meshID); + theLoader.meshesRequested++; + meshEntry.object.onComplete(function(mesh) { + + skin.meshes.push(mesh); + theLoader.meshesLoaded++; + theLoader.checkComplete(); + }); + }, this); + + } + } + + if (description.camera) { + var cameraEntry = this.resources.getEntry(description.camera); + if (cameraEntry) { + threeNode.add(cameraEntry.object); + this.cameras.push(cameraEntry.object); + } + } + + if (description.light) { + var lightEntry = this.resources.getEntry(description.light); + if (lightEntry) { + threeNode.add(lightEntry.object); + this.lights.push(lightEntry.object); + } + } + + return true; + } + }, + + buildNodeHirerachy: { + value: function(nodeEntryId, parentThreeNode) { + var nodeEntry = this.resources.getEntry(nodeEntryId); + var threeNode = nodeEntry.object; + parentThreeNode.add(threeNode); + + var children = nodeEntry.description.children; + if (children) { + children.forEach( function(childID) { + this.buildNodeHirerachy(childID, threeNode); + }, this); + } + + return threeNode; + } + }, + + buildSkin: { + value: function(node) { + + var skin = node.instanceSkin.skin; + if (skin) { + node.instanceSkin.skeletons.forEach(function(skeleton) { + var nodeEntry = this.resources.getEntry(skeleton); + if (nodeEntry) { + + var rootSkeleton = nodeEntry.object; + + var dobones = true; + + var i, len = skin.meshes.length; + for (i = 0; i < len; i++) { + var mesh = skin.meshes[i]; + var threeMesh = null; + mesh.primitives.forEach(function(primitive) { + + var material = primitive.material; + var materialParams = material.params; + if (!(material instanceof THREE.Material)) { + material = createShaderMaterial(material, primitive.geometry.geometry); + } + + threeMesh = new THREE.SkinnedMesh(primitive.geometry.geometry, material, false); + threeMesh.add(rootSkeleton); + + var geometry = primitive.geometry.geometry; + var j; + if (geometry.vertices) { + for ( j = 0; j < geometry.vertices.length; j ++ ) { + geometry.vertices[j].applyMatrix4( skin.bindShapeMatrix ); + } + } + else if (geometry.attributes.position) { + var a = geometry.attributes.position.array; + var v = new THREE.Vector3; + for ( j = 0; j < a.length / 3; j++ ) { + v.set(a[j * 3], a[j * 3 + 1], a[j * 3 + 2]); + v.applyMatrix4( skin.bindShapeMatrix ); + a[j * 3] = v.x; + a[j * 3 + 1] = v.y; + a[j * 3 + 2] = v.z; + } + } + + if (threeMesh && dobones) { + + material.skinning = true; + + var jointNames = skin.jointNames; + var joints = []; + threeMesh.skeleton.bones = []; + threeMesh.skeleton.boneInverses = []; + threeMesh.skeleton.boneMatrices = new Float32Array( 16 * jointNames.length ); + var i, len = jointNames.length; + for (i = 0; i < len; i++) { + var jointName = jointNames[i]; + var nodeForJoint = this.joints[jointName]; + var joint = this.resources.getEntry(nodeForJoint).object; + if (joint) { + + joint.skin = threeMesh; + joints.push(joint); + threeMesh.skeleton.bones.push(joint); + + var m = skin.inverseBindMatrices; + var mat = new THREE.Matrix4().set( + m[i * 16 + 0], m[i * 16 + 4], m[i * 16 + 8], m[i * 16 + 12], + m[i * 16 + 1], m[i * 16 + 5], m[i * 16 + 9], m[i * 16 + 13], + m[i * 16 + 2], m[i * 16 + 6], m[i * 16 + 10], m[i * 16 + 14], + m[i * 16 + 3], m[i * 16 + 7], m[i * 16 + 11], m[i * 16 + 15] + ); + threeMesh.skeleton.boneInverses.push(mat); + threeMesh.skeleton.pose(); + + } else { + console.log("WARNING: jointName:"+jointName+" cannot be found in skeleton:"+skeleton); + } + } + } + + if (threeMesh) { + threeMesh.castShadow = true; + node.add(threeMesh); + + if (material instanceof THREE.ShaderMaterial) { + materialParams.joints = joints; + var glTFShader = new THREE.glTFShader(material, materialParams, threeMesh, theLoader.rootObj); + THREE.glTFShaders.add(glTFShader); + + } + } + + }, this); + } + + } + + + }, this); + + } + } + }, + + buildSkins: { + value: function(node) { + + if (node.instanceSkin) + this.buildSkin(node); + + var children = node.children; + if (children) { + children.forEach( function(child) { + this.buildSkins(child); + }, this); + } + } + }, + + createMeshAnimations : { + value : function(root) { + this.buildSkins(root); + } + }, + + handleScene: { + value: function(entryID, description, userInfo) { + + if (!description.nodes) { + console.log("ERROR: invalid file required nodes property is missing from scene"); + return false; + } + + description.nodes.forEach( function(nodeUID) { + this.buildNodeHirerachy(nodeUID, userInfo.rootObj); + }, this); + + if (this.delegate) { + this.delegate.loadCompleted(userInfo.callback, userInfo.rootObj); + } + + return true; + } + }, + + handleImage: { + value: function(entryID, description, userInfo) { + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + addNodeAnimationChannel : { + value : function(name, channel, interp) { + if (!this.nodeAnimationChannels) + this.nodeAnimationChannels = {}; + + if (!this.nodeAnimationChannels[name]) { + this.nodeAnimationChannels[name] = []; + } + + this.nodeAnimationChannels[name].push(interp); + }, + }, + + createAnimations : { + value : function() { + for (var name in this.nodeAnimationChannels) { + var nodeAnimationChannels = this.nodeAnimationChannels[name]; + var i, len = nodeAnimationChannels.length; + //console.log(" animation channels for node " + name); + //for (i = 0; i < len; i++) { + // console.log(nodeAnimationChannels[i]); + //} + var anim = new THREE.glTFAnimation(nodeAnimationChannels); + anim.name = "animation_" + name; + this.animations.push(anim); + } + } + }, + + buildAnimation: { + value : function(animation) { + + var interps = []; + var i, len = animation.channels.length; + for (i = 0; i < len; i++) { + + var channel = animation.channels[i]; + var sampler = animation.samplers[channel.sampler]; + if (sampler) { + + var input = animation.parameters[sampler.input]; + if (input && input.data) { + + var output = animation.parameters[sampler.output]; + if (output && output.data) { + + var target = channel.target; + var node = this.resources.getEntry(target.id); + if (node) { + + var path = target.path; + + if (path == "rotation") + { + convertAxisAngleToQuaternion(output.data, output.count); + } + + var interp = { + keys : input.data, + values : output.data, + count : input.count, + target : node.object, + path : path, + type : sampler.interpolation + }; + + this.addNodeAnimationChannel(target.id, channel, interp); + interps.push(interp); + } + } + } + } + } + } + }, + + handleAnimation: { + value: function(entryID, description, userInfo) { + + var self = this; + theLoader.animationsRequested++; + var animation = new Animation(); + animation.name = entryID; + animation.onload = function() { + // self.buildAnimation(animation); + theLoader.animationsLoaded++; + theLoader.animations.push(animation); + theLoader.checkComplete(); + }; + + animation.channels = description.channels; + animation.samplers = description.samplers; + this.resources.setEntry(entryID, animation, description); + var parameters = description.parameters; + if (!parameters) { + //FIXME: not implemented in delegate + console.log("MISSING_PARAMETERS for animation:"+ entryID); + return false; + } + + // Load parameter buffers + var params = Object.keys(parameters); + params.forEach( function(param) { + + animation.totalParameters++; + var parameter = parameters[param]; + var accessor = this.resources.getEntry(parameter); + if (!accessor) + console.log("Invalid accessor in handleAnimation"); //debugger; // Changed for Qt, debugger keyword is not supported + accessor = accessor.object; + var bufferView = this.resources.getEntry(accessor.bufferView); + var paramObject = { + bufferView : bufferView, + byteOffset : accessor.byteOffset, + count : accessor.count, + componentType : accessor.componentType, + type : accessor.type, + id : accessor.bufferView, + name : param + }; + + var paramContext = new AnimationParameterContext(paramObject, animation); + + var alreadyProcessedAttribute = THREE.GLTFLoaderUtils.getBuffer(paramObject, animationParameterDelegate, paramContext); + /*if(alreadyProcessedAttribute) { + vertexAttributeDelegate.resourceAvailable(alreadyProcessedAttribute, attribContext); + }*/ + }, this); + + return true; + } + }, + + handleAccessor: { + value: function(entryID, description, userInfo) { + // Save attribute entry + this.resources.setEntry(entryID, description, description); + return true; + } + }, + + handleSkin: { + value: function(entryID, description, userInfo) { + // Save skin entry + + var skin = { + }; + + var m = description.bindShapeMatrix; + skin.bindShapeMatrix = new THREE.Matrix4().set( + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15] + ); + + skin.jointNames = description.jointNames; + var inverseBindMatricesDescription = this.resources.getEntry(description.inverseBindMatrices); + inverseBindMatricesDescription = inverseBindMatricesDescription.description; + skin.inverseBindMatricesDescription = inverseBindMatricesDescription; + skin.inverseBindMatricesDescription.id = description.inverseBindMatrices; + + var bufferEntry = this.resources.getEntry(inverseBindMatricesDescription.bufferView); + + var paramObject = { + bufferView : bufferEntry, + byteOffset : inverseBindMatricesDescription.byteOffset, + count : inverseBindMatricesDescription.count, + componentType : inverseBindMatricesDescription.componentType, + type : inverseBindMatricesDescription.type, + id : inverseBindMatricesDescription.bufferView, + name : skin.inverseBindMatricesDescription.id + }; + + var context = new InverseBindMatricesContext(paramObject, skin); + + var alreadyProcessedAttribute = THREE.GLTFLoaderUtils.getBuffer(paramObject, inverseBindMatricesDelegate, context); + + var bufferView = this.resources.getEntry(skin.inverseBindMatricesDescription.bufferView); + skin.inverseBindMatricesDescription.bufferView = + bufferView.object; + this.resources.setEntry(entryID, skin, description); + return true; + } + }, + + handleSampler: { + value: function(entryID, description, userInfo) { + // Save attribute entry + this.resources.setEntry(entryID, description, description); + return true; + } + }, + + handleTexture: { + value: function(entryID, description, userInfo) { + // Save attribute entry + this.resources.setEntry(entryID, null, description); + return true; + } + }, + + handleError: { + value: function(msg) { + + throw new Error(msg); + return true; + } + }, + + _delegate: { + value: new LoadDelegate, + writable: true + }, + + delegate: { + enumerable: true, + get: function() { + return this._delegate; + }, + set: function(value) { + this._delegate = value; + } + } + }); + + + // Loader + + var Context = function(rootObj, callback) { + this.rootObj = rootObj; + this.callback = callback; + }; + + var rootObj = new THREE.Object3D(); + + var self = this; + + var loader = Object.create(ThreeGLTFLoader); + loader.initWithPath(url); + loader.load(new Context(rootObj, function(obj) {theLoader.checkComplete()}), null); // Changed for Qt + + this.loader = loader; + this.callback = callback; + this.rootObj = rootObj; + return rootObj; +} + +THREE.glTFLoader.prototype.callLoadedCallback = function() { + var result = { + scene : this.rootObj, + cameras : this.loader.cameras, + lights: this.loader.lights, // Added for Qt + animations : this.loader.animations, + shaders : this.loader.shaders, + }; + + this.callback(result); +} + +THREE.glTFLoader.prototype.checkComplete = function() { + if (this.meshesLoaded == this.meshesRequested + && this.shadersLoaded == this.shadersRequested + && this.animationsLoaded == this.animationsRequested) + { + for (var i = 0; i < this.pendingMeshes.length; i++) { + var pending = this.pendingMeshes[i]; + pending.mesh.attachToNode(pending.node); + } + + for (var i = 0; i < this.animationsLoaded; i++) { + var animation = this.animations[i]; + this.loader.buildAnimation(animation); + } + + this.loader.createAnimations(); + this.loader.createMeshAnimations(this.rootObj); + THREE.glTFShaders.bindShaderParameters(this.rootObj); + + this.callLoadedCallback(); + } +} + + + + +// File:src/gltf/glTFLoaderUtils.js + +/** + * @author Tony Parisi / http://www.tonyparisi.com/ + */ + +THREE.GLTFLoaderUtils = Object.create(Object, { + + // errors + MISSING_DESCRIPTION: { value: "MISSING_DESCRIPTION" }, + INVALID_PATH: { value: "INVALID_PATH" }, + INVALID_TYPE: { value: "INVALID_TYPE" }, + XMLHTTPREQUEST_STATUS_ERROR: { value: "XMLHTTPREQUEST_STATUS_ERROR" }, + NOT_FOUND: { value: "NOT_FOUND" }, + // misc constants + ARRAY_BUFFER: { value: "ArrayBuffer" }, + + _streams : { value:{}, writable: true }, + + _streamsStatus: { value: {}, writable: true }, + + _resources: { value: {}, writable: true }, + + _resourcesStatus: { value: {}, writable: true }, + + // initialization + init: { + value: function() { + this._streams = {}; + this._streamsStatus = {}; + this._resources = {}; + this._resourcesStatus = {}; + } + }, + + //manage entries + _containsResource: { + enumerable: false, + value: function(resourceID) { + return this._resources[resourceID] ? true : false; + } + }, + + _storeResource: { + enumerable: false, + value: function(resourceID, resource) { + if (!resourceID) { + console.log("ERROR: entry does not contain id, cannot store"); + return; + } + + if (this._containsResource[resourceID]) { + console.log("WARNING: resource:"+resourceID+" is already stored, overriding"); + } + + this._resources[resourceID] = resource; + } + }, + + _getResource: { + enumerable: false, + value: function(resourceID) { + return this._resources[resourceID]; + } + }, + + _loadStream: { + value: function(path, type, delegate) { + + + + var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; + + function decodeDataUriText(isBase64, data) { + var result = decodeURIComponent(data); + if (isBase64) { + return atob(result); + } + return result; + } + + function decodeDataUriArrayBuffer(isBase64, data) { + var byteString = decodeDataUriText(isBase64, data); + var buffer = new ArrayBuffer(byteString.length); + var view = new Uint8Array(buffer); + for (var i = 0; i < byteString.length; i++) { + view[i] = byteString.charCodeAt(i); + } + return buffer; + } + + function decodeDataUri(dataUriRegexResult, responseType) { + responseType = typeof responseType !== 'undefined' ? responseType : ''; + var mimeType = dataUriRegexResult[1]; + var isBase64 = !!dataUriRegexResult[2]; + var data = dataUriRegexResult[3]; + + switch (responseType) { + case '': + case 'text': + return decodeDataUriText(isBase64, data); + case 'ArrayBuffer': + return decodeDataUriArrayBuffer(isBase64, data); + case 'blob': + var buffer = decodeDataUriArrayBuffer(isBase64, data); + return new Blob([buffer], { + type : mimeType + }); + case 'document': + var parser = new DOMParser(); + return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType); + case 'json': + return JSON.parse(decodeDataUriText(isBase64, data)); + default: + throw 'Unhandled responseType: ' + responseType; + } + } + + var dataUriRegexResult = dataUriRegex.exec(path); + if (dataUriRegexResult !== null) { + delegate.streamAvailable(path, decodeDataUri(dataUriRegexResult, type)); + return; + } + + var self = this; + + if (!type) { + delegate.handleError(THREE.GLTFLoaderUtils.INVALID_TYPE, null); + return; + } + + if (!path) { + delegate.handleError(THREE.GLTFLoaderUtils.INVALID_PATH); + return; + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', path, true); + xhr.responseType = (type === this.ARRAY_BUFFER) ? "arraybuffer" : "text"; + + //if this is not specified, 1 "big blob" scenes fails to load. + // Changed for Qt --> + //xhr.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT"); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (xhr.status == 200 || xhr.status == 206) { + delegate.streamAvailable(path, xhr.response); + } else { + console.error("XMLHttpRequest load failed") + delegate.handleError(THREE.GLTFLoaderUtils.XMLHTTPREQUEST_STATUS_ERROR, + xhr.status); + } + } + }; + // <-- Changed for Qt + xhr.send(null); + } + }, + + send: { value: 0, writable: true }, + requested: { value: 0, writable: true }, + + _handleRequest: { + value: function(request) { + var resourceStatus = this._resourcesStatus[request.id]; + if (resourceStatus) + { + this._resourcesStatus[request.id]++; + } + else + { + this._resourcesStatus[request.id] = 1; + } + + var streamStatus = this._streamsStatus[request.uri]; + if (streamStatus && streamStatus.status === "loading" ) + { + streamStatus.requests.push(request); + return; + } + + this._streamsStatus[request.uri] = { status : "loading", requests : [request] }; + + var self = this; + var processResourceDelegate = {}; + + processResourceDelegate.streamAvailable = function(path, res_) { + var streamStatus = self._streamsStatus[path]; + var requests = streamStatus.requests; + requests.forEach( function(req_) { + var subArray = res_.slice(req_.range[0], req_.range[1]); + var convertedResource = req_.delegate.convert(subArray, req_.ctx); + self._storeResource(req_.id, convertedResource); + req_.delegate.resourceAvailable(convertedResource, req_.ctx); + --self._resourcesStatus[req_.id]; + + }, this); + + delete self._streamsStatus[path]; + + }; + + processResourceDelegate.handleError = function(errorCode, info) { + request.delegate.handleError(errorCode, info); + } + + this._loadStream(request.uri, request.type, processResourceDelegate); + } + }, + + + _elementSizeForGLType: { + value: function(componentType, type) { + + var nElements = 0; + switch(type) { + case "SCALAR" : + nElements = 1; + break; + case "VEC2" : + nElements = 2; + break; + case "VEC3" : + nElements = 3; + break; + case "VEC4" : + nElements = 4; + break; + case "MAT2" : + nElements = 4; + break; + case "MAT3" : + nElements = 9; + break; + case "MAT4" : + nElements = 16; + break; + default : + console.log("Invalid type in _elementSizeForGLType:", type); //debugger; // Changed for Qt, debugger keyword is not supported + break; + } + + switch (componentType) { + case Context3D.FLOAT : // Changed for Qt + return Float32Array.BYTES_PER_ELEMENT * nElements; + case Context3D.UNSIGNED_BYTE : // Changed for Qt + return Uint8Array.BYTES_PER_ELEMENT * nElements; + case Context3D.UNSIGNED_SHORT : // Changed for Qt + return Uint16Array.BYTES_PER_ELEMENT * nElements; + default : + console.log("Invalid componentType in _elementSizeForGLType:", componentType); //debugger; // Changed for Qt, debugger keyword is not supported + return null; + } + } + }, + + _handleWrappedBufferViewResourceLoading: { + value: function(wrappedBufferView, delegate, ctx) { + var bufferView = wrappedBufferView.bufferView; + var buffer = bufferView.buffer; + var byteOffset = wrappedBufferView.byteOffset + bufferView.description.byteOffset; + var range = [byteOffset , (this._elementSizeForGLType(wrappedBufferView.componentType, wrappedBufferView.type) * wrappedBufferView.count) + byteOffset]; + + this._handleRequest({ "id" : wrappedBufferView.id, + "range" : range, + "type" : buffer.description.type, + "uri" : buffer.description.uri, + "delegate" : delegate, + "ctx" : ctx }, null); + } + }, + + getBuffer: { + + value: function(wrappedBufferView, delegate, ctx) { + + var savedBuffer = this._getResource(wrappedBufferView.id); + if (savedBuffer) { + return savedBuffer; + } else { + this._handleWrappedBufferViewResourceLoading(wrappedBufferView, delegate, ctx); + } + + return null; + } + }, + + getFile: { + + value: function(request, delegate, ctx) { + + request.delegate = delegate; + request.ctx = ctx; + + this._handleRequest({ "id" : request.id, + "uri" : request.uri, + "range" : [0], + "type" : "text", + "delegate" : delegate, + "ctx" : ctx }, null); + + return null; + } + }, +}); + +// File:src/gltf/glTFShaders.js + +/** + * @author Tony Parisi / http://www.tonyparisi.com/ + */ + +THREE.glTFShaders = ( function () { + + var shaders = []; + + return { + add : function(shader) { + shaders.push(shader); + }, + + remove: function(shader) { + + var i = shaders.indexOf(shader); + + if ( i !== -1 ) { + shaders.splice( i, 1 ); + } + }, + + removeAll: function(shader) { + + // probably want to clean up the shaders, too, but not for now + shaders = []; + }, + + bindShaderParameters: function(scene) { + for (var i = 0; i < shaders.length; i++) // Changed for Qt + { + shaders[i].bindParameters(scene); + } + }, + + update : function(scene, camera) { + for (var i = 0; i < shaders.length; i++) // Changed for Qt + { + shaders[i].update(scene, camera); + } + }, + }; +})(); + +// Construction/initialization +THREE.glTFShader = function(material, params, object, scene) { + this.material = material; + this.parameters = params.parameters; + this.program = params.program; + this.joints = params.joints; + this.object = object; + this.semantics = {}; + this.m4 = new THREE.Matrix4; +} + + +// bindParameters - connect the uniform values to their source parameters +THREE.glTFShader.prototype.bindParameters = function(scene) { + + function findObject(o, param) { + if (o.glTFID == param.source) { + param.sourceObject = o; + } + } + + for (var uniform in this.program.uniforms) { + var pname = this.program.uniforms[uniform]; + var param = this.parameters[pname]; + if (param.semantic) { + + if (param.source) { + scene.traverse(function(o) { findObject(o, param)}); + } + else { + param.sourceObject = this.object; + } + + param.uniform = this.material.uniforms[uniform]; + this.semantics[pname] = param; + + if (param.semantic == "JOINTMATRIX") { + var m4v = param.uniform.value; + for (var vi = 0; vi < m4v.length; vi++) { + m4v[vi] = this.joints[vi].matrix; + } + } + //console.log("parameter:", pname, param ); + } + } + +} + +// Update - update all the uniform values +THREE.glTFShader.prototype.update = function(scene, camera) { + + for (var sname in this.semantics) { + var semantic = this.semantics[sname]; + if (semantic && semantic.sourceObject) { + switch (semantic.semantic) { + case "MODELVIEW" : + var m4 = semantic.uniform.value; + m4.multiplyMatrices( camera.matrixWorldInverse, + semantic.sourceObject.matrixWorld ); + break; + + case "MODELVIEWINVERSETRANSPOSE" : + var m3 = semantic.uniform.value; + this.m4.multiplyMatrices( camera.matrixWorldInverse, + semantic.sourceObject.matrixWorld ); + m3.getNormalMatrix( this.m4 ); + break; + + case "PROJECTION" : + var m4 = semantic.uniform.value; + m4.copy(camera.projectionMatrix); + break; + + case "JOINTMATRIX" : + /* + var m4v = semantic.uniform.value; + for (var mi = 0; mi < m4v.length; mi++) { + m4v[mi].copy(this.joints[mi].matrixWorld); + } + */ + //console.log("Joint:", semantic) + break; + + default : + //console.log("Unhandled shader semantic", semantic) + break; + } + } + } +} + diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/glcode.js b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/glcode.js index 082d605d951..a5f14f40558 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/glcode.js +++ b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/threejs/glcode.js @@ -9,7 +9,6 @@ function initializeGL(canvas) { camera.position.z = 5; var material = new THREE.MeshBasicMaterial({ color: 0x80c342, - ambient: 0x000000, shading: THREE.SmoothShading }); var cubeGeometry = new THREE.BoxGeometry(1, 1, 1); cube = new THREE.Mesh(cubeGeometry, material); diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/wizard.json index 87b6079287b..365f30e22a8 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/qtcanvas3dapplication/wizard.json @@ -7,7 +7,7 @@ "trDisplayName": "Qt Canvas 3D Application", "trDisplayCategory": "Application", "icon": "3dapplication.png", - "featuresRequired": [ "QtSupport.Wizards.FeatureQtCanvas3d" ], + "featuresRequired": [ "QtSupport.Wizards.FeatureQtCanvas3d1.1" ], "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0}", "options": diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index acc3eb915ad..d2529d527c3 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -441,10 +441,10 @@ QSet BaseQtVersion::availableFeatures() const return features; features.insert(Constants::FEATURE_QT_3D); - features.insert(Constants::FEATURE_QT_CANVAS3D); features.unite(versionedIds(Constants::FEATURE_QT_QUICK_PREFIX, 2, 5)); features.unite(versionedIds(Constants::FEATURE_QT_QUICK_CONTROLS_PREFIX, 1, 4)); + features.unite(versionedIds(Constants::FEATURE_QT_CANVAS3D_PREFIX, 1, 0)); if (qtVersion().matches(5, 5)) return features; @@ -452,6 +452,7 @@ QSet BaseQtVersion::availableFeatures() const features.unite(versionedIds(Constants::FEATURE_QT_QUICK_PREFIX, 2, 6)); features.unite(versionedIds(Constants::FEATURE_QT_QUICK_CONTROLS_PREFIX, 1, 5)); features.unite(versionedIds(Constants::FEATURE_QT_LABS_CONTROLS_PREFIX, 1, 0)); + features.unite(versionedIds(Constants::FEATURE_QT_CANVAS3D_PREFIX, 1, 1)); if (qtVersion().matches(5, 6)) return features; diff --git a/src/plugins/qtsupport/qtsupportconstants.h b/src/plugins/qtsupport/qtsupportconstants.h index a27f94ef888..551e201c2e3 100644 --- a/src/plugins/qtsupport/qtsupportconstants.h +++ b/src/plugins/qtsupport/qtsupportconstants.h @@ -54,7 +54,7 @@ const char FEATURE_QT_LABS_CONTROLS_PREFIX[] = "QtSupport.Wizards.FeatureQt.labs const char FEATURE_QT_QUICK_UI_FILES[] = "QtSupport.Wizards.FeatureQtQuick.UiFiles"; const char FEATURE_QT_WEBKIT[] = "QtSupport.Wizards.FeatureQtWebkit"; const char FEATURE_QT_3D[] = "QtSupport.Wizards.FeatureQt3d"; -const char FEATURE_QT_CANVAS3D[] = "QtSupport.Wizards.FeatureQtCanvas3d"; +const char FEATURE_QT_CANVAS3D_PREFIX[] = "QtSupport.Wizards.FeatureQtCanvas3d"; const char FEATURE_QT_CONSOLE[] = "QtSupport.Wizards.FeatureQtConsole"; const char FEATURE_MOBILE[] = "QtSupport.Wizards.FeatureMobile"; const char FEATURE_DESKTOP[] = "QtSupport.Wizards.FeatureDesktop"; From d9a57623afb7331e9c8ec064b3f047eb32b73b94 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Sat, 9 Jan 2016 20:57:11 +0200 Subject: [PATCH 5/9] Clang: Add some tests for lambda highlighting Test that local, argument and member variables are highlighted correctly when referenced inside a lambda struct LambdaTester { int member = 0; void func() { const int var = 42, var2 = 84; auto lambda = [var, this](int input) { return var + input + member; // All variables here }; lambda(var2); } }; Change-Id: I3b7b86c57a91f0f254715770dd870033be928b28 Reviewed-by: Marco Bubke --- .../unittest/highlightinginformationstest.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/unit/unittest/highlightinginformationstest.cpp b/tests/unit/unittest/highlightinginformationstest.cpp index ce4b8a173e2..379319ec1d3 100644 --- a/tests/unit/unittest/highlightinginformationstest.cpp +++ b/tests/unit/unittest/highlightinginformationstest.cpp @@ -976,6 +976,27 @@ TEST_F(HighlightingInformations, LambdaCapture) ASSERT_THAT(infos[4], HasType(HighlightingType::LocalVariable)); } +TEST_F(HighlightingInformations, LambdaCapturedVarUsage) +{ + const auto infos = translationUnit.highlightingInformationsInRange(sourceRange(443, 41)); + + ASSERT_THAT(infos[1], HasType(HighlightingType::LocalVariable)); +} + +TEST_F(HighlightingInformations, LambdaArgumentUsage) +{ + const auto infos = translationUnit.highlightingInformationsInRange(sourceRange(443, 41)); + + ASSERT_THAT(infos[3], HasType(HighlightingType::LocalVariable)); +} + +TEST_F(HighlightingInformations, LambdaFieldUsage) +{ + const auto infos = translationUnit.highlightingInformationsInRange(sourceRange(443, 41)); + + ASSERT_THAT(infos[5], HasType(HighlightingType::Field)); +} + Data *HighlightingInformations::d; void HighlightingInformations::SetUpTestCase() From b590642a859fba9290e031cd745ca5be24662408 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Fri, 26 Feb 2016 16:29:01 +0100 Subject: [PATCH 6/9] FancyLineEdit: Implement flat style Follow Diana's guide for flat themes: http://blog.qt.io/blog/2015/02/09/qt-creator-flat-style-ui-design-new- light-theme/ Change-Id: Ia15f266dbf9138e9cac61e04265970543b2505bf Reviewed-by: Alessandro Portale --- src/plugins/coreplugin/manhattanstyle.cpp | 42 ++++++++++++++--------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index 83d821985ae..3c16e239d2b 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -130,9 +130,16 @@ public: StyleAnimator animator; }; +QString lineEditImageFileName(const QString &pngFileName) +{ + return Utils::creatorTheme()->widgetStyle() == Utils::Theme::StyleDefault + ? StyleHelper::dpiSpecificImageFile(pngFileName) + : QString(); +} + ManhattanStylePrivate::ManhattanStylePrivate() : - lineeditImage(StyleHelper::dpiSpecificImageFile(QStringLiteral(":/core/images/inputfield.png"))), - lineeditImage_disabled(StyleHelper::dpiSpecificImageFile(QStringLiteral(":/core/images/inputfield_disabled.png"))), + lineeditImage(lineEditImageFileName(QLatin1String(":/core/images/inputfield.png"))), + lineeditImage_disabled(lineEditImageFileName(QLatin1String(":/core/images/inputfield_disabled.png"))), extButtonPixmap(Core::Icons::TOOLBAR_EXTENSION.pixmap()), closeButtonPixmap(Core::Icons::CLOSE_FOREGROUND.pixmap()) { @@ -451,24 +458,25 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption painter->save(); // Fill the line edit background - QRect filledRect = option->rect.adjusted(1, 1, -1, -1); - painter->setBrushOrigin(filledRect.topLeft()); - painter->fillRect(filledRect, option->palette.base()); + QRectF backgroundRect = option->rect; + if (Utils::creatorTheme()->widgetStyle() == Utils::Theme::StyleDefault) { + backgroundRect.adjust(1, 1, -1, -1); + painter->setBrushOrigin(backgroundRect.topLeft()); + painter->fillRect(backgroundRect, option->palette.base()); - if (option->state & State_Enabled) - StyleHelper::drawCornerImage(d->lineeditImage, painter, option->rect, 5, 5, 5, 5); - else - StyleHelper::drawCornerImage(d->lineeditImage_disabled, painter, option->rect, 5, 5, 5, 5); + const bool enabled = option->state & State_Enabled; + StyleHelper::drawCornerImage(enabled ? d->lineeditImage : d->lineeditImage_disabled, + painter, option->rect, 5, 5, 5, 5); + } else { + painter->fillRect(backgroundRect, option->palette.base()); + } - if (option->state & State_HasFocus || option->state & State_MouseOver) { + const bool hasFocus = state & State_HasFocus; + if (hasFocus || state & State_MouseOver) { QColor hover = StyleHelper::baseColor(); - if (state & State_HasFocus) - hover.setAlpha(100); - else - hover.setAlpha(50); - - painter->setPen(QPen(hover, 1)); - painter->drawRect(QRectF(option->rect).adjusted(1.5, 1.5, -1.5, -1.5)); + hover.setAlpha(hasFocus ? 100 : 50); + painter->setPen(QPen(hover, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); + painter->drawRect(backgroundRect.adjusted(0.5, 0.5, -0.5, -0.5)); } painter->restore(); } From 83e18c127c5c9b811eeb088c37cd94b70e166482 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Fri, 26 Feb 2016 17:50:38 +0100 Subject: [PATCH 7/9] Clang: Introduce warning configuration per project Change-Id: I5b9a330274e6f72b0786259eb25aa454877d4eef Reviewed-by: Leena Miettinen Reviewed-by: Marco Bubke --- src/plugins/clangcodemodel/clangcodemodel.pro | 6 + src/plugins/clangcodemodel/clangcodemodel.qbs | 5 + .../clangcodemodel/clangcodemodelplugin.cpp | 15 ++ .../clangeditordocumentprocessor.cpp | 22 ++- .../clangcodemodel/clangprojectsettings.cpp | 91 ++++++++++ .../clangcodemodel/clangprojectsettings.h | 66 ++++++++ .../clangprojectsettingswidget.cpp | 160 ++++++++++++++++++ .../clangprojectsettingswidget.h | 70 ++++++++ .../clangprojectsettingswidget.ui | 29 ++++ .../cpptools/clangdiagnosticconfigsmodel.cpp | 21 ++- .../cpptools/clangdiagnosticconfigsmodel.h | 11 +- .../cpptools/clangdiagnosticconfigswidget.cpp | 100 ++++++++--- .../cpptools/clangdiagnosticconfigswidget.h | 30 +++- .../cpptools/clangdiagnosticconfigswidget.ui | 6 +- src/plugins/cpptools/cppcodemodelsettings.cpp | 2 +- .../cpptools/cppcodemodelsettingspage.cpp | 3 +- .../cpptools/cppcodemodelsettingspage.h | 3 +- 17 files changed, 600 insertions(+), 40 deletions(-) create mode 100644 src/plugins/clangcodemodel/clangprojectsettings.cpp create mode 100644 src/plugins/clangcodemodel/clangprojectsettings.h create mode 100644 src/plugins/clangcodemodel/clangprojectsettingswidget.cpp create mode 100644 src/plugins/clangcodemodel/clangprojectsettingswidget.h create mode 100644 src/plugins/clangcodemodel/clangprojectsettingswidget.ui diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro index 17a63638e52..ff3ce0aea49 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.pro +++ b/src/plugins/clangcodemodel/clangcodemodel.pro @@ -29,6 +29,8 @@ SOURCES += \ clanghighlightingmarksreporter.cpp \ clangmodelmanagersupport.cpp \ clangpreprocessorassistproposalitem.cpp \ + clangprojectsettings.cpp \ + clangprojectsettingswidget.cpp \ clangtextmark.cpp \ clangutils.cpp @@ -58,9 +60,13 @@ HEADERS += \ clangisdiagnosticrelatedtolocation.h \ clangmodelmanagersupport.h \ clangpreprocessorassistproposalitem.h \ + clangprojectsettings.h \ + clangprojectsettingswidget.h \ clangtextmark.h \ clangutils.h +FORMS += clangprojectsettingswidget.ui + RESOURCES += \ clangcodemodel.qrc diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index 9459aa83911..3109e20df48 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -84,6 +84,11 @@ QtcPlugin { "clangmodelmanagersupport.h", "clangpreprocessorassistproposalitem.cpp", "clangpreprocessorassistproposalitem.h", + "clangprojectsettings.cpp", + "clangprojectsettings.h", + "clangprojectsettingswidget.cpp", + "clangprojectsettingswidget.h", + "clangprojectsettingswidget.ui", "clangtextmark.cpp", "clangtextmark.h", "clangutils.cpp", diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index f81eb2d7340..285042cc315 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -26,6 +26,7 @@ #include "clangcodemodelplugin.h" #include "clangconstants.h" +#include "clangprojectsettingswidget.h" #ifdef WITH_TESTS # include "test/clangcodecompletion_test.h" @@ -33,6 +34,10 @@ #include +#include +#include +#include + #include namespace ClangCodeModel { @@ -48,6 +53,15 @@ void initializeTextMarks() Utils::Theme::ClangCodeModel_Error_TextMarkColor); } +void addProjectPanelWidget() +{ + auto panelFactory = new ProjectExplorer::ProjectPanelFactory(); + panelFactory->setPriority(60); + panelFactory->setDisplayName(ClangProjectSettingsWidget::tr("Clang Code Model")); + panelFactory->setSimpleCreateWidgetFunction(QIcon()); + ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); +} + } // anonymous namespace bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *errorMessage) @@ -58,6 +72,7 @@ bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *err CppTools::CppModelManager::instance()->activateClangCodeModel(&m_modelManagerSupportProvider); initializeTextMarks(); + addProjectPanelWidget(); return true; } diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 38f7cb2d51f..6428abcc5b4 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -30,11 +30,14 @@ #include "clangfixitoperation.h" #include "clangfixitoperationsextractor.h" #include "clanghighlightingmarksreporter.h" +#include "clangprojectsettings.h" #include "clangutils.h" #include #include +#include +#include #include #include #include @@ -340,10 +343,25 @@ static QStringList languageOptions(const QString &filePath, CppTools::ProjectPar return builder.options(); } +static QStringList warningOptions(CppTools::ProjectPart *projectPart) +{ + if (projectPart && projectPart->project) { + ClangProjectSettings projectSettings(projectPart->project); + if (!projectSettings.useGlobalWarningConfig()) { + const Core::Id warningConfigId = projectSettings.warningConfigId(); + const CppTools::ClangDiagnosticConfigsModel configsModel( + CppTools::codeModelSettings()->clangCustomDiagnosticConfigs()); + if (configsModel.hasConfigWithId(warningConfigId)) + return configsModel.configWithId(warningConfigId).commandLineOptions(); + } + } + + return CppTools::codeModelSettings()->clangDiagnosticConfig().commandLineOptions(); +} + static QStringList fileArguments(const QString &filePath, CppTools::ProjectPart *projectPart) { - return languageOptions(filePath, projectPart) - + CppTools::codeModelSettings()->clangDiagnosticConfig().commandLineOptions(); + return languageOptions(filePath, projectPart) + warningOptions(projectPart); } ClangBackEnd::FileContainer diff --git a/src/plugins/clangcodemodel/clangprojectsettings.cpp b/src/plugins/clangcodemodel/clangprojectsettings.cpp new file mode 100644 index 00000000000..a5311513b8c --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettings.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "clangprojectsettings.h" + +namespace ClangCodeModel { +namespace Internal { + +static QString useGlobalWarningConfigKey() +{ return QStringLiteral("ClangCodeModel.UseGlobalWarningConfig"); } + +static QString warningConfigIdKey() +{ return QStringLiteral("ClangCodeModel.WarningConfigId"); } + +ClangProjectSettings::ClangProjectSettings(ProjectExplorer::Project *project) + : m_project(project) +{ + load(); + + connect(project, &ProjectExplorer::Project::settingsLoaded, + this, &ClangProjectSettings::load); + connect(project, &ProjectExplorer::Project::aboutToSaveSettings, + this, &ClangProjectSettings::store); +} + +Core::Id ClangProjectSettings::warningConfigId() const +{ + return m_warningConfigId; +} + +void ClangProjectSettings::setWarningConfigId(const Core::Id &customConfigId) +{ + m_warningConfigId = customConfigId; +} + +bool ClangProjectSettings::useGlobalWarningConfig() const +{ + return m_useGlobalWarningConfig; +} + +void ClangProjectSettings::setUseGlobalWarningConfig(bool useGlobalWarningConfig) +{ + m_useGlobalWarningConfig = useGlobalWarningConfig; +} + +void ClangProjectSettings::load() +{ + const QVariant useGlobalConfigVariant = m_project->namedSettings(useGlobalWarningConfigKey()); + const bool useGlobalConfig = useGlobalConfigVariant.isValid() + ? useGlobalConfigVariant.toBool() + : true; + + setUseGlobalWarningConfig(useGlobalConfig); + setWarningConfigId(Core::Id::fromSetting(m_project->namedSettings(warningConfigIdKey()))); +} + +void ClangProjectSettings::store() +{ + m_project->setNamedSettings(useGlobalWarningConfigKey(), useGlobalWarningConfig()); + m_project->setNamedSettings(warningConfigIdKey(), warningConfigId().toSetting()); +} + +} // namespace Internal +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangprojectsettings.h b/src/plugins/clangcodemodel/clangprojectsettings.h new file mode 100644 index 00000000000..5ffe12aa462 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettings.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +namespace ClangCodeModel { +namespace Internal { + +class ClangProjectSettings: public QObject +{ + Q_OBJECT + +public: + ClangProjectSettings(ProjectExplorer::Project *project); + + bool useGlobalWarningConfig() const; + void setUseGlobalWarningConfig(bool useGlobalWarningConfig); + + Core::Id warningConfigId() const; + void setWarningConfigId(const Core::Id &warningConfigId); + +public slots: + void load(); + void store(); + +private: + ProjectExplorer::Project *m_project; + bool m_useGlobalWarningConfig = true; + Core::Id m_warningConfigId; +}; + +} // namespace Internal +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangprojectsettingswidget.cpp b/src/plugins/clangcodemodel/clangprojectsettingswidget.cpp new file mode 100644 index 00000000000..b3c01f18310 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettingswidget.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "clangprojectsettingswidget.h" + +#include "clangprojectsettings.h" + +#include + +#include +#include +#include +#include + +static const char GLOBAL_PROXY_CONFIG_ID[] = "globalProxyConfig"; + +namespace ClangCodeModel { +namespace Internal { + +static CppTools::ClangDiagnosticConfig +createConfigRepresentingGlobalSetting(const CppTools::ClangDiagnosticConfig &baseConfig) +{ + CppTools::ClangDiagnosticConfig config = baseConfig; + config.setId(GLOBAL_PROXY_CONFIG_ID); + + QString displayName = config.displayName(); + if (config.isReadOnly()) + displayName = CppTools::ClangDiagnosticConfigsModel::displayNameWithBuiltinIndication(config); + displayName = ClangProjectSettingsWidget::tr("Global setting (%1)").arg(displayName); + + config.setDisplayName(displayName); + config.setIsReadOnly(true); + + return config; +} + +static Core::Id globalConfigId(const CppTools::CppCodeModelSettings &settings, + const CppTools::ClangDiagnosticConfigsModel &model) +{ + const Core::Id configId = settings.clangDiagnosticConfigId(); + + if (model.hasConfigWithId(configId)) + return configId; + + return model.at(0).id(); // Config saved in the settings was removed, fallback to first. +} + +static CppTools::ClangDiagnosticConfigsModel +createConfigsModelWithGlobalProxyConfig(const CppTools::CppCodeModelSettings &settings) +{ + using namespace CppTools; + ClangDiagnosticConfigsModel configsModel(settings.clangCustomDiagnosticConfigs()); + + const Core::Id globalId = globalConfigId(settings, configsModel); + const ClangDiagnosticConfig globalConfig = configsModel.configWithId(globalId); + const ClangDiagnosticConfig globalProxy + = createConfigRepresentingGlobalSetting(globalConfig); + + configsModel.prepend(globalProxy); + + return configsModel; +} + +static Core::Id configIdForProject(const ClangProjectSettings &projectSettings) +{ + return projectSettings.useGlobalWarningConfig() + ? Core::Id(GLOBAL_PROXY_CONFIG_ID) + : projectSettings.warningConfigId(); +} + +ClangProjectSettingsWidget::ClangProjectSettingsWidget(ProjectExplorer::Project *project) + : m_projectSettings(project) +{ + m_ui.setupUi(this); + + using namespace CppTools; + + m_diagnosticConfigWidget = new ClangDiagnosticConfigsWidget; + m_diagnosticConfigWidget->setConfigWithUndecoratedDisplayName(Core::Id(GLOBAL_PROXY_CONFIG_ID)); + refreshDiagnosticConfigsWidgetFromSettings(); + connectToCppCodeModelSettingsChanged(); + connect(m_diagnosticConfigWidget.data(), &ClangDiagnosticConfigsWidget::currentConfigChanged, + this, &ClangProjectSettingsWidget::onCurrentWarningConfigChanged); + connect(m_diagnosticConfigWidget.data(), &ClangDiagnosticConfigsWidget::customConfigsChanged, + this, &ClangProjectSettingsWidget::onCustomWarningConfigsChanged); + + m_ui.diagnosticConfigurationGroupBox->layout()->addWidget(m_diagnosticConfigWidget); +} + +void ClangProjectSettingsWidget::onCurrentWarningConfigChanged(const Core::Id ¤tConfigId) +{ + const bool useGlobalConfig = currentConfigId == Core::Id(GLOBAL_PROXY_CONFIG_ID); + + m_projectSettings.setUseGlobalWarningConfig(useGlobalConfig); + m_projectSettings.setWarningConfigId(currentConfigId); + + m_projectSettings.store(); +} + +void ClangProjectSettingsWidget::onCustomWarningConfigsChanged( + const CppTools::ClangDiagnosticConfigs &customConfigs) +{ + disconnectFromCppCodeModelSettingsChanged(); + + const QSharedPointer codeModelSettings + = CppTools::codeModelSettings(); + codeModelSettings->setClangCustomDiagnosticConfigs(customConfigs); + codeModelSettings->toSettings(Core::ICore::settings()); + + connectToCppCodeModelSettingsChanged(); +} + +void ClangProjectSettingsWidget::refreshDiagnosticConfigsWidgetFromSettings() +{ + m_diagnosticConfigWidget->refresh( + createConfigsModelWithGlobalProxyConfig(*CppTools::codeModelSettings()), + configIdForProject(m_projectSettings)); +} + +void ClangProjectSettingsWidget::connectToCppCodeModelSettingsChanged() +{ + connect(CppTools::codeModelSettings().data(), &CppTools::CppCodeModelSettings::changed, + this, &ClangProjectSettingsWidget::refreshDiagnosticConfigsWidgetFromSettings); +} + +void ClangProjectSettingsWidget::disconnectFromCppCodeModelSettingsChanged() +{ + disconnect(CppTools::codeModelSettings().data(), &CppTools::CppCodeModelSettings::changed, + this, &ClangProjectSettingsWidget::refreshDiagnosticConfigsWidgetFromSettings); +} + +} // namespace Internal +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangprojectsettingswidget.h b/src/plugins/clangcodemodel/clangprojectsettingswidget.h new file mode 100644 index 00000000000..944a0433298 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettingswidget.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#pragma once + +#include "ui_clangprojectsettingswidget.h" + +#include "clangprojectsettings.h" + +#include + +#include + +namespace ProjectExplorer { class Project; } +namespace CppTools { class ClangDiagnosticConfigsWidget; } + +namespace ClangCodeModel { +namespace Internal { + +class ClangProjectSettingsWidget: public QWidget +{ + Q_OBJECT + +public: + explicit ClangProjectSettingsWidget(ProjectExplorer::Project *project); + +private slots: + void onCurrentWarningConfigChanged(const Core::Id ¤tConfigId); + void onCustomWarningConfigsChanged(const CppTools::ClangDiagnosticConfigs &customConfigs); + +private: + void refreshDiagnosticConfigsWidgetFromSettings(); + void connectToCppCodeModelSettingsChanged(); + void disconnectFromCppCodeModelSettingsChanged(); + +private: + Ui::ClangProjectSettingsWidget m_ui; + ClangProjectSettings m_projectSettings; + QPointer m_diagnosticConfigWidget; +}; + +} // namespace Internal +} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangprojectsettingswidget.ui b/src/plugins/clangcodemodel/clangprojectsettingswidget.ui new file mode 100644 index 00000000000..085294b8406 --- /dev/null +++ b/src/plugins/clangcodemodel/clangprojectsettingswidget.ui @@ -0,0 +1,29 @@ + + + ClangCodeModel::Internal::ClangProjectSettingsWidget + + + true + + + + 0 + 0 + 814 + 330 + + + + + + + Warnings + + + + + + + + + diff --git a/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp b/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp index ccd78141d87..4ecdda76f5f 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfigsmodel.cpp @@ -32,7 +32,6 @@ #include namespace CppTools { -namespace Internal { static void addConfigForQuestionableConstructs(ClangDiagnosticConfigsModel &model) { @@ -110,6 +109,11 @@ const ClangDiagnosticConfig &ClangDiagnosticConfigsModel::at(int index) const return m_diagnosticConfigs.at(index); } +void ClangDiagnosticConfigsModel::prepend(const ClangDiagnosticConfig &config) +{ + m_diagnosticConfigs.prepend(config); +} + void ClangDiagnosticConfigsModel::appendOrUpdate(const ClangDiagnosticConfig &config) { const int index = indexOfConfig(config.id()); @@ -130,11 +134,25 @@ ClangDiagnosticConfigs ClangDiagnosticConfigsModel::configs() const return m_diagnosticConfigs; } +bool ClangDiagnosticConfigsModel::hasConfigWithId(const Core::Id &id) const +{ + return indexOfConfig(id) != -1; +} + const ClangDiagnosticConfig &ClangDiagnosticConfigsModel::configWithId(const Core::Id &id) const { return m_diagnosticConfigs.at(indexOfConfig(id)); } +QString +ClangDiagnosticConfigsModel::displayNameWithBuiltinIndication(const ClangDiagnosticConfig &config) +{ + return config.isReadOnly() + ? QCoreApplication::translate("ClangDiagnosticConfigsModel", "%1 [built-in]") + .arg(config.displayName()) + : config.displayName(); +} + int ClangDiagnosticConfigsModel::indexOfConfig(const Core::Id &id) const { return Utils::indexOf(m_diagnosticConfigs, [&](const ClangDiagnosticConfig &config) { @@ -142,5 +160,4 @@ int ClangDiagnosticConfigsModel::indexOfConfig(const Core::Id &id) const }); } -} // namespace Internal } // namespace CppTools diff --git a/src/plugins/cpptools/clangdiagnosticconfigsmodel.h b/src/plugins/cpptools/clangdiagnosticconfigsmodel.h index ffccd0868a2..14ef823f3d2 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigsmodel.h +++ b/src/plugins/cpptools/clangdiagnosticconfigsmodel.h @@ -25,25 +25,31 @@ #pragma once +#include "cpptools_global.h" + #include "clangdiagnosticconfig.h" namespace CppTools { -namespace Internal { -class ClangDiagnosticConfigsModel +class CPPTOOLS_EXPORT ClangDiagnosticConfigsModel { public: + ClangDiagnosticConfigsModel() = default; ClangDiagnosticConfigsModel(const ClangDiagnosticConfigs &customConfigs); int size() const; const ClangDiagnosticConfig &at(int index) const; + void prepend(const ClangDiagnosticConfig &config); void appendOrUpdate(const ClangDiagnosticConfig &config); void removeConfigWithId(const Core::Id &id); ClangDiagnosticConfigs configs() const; + bool hasConfigWithId(const Core::Id &id) const; const ClangDiagnosticConfig &configWithId(const Core::Id &id) const; + static QString displayNameWithBuiltinIndication(const ClangDiagnosticConfig &config); + private: int indexOfConfig(const Core::Id &id) const; @@ -51,5 +57,4 @@ private: ClangDiagnosticConfigs m_diagnosticConfigs; }; -} // namespace Internal } // namespace CppTools diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp index fbecf049721..55213d4f246 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp @@ -34,28 +34,23 @@ #include namespace CppTools { -namespace Internal { ClangDiagnosticConfigsWidget::ClangDiagnosticConfigsWidget( - const ClangDiagnosticConfigs &customConfigs, + const ClangDiagnosticConfigsModel &diagnosticConfigsModel, const Core::Id &configToSelect, QWidget *parent) : QWidget(parent) , m_ui(new Ui::ClangDiagnosticConfigsWidget) - , m_diagnosticConfigsModel(customConfigs) + , m_diagnosticConfigsModel(diagnosticConfigsModel) { m_ui->setupUi(this); - connect(m_ui->configChooserComboBox, - static_cast(&QComboBox::currentIndexChanged), - this, - &ClangDiagnosticConfigsWidget::onCurrentConfigChanged); + connectConfigChooserCurrentIndex(); connect(m_ui->copyButton, &QPushButton::clicked, this, &ClangDiagnosticConfigsWidget::onCopyButtonClicked); connect(m_ui->removeButton, &QPushButton::clicked, this, &ClangDiagnosticConfigsWidget::onRemoveButtonClicked); - connect(m_ui->diagnosticOptionsTextEdit->document(), &QTextDocument::contentsChanged, - this, &ClangDiagnosticConfigsWidget::onDiagnosticOptionsEdited); + connectDiagnosticOptionsChanged(); syncWidgetsToModel(configToSelect); } @@ -68,6 +63,8 @@ ClangDiagnosticConfigsWidget::~ClangDiagnosticConfigsWidget() void ClangDiagnosticConfigsWidget::onCurrentConfigChanged(int) { syncOtherWidgetsToComboBox(); + + emit currentConfigChanged(currentConfigId()); } static ClangDiagnosticConfig createCustomConfig(const ClangDiagnosticConfig &config, @@ -95,6 +92,7 @@ void ClangDiagnosticConfigsWidget::onCopyButtonClicked() if (diaglogAccepted) { const ClangDiagnosticConfig customConfig = createCustomConfig(config, newName); m_diagnosticConfigsModel.appendOrUpdate(customConfig); + emit customConfigsChanged(customConfigs()); syncConfigChooserToModel(customConfig.id()); m_ui->diagnosticOptionsTextEdit->setFocus(); @@ -104,6 +102,7 @@ void ClangDiagnosticConfigsWidget::onCopyButtonClicked() void ClangDiagnosticConfigsWidget::onRemoveButtonClicked() { m_diagnosticConfigsModel.removeConfigWithId(currentConfigId()); + emit customConfigsChanged(customConfigs()); syncConfigChooserToModel(); } @@ -119,6 +118,7 @@ void ClangDiagnosticConfigsWidget::onDiagnosticOptionsEdited() updatedConfig.setCommandLineOptions(updatedCommandLine); m_diagnosticConfigsModel.appendOrUpdate(updatedConfig); + emit customConfigsChanged(customConfigs()); } void ClangDiagnosticConfigsWidget::syncWidgetsToModel(const Core::Id &configToSelect) @@ -127,29 +127,40 @@ void ClangDiagnosticConfigsWidget::syncWidgetsToModel(const Core::Id &configToSe syncOtherWidgetsToComboBox(); } -static QString adaptedDisplayName(const ClangDiagnosticConfig &config) +static QString displayNameWithBuiltinIndication(const ClangDiagnosticConfig &config, + const Core::Id &exceptionalConfig) { - return config.isReadOnly() - ? QObject::tr("%1 [built-in]").arg(config.displayName()) - : config.displayName(); + if (exceptionalConfig == config.id()) + return config.displayName(); + + return ClangDiagnosticConfigsModel::displayNameWithBuiltinIndication(config); } void ClangDiagnosticConfigsWidget::syncConfigChooserToModel(const Core::Id &configToSelect) { + disconnectConfigChooserCurrentIndex(); + + const int previousCurrentIndex = m_ui->configChooserComboBox->currentIndex(); m_ui->configChooserComboBox->clear(); - int currentIndex = -1; + int configToSelectIndex = -1; const int size = m_diagnosticConfigsModel.size(); for (int i = 0; i < size; ++i) { const ClangDiagnosticConfig &config = m_diagnosticConfigsModel.at(i); - m_ui->configChooserComboBox->addItem(adaptedDisplayName(config), config.id().toSetting()); + const QString displayName + = displayNameWithBuiltinIndication(config, m_configWithUndecoratedDisplayName); + m_ui->configChooserComboBox->addItem(displayName, config.id().toSetting()); if (configToSelect == config.id()) - currentIndex = i; + configToSelectIndex = i; } - if (currentIndex != -1) - m_ui->configChooserComboBox->setCurrentIndex(currentIndex); + connectConfigChooserCurrentIndex(); + + if (configToSelectIndex != -1) + m_ui->configChooserComboBox->setCurrentIndex(configToSelectIndex); + else if (previousCurrentIndex != m_ui->configChooserComboBox->currentIndex()) + emit currentConfigChanged(currentConfigId()); } void ClangDiagnosticConfigsWidget::syncOtherWidgetsToComboBox() @@ -164,7 +175,7 @@ void ClangDiagnosticConfigsWidget::syncOtherWidgetsToComboBox() // Update child widgets const QString commandLineOptions = config.commandLineOptions().join(QLatin1Char(' ')); - m_ui->diagnosticOptionsTextEdit->document()->setPlainText(commandLineOptions); + setDiagnosticOptions(commandLineOptions); m_ui->diagnosticOptionsTextEdit->setReadOnly(config.isReadOnly()); } @@ -178,6 +189,48 @@ const ClangDiagnosticConfig &ClangDiagnosticConfigsWidget::currentConfig() const return m_diagnosticConfigsModel.configWithId(currentConfigId()); } +void ClangDiagnosticConfigsWidget::setDiagnosticOptions(const QString &options) +{ + if (options != m_ui->diagnosticOptionsTextEdit->document()->toPlainText()) { + disconnectDiagnosticOptionsChanged(); + m_ui->diagnosticOptionsTextEdit->document()->setPlainText(options); + connectDiagnosticOptionsChanged(); + } +} + +void ClangDiagnosticConfigsWidget::connectConfigChooserCurrentIndex() +{ + connect(m_ui->configChooserComboBox, + static_cast(&QComboBox::currentIndexChanged), + this, + &ClangDiagnosticConfigsWidget::onCurrentConfigChanged); +} + +void ClangDiagnosticConfigsWidget::disconnectConfigChooserCurrentIndex() +{ + disconnect(m_ui->configChooserComboBox, + static_cast(&QComboBox::currentIndexChanged), + this, + &ClangDiagnosticConfigsWidget::onCurrentConfigChanged); +} + +void ClangDiagnosticConfigsWidget::connectDiagnosticOptionsChanged() +{ + connect(m_ui->diagnosticOptionsTextEdit->document(), &QTextDocument::contentsChanged, + this, &ClangDiagnosticConfigsWidget::onDiagnosticOptionsEdited); +} + +void ClangDiagnosticConfigsWidget::disconnectDiagnosticOptionsChanged() +{ + disconnect(m_ui->diagnosticOptionsTextEdit->document(), &QTextDocument::contentsChanged, + this, &ClangDiagnosticConfigsWidget::onDiagnosticOptionsEdited); +} + +void ClangDiagnosticConfigsWidget::setConfigWithUndecoratedDisplayName(const Core::Id &id) +{ + m_configWithUndecoratedDisplayName = id; +} + Core::Id ClangDiagnosticConfigsWidget::currentConfigId() const { return Core::Id::fromSetting(m_ui->configChooserComboBox->currentData()); @@ -192,5 +245,12 @@ ClangDiagnosticConfigs ClangDiagnosticConfigsWidget::customConfigs() const }); } -} // Internal namespace +void ClangDiagnosticConfigsWidget::refresh( + const ClangDiagnosticConfigsModel &diagnosticConfigsModel, + const Core::Id &configToSelect) +{ + m_diagnosticConfigsModel = diagnosticConfigsModel; + syncWidgetsToModel(configToSelect); +} + } // CppTools namespace diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.h b/src/plugins/cpptools/clangdiagnosticconfigswidget.h index 2938afd2743..6c34792b4a1 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.h +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.h @@ -25,29 +25,38 @@ #pragma once +#include "cpptools_global.h" + #include "clangdiagnosticconfig.h" #include "clangdiagnosticconfigsmodel.h" #include namespace CppTools { -namespace Internal { namespace Ui { class ClangDiagnosticConfigsWidget; } -class ClangDiagnosticConfigsWidget : public QWidget +class CPPTOOLS_EXPORT ClangDiagnosticConfigsWidget : public QWidget { Q_OBJECT public: - explicit ClangDiagnosticConfigsWidget(const ClangDiagnosticConfigs &customConfigs, - const Core::Id &configToSelect, - QWidget *parent = 0); + explicit ClangDiagnosticConfigsWidget( + const ClangDiagnosticConfigsModel &diagnosticConfigsModel = ClangDiagnosticConfigsModel(), + const Core::Id &configToSelect = Core::Id(), + QWidget *parent = 0); + ~ClangDiagnosticConfigsWidget(); Core::Id currentConfigId() const; ClangDiagnosticConfigs customConfigs() const; - ~ClangDiagnosticConfigsWidget(); + void setConfigWithUndecoratedDisplayName(const Core::Id &id); + void refresh(const ClangDiagnosticConfigsModel &diagnosticConfigsModel, + const Core::Id &configToSelect); + +signals: + void currentConfigChanged(const Core::Id ¤tConfigId); + void customConfigsChanged(const CppTools::ClangDiagnosticConfigs &customConfigs); private slots: void onCurrentConfigChanged(int); @@ -64,10 +73,17 @@ private: bool isConfigChooserEmpty() const; const ClangDiagnosticConfig ¤tConfig() const; + void setDiagnosticOptions(const QString &options); + + void connectConfigChooserCurrentIndex(); + void disconnectConfigChooserCurrentIndex(); + void connectDiagnosticOptionsChanged(); + void disconnectDiagnosticOptionsChanged(); + private: Ui::ClangDiagnosticConfigsWidget *m_ui; ClangDiagnosticConfigsModel m_diagnosticConfigsModel; + Core::Id m_configWithUndecoratedDisplayName; }; -} // Internal namespace } // CppTools namespace diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.ui b/src/plugins/cpptools/clangdiagnosticconfigswidget.ui index 60776e5d479..94af6e17aaf 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.ui +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.ui @@ -1,12 +1,12 @@ - CppTools::Internal::ClangDiagnosticConfigsWidget - + CppTools::ClangDiagnosticConfigsWidget + 0 0 - 545 + 597 300 diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index 246080dc11a..724d45564c9 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -121,7 +121,7 @@ void CppCodeModelSettings::setClangDiagnosticConfigId(const Core::Id &configId) const ClangDiagnosticConfig CppCodeModelSettings::clangDiagnosticConfig() const { - const Internal::ClangDiagnosticConfigsModel configsModel(m_clangCustomDiagnosticConfigs); + const ClangDiagnosticConfigsModel configsModel(m_clangCustomDiagnosticConfigs); return configsModel.configWithId(clangDiagnosticConfigId()); } diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.cpp b/src/plugins/cpptools/cppcodemodelsettingspage.cpp index 95d4d933469..b86bdadb86f 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.cpp +++ b/src/plugins/cpptools/cppcodemodelsettingspage.cpp @@ -78,8 +78,9 @@ void CppCodeModelSettingsWidget::setupClangCodeModelWidgets() m_ui->activateClangCodeModelPluginHint->setVisible(!isClangActive); m_ui->clangSettingsGroupBox->setEnabled(isClangActive); + ClangDiagnosticConfigsModel diagnosticConfigsModel(m_settings->clangCustomDiagnosticConfigs()); m_clangDiagnosticConfigsWidget = new ClangDiagnosticConfigsWidget( - m_settings->clangCustomDiagnosticConfigs(), + diagnosticConfigsModel, m_settings->clangDiagnosticConfigId()); m_ui->clangSettingsGroupBox->layout()->addWidget(m_clangDiagnosticConfigsWidget); } diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.h b/src/plugins/cpptools/cppcodemodelsettingspage.h index 6462d634c96..29fbb770697 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.h +++ b/src/plugins/cpptools/cppcodemodelsettingspage.h @@ -37,10 +37,11 @@ QT_FORWARD_DECLARE_CLASS(QComboBox) QT_FORWARD_DECLARE_CLASS(QSettings) namespace CppTools { -namespace Internal { class ClangDiagnosticConfigsWidget; +namespace Internal { + namespace Ui { class CppCodeModelSettingsPage; } class CppCodeModelSettingsWidget: public QWidget From 7ef8ba082d3aeef6b0600f62db950d3f4ff9eff8 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Tue, 1 Mar 2016 13:01:20 +0100 Subject: [PATCH 8/9] Application wizard for Qt.labs.control based applications Let's make it easier to create applications with Qt labs controls. This creates a tabbed navigation style. We can add options to the wizard to create drill-down and other common navigation styles. Also an option for the controls style (material, universal, etc.). And perhaps a checkbox to include the virtual keyboard. Change-Id: I9672ab12aa616e7ce08e90eb244fad525e5ab2b2 Reviewed-by: Alessandro Portale --- .../qtlabscontrolsapplication/Page1.qml.tpl | 10 +++ .../Page1Form.ui.qml.tpl | 22 +++++ .../qmake/qtlabscontrolsapplication/app.pro | 13 +++ .../qmake/qtlabscontrolsapplication/main.cpp | 12 +++ .../qtlabscontrolsapplication/main.qml.tpl | 37 ++++++++ .../qmake/qtlabscontrolsapplication/qml.qrc | 7 ++ .../qtlabscontrolsapplication/wizard.json | 86 +++++++++++++++++++ 7 files changed, 187 insertions(+) create mode 100644 share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1.qml.tpl create mode 100644 share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1Form.ui.qml.tpl create mode 100644 share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/app.pro create mode 100644 share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.cpp create mode 100644 share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.qml.tpl create mode 100644 share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/qml.qrc create mode 100644 share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/wizard.json diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1.qml.tpl b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1.qml.tpl new file mode 100644 index 00000000000..bb8b52b439f --- /dev/null +++ b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1.qml.tpl @@ -0,0 +1,10 @@ +import QtQuick %{QtQuickVersion} + +Page1Form { + button1.onClicked: { + console.log("Button 1 clicked."); + } + button2.onClicked: { + console.log("Button 2 clicked."); + } +} diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1Form.ui.qml.tpl b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1Form.ui.qml.tpl new file mode 100644 index 00000000000..863df116c72 --- /dev/null +++ b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/Page1Form.ui.qml.tpl @@ -0,0 +1,22 @@ +import QtQuick %{QtQuickVersion} +import Qt.labs.controls %{QtLabsControlsVersion} +import QtQuick.Layouts %{QtQuickLayoutsVersion} + +Item { + property alias button1: button1 + property alias button2: button2 + + RowLayout { + anchors.centerIn: parent + + Button { + id: button1 + text: qsTr("Press Me 1") + } + + Button { + id: button2 + text: qsTr("Press Me 2") + } + } +} diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/app.pro b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/app.pro new file mode 100644 index 00000000000..d778e1cb34d --- /dev/null +++ b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/app.pro @@ -0,0 +1,13 @@ +QT += qml quick + +CONFIG += c++11 + +SOURCES += %{MainCppFileName} + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Default rules for deployment. +include(deployment.pri) diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.cpp b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.cpp new file mode 100644 index 00000000000..4142815437d --- /dev/null +++ b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.cpp @@ -0,0 +1,12 @@ +%{Cpp:LicenseTemplate}\ +%{JS: QtSupport.qtIncludes([], ["QtGui/QGuiApplication", "QtQml/QQmlApplicationEngine"])} +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QLatin1String("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.qml.tpl b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.qml.tpl new file mode 100644 index 00000000000..284a7b30dbe --- /dev/null +++ b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/main.qml.tpl @@ -0,0 +1,37 @@ +import QtQuick %{QtQuickVersion} +import QtQuick.Layouts %{QtQuickLayoutsVersion} +import Qt.labs.controls %{QtLabsControlsVersion} + +ApplicationWindow { + visible: true + width: 640 + height: 480 + title: qsTr("Hello World") + + SwipeView { + id: swipeView + anchors.fill: parent + currentIndex: tabBar.currentIndex + + Page1 { + } + + Page { + Label { + text: qsTr("Second page") + anchors.centerIn: parent + } + } + } + + footer: TabBar { + id: tabBar + currentIndex: swipeView.currentIndex + TabButton { + text: qsTr("First") + } + TabButton { + text: qsTr("Second") + } + } +} diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/qml.qrc b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/qml.qrc new file mode 100644 index 00000000000..67415af3119 --- /dev/null +++ b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/qml.qrc @@ -0,0 +1,7 @@ + + + main.qml + Page1.qml + Page1Form.ui.qml + + diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/wizard.json new file mode 100644 index 00000000000..381e0508519 --- /dev/null +++ b/share/qtcreator/templates/wizards/projects/qmake/qtlabscontrolsapplication/wizard.json @@ -0,0 +1,86 @@ +{ + "version": 1, + "supportedProjectTypes": [ "Qt4ProjectManager.Qt4Project" ], + "id": "W.QtLabsControlsApplication", + "category": "F.Application", + "trDescription": "Creates a deployable Qt Quick 2 application using Qt Labs Controls.", + "trDisplayName": "Qt Labs Controls Application", + "trDisplayCategory": "Application", + "icon": "../qtquickapplication/qml_wizard.png", + "featuresRequired": [ "QtSupport.Wizards.FeatureQt.labs.controls.1.0" ], + "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0}", + + "options": + [ + { "key": "ProFileName", "value": "%{JS: Util.fileName('%{ProjectDirectory}/%{ProjectName}', 'pro')}" }, + { "key": "MainCppFileName", "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src')}" }, + { "key": "QtQuickVersion", "value": "2.6" }, + { "key": "QtLabsControlsVersion", "value": "1.0" }, + { "key": "QtQuickDialogsVersion", "value": "1.0" }, + { "key": "QtQuickLayoutsVersion", "value": "1.0" } + ], + + "pages": + [ + { + "trDisplayName": "Project Location", + "trShortTitle": "Location", + "typeId": "Project" + }, + { + "trDisplayName": "Kit Selection", + "trShortTitle": "Kits", + "typeId": "Kits", + "enabled": "%{IsTopLevelProject}", + "data": { "projectFilePath": "%{ProFileName}" } + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "app.pro", + "target": "%{ProFileName}", + "openAsProject": true + }, + { + "source": "main.cpp", + "target": "%{MainCppFileName}" + }, + { + "source": "main.qml.tpl", + "target": "main.qml", + "openInEditor": true + }, + { + "source": "Page1.qml.tpl", + "target": "Page1.qml" + }, + { + "source": "Page1Form.ui.qml.tpl", + "target": "Page1Form.ui.qml" + }, + { + "source": "qml.qrc" + }, + { + "source": "../../../../shared/qrcdeployment.pri", + "target": "%{ProjectDirectory}/deployment.pri" + }, + { + "source": "../../git.ignore", + "target": "%{ProjectDirectory}/.gitignore", + "condition": "%{JS: !%{IsSubproject} && '%{VersionControl}' === 'G.Git'}" + } + ] + } + ] +} From c43c0163a568a5754647f50963046d465ee12a9c Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Tue, 1 Mar 2016 19:27:36 +0100 Subject: [PATCH 9/9] Add the "Dark frame" Qt Creator theme http://blog.qt.io/blog/2015/02/09/qt-creator-flat-style-ui-design-new-light-theme/ defines a dark and a light theme. We start by implementing the dark one. "Dark frame" stands for dark toolbar areas and light content areas. The name is silly, and we need to find a proper one before the release (we will most likely keep the silly name, though). Since the existing "dark" theme is much darker and has a dark content area, we don't want to simply replace that. Change-Id: If2197c4a07ae1e374be4449b6ed0383cdbe314b3 Reviewed-by: Alessandro Portale --- share/qtcreator/themes/darkframe.creatortheme | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 share/qtcreator/themes/darkframe.creatortheme diff --git a/share/qtcreator/themes/darkframe.creatortheme b/share/qtcreator/themes/darkframe.creatortheme new file mode 100644 index 00000000000..d16d22c1c99 --- /dev/null +++ b/share/qtcreator/themes/darkframe.creatortheme @@ -0,0 +1,184 @@ +[General] +ThemeName=Dark Frame +PreferredStyles=Fusion + +[Palette] +shadowBackground=ff404244 +text=ff000000 +textDisabled=55000000 +toolBarItem=b6fbfdff +toolBarItemDisabled=38fbfdff +hoverBackground=ff515151 +selectedBackground=ff151515 +normalBackground=ffffffff +alternateBackground=ff515151 +error=ffe41e25 + +[Colors] +BackgroundColorAlternate=alternateBackground +BackgroundColorDark=shadowBackground +BackgroundColorHover=hoverBackground +BackgroundColorNormal=normalBackground +BackgroundColorDisabled=ff444444 +BackgroundColorSelected=ff909090 +BadgeLabelBackgroundColorChecked=normalBackground +BadgeLabelBackgroundColorUnchecked=selectedBackground +BadgeLabelTextColorChecked=text +BadgeLabelTextColorUnchecked=text +CanceledSearchTextColor=ff0000 +ComboBoxArrowColor=toolBarItem +ComboBoxArrowColorDisabled=toolBarItemDisabled +ComboBoxTextColor=toolBarItem +DetailsButtonBackgroundColorHover=hoverBackground +DetailsWidgetBackgroundColor=ff4a4a4a +DockWidgetResizeHandleColor=shadowBackground +DoubleTabWidget1stEmptyAreaBackgroundColor=normalBackground +DoubleTabWidget1stSeparatorColor=hoverBackground +DoubleTabWidget1stTabActiveTextColor=text +DoubleTabWidget1stTabBackgroundColor=ff4a4a4a +DoubleTabWidget1stTabInactiveTextColor=textDisabled +DoubleTabWidget2ndSeparatorColor=hoverBackground +DoubleTabWidget2ndTabActiveTextColor=text +DoubleTabWidget2ndTabBackgroundColor=ff434343 +DoubleTabWidget2ndTabInactiveTextColor=textDisabled +EditorPlaceholderColor=normalBackground +FancyTabBarBackgroundColor=shadowBackground +FancyTabWidgetDisabledSelectedTextColor=toolBarItemDisabled +FancyTabWidgetDisabledUnselectedTextColor=toolBarItemDisabled +FancyTabWidgetEnabledSelectedTextColor=toolBarItem +FancyTabWidgetEnabledUnselectedTextColor=toolBarItem +FancyToolButtonHoverColor=35ffffff +FancyToolButtonSelectedColor=66000000 +FutureProgressBackgroundColor=shadowBackground +IconsBaseColor=toolBarItem +IconsDisabledColor=toolBarItemDisabled +IconsInfoColor=ff3099dc +IconsInfoToolBarColor=ff71b2db +IconsWarningColor=ffecbc1c +IconsWarningToolBarColor=fff2d76e +IconsErrorColor=ffdf4f4f +IconsErrorToolBarColor=ffdb6f71 +IconsRunColor=dda4d576 +IconsStopColor=dddb6f71 +IconsDebugColor=toolBarItem +IconsInterruptColor=dd7488db +IconsNavigationArrowsColor=ffebc322 +IconsBuildHammerHandleColor=dddd7710 +IconsBuildHammerHeadColor=dd989898 +IconsModeWelcomeActiveColor=ff80c342 +IconsModeEditActiveColor=ff99aaef +IconsModeDesignActiveColor=ffbb6000 +IconsModeDebugActiveColor=ff99aaef +IconsModeProjetcsActiveColor=ff80c342 +IconsModeAnalyzeActiveColor=ff43adee +IconsModeHelpActiveColor=fff4be04 +InfoBarBackground=ff505000 +InfoBarText=text +MenuBarEmptyAreaBackgroundColor=shadowBackground +MenuBarItemBackgroundColor=shadowBackground +MenuBarItemTextColorDisabled=toolBarItemDisabled +MenuBarItemTextColorNormal=toolBarItem +MenuItemTextColorDisabled=textDisabled +MenuItemTextColorNormal=text +MiniProjectTargetSelectorBackgroundColor=shadowBackground +MiniProjectTargetSelectorBorderColor=shadowBackground +MiniProjectTargetSelectorSummaryBackgroundColor=shadowBackground +MiniProjectTargetSelectorTextColor=text +PanelButtonToolBackgroundColorHover=hoverBackground +PanelStatusBarBackgroundColor=shadowBackground +PanelsWidgetSeparatorLineColor=0 +PanelTextColorDark=toolBarItem +PanelTextColorMid=ff666666 +PanelTextColorLight=toolBarItem +ProgressBarColorError=error +ProgressBarColorFinished=ff5aaa3c +ProgressBarColorNormal=hoverBackground +ProgressBarTitleColor=toolBarItem +SplitterColor=ff000000 +TextColorDisabled=textDisabled +TextColorError=ffff4040 +TextColorHighlight=ffff0000 +TextColorLink=ff007af4 +TextColorLinkVisited=ffa57aff +TextColorNormal=text +TodoItemTextColor=text +ToggleButtonBackgroundColor=shadowBackground +ToolBarBackgroundColor=shadowBackground +TreeViewArrowColorNormal=hoverBackground +TreeViewArrowColorSelected=text + +OutputPanes_DebugTextColor=text +OutputPanes_ErrorMessageTextColor=ffff6c6c +OutputPanes_MessageOutput=ff008787 +OutputPanes_NormalMessageTextColor=text +OutputPanes_StdErrTextColor=ffff6666 +OutputPanes_StdOutTextColor=text +OutputPanes_WarningMessageTextColor=fff3c300 +OutputPaneButtonFlashColor=error +OutputPaneToggleButtonTextColorChecked=toolBarItem +OutputPaneToggleButtonTextColorUnchecked=toolBarItem + +Debugger_LogWindow_LogInput=ff00acac +Debugger_LogWindow_LogStatus=ff00875a +Debugger_LogWindow_LogTime=ffbf0303 + +Debugger_WatchItem_ValueNormal=text +Debugger_WatchItem_ValueInvalid=textDisabled +Debugger_WatchItem_ValueChanged=ffbf0303 + +Debugger_Breakpoint_TextMarkColor=ffff4040 + +Welcome_BackgroundColorNormal=normalBackground +Welcome_Button_BorderColorNormal=ff727476 +Welcome_Button_BorderColorPressed=ff727476 +Welcome_Button_TextColorNormal=text +Welcome_Button_TextColorPressed=text +Welcome_Caption_TextColorNormal=text +Welcome_DividerColor=ffd6d6d6 +Welcome_Link_BackgroundColor=normalBackground +Welcome_Link_TextColorActive=text +Welcome_Link_TextColorNormal=text +Welcome_ProjectItem_BackgroundColorHover=ffd2d4d6 +Welcome_ProjectItem_TextColorFilepath=textDisabled +Welcome_SessionItemExpanded_BackgroundColorHover=hoverBackground +Welcome_SessionItemExpanded_BackgroundColorNormal=selectedBackground +Welcome_SessionItem_BackgroundColorHover=hoverBackground +Welcome_SessionItem_BackgroundColorNormal=normalBackground +Welcome_SideBar_BackgroundColor=normalBackground +Welcome_TextColorHeading=text +Welcome_TextColorNormal=text + +VcsBase_FileStatusUnknown_TextColor=text +VcsBase_FileAdded_TextColor=ff00ff00 +VcsBase_FileModified_TextColor=ff8ee0ff +VcsBase_FileDeleted_TextColor=fffff6c6c +VcsBase_FileRenamed_TextColor=ffffa500 + +Bookmarks_TextMarkColor=ff8080ff + +TextEditor_SearchResult_ScrollBarColor=ff00c000 +TextEditor_CurrentLine_ScrollBarColor=ffffffff + +ProjectExplorer_TaskError_TextMarkColor=ffff4040 +ProjectExplorer_TaskWarn_TextMarkColor=ffffff40 + +ClangCodeModel_Error_TextMarkColor=ffff882f +ClangCodeModel_Warning_TextMarkColor=ffceff40 + +[Flags] +ComboBoxDrawTextShadow=false +DerivePaletteFromTheme=false +DrawIndicatorBranch=true +DrawProgressBarSunken=false +DrawSearchResultWidgetFrame=false +DrawTargetSelectorBottom=false +ApplyThemePaletteGlobally=false +FlatSideBarIcons=true + +[Gradients] +DetailsWidgetHeaderGradient\1\color=0 +DetailsWidgetHeaderGradient\1\pos=1 +DetailsWidgetHeaderGradient\size=1 + +[Style] +WidgetStyle=StyleFlat