Three.js: Переход к модульной архитектуре

Созданный на 6 мая 2014  ·  153Комментарии  ·  Источник: mrdoob/three.js

Просматривать
Переход к этой архитектуре имеет свои преимущества и недостатки. Пожалуйста, поделитесь своими мыслями.

Примечание: для этого не требуется, чтобы потребители three.js использовали browserify.

Suggestion

Самый полезный комментарий

Верно, потребуется немного рефакторинга ...

Понял тебя! Поскольку за последние пару дней эта тема оживилась, я немного больше поработал над KeyframeTrack ), но скоро должно быть чем поделиться. Насколько я могу судить, все примеры продолжают работать, а минифицированная сборка меньше текущей (с использованием Rollup для создания файла UMD), так что это хорошие новости.

Все 153 Комментарий

Одно из преимуществ состоит в том, что это потребует модульной архитектуры для продолжающейся разработки three.js.

В общем стиле в node / browserify каждый файл объявляет свои зависимости вверху, а глобальные переменные рассматриваются как анти-шаблон.

Вот пример фрагмента:

// src/geometry/BoxGeometry.js
var Geometry = require('./Geometry.js');
var Vector3 = require('../core/Vector3.js');
module.exports = BoxGeometry;

function BoxGeometry() {
  // ...
}

BoxGeometry.prototype = Geometry.prototype;

Еще одно преимущество состоит в том, что потребители three.js использующие browserify, смогут выбирать те части, которые им нужны. Они могли просто импортировать Scene , BoxGeometry , PerspectiveCamera и WebGLRenderer , получить зависимости для всех этих автоматически ( Object3D т. Д.), и иметь небольшой пакет javascript, который поддерживает только тот набор функций, который им нужен.

Это можно было бы сделать так, чтобы не вносить критических изменений. На верхнем уровне мы экспортируем все классы, которые считаем частью стандартного пакета.

// src/three.js
var THREE = { rev: 101 }
module.exports = THREE

THREE.Geometry = require('./geometry/Geometry.js')
THREE.BoxGeometry = require('./geometry/BoxGeometry.js')
// ...

примечание: я не совсем нуждаюсь в зависимостях вверху в этом примере, потому что этот файл будет почти полностью содержать операторы require.

Наконец, мы бы обернули это в универсальное определение модуля, которое определяет, используется ли модульная система (node ​​/ browserify, AMD), и, если да, экспортирует ее или иным образом добавляет ее к глобальному объекту ( window ).

Давайте рассмотрим:

  • обеспечивает хороший модульный стиль
  • позволяет потребителям three.js использующим browserify, выбирать функции
  • без критических изменений

Это потребует замены системы сборки, но новая будет довольно простой.

Некоторые другие преимущества:

  • Вы можете структурировать свой код
  • Вы можете создавать / повторно использовать модули, не загрязняя глобальное пространство имен
  • Вы можете построить для производства
  • Вы можете легче отлаживать, поскольку у каждого модуля есть собственный файл, вам не нужно искать соответствующий модуль в большом файле three.js.

@ shi-314 Думаю, я немного запутался, мне кажется, что You can structure your code и You can build for production - это то, что вы можете сделать без архитектурного сдвига? Вы говорите об исходном коде three.js или о вещах, построенных с использованием three.js?

Одна практика, которую использует three.js, которая усложняет использование в средах commonjs, - это использование instanceof : https://github.com/mrdoob/three.js/blob/master/src/core/Geometry .js # L82

Это связано с тем, что в приложении вы часто получаете разные версии одной и той же библиотеки в исходном дереве, поэтому проверка instanceof не работает между разными версиями одной и той же библиотеки. Было бы хорошо подготовиться к переходу на модульную систему commonjs, чтобы заменить эти проверки instanceof проверкой функций за интерфейсом в стиле Geometry.isGeometry(geom) .

@kumavis Я говорю о вещах, построенных на three.js. Допустим, вы хотите создать свой собственный материал с помощью своих шейдеров и т. Д. На данный момент вам нужно расширить глобальный объект THREE, чтобы он оставался согласованным с остальной частью кода three.js:

THREE.MeshMyCoolMaterial = function (...) { ... }

Но если бы у нас был Browserify, вы могли бы это сделать:

var MeshLambertMaterial = require('./../MeshLambertMaterial');
var MeshMyCoolMaterial = function (...) {...}

Таким образом, ваше пространство имен остается согласованным, и вам не нужно использовать в коде THREE.MeshLambertMaterial и MeshMyCoolMaterial .

И с You can build for production я имел в виду то же самое, что вы упомянули: allows three.js consumers using browserify to pick and choose functionality .

@ shi-314 спасибо, это более понятно. Это действительно влияет на предлагаемое мной общее решение десериализации классов, определяемых потребителем:

// given that `data` is a hash of a serialized object
var ObjectClass = THREE[ data.type ]
new ObjectClass.fromJSON( data )

Это из предложенного мной рефакторинга сериализации / десериализации
https://github.com/mrdoob/three.js/pull/4621

Подобные изменения не должны влиять на производительность.

Это довольно серьезное изменение, но я тоже за него.

Некоторые другие важные преимущества:

  • Вы можете использовать опцию browserify standalone чтобы сгенерировать для вас UMD-сборку. Не нужно вручную возиться с обертками UMD.
  • Пакет может быть легко использован пользователями browserify / NPM.
  • Получение зависимостей для threejs (таких как poly2tri, color-string и т. Д.) Становится намного проще.
  • Модули, которые «не принадлежат» библиотеке рендеринга (например, векторные / математические библиотеки), могут быть извлечены как отдельные модули NPM и повторно использованы для многих других типов проектов. Одним из основных преимуществ этого является то, что отдельные модули имеют собственный репозиторий для ошибок / проблем, PR и т. Д. (Устранение проблем с ThreeJS).
  • NPM позаботится о семантическом управлении версиями за нас. например, мы можем протолкнуть критическое изменение в threejs-vecmath не беспокоясь о том, что любой нарушит код. И, с другой стороны, если мы сделаем патч или второстепенный выпуск в конкретном модуле, люди, использующие эти модули, смогут получить изменения автоматически.
  • Он упрощает упаковку и использование «дополнений», таких как EffectComposer и различных шейдеров (представьте себе npm install threejs-shader-bloom )
  • По мере извлечения модулей окончательный размер дистрибутива станет меньше и больше зависит от приложения. В конечном итоге не будет необходимости в разных «типах сборки», поскольку мы будем просто require() модулей, которые фактически использует наше приложение.

@Mrdoob и другим авторам; Если у вас нет большого опыта работы с NPM / Browserify, я бы посоветовал сделать с ним пару небольших проектов и прочувствовать его «философию». Она сильно отличается от архитектуры ThreeJS; вместо больших фреймворков он поощряет множество мелочей .

Еще одним преимуществом этого подхода является то, что может существовать экосистема модулей Three.JS с открытым исходным кодом, сторонних разработчиков, особенно шейдеров, геометрии, загрузчиков моделей и т. Д. Публикуемых через NPM или Github / Component, которые люди могут затем легко ссылаться и использовать. В настоящее время материалы распространяются посредством демонстрации, на которой люди затем «просматривают исходный код». Three.JS заслуживает лучшего!

Я думаю, что одна из моих проблем с Three.JS - это то, как быстро код становится несовместимым с текущей версией Three.JS. Еще одно преимущество перехода на что-то подобное - возможность указать конкретные версии _bits_ of Three.JS было бы очень мощным и удобным.

+1

+1 для архитектуры CommonJS / browserify, это сделает ядро ​​более легким, и расширения будут подходить, даже если они поступают от сторонних производителей.

Фрагментация three.js на маленькие модули также требует больших затрат. Текущая система допускает довольно простые сторонние надстройки (например, модули THREEx от jetienne). О простоте текущей настройки можно много сказать, поскольку системы модулей JS являются просто оболочкой для систем сборки.

Другой способ минимизировать размер сборки - это то, что делает ClojureScript. Они следуют некоторым соглашениям, позволяющим компилятору Google Closure выполнять анализ всей программы и устранять мертвый код.

+1 за недооцененную и часто упускаемую из виду элегантность простоты

+1

Фрагментация three.js на маленькие модули также требует больших затрат. Текущая система допускает довольно простые сторонние надстройки (например, модули THREEx от jetienne).

Идея здесь в том, что сборка UMD по-прежнему будет предоставляться для сред, отличных от Node. Такие плагины, как THREEx, будут работать одинаково для тех, кто зависит от ThreeJS с простыми тегами <script> .

Сложность будет заключаться в следующем: как нам require() конкретный плагин, если мы находимся в среде CommonJS? Может быть, вам поможет browserify-shim.

О простоте текущей настройки можно много сказать, поскольку системы модулей JS являются просто оболочкой для систем сборки.

Текущая система плагинов / расширений ThreeJS довольно ужасна для работы и далека от «простой» или легкой. Большинство проектов ThreeJS, как правило, используют ту или иную форму плагина или расширения, например EffectComposer, или FirstPersonControls, или загрузчик модели, или один из многих других JS-файлов, плавающих в папке examples . Прямо сейчас единственный способ зависеть от этих плагинов:

  • Загрузите текущую сборку ThreeJS
  • Скопируйте и вставьте необходимые файлы в папку vendor
  • Подключите задачи gulp / grunt для объединения и минимизации всех необходимых вам плагинов; убедитесь, что вы собрали их _в правильном порядке_, иначе все сломается. Поддерживайте этот список вручную по мере добавления дополнительных модулей.
  • Повторяйте шаги 1 и 2 каждый раз при обновлении ThreeJS; а затем вытащите волосы, когда поймете, что новый код несовместим с предыдущими версиями.

А теперь представьте, с помощью browserify вы можете сделать что-то вроде этого:

var FirstPersonControls = require('threejs-controls').FirstPersonControls;

//more granular, only requiring necessary files
var FirstPersonControls = require('threejs-controls/lib/FirstPersonControls');

Эти плагины будут require('threejs') и все, что им может понадобиться (например, фрагменты GLSL или триангуляция текста ). Управление зависимостями / версиями полностью скрыто от пользователя, и нет необходимости в вручную поддерживаемых задачах grunt / gulp concat.

Сложность будет заключаться в следующем: как нам require () конкретный плагин, если мы находимся в среде CommonJS?

Я уже немного использую CommonJS для проектов THREE.js. Это немного ручной процесс, преобразование фрагментов чужого кода в модули, и я не думаю, что будет простой способ избежать этого для устаревшего кода, который не конвертируется авторами или участниками.

Важным моментом является наличие модуля, экспортирующего весь «стандартный» объект THREE, который затем может потребоваться всем, кто хочет его расширить.

var THREE = require('three');

THREE.EffectComposer = // ... etc, remembering to include copyright notices :)

У меня это сработало очень хорошо, особенно по мере того, как проект растет, и я начинаю добавлять свои собственные шейдеры и геометрию в их собственные модули и т. Д.

Пока существует пакет npm 'threejs-full' или 'threejs-classic', это становится довольно жизнеспособным способом работы со старыми материалами Three.js в среде CommonJS, но я подозреваю, что это довольно нишевый вариант!

+1
Я считаю, что когда-то фрагментированные модули threejs доступны в npm, плагине
разработчики будут рады перейти на CommonJS env.
5 июня 2014 г. в 21:19 «Шарлотта Гор» [email protected] написала:

Сложность будет заключаться в следующем: как нам использовать () конкретный плагин, если мы
находятся в среде CommonJS?

Я уже немного использую CommonJS для проектов THREE.js. Это немного
ручного процесса, конвертируя куски чужого кода в модули
и я не думаю, что будет простой способ избежать этого для устаревшего кода
который не конвертируется авторами или участниками.

Важным моментом является наличие модуля, экспортирующего весь «стандартный»
Объект THREE, который затем может потребоваться всем, что хочет расширить
Это.

var THREE = require ('три');
THREE.EffectComposer = // ... и т.д., не забывая включать уведомления об авторских правах :)

У меня это сработало очень хорошо, особенно когда проект растет, и я
начать добавлять мои собственные шейдеры и геометрию в их собственные модули и т. д.

Если есть пакет npm 'threejs-full' или 'threejs-classic', тогда
это становится довольно жизнеспособным способом работы со старыми материалами Three.js в
Среда CommonJS, но я подозреваю, что это довольно нишевая!

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/mrdoob/three.js/issues/4776#issuecomment -45236911.

Он также может сделать шейдеры модульными, например, с помощью glslify . Тогда даже такие вещи, как создание промежуточного программного обеспечения Express, которое генерирует шейдеры по запросу, становится проще.

Несколько месяцев назад я переместил frame.js в require.js и наконец понял, как работает эта штука AMD.

Однако мне все еще нужно научиться "компилировать" это. Какой инструмент / рабочий процесс для создания three.min.js из списка модулей?

Я предпочитаю gulp.js в качестве системы сборки с плагином -browserify . Это действительно легко понять, и, на мой взгляд, код выглядит чище, чем ворчание. Проверьте это: http://travismaynard.com/writing/no-need-to-grunt-take-a-gulp-of-fresh-air : wink:

некоторые мысли: (на основе моего ограниченного опыта работы с node, npm, конечно, browserify)

  1. Я думаю, что модули node.js великолепны (это npm, modules и require () s)
  2. я думаю, что браузер тоже хорош

Тем не менее, после обсуждения в этой ветке я не уверен, что у всех было одинаковое понимание browserify (browserify, commonjs, requirejs, amd, umd в некоторой степени связаны между собой, хотя они не обязательно должны быть одним и тем же).

теперь, если вы можете немного проследить мою цепочку мыслей.

  1. JS великолепен, он быстро работает во всех браузерах.
  2. вау, теперь JS работает и на стороне сервера.
  3. это node.js, это круто, поэтому давайте закодируем что-нибудь в node.js
  4. но я не хочу писать / не могу написать все / найти что-нибудь для использования.
  5. не беспокойтесь, теперь запустите модули установки npm
  6. теперь требуются эти классные модули, чтобы мы могли их использовать.
  7. работает отлично!
  8. теперь подождите, мы только что написали на JS целую кучу вещей, которые работают на node.js
  9. разве js не предполагается в браузерах? как нам снова запустить этот код?

Вот где на сцену выходит Browserify. Что ж, технически можно использовать requireJS в браузере. Но вы хотите объединить файлы js вместе, не делая слишком много сетевых вызовов (в отличие от файловой системы require (), которые работают быстро). Там Browserify выполняет некоторые интересные вещи, такие как статический анализ, чтобы увидеть, какие модули необходимо импортировать, и создает сборки, более оптимизированные для вашего приложения. (Конечно, есть ограничения, он, вероятно, не может анализировать require ('bla' + variable)), он даже может заменять части, для которых требуется уровень эмуляции для зависимых от node.js вещей. да, он генерирует сборку js, которую я теперь могу включить в свой браузер.

Вот некоторые из вещей, которые может делать browserify https://github.com/substack/node-browserify#usage

Похоже, что пока все отлично ... но есть несколько моментов, которые, как мне показалось, стоит учесть, когда мы переходим к "архитектурному просмотру"

  • необходимо изменить образ мышления разработчиков three.js (конечно, необходимо использовать систему требуемых модулей)
  • уровень совместимости может быть построен так, что пользователи three.js могут по-прежнему использовать three.js старым способом, не используя модульные преимущества
  • чтобы иметь возможность создавать оптимизированные сборки, пользователям three.js необходимо перейти на требуемую систему.
  • новый процесс сборки, вероятно, будет включать в себя цепочку инструментов просмотра (в настоящее время мы могли бы использовать python, node.js или простое копирование и вставку и т. д.) или некоторые инструменты requireJS.
  • если мы хотим, чтобы three.js был действительно более модульным, с версией для каждого компонента, например, TrackballControls, нам нужно было бы разделить их, и это может привести к фрагментации
  • это тоже может привести к разнообразию, однако одна из сильных сторон three.js в настоящее время кажется, что это централизованная точка многих расширений.

Так что, если мы увидим это разнообразие и удобную загрузку модулей (в основном на основе экосистемы npm) вместе с настраиваемыми сборками, это здорово, тогда, возможно, стоит коротко изменить парадигму, провести рефакторинг кода и изменить нашу текущую систему сборки.

@mrdoob некоторые инструменты для просмотра перечислены здесь: https://github.com/substack/node-browserify/wiki/browserify-tools.

что касается three.min.js , вы не будете использовать минифицированный код в своем проекте. все, что вы делаете, это var three = require('three') в своем project.js а затем запускаете browserify project.js > bundle.js && uglifyjs bundle.js > bundle.min.js . примечание: вы по-прежнему можете отправить мини-код за <script src="min.js"> .

В настоящее время я упаковываю three.js с помощью

if ('undefined' === typeof(window))
  var window = global && global.window ? global.window : this
var self = window

а также

module.exports = THREE

затем я оборачиваю расширения с

module.exports = function(THREE) { /* extension-code here */ }

так что я могу потребовать это вот так:

var three = require('./wrapped-three.js')
require('./three-extension')(three)

так что это не оптимально, но я лично могу жить с этим и думать, что это не так уж плохо, хотя предложение @kumavis было бы _ огромным_ преимуществом.

но, возможно, имеет смысл разделить три и поместить все в отдельные модули, чтобы посмотреть, как это сработает.

также проверьте http://modules.gl/, который в значительной степени основан на браузере (хотя вы можете использовать каждый модуль отдельно без просмотра).

@mrdoob @ shi-314 gulp-browserify был занесен в черный список в пользу простого использования browserify напрямую (то есть через Vin-source-stream).

Такие инструменты, как grunt / gulp / etc, постоянно меняются, и вы найдете множество разных мнений. В конце концов, не имеет значения, что вы выберете или просто сделаете это с помощью специального сценария. Более важные вопросы: как пользователи будут использовать ThreeJS и какую обратную совместимость вы хотите поддерживать?

Поразмыслив еще немного, я думаю, что будет действительно очень сложно разделить все на модули без полного рефакторинга фреймворка и его архитектуры. Вот некоторые проблемы:

  • Весь код пространства имен должен быть изменен на CommonJS exports / require. Это довольно масштабное мероприятие, и в нем будет много уродливых ../../../math/Vector2 и т. Д.
  • В идеальном мире библиотека была бы фрагментированной, поэтому three-scene было бы отделено от three-lights и т. Д. Затем вы можете редактировать каждый пакет отдельно. Такая фрагментация кажется нереальной для такого большого фреймворка, как ThreeJS, и его было бы сложно поддерживать.
  • Если мы _не__ фрагментируем фреймворк на крошечные компоненты, то семантическое управление версиями станет кошмаром. Небольшое критическое изменение в любом месте фреймворка потребует увеличения основной версии для всего этого. А использование API было бы довольно некрасивым: require('three/src/math/Vector2')

Мое предложение? В дальнейшем мы рассматриваем две вещи:

  1. Начните с малого; извлеките несколько важных и многоразовых функций, таких как Vector / Quaternion, преобразование цветов, триангуляция и т. д. Эти вещи являются хорошими кандидатами для NPM, поскольку они полезны за пределами возможностей ThreeJS. У них также может быть собственный набор тестов, управление версиями и отслеживание проблем.
  2. Когда в ThreeJS необходимо добавить новый код, например новую функцию или зависимость (например, poly2tri / Tess2), подумайте о том, чтобы вывести его как отдельный модуль и в зависимости от него через NPM.

Мне бы хотелось, чтобы все было разделено на модули, но я не уверен в подходе, который реалистичен для ThreeJS. Может быть, кому-нибудь стоит поэкспериментировать с вилкой, чтобы увидеть, насколько это возможно.

Спасибо за объяснения, ребята!

Я боюсь усложнять вещи для людей, которые только начинают. Заставлять их изучать этот браузер / модули может быть не очень хорошей идеей ...

Придется согласиться с

С предварительно скомпилированной сборкой UMD ( browserify --umd ) в репозитории нет изменений в рабочем процессе для существующих разработчиков.

@mrdoob Идея системы управления зависимостями проста. Чтение десятков сообщений о вариантах и ​​системах сборки может быть утомительным, но в конечном итоге текущая система не является устойчивой. Каждый раз, когда один файл зависит от другого, это охота и поиск, который должен выполнить любой новый разработчик, чтобы найти ссылку. С помощью browserify зависимость является явной и есть путь к файлу.

@repsac Система зависимостей должна сделать Three более доступными для пользователей других языков, поскольку она избегает глобальной области видимости, кошмаров порядка загрузки и следует парадигме, аналогичной другим популярным языкам. var foo = require('./foo'); (слабо) сродни using foo; в C # или import foo; Java

Мне бы хотелось, чтобы все было разделено на модули, но я не уверен в подходе, который реалистичен для ThreeJS. Может быть, кому-нибудь стоит поэкспериментировать с вилкой, чтобы увидеть, насколько это возможно.

Я думаю, что это действительно правильный путь. Сделайте работу, покажите, как она работает.

И использование API было бы довольно ugly: require('three/src/math/Vector2')

В качестве эксперимента я просто преобразовал «начало работы» из трех документов в этот новый модульный подход. Я могу представить, что ссылок будет много, если только люди не будут достаточно строго разделять свой код на крошечные модули.

Основным преимуществом этого будет то, что размер результирующей сборки будет крошечной долей размера полного Three.js, потому что вы включите только те вещи, на которые здесь конкретно ссылаются, а затем то, от чего эти вещи зависят.

Я предполагаю, что ссылка на все необходимые зависимости (и установка их всех по отдельности) на практике может оказаться слишком ужасной.

Если вы явно нацеливаетесь на мобильные устройства, то этот высокодетализированный подход был бы идеальным, но на самом деле я подозреваю, что нам понадобятся пакеты, которые экспортируют все ТРИ api, которые будут работать как обычно, а затем меньшие пакеты, которые инкапсулируют всю бонусную геометрию, все рендереры, вся математика, все материалы и т. д., а затем вплоть до уровня отдельного модуля, чтобы разработчики могли решать сами.

И да, программирование для Интернета - это боль.

Во всяком случае, к эксперименту ...

Установите наши зависимости ..

npm install three-scene three-perspective-camera three-webgl-renderer three-cube-geometry three-mesh-basic-material three-mesh three-raf

Напишите наш код ...

// import our dependencies..
var Scene = require('three-scene'),
  Camera = require('three-perspective-camera'),
  Renderer = require('three-webgl-renderer'),
  CubeGeometry = require('three-cube-geometry'),
  MeshBasicMaterial = require('three-mesh-basic-material'),
  Mesh = require('three-mesh'),
  requestAnimationFrame = require('three-raf');

// set up our scene...
var scene = new Scene();
var camera = new Camera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new Renderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// create the cube...
var geometry = new CubeGeometry(1, 1, 1);
var material = new MeshBasicMaterial({color: 0x00ff00});
var cube = new Mesh(geometry, material);
scene.add(cube);
// position the camera...
camera.position.z = 5;
// animate the cube..
var render = function () {
  requestAnimationFrame(render);
  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  renderer.render(scene, camera);
};
// begin!
render();

затем создайте наш файл

browserify entry.js -o scripts/hello-world.js

затем включите его на нашу страницу

<script src="/scripts/hello-world.js" type="text/javascript"></script>

Я предполагаю, что ссылка на все необходимые зависимости (и установка их всех по отдельности) на практике может оказаться слишком ужасной.

Конечному пользователю не обязательно использовать browserify в своем проекте, чтобы Three мог использовать browserify для управления своей кодовой базой. Три могут быть представлены как глобальный THREE как сейчас ... включить файл сборки и запустить с ним.

@repsac @mrdoob изменения будут обратно совместимы, так что текущим пользователям не нужно ничего менять, если они не хотят. Эти предложения призваны улучшить долгосрочную ремонтопригодность и долговечность разросшейся и монолитной кодовой базы ThreeJS. Такие вещи, как зависимости и управление версиями, могут показаться головной болью для непосвященных, но они прекрасны для тех, кто разрабатывает инструменты, фреймворки, плагины и крупномасштабные веб-сайты на основе ThreeJS.

т.е. код конечного пользователя может по-прежнему выглядеть так же, и examples вообще не нужно менять:

<script src="three.min.js"></script>

<script>
var renderer = new THREE.WebGLRenderer();
</script>

Для более амбициозных разработчиков, которые ищут модульную сборку, _или_ для тех, кто хочет разрабатывать долгосрочные решения на основе ThreeJS (то есть и использовать преимущества управления версиями / зависимостями), это может выглядеть примерно так:
npm install three-vecmath --save

Затем в коде:

var Vector2 = require('three-vecmath').Vector2;

//.. do something with Vector2

И, кроме того, это позволяет людям использовать такие вещи, как векторная математика ThreeJS, преобразование цветов, триангуляция и т. Д. За пределами ThreeJS.

Несмотря на то, что я думаю, что беспорядок с require () - плохая идея и плохой компромисс, было бы еще хуже, если бы пользователи увидели два разных типа кода three.js, сообщая пользователям, что один - это модная модульная система, а другой - более простая (но второсортная) модульная система.

@erno Я думаю, вы упустили суть, three.js будет организовано внутренней структурой модуля, но это используется для создания файла сборки, не отличающегося от текущей настройки.

Основное преимущество - улучшение опыта разработки и поддержки three.js .

@kumavis - нет @erno на самом деле этого не пропустил, но я понял (*), что он подчеркивает, что если three.js иногда используется через require, а иногда нет, это может сбивать с толку. Например, кто-то смотрит как на три источника, так и на некоторые сторонние примеры, и обнаруживает различия в том, как все это есть и работает.

(*) мы говорили об этом сегодня на irc.

Я думаю, что это своего рода верная точка зрения, но я не уверен, получится ли / как это сработает в конце - действительно ли проблема с использованием модуля и сборки. Но, безусловно, кажется, стоит подумать, и в целом мне показалось хорошо, что этот вопрос был тщательно рассмотрен здесь, спасибо за информацию и мнения, так далекие с моей стороны.

@antont Понятно . Люди предлагали здесь множество различных подходов, я предполагал, что мы в основном предоставим документацию для использования на верхнем уровне (вытаскивая все из THREE ), но другие могут создавать примеры, которые не будут следовать этому, и это может привести к некоторой путанице. И это серьезная проблема.

Думаю, меня немного смутил язык.

и other - более простой (но второсортный) вариант модульной системы.

Это просто относится к файлу сборки, да?

В моем понимании да. Не могу представить, что еще, но может чего-то упустить.

antont, kumavis: В предложениях здесь говорилось о том, чтобы предоставить конечным пользователям также код в стиле require (), см. например. Последний комментарий mattdesl.

«Для более амбициозных разработчиков, которые ищут модульную сборку, или для тех, кто хочет разрабатывать долгосрочные решения на основе ThreeJS (т.е. и использовать преимущества управления версиями / зависимостями) [...]»

один из способов получить более оптимизированные сборки - это на самом деле иметь сценарий, который автоматически определяет ваши зависимости и производит необходимые модули.

прямо сейчас bower & browserify справляется с требованиями, но это не единственные решения. Я не знаю, есть ли другие готовые проекты с открытым исходным кодом, которые делают это (возможно, как ng-dependencies), но я писал такие инструменты раньше, и я думаю, что будут другие подходы к решению этих проблем.

Компилятор закрытия Google может быть таким инструментом?

Может ли это помочь со стороны пользователя?
http://marcinwieprzkowicz.github.io/three.js-builder/

это довольно интересно @erichlof :) Интересно, создал ли https://github.com/marcinwieprzkowicz/three.js-builder/blob/gh-pages/threejs-src/r66/modules.json

Одна практика, которую использует three.js, которая усложняет использование в средах commonjs, - это использование instanceof: https://github.com/mrdoob/three.js/blob/master/src/core/Geometry.js#L82

Это связано с тем, что в приложении вы часто получаете разные версии одной и той же библиотеки в исходном дереве, поэтому проверка instanceof не работает между разными версиями одной и той же библиотеки. Было бы хорошо подготовиться к переходу на модульную систему commonjs, чтобы заменить эти проверки instanceof проверкой функций за интерфейсом стиля Geometry.isGeometry (geom).

в git / three.js / src:

grep -r instanceof . | wc -l 
164

в git / three.js / examples:

grep -r instanceof . | wc -l 
216

так что всего 380 использований instanceof в three.js. Что было бы лучшей реализацией в качестве замены?

Недавно я добавил свойство type которое можно использовать для замены большинства из них.

Недавно я добавил свойство типа, которое можно использовать для замены большинства из них.

отлично! Подготовим PR.

Пример того, как это делается в другой популярной и большой JS-библиотеке, можно найти на https://github.com/facebook/react . Кодовая база структурирована с использованием модульной системы на основе стиля узлов (которая реализует браузер), но построена для выпуска с использованием grunt. Это решение гибко для трех вариантов использования.

  1. Чистый потребитель Three.js, пишущий ванильный JS, по-прежнему может просто использовать файл сборки, как всегда. Для этого варианта использования изменений нет.
  2. Потребитель Three.js, использующий browserify, может объявить Three.js как зависимость в проекте и только специфические зависимости require . Преимущества правильного управления зависимостями хорошо задокументированы.
  3. Внесение вклада в Three.js теперь должно быть проще, поскольку зависимости между компонентами явно задокументированы.

Я провел небольшое исследование ...

Вчера я собрал (довольно глупый) скрипт, который преобразует исходный код Three.js для использования операторов CommonJS require() для объявления зависимостей между файлами. Просто чтобы посмотреть, что происходит ... Это:

  1. В итоге получаются довольно нелепые утверждения типа require (из WebGLRenderer):

var THREE = require('../Three.js'); require('../math/Color.js'); require('../math/Frustum.js'); require('../math/Matrix4.js'); require('../math/Vector3.js'); require('./webgl/WebGLExtensions.js'); require('./webgl/plugins/ShadowMapPlugin.js'); require('./webgl/plugins/SpritePlugin.js'); require('./webgl/plugins/LensFlarePlugin.js'); require('../core/BufferGeometry.js'); require('./WebGLRenderTargetCube.js'); require('../materials/MeshFaceMaterial.js'); require('../objects/Mesh.js'); require('../objects/PointCloud.js'); require('../objects/Line.js'); require('../cameras/Camera.js'); require('../objects/SkinnedMesh.js'); require('../scenes/Scene.js'); require('../objects/Group.js'); require('../lights/Light.js'); require('../objects/Sprite.js'); require('../objects/LensFlare.js'); require('../math/Matrix3.js'); require('../core/Geometry.js'); require('../extras/objects/ImmediateRenderObject.js'); require('../materials/MeshDepthMaterial.js'); require('../materials/MeshNormalMaterial.js'); require('../materials/MeshBasicMaterial.js'); require('../materials/MeshLambertMaterial.js'); require('../materials/MeshPhongMaterial.js'); require('../materials/LineBasicMaterial.js'); require('../materials/LineDashedMaterial.js'); require('../materials/PointCloudMaterial.js'); require('./shaders/ShaderLib.js'); require('./shaders/UniformsUtils.js'); require('../scenes/FogExp2.js'); require('./webgl/WebGLProgram.js'); require('../materials/ShaderMaterial.js'); require('../scenes/Fog.js'); require('../lights/SpotLight.js'); require('../lights/DirectionalLight.js'); require('../textures/CubeTexture.js'); require('../lights/AmbientLight.js'); require('../lights/PointLight.js'); require('../lights/HemisphereLight.js'); require('../math/Math.js'); require('../textures/DataTexture.js'); require('../textures/CompressedTexture.js');

Нам понадобится серьезный рефакторинг, возможно, разделение WebGLRenderer (и т.п.) на несколько модулей (atm файл имеет длину более 6000 строк).

  1. Нам нужно найти решение для блоков GLSL. Atm эти файлы компилируются в THREE.ShaderChunk во время компиляции, а затем в THREE.ShaderLib во время выполнения (объединение с другими массивами THREE.ShaderChunk s), что довольно сложно сделать, используя только просмотр. Я предполагаю, что для этого потребуется преобразование просмотра, которое делает то же самое.

React.js использует commoner для поиска своих модулей, не обращаясь к ним по пути к файлу. Возможно, мы могли бы сделать то же самое, а также определить собственные правила, которые позволят нам преобразовать файлы require GLSL в синтаксис JS.

@rasteiner, вы можете быть очень рады узнать о https://github.com/stackgl/glslify , это происходит от растущего семейства http://stack.gl

У меня был достаточный опыт работы с модулями и «unixy» подходом за последние пару месяцев, и сейчас я думаю, что слишком поздно, и рефакторинг threejs для модульности или модулей npm был бы нереальной целью.

Вот что я сейчас делаю, чтобы решить проблему модульности / возможности повторного использования:

  • Я помещаю в NPM несколько многоразовых шейдеров для размытия / fxaa / и т. Д. Видеть это:
    https://www.npmjs.org/package/three-shader-fxaa (который использует независимый от движка glsl-fxaa)
  • повторно используемые компоненты, такие как OrbitController и EffectComposer, также публикуются по мере необходимости. Например:
    https://www.npmjs.org/package/three-orbit-controls
  • вместо того, чтобы зависеть от «трех», эти модули экспортируют функцию, которая принимает ТРИ и возвращает служебный класс. Таким образом, его можно использовать с глобальными threejs или commonjs.
  • управление версиями - это боль. Я пытаюсь согласовать свои основные версии с номерами выпусков threejs. Каждая новая версия threejs вносит множество критических изменений, поэтому с ней придется обращаться осторожно.
  • модули, которые имеют дело с математикой, должны просто работать с массивами и использовать модульные gl-vec3, gl-mat4 и т. д., поэтому они являются общими и полезными за пределами threejs. Затем пользователям threejs просто нужно будет обрабатывать упаковку / разворачивание в массивы. См. Verlet-system, simpleify-path и т. Д.
  • если мне нужна действительно модульная или крошечная функция webGL, я буду использовать stackgl / glslify в будущем. Они также могут работать в ThreeJS, если вы сбрасываете состояние GL. Например: https://www.npmjs.org/package/gl-sprite-text.

Мои новые проекты, как правило, используют «тройку» в npm просто для того, чтобы начать работу. Было бы здорово, если бы ThreeJS официально опубликовал сборку в npm, используя теги версий, которые совпадают с номерами выпусков.

PS: для тех, кто заинтересован в использовании многоразовых / модульных шейдеров в своем рабочем процессе:
https://gist.github.com/mattdesl/b04c90306ee0d2a412ab

отправлено из моего Айфона

20 ноября 2014 г. в 7:42 aaron [email protected] написал:

@rasteiner, вы можете быть очень рады узнать о https://github.com/stackgl/glslify , это происходит от растущего семейства http://stack.gl

-
Ответьте на это письмо напрямую или просмотрите его на GitHub.

В случае, если это поможет другим, которые могут искать, как использовать Three.js с browserify, и наткнуться на эту тему, я только что настроил его сам - это использовать browserify-shim .

Следуя разделу README на _ "Иногда вы будете а) открывать глобальные переменные через глобальные" _, я включил отдельный тег скрипта для Three.js и настроил его для отображения глобальной переменной THREE.

А потом мне самому пришлось решить, как включить дополнительные функции, такие как ColladaLoader, OrbitControls и т. Д. Я сделал это так:

Из package.json:

    "browser": {
        "three": "bower_components/threejs/build/three.js"
    },
    "browserify-shim": "browserify-shim-config.js",
    "browserify": {
        "transform": [ "browserify-shim" ]
    }

browserify-shim-config.js:

    module.exports = {
        "three": { exports: "global:THREE" },
        "./vendor/threejs-extras/ColladaLoader.js": { depends: {"three": null}, exports: "global:THREE.ColladaLoader" },
        "./vendor/threejs-extras/OrbitControls.js": { depends: {"three": null}, exports: "global:THREE.OrbitControls" }
    };

Затем в моем собственном скрипте main.js:

    require('../vendor/threejs-extras/ColladaLoader.js');
    require('../vendor/threejs-extras/OrbitControls.js');

    var loader = new THREE.ColladaLoader(),
        controls = new THREE.OrbitControls(camera);
...

Browserify требует перестройки всего скрипта при изменении даже байтов. Однажды я использую browserify для упаковки проекта, для которого требуется THREE.js, затем требуется более двух секунд, чтобы построить границу и блокировать livereload каждый раз, когда я вношу изменения. Это слишком расстраивает.

Обычно вы используете watchify во время разработки с livereload. Тот строит пакет постепенно.

watchify у меня не работает. Когда я изменяю файл и сохраняю его, livereload watchify и beefy обслуживает старую / кешированную версию. Понятия не имею, почему это происходит. К счастью, browserify уже работает довольно хорошо.

@ChiChou Передайте --noparse=three для просмотра. Это увеличивает шаг пакета browserify с 1000 мс до 500 мс на моей машине, что достаточно прилично для мгновенного ощущения обратной связи.

@rasteiner Я хочу еще раз поблагодарить вас за ваше неформальное исследование взаимозависимостей three.js. Хотя этот огромный список deps представляет собой некрасивый код, на самом деле это безобразие присутствует как есть, просто невидимо. Сила Browserify в том, что он требует от нас проветривания грязного белья и поиска менее запутанных систем.

В Three.js есть много мест, где мы берем какой-то объект, воспринимаем его тип и выполняем различные задачи на основе этого типа. В большинстве случаев этот зависящий от типа код может быть перемещен в сам тип, и нам не нужно будет понимать все возможные типы, с которыми мы работаем.

Ниже приводится сокращенный пример из WebGLRenderer :

if ( texture instanceof THREE.DataTexture ) {
  // ...
} else if ( texture instanceof THREE.CompressedTexture ) {
  // ...
} else { // regular Texture (image, video, canvas)
  // ...
}

должно быть больше формы

texture.processTexImage( _gl, mipmaps, otherData )

Позвольте типу определять, как себя вести. Это также позволяет потребителю библиотеки использовать новые типы текстур, о которых мы не думали. Эта структура должна уменьшить взаимозависимость.

Я думаю, что переход на архитектуру просмотра с браузером - определенно правильный путь. Сборка UMD упростит использование THREE.js. Это также позволит нам разделить WebGLRenderer на несколько файлов, потому что сейчас он выглядит довольно монолитным и пугающим.

Я запустил ветку, над перемещением которой работаю в настоящее время: https://github.com/coballast/three.js/tree/browserify-build-system

Пожалуйста, дай мне знать, что ты думаешь.

Вот изменения @coballast .

Похоже, вы используете подход автоматического преобразования с вашим файлом browserifyify.js , и я думаю, что это правильный путь.

Одна вещь, которую мы все еще не обсуждали, - это то, как лучше всего перевести эту большую, постоянно меняющуюся библиотеку на просмотр. Вы можете внести изменения, а затем открыть PR, но он сразу устареет. Вот что убедительно в автоматизированном подходе.

Если мы можем:

  • предоставить скрипт преобразования three.js src (например, ваш browserifyify.js )
  • предоставить документ, объясняющий, как работает процесс преобразования
  • предоставить документ, объясняющий, как работает новая система сборки
  • запустить тесты после конвертации
  • не включать какие-либо изменения в существующие файлы, которые могут привести к конфликту слияния

... тогда мы можем превратить это в кнопочное преобразование, которое будет работать в обозримом будущем. Эта простота позволяет осуществить это мечтательное представление о фундаментальном архитектурном переходе к такому большому проекту, когда победят идеологические аргументы.

@coballast с этой целью, я бы удалил изменения в src / Three.js, если он работает точно так же.

Примечание: не просто вернуться, но и удалить эти изменения из истории ветки с помощью новой ветки или принудительного нажатия.

@coballast Интересно, было бы three.js , а внешней утилитой, которую вы указываете на каталог разработки three.js , и она преобразует исходный код файлы, добавляет сценарий сборки и запускает тесты.

@kumavis Я согласен оставить каталог src в покое. Я думаю, что нужно сделать так, чтобы утилита написала дублированный каталог с кодом commonjs, и мы можем протестировать и запустить сборку браузера оттуда, чтобы убедиться, что все примеры работают, прежде чем мы попытаемся сделать что-нибудь серьезное.

Здесь также есть интересная возможность написать некоторые элементы статического анализа, которые будут автоматически проверять и обеспечивать согласованный стиль во всей кодовой базе.

@coballast - великолепно звучит.
Существует множество инструментов для автоматического переписывания кода, например escodegen . Необходимо убедиться, что мы сохраняем комментарии и т. Д.
Хотите запустить репозиторий утилиты threejs-преобразования?

@coballast при этом важно поддерживать

@kumavis Считай, что это сделано. Я очень хочу, чтобы это произошло. Это только один из двух проектов, над которыми я работаю в данный момент.

@kumavis @mrdoob Некоторые из обсуждаемых здесь вопросов, похоже,

Мне будет любопытно посмотреть, что выдает эта утилита ^^

@coballast связывает репо, чтобы мы могли отслеживать его, даже если на данный момент он просто пуст. Мы можем строить оттуда.

https://github.com/coballast/threejs-browserify-conversion-utility

Код запутался, скоро уберу.

вот так! : ракета:

Теперь у меня есть утилита в состоянии, когда она генерирует browserify src и без проблем построит ее. Я обновлю репо с инструкциями, как это сделать самостоятельно. На данный момент примеры не работают. Чтобы это исправить, необходимо решить несколько проблем. Я добавлю их в репо, если кто-то захочет засучить рукава и выручить.

@coballast да, пожалуйста,

Возникли серьезные проблемы. См. № 6241

Вот мой анализ того, что должно произойти, чтобы это сработало: https://github.com/coballast/threejs-browserify-conversion-utility/issues/9#issuecomment -83147463

Browserify, по крайней мере, является избыточным (конъюнктурным) транспортом из-за его дизайна. Это делает его использование завышенным (у кого-нибудь тарифный план?) И медленным.

Простое решение этой проблемы - отделить документ от кода библиотеки, что повлечет за собой два клиентских файла, а не один. Это обычная практика для js на стороне клиента.

Если вначале browserify неисправен и сам требует исправления, я не понимаю, почему его вообще следует рассматривать для улучшения чего-либо, не говоря уже о том, чтобы что-то вроде threejs.

@spaesani Потому что данные для threejs все равно нужно загружать. Если мы разделим threejs на более мелкие модули и позволим автоматизированной системе сборки выбрать то, что ей нужно для одного приложения, на самом деле большинство трех приложений будет легче.

Если по какой-то причине вы все еще хотите отделить «документ от кода библиотеки», вы все равно можете сделать это и использовать предварительно созданную версию, как это делаем мы сейчас. Вы даже можете разделить приложение, созданное с помощью браузера, на отдельные модули, используя флаги

Browserify - это просто способ использовать проверенный в боях API определения модулей (CommonJS) в браузере. Это значительно упростило бы разработку плагинов threejs и повысило бы ясность кода и, следовательно, производительность, это позволило бы нам интегрироваться в более крупную экосистему (npm), где код по своей сути поддерживается большим количеством людей, при этом сохраняя целостность через систему управления версиями (подумайте о семейство stackgl ), и это даже не заставит людей перейти на CommonJS, если они этого не захотят.

Конечно, есть и недостатки, но они не те, о которых вы упомянули.

three.js и three.min.js можно кэшировать для экономии на транспорте (данных) через прокси, обычное мобильное решение или кеширующий браузер.
В тот момент, когда вы выбираете и объединяете код threejs с кодом документа, кеширование становится невозможным.
Если браузер позволяет

Смежные вопросы

filharvey picture filharvey  ·  3Комментарии

yqrashawn picture yqrashawn  ·  3Комментарии

clawconduce picture clawconduce  ·  3Комментарии

zsitro picture zsitro  ·  3Комментарии

fuzihaofzh picture fuzihaofzh  ·  3Комментарии